/* * Vulkan Example - Hardware accelerated ray tracing example for doing reflections * * Renders a complex scene doing recursion inside the shaders for creating reflections * * Copyright (C) 2019-2020 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ #include "VulkanRaytracingSample.h" #include "VulkanglTFModel.h" class VulkanExample : public VulkanRaytracingSample { public: AccelerationStructure bottomLevelAS{}; AccelerationStructure topLevelAS{}; std::vector shaderGroups{}; struct ShaderBindingTables { ShaderBindingTable raygen; ShaderBindingTable miss; ShaderBindingTable hit; } shaderBindingTables; struct UniformData { glm::mat4 viewInverse; glm::mat4 projInverse; glm::vec4 lightPos; int32_t vertexSize; } uniformData; vks::Buffer ubo; VkPipeline pipeline; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout; vkglTF::Model scene; // This sample is derived from an extended base class that saves most of the ray tracing setup boiler plate VulkanExample() : VulkanRaytracingSample() { title = "Ray tracing reflections"; settings.overlay = false; timerSpeed *= 0.5f; camera.rotationSpeed *= 0.25f; camera.type = Camera::CameraType::firstperson; camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); camera.setRotation(glm::vec3(0.0f, 0.0f, 0.0f)); camera.setTranslation(glm::vec3(0.0f, 0.5f, -2.0f)); enableExtensions(); } ~VulkanExample() { vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); deleteStorageImage(); deleteAccelerationStructure(bottomLevelAS); deleteAccelerationStructure(topLevelAS); shaderBindingTables.raygen.destroy(); shaderBindingTables.miss.destroy(); shaderBindingTables.hit.destroy(); ubo.destroy(); } /* Create the bottom level acceleration structure contains the scene's actual geometry (vertices, triangles) */ void createBottomLevelAccelerationStructure() { // Instead of a simple triangle, we'll be loading a more complex scene for this example // The shaders are accessing the vertex and index buffers of the scene, so the proper usage flag has to be set on the vertex and index buffers for the scene vkglTF::memoryPropertyFlags = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; scene.loadFromFile(getAssetPath() + "models/reflection_scene.gltf", vulkanDevice, queue, glTFLoadingFlags); VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{}; VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{}; vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.vertices.buffer); indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.indices.buffer); uint32_t numTriangles = static_cast(scene.indices.count) / 3; uint32_t maxVertex = scene.vertices.count; // Build VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR(); accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; accelerationStructureGeometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; accelerationStructureGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; accelerationStructureGeometry.geometry.triangles.vertexData = vertexBufferDeviceAddress; accelerationStructureGeometry.geometry.triangles.maxVertex = maxVertex; accelerationStructureGeometry.geometry.triangles.vertexStride = sizeof(vkglTF::Vertex); accelerationStructureGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; accelerationStructureGeometry.geometry.triangles.indexData = indexBufferDeviceAddress; accelerationStructureGeometry.geometry.triangles.transformData.deviceAddress = 0; accelerationStructureGeometry.geometry.triangles.transformData.hostAddress = nullptr; // Get size info VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationStructureBuildGeometryInfo.geometryCount = 1; accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR(); vkGetAccelerationStructureBuildSizesKHR( device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &accelerationStructureBuildGeometryInfo, &numTriangles, &accelerationStructureBuildSizesInfo); createAccelerationStructure(bottomLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, accelerationStructureBuildSizesInfo); // Create a small scratch buffer used during build of the bottom level acceleration structure ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; accelerationBuildGeometryInfo.dstAccelerationStructure = bottomLevelAS.handle; accelerationBuildGeometryInfo.geometryCount = 1; accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; accelerationStructureBuildRangeInfo.primitiveCount = numTriangles; accelerationStructureBuildRangeInfo.primitiveOffset = 0; accelerationStructureBuildRangeInfo.firstVertex = 0; accelerationStructureBuildRangeInfo.transformOffset = 0; std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; if (accelerationStructureFeatures.accelerationStructureHostCommands) { // Implementation supports building acceleration structure building on host vkBuildAccelerationStructuresKHR( device, VK_NULL_HANDLE, 1, &accelerationBuildGeometryInfo, accelerationBuildStructureRangeInfos.data()); } else { // Acceleration structure needs to be build on the device VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); vkCmdBuildAccelerationStructuresKHR( commandBuffer, 1, &accelerationBuildGeometryInfo, accelerationBuildStructureRangeInfos.data()); vulkanDevice->flushCommandBuffer(commandBuffer, queue); } deleteScratchBuffer(scratchBuffer); } /* The top level acceleration structure contains the scene's object instances */ void createTopLevelAccelerationStructure() { VkTransformMatrixKHR transformMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }; VkAccelerationStructureInstanceKHR instance{}; instance.transform = transformMatrix; instance.instanceCustomIndex = 0; instance.mask = 0xFF; instance.instanceShaderBindingTableRecordOffset = 0; instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; instance.accelerationStructureReference = bottomLevelAS.deviceAddress; // Buffer for instance data vks::Buffer instancesBuffer; VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &instancesBuffer, sizeof(VkAccelerationStructureInstanceKHR), &instance)); VkDeviceOrHostAddressConstKHR instanceDataDeviceAddress{}; instanceDataDeviceAddress.deviceAddress = getBufferDeviceAddress(instancesBuffer.buffer); VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR(); accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; accelerationStructureGeometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; accelerationStructureGeometry.geometry.instances.arrayOfPointers = VK_FALSE; accelerationStructureGeometry.geometry.instances.data = instanceDataDeviceAddress; // Get size info VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationStructureBuildGeometryInfo.geometryCount = 1; accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; uint32_t primitive_count = 1; VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR(); vkGetAccelerationStructureBuildSizesKHR( device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &accelerationStructureBuildGeometryInfo, &primitive_count, &accelerationStructureBuildSizesInfo); createAccelerationStructure(topLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, accelerationStructureBuildSizesInfo); // Create a small scratch buffer used during build of the top level acceleration structure ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; accelerationBuildGeometryInfo.dstAccelerationStructure = topLevelAS.handle; accelerationBuildGeometryInfo.geometryCount = 1; accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; accelerationStructureBuildRangeInfo.primitiveCount = 1; accelerationStructureBuildRangeInfo.primitiveOffset = 0; accelerationStructureBuildRangeInfo.firstVertex = 0; accelerationStructureBuildRangeInfo.transformOffset = 0; std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; if (accelerationStructureFeatures.accelerationStructureHostCommands) { // Implementation supports building acceleration structure building on host vkBuildAccelerationStructuresKHR( device, VK_NULL_HANDLE, 1, &accelerationBuildGeometryInfo, accelerationBuildStructureRangeInfos.data()); } else { // Acceleration structure needs to be build on the device VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); vkCmdBuildAccelerationStructuresKHR( commandBuffer, 1, &accelerationBuildGeometryInfo, accelerationBuildStructureRangeInfos.data()); vulkanDevice->flushCommandBuffer(commandBuffer, queue); } deleteScratchBuffer(scratchBuffer); instancesBuffer.destroy(); } /* Create the Shader Binding Tables that binds the programs and top-level acceleration structure SBT Layout used in this sample: /-----------\ | raygen | |-----------| | miss | |-----------| | hit | \-----------/ */ void createShaderBindingTables() { const uint32_t handleSize = rayTracingPipelineProperties.shaderGroupHandleSize; const uint32_t handleSizeAligned = vks::tools::alignedSize(rayTracingPipelineProperties.shaderGroupHandleSize, rayTracingPipelineProperties.shaderGroupHandleAlignment); const uint32_t groupCount = static_cast(shaderGroups.size()); const uint32_t sbtSize = groupCount * handleSizeAligned; std::vector shaderHandleStorage(sbtSize); VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesKHR(device, pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data())); createShaderBindingTable(shaderBindingTables.raygen, 1); createShaderBindingTable(shaderBindingTables.miss, 1); createShaderBindingTable(shaderBindingTables.hit, 1); // Copy handles memcpy(shaderBindingTables.raygen.mapped, shaderHandleStorage.data(), handleSize); memcpy(shaderBindingTables.miss.mapped, shaderHandleStorage.data() + handleSizeAligned, handleSize); memcpy(shaderBindingTables.hit.mapped, shaderHandleStorage.data() + handleSizeAligned * 2, handleSize); } /* Create the descriptor sets used for the ray tracing dispatch */ void createDescriptorSets() { std::vector poolSizes = { { VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1 }, { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2 } }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSet)); VkWriteDescriptorSetAccelerationStructureKHR descriptorAccelerationStructureInfo = vks::initializers::writeDescriptorSetAccelerationStructureKHR(); descriptorAccelerationStructureInfo.accelerationStructureCount = 1; descriptorAccelerationStructureInfo.pAccelerationStructures = &topLevelAS.handle; VkWriteDescriptorSet accelerationStructureWrite{}; accelerationStructureWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; // The specialized acceleration structure descriptor has to be chained accelerationStructureWrite.pNext = &descriptorAccelerationStructureInfo; accelerationStructureWrite.dstSet = descriptorSet; accelerationStructureWrite.dstBinding = 0; accelerationStructureWrite.descriptorCount = 1; accelerationStructureWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; VkDescriptorImageInfo storageImageDescriptor{ VK_NULL_HANDLE, storageImage.view, VK_IMAGE_LAYOUT_GENERAL }; VkDescriptorBufferInfo vertexBufferDescriptor{ scene.vertices.buffer, 0, VK_WHOLE_SIZE }; VkDescriptorBufferInfo indexBufferDescriptor{ scene.indices.buffer, 0, VK_WHOLE_SIZE }; std::vector writeDescriptorSets = { // Binding 0: Top level acceleration structure accelerationStructureWrite, // Binding 1: Ray tracing result image vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor), // Binding 2: Uniform data vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &ubo.descriptor), // Binding 3: Scene vertex buffer vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &vertexBufferDescriptor), // Binding 4: Scene index buffer vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4, &indexBufferDescriptor), }; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, VK_NULL_HANDLE); } /* Create our ray tracing pipeline */ void createRayTracingPipeline() { std::vector setLayoutBindings = { // Binding 0: Acceleration structure vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 0), // Binding 1: Storage image vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_RAYGEN_BIT_KHR, 1), // Binding 2: Uniform buffer vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR, 2), // Binding 3: Vertex buffer vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 3), // Binding 4: Index buffer vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, 4), }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout)); VkPipelineLayoutCreateInfo pPipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCI, nullptr, &pipelineLayout)); /* Setup ray tracing shader groups */ std::vector shaderStages; // Ray generation group { shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_KHR)); // Pass recursion depth for reflections to ray generation shader via specialization constant VkSpecializationMapEntry specializationMapEntry = vks::initializers::specializationMapEntry(0, 0, sizeof(uint32_t)); uint32_t maxRecursion = 4; VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(maxRecursion), &maxRecursion); shaderStages.back().pSpecializationInfo = &specializationInfo; VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; shaderGroup.generalShader = static_cast(shaderStages.size()) - 1; shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR; shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; shaderGroups.push_back(shaderGroup); } // Miss group { shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_KHR)); VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; shaderGroup.generalShader = static_cast(shaderStages.size()) - 1; shaderGroup.closestHitShader = VK_SHADER_UNUSED_KHR; shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; shaderGroups.push_back(shaderGroup); } // Closest hit group { shaderStages.push_back(loadShader(getShadersPath() + "raytracingreflections/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR)); VkRayTracingShaderGroupCreateInfoKHR shaderGroup{}; shaderGroup.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; shaderGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR; shaderGroup.generalShader = VK_SHADER_UNUSED_KHR; shaderGroup.closestHitShader = static_cast(shaderStages.size()) - 1; shaderGroup.anyHitShader = VK_SHADER_UNUSED_KHR; shaderGroup.intersectionShader = VK_SHADER_UNUSED_KHR; shaderGroups.push_back(shaderGroup); } VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCI = vks::initializers::rayTracingPipelineCreateInfoKHR(); rayTracingPipelineCI.stageCount = static_cast(shaderStages.size()); rayTracingPipelineCI.pStages = shaderStages.data(); rayTracingPipelineCI.groupCount = static_cast(shaderGroups.size()); rayTracingPipelineCI.pGroups = shaderGroups.data(); rayTracingPipelineCI.maxPipelineRayRecursionDepth = 4; rayTracingPipelineCI.layout = pipelineLayout; VK_CHECK_RESULT(vkCreateRayTracingPipelinesKHR(device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &rayTracingPipelineCI, nullptr, &pipeline)); } /* Create the uniform buffer used to pass matrices to the ray tracing ray generation shader */ void createUniformBuffer() { VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &ubo, sizeof(uniformData), &uniformData)); VK_CHECK_RESULT(ubo.map()); updateUniformBuffers(); } /* If the window has been resized, we need to recreate the storage image and it's descriptor */ void handleResize() { // Recreate image createStorageImage(swapChain.colorFormat, { width, height, 1 }); // Update descriptor VkDescriptorImageInfo storageImageDescriptor{ VK_NULL_HANDLE, storageImage.view, VK_IMAGE_LAYOUT_GENERAL }; VkWriteDescriptorSet resultImageWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor); vkUpdateDescriptorSets(device, 1, &resultImageWrite, 0, VK_NULL_HANDLE); } /* Command buffer generation */ void buildCommandBuffers() { if (resized) { handleResize(); } VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); /* Dispatch the ray tracing commands */ vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout, 0, 1, &descriptorSet, 0, 0); /* Dispatch the ray tracing commands */ vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline); vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineLayout, 0, 1, &descriptorSet, 0, 0); VkStridedDeviceAddressRegionKHR emptySbtEntry = {}; vkCmdTraceRaysKHR( drawCmdBuffers[i], &shaderBindingTables.raygen.stridedDeviceAddressRegion, &shaderBindingTables.miss.stridedDeviceAddressRegion, &shaderBindingTables.hit.stridedDeviceAddressRegion, &emptySbtEntry, width, height, 1); /* Copy ray tracing output to swap chain image */ // Prepare current swap chain image as transfer destination vks::tools::setImageLayout( drawCmdBuffers[i], swapChain.images[i], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Prepare ray tracing output image as transfer source vks::tools::setImageLayout( drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, subresourceRange); VkImageCopy copyRegion{}; copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copyRegion.srcOffset = { 0, 0, 0 }; copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copyRegion.dstOffset = { 0, 0, 0 }; copyRegion.extent = { width, height, 1 }; vkCmdCopyImage(drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapChain.images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region); // Transition swap chain image back for presentation vks::tools::setImageLayout( drawCmdBuffers[i], swapChain.images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subresourceRange); // Transition ray tracing output image back to general layout vks::tools::setImageLayout( drawCmdBuffers[i], storageImage.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, subresourceRange); //@todo: Default render pass setup will overwrite contents //vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); //drawUI(drawCmdBuffers[i]); //vkCmdEndRenderPass(drawCmdBuffers[i]); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); } } void updateUniformBuffers() { uniformData.projInverse = glm::inverse(camera.matrices.perspective); uniformData.viewInverse = glm::inverse(camera.matrices.view); uniformData.lightPos = glm::vec4(cos(glm::radians(timer * 360.0f)) * 40.0f, -20.0f + sin(glm::radians(timer * 360.0f)) * 20.0f, 25.0f + sin(glm::radians(timer * 360.0f)) * 5.0f, 0.0f); // Pass the vertex size to the shader for unpacking vertices uniformData.vertexSize = sizeof(vkglTF::Vertex); memcpy(ubo.mapped, &uniformData, sizeof(uniformData)); } void getEnabledFeatures() { // Enable features required for ray tracing using feature chaining via pNext enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE; enabledRayTracingPipelineFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; enabledRayTracingPipelineFeatures.rayTracingPipeline = VK_TRUE; enabledRayTracingPipelineFeatures.pNext = &enabledBufferDeviceAddresFeatures; enabledAccelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; enabledAccelerationStructureFeatures.accelerationStructure = VK_TRUE; enabledAccelerationStructureFeatures.pNext = &enabledRayTracingPipelineFeatures; deviceCreatepNextChain = &enabledAccelerationStructureFeatures; } void prepare() { VulkanRaytracingSample::prepare(); // Create the acceleration structures used to render the ray traced scene createBottomLevelAccelerationStructure(); createTopLevelAccelerationStructure(); createStorageImage(swapChain.colorFormat, { width, height, 1 }); createUniformBuffer(); createRayTracingPipeline(); createShaderBindingTables(); createDescriptorSets(); buildCommandBuffers(); prepared = true; } void draw() { VulkanExampleBase::prepareFrame(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VulkanExampleBase::submitFrame(); } virtual void render() { if (!prepared) return; draw(); if (!paused || camera.updated) updateUniformBuffers(); } }; VULKAN_EXAMPLE_MAIN()