From 89fc84bf14a56384ba0e3cec8c41af20ce3ab950 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Thu, 3 Nov 2022 19:27:51 +0100 Subject: [PATCH] Added a basic mesh shader example --- README.md | 4 + data/shaders/glsl/meshshader/meshshader.frag | 19 ++ .../glsl/meshshader/meshshader.frag.spv | Bin 0 -> 512 bytes data/shaders/glsl/meshshader/meshshader.mesh | 36 +++ .../glsl/meshshader/meshshader.mesh.spv | Bin 0 -> 2460 bytes examples/CMakeLists.txt | 1 + examples/meshshader/meshshader.cpp | 238 ++++++++++++++++++ 7 files changed, 298 insertions(+) create mode 100644 data/shaders/glsl/meshshader/meshshader.frag create mode 100644 data/shaders/glsl/meshshader/meshshader.frag.spv create mode 100644 data/shaders/glsl/meshshader/meshshader.mesh create mode 100644 data/shaders/glsl/meshshader/meshshader.mesh.spv create mode 100644 examples/meshshader/meshshader.cpp diff --git a/README.md b/README.md index c2b3f7d9..cca56828 100644 --- a/README.md +++ b/README.md @@ -439,6 +439,10 @@ Shows usage of the VK_KHR_dynamic_rendering extension, which simplifies the rend #### [Graphics pipeline library (VK_EXT_graphics_pipeline_library)](./examples/graphicspipelinelibrary)
Uses the graphics pipeline library extensions to improve run-time pipeline creation. Instead of creating the whole pipeline at once, this sample pre builds shared pipeline parts like like vertex input state and fragment output state. These are then used to create full pipelines at runtime, reducing build times and possible hick-ups. +#### [Mesh shaders (VK_EXT_mesh_shader)](./examples/meshshaders)
+ +Basic sample demonstrating how to use the mesh shading pipeline as a replacement for the traditional vertex pipeline. + ### Misc #### [Vulkan Gears](examples/gears/) diff --git a/data/shaders/glsl/meshshader/meshshader.frag b/data/shaders/glsl/meshshader/meshshader.frag new file mode 100644 index 00000000..efb12b1c --- /dev/null +++ b/data/shaders/glsl/meshshader/meshshader.frag @@ -0,0 +1,19 @@ +/* Copyright (c) 2021, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 450 + +layout (location = 0) in VertexInput { + vec4 color; +} vertexInput; + +layout(location = 0) out vec4 FragColor; + + +void main() +{ + FragColor = vertexInput.color; +} \ No newline at end of file diff --git a/data/shaders/glsl/meshshader/meshshader.frag.spv b/data/shaders/glsl/meshshader/meshshader.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..115402ae949ec8ff4cf44685159f521057477a24 GIT binary patch literal 512 zcmYk2OH0F05QT3O)7Dn|P}E(CxD<*D6+sk4FbLfV?u!-?w3Qge-{-G#BlvwugiM%w z?wmRExQU~KPFQP&Ug(CcP}w*{p~bZy+9hVQyV>-;c$^-e9H|(DxD=`x2FgMGb^g3G zv5s%xBXsf|Jm!nQZ;BGwrapnO<|dy%U9VnNId{1pc)!St#nuwum5QaB#?1UC*N{>JroP<)KAuZ6BLTT%`2PY_1Bc)XQi;S~sxAGyf9aHhE zw*Wu50WJW<4Ipt(Nc^eP=UMLrLpK`F%scNKzHi4)X?l4!nVCusC3DF)$@n>%Oeen{ zPrxe4_SU1FyWc(B>7;`vJA)_9-L#(^2A_i3*xcS+9SnC@FJHN2=9@_=FJSBl*?&`G z2D{VDx(4QvCFCknL+Z#pwd5Q{%nO+Z6@Le@YWTlUQ_ei7@jviO{7qy3MPP<{jm=j^ zPRd2Sn59h48{~9Gd&El2o+RJaACO(iZQNw9-+P+2hFOp4E3o9)yVKiE+vvdTJ<8G_ zPfcWO<`Xkz=16&$`3y?8j7I z#Oz1xc01dAc~{2v+wFsxb1aQ<=WvLul@1((87s->#Kx07*dOlg4{<@u zIj-I6wR?TET;JU&KgGY?GVi(u?!5i1!$C&r!+zH6K5nNQ-Q9^^bNM@mx7lko+Z){x z%45SypC#+OBk$O!jI8q+{&J4~7n=q1t|QMoo6OTUuZBb|vH$0JpObm|<~>IuPb~5p z#lDC&m^tpS;pKAlBOzsl$9Xlsct6z^I)M9vX>XK@d%7ryr$^ZC0`MeOf9`fnjV<{v}LwPwUm z7FhWH#+ZKwzW3+Mo@s&8d)ILeV~V^g+L-g(XA$k4R1xRYUqa%Ztoc6TV_*Gqx%d3f z@IM&)w^pcX&+~{m^3=xd^%2^-a)P^pEl*BxBX+64Cu0j#@Oy6!&vO~AALqG(Z4YtJ zqwju+Mcps3qgUvNeObf;w|B&@W5+BJcVBLRR}gzR(@nHk%yg@Wi93^it-!pOHS8(G z8pieO1?C-#-$8uBzl-gDtPr)=Jw%T0-&zfN^8W!Tqn*WF z_y3J&`R#Zq*XFLla7X!y=D#EE?hW|v>xlvn&Z&aq{s!kXw)oG~F~{$+dE$}xF1Ftp z_dN2>V2elIS!{E~Z!*2JiUofeTkI$LxHBIjV&{=H?#`Y37+FF*gJK;qxl!i=_GFz; z@WtHUsI!V~9p{ZYVsfL-MeNBspW=(T>rv-3Z0mSmQAbQ}aIRsy*T#b*=57b)D{MKF q^N6|Q!TGx2O!g9U*PYqjyN%Ql--|o7j?@rw?@HhOd%+f8LtX-_60bJ^ literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d24c6fc0..0e8a1271 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -111,6 +111,7 @@ set(EXAMPLES inlineuniformblocks inputattachments instancing + meshshader multisampling multithreading multiview diff --git a/examples/meshshader/meshshader.cpp b/examples/meshshader/meshshader.cpp new file mode 100644 index 00000000..e8667ceb --- /dev/null +++ b/examples/meshshader/meshshader.cpp @@ -0,0 +1,238 @@ +/* + * Vulkan Example - Using mesh shaders + * + * Copyright (C) 2022 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ + +#include "vulkanexamplebase.h" +#include "VulkanglTFModel.h" +#include "meshoptimizer/meshoptimizer.h" + +#define ENABLE_VALIDATION false + +class VulkanExample : public VulkanExampleBase +{ +public: + struct UniformData { + glm::mat4 projection; + glm::mat4 model; + glm::mat4 view; + } uniformData; + vks::Buffer uniformBuffer; + + uint32_t indexCount; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; + VkDescriptorSetLayout descriptorSetLayout; + + PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXT; + + VkPhysicalDeviceMeshShaderFeaturesEXT enabledMeshShaderFeatures{}; + + VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) + { + title = "Mesh shaders"; + timerSpeed *= 0.25f; + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -2.0f)); + + // Extension require at least Vulkan 1.1 + apiVersion = VK_API_VERSION_1_1; + + // Extensions required by mesh shading + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + + // Required by VK_KHR_spirv_1_4 + enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + uniformBuffer.destroy(); + } + + void getEnabledFeatures() + { + enabledMeshShaderFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV; + enabledMeshShaderFeatures.meshShader = VK_TRUE; + enabledMeshShaderFeatures.taskShader = VK_FALSE; + + deviceCreatepNextChain = &enabledMeshShaderFeatures; + } + + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 1.0f } };; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdDrawMeshTasksEXT(drawCmdBuffers[i], 1, 1, 1); + + drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void setupDescriptors() + { + // Pool + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(static_cast(poolSizes.size()), poolSizes.data(), 1); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_MESH_BIT_EXT, 0), + }; + VkDescriptorSetLayoutCreateInfo descriptorLayoutInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutInfo, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + std::vector modelWriteDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &uniformBuffer.descriptor), + }; + vkUpdateDescriptorSets(device, static_cast(modelWriteDescriptorSets.size()), modelWriteDescriptorSets.data(), 0, nullptr); + } + + void preparePipelines() + { + // Layout + VkPipelineLayoutCreateInfo pipelineLayoutInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + // Pipeline + VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationState = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportState = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables); + std::array shaderStages; + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + + // Mesh shading doesn't require vertex input state + pipelineCI.pInputAssemblyState = nullptr; + pipelineCI.pVertexInputState = nullptr; + + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = static_cast(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); + + shaderStages[0] = loadShader(getShadersPath() + "meshshader/meshshader.mesh.spv", VK_SHADER_STAGE_MESH_BIT_EXT); + shaderStages[1] = loadShader(getShadersPath() + "meshshader/meshshader.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + } + + // Prepare and initialize uniform buffer containing shader uniforms + void prepareUniformBuffers() + { + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &uniformBuffer, sizeof(UniformData))); + VK_CHECK_RESULT(uniformBuffer.map()); + updateUniformBuffers(); + } + + void updateUniformBuffers() + { + uniformData.projection = camera.matrices.perspective; + uniformData.view = camera.matrices.view; + uniformData.model = glm::mat4(1.0f); + memcpy(uniformBuffer.mapped, &uniformData, sizeof(UniformData)); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + } + + void prepare() + { + VulkanExampleBase::prepare(); + + // Get the function pointer of the mesh shader drawing funtion + vkCmdDrawMeshTasksEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT")); + + prepareUniformBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + virtual void render() + { + if (!prepared) + return; + draw(); + } + + virtual void viewChanged() + { + updateUniformBuffers(); + } +}; + +VULKAN_EXAMPLE_MAIN()