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 00000000..115402ae Binary files /dev/null and b/data/shaders/glsl/meshshader/meshshader.frag.spv differ diff --git a/data/shaders/glsl/meshshader/meshshader.mesh b/data/shaders/glsl/meshshader/meshshader.mesh new file mode 100644 index 00000000..d75f7dba --- /dev/null +++ b/data/shaders/glsl/meshshader/meshshader.mesh @@ -0,0 +1,36 @@ +/* Copyright (c) 2021, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 450 +#extension GL_EXT_mesh_shader : require + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +layout(triangles, max_vertices = 3, max_primitives = 1) out; + +layout(location = 0) out VertexOutput +{ + vec4 color; +} vertexOutput[]; + +void main() +{ + SetMeshOutputsEXT(3, 1); + mat4 mvp = ubo.projection * ubo.view * ubo.model; + gl_MeshVerticesEXT[0].gl_Position = mvp * vec4( 0.0, -1.0, 0.0, 1.0); + gl_MeshVerticesEXT[1].gl_Position = mvp * vec4(-1.0, 1.0, 0.0, 1.0); + gl_MeshVerticesEXT[2].gl_Position = mvp * vec4( 1.0, 1.0, 0.0, 1.0); + vertexOutput[0].color = vec4(0.0, 1.0, 0.0, 1.0); + vertexOutput[1].color = vec4(0.0, 0.0, 1.0, 1.0); + vertexOutput[2].color = vec4(1.0, 0.0, 0.0, 1.0); + gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2); +} diff --git a/data/shaders/glsl/meshshader/meshshader.mesh.spv b/data/shaders/glsl/meshshader/meshshader.mesh.spv new file mode 100644 index 00000000..d68603c6 Binary files /dev/null and b/data/shaders/glsl/meshshader/meshshader.mesh.spv differ 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()