From 82747df5404b49897ae12744821975f8b033a0a0 Mon Sep 17 00:00:00 2001 From: daemyung jang Date: Fri, 21 Aug 2020 16:27:06 +0900 Subject: [PATCH] Add Order Independent Transparency example (#755) * Add Order Independent Transparency example * Update README.md * Add copyright at Order Independent Transparency example * Disable the validation by default --- README.md | 4 + data/shaders/glsl/oit/color.frag | 56 ++ data/shaders/glsl/oit/color.frag.spv | Bin 0 -> 3664 bytes data/shaders/glsl/oit/color.vert | 7 + data/shaders/glsl/oit/color.vert.spv | Bin 0 -> 1152 bytes data/shaders/glsl/oit/geometry.frag | 49 ++ data/shaders/glsl/oit/geometry.frag.spv | Bin 0 -> 2156 bytes data/shaders/glsl/oit/geometry.vert | 21 + data/shaders/glsl/oit/geometry.vert.spv | Bin 0 -> 1656 bytes examples/CMakeLists.txt | 1 + examples/oit/oit.cpp | 684 ++++++++++++++++++++++++ 11 files changed, 822 insertions(+) create mode 100644 data/shaders/glsl/oit/color.frag create mode 100644 data/shaders/glsl/oit/color.frag.spv create mode 100644 data/shaders/glsl/oit/color.vert create mode 100644 data/shaders/glsl/oit/color.vert.spv create mode 100644 data/shaders/glsl/oit/geometry.frag create mode 100644 data/shaders/glsl/oit/geometry.frag.spv create mode 100644 data/shaders/glsl/oit/geometry.vert create mode 100644 data/shaders/glsl/oit/geometry.vert.spv create mode 100644 examples/oit/oit.cpp diff --git a/README.md b/README.md index cc78f441..fb532fad 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,10 @@ Generating a complete mip-chain at runtime instead of loading it from a file, by Capturing and saving an image after a scene has been rendered using blits to copy the last swapchain image from optimal device to host local linear memory, so that it can be stored into a ppm image. +#### [08 - Order Independent Transparency](examples/oit) + +Implements order independent transparency based on the linked list. This example use the storage buffer, the image load and store operations and atomic operations. + ### Performance #### [01 - Multi threaded command buffer generation](examples/multithreading/) diff --git a/data/shaders/glsl/oit/color.frag b/data/shaders/glsl/oit/color.frag new file mode 100644 index 00000000..06a53080 --- /dev/null +++ b/data/shaders/glsl/oit/color.frag @@ -0,0 +1,56 @@ +#version 450 + +#define MAX_FRAGMENT_COUNT 128 + +struct Node +{ + vec4 color; + float depth; + uint next; +}; + +layout (location = 0) out vec4 outFragColor; + +layout (set = 0, binding = 0, r32ui) uniform uimage2D headIndexImage; + +layout (set = 0, binding = 1) buffer LinkedListSBO +{ + Node nodes[]; +}; + +void main() +{ + Node fragments[MAX_FRAGMENT_COUNT]; + int count = 0; + + uint nodeIdx = imageLoad(headIndexImage, ivec2(gl_FragCoord.xy)).r; + + while (nodeIdx != 0xffffffff && count < MAX_FRAGMENT_COUNT) + { + fragments[count] = nodes[nodeIdx]; + nodeIdx = fragments[count].next; + ++count; + } + + // Do the insertion sort + for (uint i = 1; i < count; ++i) + { + Node insert = fragments[i]; + uint j = i; + while (j > 0 && insert.depth > fragments[j - 1].depth) + { + fragments[j] = fragments[j-1]; + --j; + } + fragments[j] = insert; + } + + // Do blending + vec4 color = vec4(0.025, 0.025, 0.025, 1.0f); + for (int i = 0; i < count; ++i) + { + color = mix(color, fragments[i].color, fragments[i].color.a); + } + + outFragColor = color; +} \ No newline at end of file diff --git a/data/shaders/glsl/oit/color.frag.spv b/data/shaders/glsl/oit/color.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..8b5d3670266302c14dad9571c676694a218ada26 GIT binary patch literal 3664 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 TA`dvezw{>dHt;{1`l9GBM_+vN literal 0 HcmV?d00001 diff --git a/data/shaders/glsl/oit/geometry.vert b/data/shaders/glsl/oit/geometry.vert new file mode 100644 index 00000000..4ed12046 --- /dev/null +++ b/data/shaders/glsl/oit/geometry.vert @@ -0,0 +1,21 @@ +#version 450 + +layout (location = 0) in vec3 inPos; + +layout (set = 0, binding = 0) uniform RenderPassUBO +{ + mat4 projection; + mat4 view; +} renderPassUBO; + +layout (set = 0, binding = 1) uniform ObjectUBO +{ + mat4 model; + vec4 color; +} objectUBO; + +void main() +{ + mat4 PVM = renderPassUBO.projection * renderPassUBO.view * objectUBO.model; + gl_Position = PVM * vec4(inPos, 1.0); +} diff --git a/data/shaders/glsl/oit/geometry.vert.spv b/data/shaders/glsl/oit/geometry.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..a7d75996acb42664a8445e44bba7732445e7eddb GIT binary patch literal 1656 zcmZ9M*-jKu5QYy7GvWp!vZx3RC@v^^p~l3xo4^HzQ6jenp<~(%(`05`;+3zUkK|K% zW8(L9_aUukQe9Plt^J?QG=`Utg)kIG`P~WSnFzxmA)E}YLa%RbZ?5$R`)haa6)9t( zQ6!o(9w_IGAP2iyS9=LrK^pvq(Z5J+l2OlT5<5Yxz4MA#i&-JONxS=Luf5yvzkR-i z->PuTaM;T~rh9`dXF`h#^PT@WOTQSm$7yu$zaC8@GvHhAEsuRm%({boKkcCHX`Vgz z@=o4ETYDaz*J_=UfPLt^Yp1=Pv^Pk<;)0gAwz0gQ70b5ZCjql>foI*pcJ?jBrJ{_< z5wRDY?C@pQAMAGb(xaG68~fDh9L3~Ljy>`!*IAd5=nZ_i@idVQp3z?}(0?&+?@W(<%D{=UOHv%Q13Tl5#fJnkmPo;V}=_1z*CXE-5i>4U&$Fjk1BrRsVt&KUPSA0-{LdqvNl@`ca;(vw#%iGxWQGpD zm%hDbd5)sR^b?-PD_?vb-?`d~@l)uyY8 zn7my^+o#MK30JU(5OMSMS1Qas%)N$$@coJX*RZU0_I#xQSSAzHxc|L0@CH5NDg? V`^fWc#Jlr-+*RMaKit>@createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.renderPass, + sizeof(renderPassUBO))); + + VK_CHECK_RESULT(uniformBuffers.renderPass.map()); + + // This example has many object and the information of objects will be stored in one buffer. + // This buffer will be used for the uniform buffer dynamic. + // So we need to calculate a object uniform buffer size based on minUniformBufferOffsetAlignment. + objectUniformBufferSize = + (sizeof(objectUBO) + deviceProperties.limits.minUniformBufferOffsetAlignment) & ~(deviceProperties.limits.minUniformBufferOffsetAlignment - 1); + + // Create an uniform buffer for objects. + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &uniformBuffers.objects, + objectUniformBufferSize * (SPHERE_COUNT + CUBE_COUNT))); + + VK_CHECK_RESULT(uniformBuffers.objects.map()); + + // Set up the scene. + uint8_t* objectUniformBufferData = static_cast(uniformBuffers.objects.mapped); + assert(SPHERE_COUNT == 5 * 5 * 5); + for (int i = 0; i != 5; i++) + { + for (int j = 0; j != 5; j++) + { + for (int k = 0; k != 5; k++) + { + auto T = glm::translate(glm::mat4(1.0f), glm::vec3(i - 2, j - 2, k - 2)); + auto S = glm::scale(glm::mat4(1.0f), glm::vec3(0.3f)); + objectUBO.model = T * S; + objectUBO.color = glm::vec4(1.0f, 0.0f, 0.0f, 0.5f); + memcpy(objectUniformBufferData, &objectUBO, sizeof(objectUBO)); + objectUniformBufferData += objectUniformBufferSize; + } + } + } + for (auto i = 0; i != CUBE_COUNT; ++i) + { + auto T = glm::translate(glm::mat4(1.0f), glm::vec3(3.0f * i - 1.5f, 0.0f, 0.0f)); + auto S = glm::scale(glm::mat4(1.0f), glm::vec3(0.2f)); + objectUBO.model = T * S; + objectUBO.color = glm::vec4(0.0f, 0.0f, 1.0f, 0.5f); + memcpy(objectUniformBufferData, &objectUBO, sizeof(objectUBO)); + objectUniformBufferData += objectUniformBufferSize; + } + } + + void prepareGeometryPass() + { + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + + // Geometry render pass doesn't need any output attachment. + auto renderPassInfo = vks::initializers::renderPassCreateInfo(); + renderPassInfo.attachmentCount = 0; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpassDescription; + + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &geometryPass.renderPass)); + + // Geometry framebuffer doesn't need any output attachment. + VkFramebufferCreateInfo fbufCreateInfo = vks::initializers::framebufferCreateInfo(); + fbufCreateInfo.renderPass = geometryPass.renderPass; + fbufCreateInfo.attachmentCount = 0; + fbufCreateInfo.width = width; + fbufCreateInfo.height = height; + fbufCreateInfo.layers = 1; + + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &geometryPass.framebuffer)); + + // Create a buffer for GeometrySBO + // Using the device memory will be best but I will use the host visible buffer to make this example simple. + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &geometryPass.geometry, + sizeof(geometrySBO))); + + VK_CHECK_RESULT(geometryPass.geometry.map()); + + // Set up GeometrySBO data. + geometrySBO.count = 0; + geometrySBO.maxNodeCount = NODE_COUNT * width * height; + memcpy(geometryPass.geometry.mapped, &geometrySBO, sizeof(geometrySBO)); + + // Create a texture for HeadIndex. + // This image will track the head index of each fragment. + geometryPass.headIndex.device = vulkanDevice; + + VkImageCreateInfo imageInfo = vks::initializers::imageCreateInfo(); + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = VK_FORMAT_R32_UINT; + imageInfo.extent.width = width; + imageInfo.extent.height = height; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + + VK_CHECK_RESULT(vkCreateImage(device, &imageInfo, nullptr, &geometryPass.headIndex.image)); + + geometryPass.headIndex.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, geometryPass.headIndex.image, &memReqs); + + VkMemoryAllocateInfo memAlloc = vks::initializers::memoryAllocateInfo(); + memAlloc.allocationSize = memReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &geometryPass.headIndex.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device, geometryPass.headIndex.image, geometryPass.headIndex.deviceMemory, 0)); + + VkImageViewCreateInfo imageViewInfo = vks::initializers::imageViewCreateInfo(); + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewInfo.format = VK_FORMAT_R32_UINT; + imageViewInfo.flags = 0; + imageViewInfo.image = geometryPass.headIndex.image; + imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewInfo.subresourceRange.baseMipLevel = 0; + imageViewInfo.subresourceRange.levelCount = 1; + imageViewInfo.subresourceRange.baseArrayLayer = 0; + imageViewInfo.subresourceRange.layerCount = 1; + + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewInfo, nullptr, &geometryPass.headIndex.view)); + + geometryPass.headIndex.width = width; + geometryPass.headIndex.height = height; + geometryPass.headIndex.mipLevels = 1; + geometryPass.headIndex.layerCount = 1; + geometryPass.headIndex.descriptor.imageView = geometryPass.headIndex.view; + geometryPass.headIndex.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + geometryPass.headIndex.sampler = VK_NULL_HANDLE; + + // Create a buffer for LinkedListSBO + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &geometryPass.linkedList, + sizeof(Node) * geometrySBO.maxNodeCount)); + + VK_CHECK_RESULT(geometryPass.linkedList.map()); + + // Change HeadInex image's layout from UNDEFINED to GENERAL + auto cmdBufAllocInfo = vks::initializers::commandBufferAllocateInfo(cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); + + VkCommandBuffer cmdBuf; + VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocInfo, &cmdBuf)); + + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuf, &cmdBufInfo)); + + auto barrier = vks::initializers::imageMemoryBarrier(); + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.image = geometryPass.headIndex.image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + + VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuf)); + + auto submitInfo = vks::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &cmdBuf; + + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VK_CHECK_RESULT(vkQueueWaitIdle(queue)); + } + + void setupDescriptorSetLayout() + { + // Create a geometry descriptor set layout. + std::vector setLayoutBindings = { + // RenderPassUBO + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0), + // ObjectUBO + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 1), + // AtomicSBO + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 2), + // headIndexImage + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + VK_SHADER_STAGE_FRAGMENT_BIT, + 3), + // LinkedListSBO + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 4), + }; + + auto descriptorLayoutCreateInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCreateInfo, nullptr, &descriptorSetLayouts.geometry)); + + // Create a geometry pipeline layout. + auto pipelineLayoutCreateInfo = + vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.geometry, 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.geometry)); + + // Create a color descriptor set layout. + setLayoutBindings = { + // headIndexImage + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + VK_SHADER_STAGE_FRAGMENT_BIT, + 0), + // LinkedListSBO + vks::initializers::descriptorSetLayoutBinding( + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_SHADER_STAGE_FRAGMENT_BIT, + 1), + }; + + descriptorLayoutCreateInfo = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayoutCreateInfo, nullptr, &descriptorSetLayouts.color)); + + // Create a color pipeline layout. + pipelineLayoutCreateInfo = + vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayouts.color, 1); + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayouts.color)); + } + + void preparePipelines() + { + 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_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + VkPipelineColorBlendStateCreateInfo colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(0, nullptr); + VkPipelineDepthStencilStateCreateInfo depthStencilState = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_FALSE, VK_FALSE, 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; + + // Create a geometry pipeline. + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.geometry, geometryPass.renderPass); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = shaderStages.size(); + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position }); + + shaderStages[0] = loadShader(getShadersPath() + "oit/geometry.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "oit/geometry.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.geometry)); + + // Create a color pipeline. + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + colorBlendState = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.color, renderPass); + pipelineCI.pInputAssemblyState = &inputAssemblyState; + pipelineCI.pRasterizationState = &rasterizationState; + pipelineCI.pColorBlendState = &colorBlendState; + pipelineCI.pMultisampleState = &multisampleState; + pipelineCI.pViewportState = &viewportState; + pipelineCI.pDepthStencilState = &depthStencilState; + pipelineCI.pDynamicState = &dynamicState; + pipelineCI.stageCount = shaderStages.size(); + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = &vertexInputInfo; + + shaderStages[0] = loadShader(getShadersPath() + "oit/color.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); + shaderStages[1] = loadShader(getShadersPath() + "oit/color.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); + rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; + rasterizationState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.color)); + } + + void setupDescriptorPool() + { + std::vector poolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3), + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2), + }; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = + vks::initializers::descriptorPoolCreateInfo( + poolSizes.size(), + poolSizes.data(), + 2); + + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + } + + void setupDescriptorSets() + { + // Update a geometry descriptor set + VkDescriptorSetAllocateInfo allocInfo = + vks::initializers::descriptorSetAllocateInfo( + descriptorPool, + &descriptorSetLayouts.geometry, + 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.geometry)); + + std::vector writeDescriptorSets = { + // Binding 0: RenderPassUBO + vks::initializers::writeDescriptorSet( + descriptorSets.geometry, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 0, + &uniformBuffers.renderPass.descriptor), + // Binding 1: ObjectUBO + vks::initializers::writeDescriptorSet( + descriptorSets.geometry, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + 1, + &uniformBuffers.objects.descriptor), + // Binding 2: GeometrySBO + vks::initializers::writeDescriptorSet( + descriptorSets.geometry, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 2, + &geometryPass.geometry.descriptor), + // Binding 3: headIndexImage + vks::initializers::writeDescriptorSet( + descriptorSets.geometry, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + 3, + &geometryPass.headIndex.descriptor), + // Binding 4: LinkedListSBO + vks::initializers::writeDescriptorSet( + descriptorSets.geometry, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 4, + &geometryPass.linkedList.descriptor) + }; + + vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); + + // Update a color descriptor set. + allocInfo = + vks::initializers::descriptorSetAllocateInfo( + descriptorPool, + &descriptorSetLayouts.color, + 1); + + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.color)); + + writeDescriptorSets = { + // Binding 0: headIndexImage + vks::initializers::writeDescriptorSet( + descriptorSets.color, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + 0, + &geometryPass.headIndex.descriptor), + // Binding 1: LinkedListSBO + vks::initializers::writeDescriptorSet( + descriptorSets.color, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 1, + &geometryPass.linkedList.descriptor) + }; + + vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL); + } + + void buildCommandBuffers() + { + if (resized) + return; + + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) + { + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + // Update dynamic viewport state + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + // Update dynamic scissor state + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + VkClearColorValue clearColor; + clearColor.uint32[0] = 0xffffffff; + + VkImageSubresourceRange subresRange = {}; + + subresRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresRange.levelCount = 1; + subresRange.layerCount = 1; + + vkCmdClearColorImage(drawCmdBuffers[i], geometryPass.headIndex.image, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresRange); + + // Begin the geometry render pass + renderPassBeginInfo.renderPass = geometryPass.renderPass; + renderPassBeginInfo.framebuffer = geometryPass.framebuffer; + renderPassBeginInfo.clearValueCount = 0; + renderPassBeginInfo.pClearValues = nullptr; + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.geometry); + uint32_t dynamicOffset = 0; + models.sphere.bindBuffers(drawCmdBuffers[i]); + for (auto j = 0; j != SPHERE_COUNT; ++j) + { + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.geometry, 0, 1, &descriptorSets.geometry, 1, &dynamicOffset); + models.sphere.draw(drawCmdBuffers[i]); + dynamicOffset += objectUniformBufferSize; + } + models.cube.bindBuffers(drawCmdBuffers[i]); + for (auto j = 0; j != CUBE_COUNT; ++j) + { + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.geometry, 0, 1, &descriptorSets.geometry, 1, &dynamicOffset); + models.cube.draw(drawCmdBuffers[i]); + dynamicOffset += objectUniformBufferSize; + } + vkCmdEndRenderPass(drawCmdBuffers[i]); + + // Make a pipeline barrier to guarantee the geometry pass is done + vkCmdPipelineBarrier(drawCmdBuffers[i], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 0, nullptr); + + // Begin the color render pass + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.framebuffer = frameBuffers[i]; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.color); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.color, 0, 1, &descriptorSets.color, 0, nullptr); + vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); + drawUI(drawCmdBuffers[i]); + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void updateUniformBuffers() + { + renderPassUBO.projection = camera.matrices.perspective; + renderPassUBO.view = camera.matrices.view; + memcpy(uniformBuffers.renderPass.mapped, &renderPassUBO, sizeof(renderPassUBO)); + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + + // Clear previous geometry pass data + memset(geometryPass.geometry.mapped, 0, sizeof(uint32_t)); + + // Command buffer to be submitted to the queue + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + + // Submit to queue + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + + VulkanExampleBase::submitFrame(); + } + + void destroyGeometryPass() + { + vkDestroyRenderPass(device, geometryPass.renderPass, nullptr); + vkDestroyFramebuffer(device, geometryPass.framebuffer, nullptr); + geometryPass.geometry.destroy(); + geometryPass.headIndex.destroy(); + geometryPass.linkedList.destroy(); + } + +private: + VkDeviceSize objectUniformBufferSize; +}; + +VULKAN_EXAMPLE_MAIN() \ No newline at end of file