From 59621894271a094b4695d879bf7c7b6ea7f8de90 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Wed, 1 Nov 2023 10:55:33 +0100 Subject: [PATCH] Add ray traced glTF sample (#1083) * Started working on a ray tracing glTF sample * Started working on a ray tracing glTF sample Added textures using descriptor indexing * Frame accumulation Pass glTF node transforms to BLAS build * Shader cleanup * Code cleanup, flip Y using TLAS transform matrix * Create AS for all primitives in the gltf scene * Remove unused variables * Added missing shaders * Minor cleanup --- base/VulkanglTFModel.cpp | 2 +- base/VulkanglTFModel.h | 1 + examples/CMakeLists.txt | 1 + examples/raytracinggltf/raytracinggltf.cpp | 790 ++++++++++++++++++ .../texturesparseresidency.cpp | 2 +- shaders/glsl/raytracinggltf/anyhit.rahit | 49 ++ shaders/glsl/raytracinggltf/anyhit.rahit.spv | Bin 0 -> 7052 bytes .../glsl/raytracinggltf/bufferreferences.glsl | 15 + shaders/glsl/raytracinggltf/closesthit.rchit | 63 ++ .../glsl/raytracinggltf/closesthit.rchit.spv | Bin 0 -> 7808 bytes .../glsl/raytracinggltf/geometrytypes.glsl | 50 ++ shaders/glsl/raytracinggltf/miss.rmiss | 15 + shaders/glsl/raytracinggltf/miss.rmiss.spv | Bin 0 -> 352 bytes shaders/glsl/raytracinggltf/random.glsl | 37 + shaders/glsl/raytracinggltf/raygen.rgen | 77 ++ shaders/glsl/raytracinggltf/raygen.rgen.spv | Bin 0 -> 7260 bytes shaders/glsl/raytracinggltf/shadow.rmiss | 9 + shaders/glsl/raytracinggltf/shadow.rmiss.spv | Bin 0 -> 304 bytes 18 files changed, 1109 insertions(+), 2 deletions(-) create mode 100644 examples/raytracinggltf/raytracinggltf.cpp create mode 100644 shaders/glsl/raytracinggltf/anyhit.rahit create mode 100644 shaders/glsl/raytracinggltf/anyhit.rahit.spv create mode 100644 shaders/glsl/raytracinggltf/bufferreferences.glsl create mode 100644 shaders/glsl/raytracinggltf/closesthit.rchit create mode 100644 shaders/glsl/raytracinggltf/closesthit.rchit.spv create mode 100644 shaders/glsl/raytracinggltf/geometrytypes.glsl create mode 100644 shaders/glsl/raytracinggltf/miss.rmiss create mode 100644 shaders/glsl/raytracinggltf/miss.rmiss.spv create mode 100644 shaders/glsl/raytracinggltf/random.glsl create mode 100644 shaders/glsl/raytracinggltf/raygen.rgen create mode 100644 shaders/glsl/raytracinggltf/raygen.rgen.spv create mode 100644 shaders/glsl/raytracinggltf/shadow.rmiss create mode 100644 shaders/glsl/raytracinggltf/shadow.rmiss.spv diff --git a/base/VulkanglTFModel.cpp b/base/VulkanglTFModel.cpp index 9d4af8cf..4505fb0f 100644 --- a/base/VulkanglTFModel.cpp +++ b/base/VulkanglTFModel.cpp @@ -1,4 +1,3 @@ - /* * Vulkan glTF model and texture loading class based on tinyglTF (https://github.com/syoyo/tinygltf) * @@ -987,6 +986,7 @@ void vkglTF::Model::loadImages(tinygltf::Model &gltfModel, vks::VulkanDevice *de for (tinygltf::Image &image : gltfModel.images) { vkglTF::Texture texture; texture.fromglTfImage(image, path, device, transferQueue); + texture.index = static_cast(textures.size()); textures.push_back(texture); } // Create an empty texture to be used for empty material images diff --git a/base/VulkanglTFModel.h b/base/VulkanglTFModel.h index dfcbfad7..76a156c1 100644 --- a/base/VulkanglTFModel.h +++ b/base/VulkanglTFModel.h @@ -63,6 +63,7 @@ namespace vkglTF uint32_t layerCount; VkDescriptorImageInfo descriptor; VkSampler sampler; + uint32_t index; void updateDescriptor(); void destroy(); void fromglTfImage(tinygltf::Image& gltfimage, std::string path, vks::VulkanDevice* device, VkQueue copyQueue); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8e7ef554..947ba9d2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -134,6 +134,7 @@ set(EXAMPLES rayquery raytracingbasic raytracingcallable + raytracinggltf raytracingintersection raytracingreflections raytracingsbtdata diff --git a/examples/raytracinggltf/raytracinggltf.cpp b/examples/raytracinggltf/raytracinggltf.cpp new file mode 100644 index 00000000..9cac21c9 --- /dev/null +++ b/examples/raytracinggltf/raytracinggltf.cpp @@ -0,0 +1,790 @@ +/* + * Vulkan Example - Rendering a glTF model using hardware accelerated ray tracing example /for proper transparency, this sample does frame accumulation) + * + * Copyright (C) 2023 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ + +/* + * @todo + */ + +#include "VulkanRaytracingSample.h" +#define VK_GLTF_MATERIAL_IDS +#include "VulkanglTFModel.h" + +class VulkanExample : public VulkanRaytracingSample +{ +public: + AccelerationStructure bottomLevelAS{}; + AccelerationStructure topLevelAS{}; + + vks::Buffer vertexBuffer; + vks::Buffer indexBuffer; + uint32_t indexCount; + vks::Buffer transformBuffer; + + struct GeometryNode { + uint64_t vertexBufferDeviceAddress; + uint64_t indexBufferDeviceAddress; + int32_t textureIndexBaseColor; + int32_t textureIndexOcclusion; + }; + vks::Buffer geometryNodesBuffer; + + std::vector shaderGroups{}; + struct ShaderBindingTables { + ShaderBindingTable raygen; + ShaderBindingTable miss; + ShaderBindingTable hit; + } shaderBindingTables; + + vks::Texture2D texture; + + struct UniformData { + glm::mat4 viewInverse; + glm::mat4 projInverse; + uint32_t frame{ 0 }; + } uniformData; + vks::Buffer ubo; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; + VkDescriptorSetLayout descriptorSetLayout; + + vkglTF::Model model; + + VkPhysicalDeviceDescriptorIndexingFeaturesEXT physicalDeviceDescriptorIndexingFeatures{}; + + VulkanExample() : VulkanRaytracingSample() + { + title = "Ray tracing glTF model"; + settings.overlay = false; + camera.type = Camera::CameraType::lookat; + //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.1f, -1.0f)); + + enableExtensions(); + + // Buffer device address requires the 64-bit integer feature to be enabled + enabledFeatures.shaderInt64 = VK_TRUE; + + enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + physicalDeviceDescriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT; + physicalDeviceDescriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing = VK_TRUE; + physicalDeviceDescriptorIndexingFeatures.runtimeDescriptorArray = VK_TRUE; + physicalDeviceDescriptorIndexingFeatures.descriptorBindingVariableDescriptorCount = VK_TRUE; + + deviceCreatepNextChain = &physicalDeviceDescriptorIndexingFeatures; + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + deleteStorageImage(); + deleteAccelerationStructure(bottomLevelAS); + deleteAccelerationStructure(topLevelAS); + vertexBuffer.destroy(); + indexBuffer.destroy(); + transformBuffer.destroy(); + shaderBindingTables.raygen.destroy(); + shaderBindingTables.miss.destroy(); + shaderBindingTables.hit.destroy(); + ubo.destroy(); + } + + void createAccelerationStructureBuffer(AccelerationStructure &accelerationStructure, VkAccelerationStructureBuildSizesInfoKHR buildSizeInfo) + { + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = buildSizeInfo.accelerationStructureSize; + bufferCreateInfo.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &accelerationStructure.buffer)); + VkMemoryRequirements memoryRequirements{}; + vkGetBufferMemoryRequirements(device, accelerationStructure.buffer, &memoryRequirements); + VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{}; + memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memoryAllocateFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VkMemoryAllocateInfo memoryAllocateInfo{}; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.pNext = &memoryAllocateFlagsInfo; + memoryAllocateInfo.allocationSize = memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &accelerationStructure.memory)); + VK_CHECK_RESULT(vkBindBufferMemory(device, accelerationStructure.buffer, accelerationStructure.memory, 0)); + } + + /* + Create the bottom level acceleration structure that contains the scene's actual geometry (vertices, triangles) + */ + void createBottomLevelAccelerationStructure() + { + // Use transform matrices from the glTF nodes + std::vector transformMatrices{}; + for (auto node : model.linearNodes) { + if (node->mesh) { + for (auto primitive : node->mesh->primitives) { + if (primitive->indexCount > 0) { + VkTransformMatrixKHR transformMatrix{}; + auto m = glm::mat3x4(glm::transpose(node->getMatrix())); + memcpy(&transformMatrix, (void*)&m, sizeof(glm::mat3x4)); + transformMatrices.push_back(transformMatrix); + } + } + } + } + + // Transform buffer + 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, + &transformBuffer, + static_cast(transformMatrices.size()) * sizeof(VkTransformMatrixKHR), + transformMatrices.data())); + + // Build + // One geometry per glTF node, so we can index materials using gl_GeometryIndexEXT + uint32_t maxPrimCount{ 0 }; + std::vector maxPrimitiveCounts{}; + std::vector geometries{}; + std::vector buildRangeInfos{}; + std::vector pBuildRangeInfos{}; + std::vector geometryNodes{}; + for (auto node : model.linearNodes) { + if (node->mesh) { + for (auto primitive : node->mesh->primitives) { + if (primitive->indexCount > 0) { + VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{}; + VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{}; + VkDeviceOrHostAddressConstKHR transformBufferDeviceAddress{}; + + vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(model.vertices.buffer);// +primitive->firstVertex * sizeof(vkglTF::Vertex); + indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(model.indices.buffer) + primitive->firstIndex * sizeof(uint32_t); + transformBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(transformBuffer.buffer) + static_cast(geometryNodes.size()) * sizeof(VkTransformMatrixKHR); + + VkAccelerationStructureGeometryKHR geometry{}; + geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; + geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; + geometry.geometry.triangles.vertexData = vertexBufferDeviceAddress; + geometry.geometry.triangles.maxVertex = model.vertices.count; + //geometry.geometry.triangles.maxVertex = primitive->vertexCount; + geometry.geometry.triangles.vertexStride = sizeof(vkglTF::Vertex); + geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; + geometry.geometry.triangles.indexData = indexBufferDeviceAddress; + geometry.geometry.triangles.transformData = transformBufferDeviceAddress; + geometries.push_back(geometry); + maxPrimitiveCounts.push_back(primitive->indexCount / 3); + maxPrimCount += primitive->indexCount / 3; + + VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo{}; + buildRangeInfo.firstVertex = 0; + buildRangeInfo.primitiveOffset = 0; // primitive->firstIndex * sizeof(uint32_t); + buildRangeInfo.primitiveCount = primitive->indexCount / 3; + buildRangeInfo.transformOffset = 0; + buildRangeInfos.push_back(buildRangeInfo); + + GeometryNode geometryNode{}; + geometryNode.vertexBufferDeviceAddress = vertexBufferDeviceAddress.deviceAddress; + geometryNode.indexBufferDeviceAddress = indexBufferDeviceAddress.deviceAddress; + geometryNode.textureIndexBaseColor = primitive->material.baseColorTexture->index; + geometryNode.textureIndexOcclusion = primitive->material.occlusionTexture ? primitive->material.occlusionTexture->index : -1; + // @todo: map material id to global texture array + geometryNodes.push_back(geometryNode); + } + } + } + } + for (auto& rangeInfo : buildRangeInfos) { + pBuildRangeInfos.push_back(&rangeInfo); + } + + // @todo: stage to device + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &geometryNodesBuffer, + static_cast(geometryNodes.size()) * sizeof(GeometryNode), + geometryNodes.data())); + + // Get size info + VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo{}; + accelerationStructureBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + accelerationStructureBuildGeometryInfo.geometryCount = geometries.size(); + accelerationStructureBuildGeometryInfo.pGeometries = geometries.data(); + + const uint32_t numTriangles = maxPrimitiveCounts[0]; + + VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo{}; + accelerationStructureBuildSizesInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + vkGetAccelerationStructureBuildSizesKHR( + device, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, + &accelerationStructureBuildGeometryInfo, + maxPrimitiveCounts.data(), + &accelerationStructureBuildSizesInfo); + + createAccelerationStructureBuffer(bottomLevelAS, accelerationStructureBuildSizesInfo); + + VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfo{}; + accelerationStructureCreateInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + accelerationStructureCreateInfo.buffer = bottomLevelAS.buffer; + accelerationStructureCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize; + accelerationStructureCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + vkCreateAccelerationStructureKHR(device, &accelerationStructureCreateInfo, nullptr, &bottomLevelAS.handle); + + // Create a small scratch buffer used during build of the bottom level acceleration structure + ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); + + accelerationStructureBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + accelerationStructureBuildGeometryInfo.dstAccelerationStructure = bottomLevelAS.handle; + accelerationStructureBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; + + const VkAccelerationStructureBuildRangeInfoKHR* buildOffsetInfo = buildRangeInfos.data(); + + // Build the acceleration structure on the device via a one-time command buffer submission + // Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds + VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBuildAccelerationStructuresKHR( + commandBuffer, + 1, + &accelerationStructureBuildGeometryInfo, + pBuildRangeInfos.data()); + vulkanDevice->flushCommandBuffer(commandBuffer, queue); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = bottomLevelAS.handle; + bottomLevelAS.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + deleteScratchBuffer(scratchBuffer); + } + + /* + The top level acceleration structure contains the scene's object instances + */ + void createTopLevelAccelerationStructure() + { + // We flip the matrix [1][1] = -1.0f to accomodate for the glTF up vector + 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{}; + accelerationStructureGeometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + 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 + /* + The pSrcAccelerationStructure, dstAccelerationStructure, and mode members of pBuildInfo are ignored. Any VkDeviceOrHostAddressKHR members of pBuildInfo are ignored by this command, except that the hostAddress member of VkAccelerationStructureGeometryTrianglesDataKHR::transformData will be examined to check if it is NULL.* + */ + VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo{}; + accelerationStructureBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + 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{}; + accelerationStructureBuildSizesInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + vkGetAccelerationStructureBuildSizesKHR( + device, + VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, + &accelerationStructureBuildGeometryInfo, + &primitive_count, + &accelerationStructureBuildSizesInfo); + + createAccelerationStructureBuffer(topLevelAS, accelerationStructureBuildSizesInfo); + + VkAccelerationStructureCreateInfoKHR accelerationStructureCreateInfo{}; + accelerationStructureCreateInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + accelerationStructureCreateInfo.buffer = topLevelAS.buffer; + accelerationStructureCreateInfo.size = accelerationStructureBuildSizesInfo.accelerationStructureSize; + accelerationStructureCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + vkCreateAccelerationStructureKHR(device, &accelerationStructureCreateInfo, nullptr, &topLevelAS.handle); + + // Create a small scratch buffer used during build of the top level acceleration structure + ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); + + VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo{}; + accelerationBuildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + 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 }; + + // Build the acceleration structure on the device via a one-time command buffer submission + // Some implementations may support acceleration structure building on the host (VkPhysicalDeviceAccelerationStructureFeaturesKHR->accelerationStructureHostCommands), but we prefer device builds + VkCommandBuffer commandBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vkCmdBuildAccelerationStructuresKHR( + commandBuffer, + 1, + &accelerationBuildGeometryInfo, + accelerationBuildStructureRangeInfos.data()); + vulkanDevice->flushCommandBuffer(commandBuffer, queue); + + VkAccelerationStructureDeviceAddressInfoKHR accelerationDeviceAddressInfo{}; + accelerationDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + accelerationDeviceAddressInfo.accelerationStructure = topLevelAS.handle; + topLevelAS.deviceAddress = vkGetAccelerationStructureDeviceAddressKHR(device, &accelerationDeviceAddressInfo); + + 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 + shadow | + |-----------| + | hit + any | + \-----------/ + + */ + 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, 2); + createShaderBindingTable(shaderBindingTables.hit, 1); + + // Copy handles + memcpy(shaderBindingTables.raygen.mapped, shaderHandleStorage.data(), handleSize); + // We are using two miss shaders, so we need to get two handles for the miss shader binding table + memcpy(shaderBindingTables.miss.mapped, shaderHandleStorage.data() + handleSizeAligned, handleSize * 2); + memcpy(shaderBindingTables.hit.mapped, shaderHandleStorage.data() + handleSizeAligned * 3, handleSize); + } + + /* + Create our ray tracing pipeline + */ + void createRayTracingPipeline() + { + // @todo: + uint32_t imageCount{ 0 }; + imageCount = static_cast(model.textures.size()); + + std::vector setLayoutBindings = { + // Binding 0: Top level 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: Ray tracing result 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: Texture image + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR, 3), + // Binding 4: Geometry node information SSBO + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR, 4), + // Binding 5: All images used by the glTF model + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR, 5, imageCount) + }; + + // Unbound set + VkDescriptorSetLayoutBindingFlagsCreateInfoEXT setLayoutBindingFlags{}; + setLayoutBindingFlags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT; + setLayoutBindingFlags.bindingCount = 6; + std::vector descriptorBindingFlags = { + 0, + 0, + 0, + 0, + 0, + 0, + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT + }; + setLayoutBindingFlags.pBindingFlags = descriptorBindingFlags.data(); + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + descriptorSetLayoutCI.pNext = &setLayoutBindingFlags; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + /* + Setup ray tracing shader groups + */ + std::vector shaderStages; + + // Ray generation group + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracinggltf/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_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); + } + + // Miss group + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracinggltf/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); + // Second shader for shadows + shaderStages.push_back(loadShader(getShadersPath() + "raytracinggltf/shadow.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_KHR)); + shaderGroup.generalShader = static_cast(shaderStages.size()) - 1; + shaderGroups.push_back(shaderGroup); + } + + // Closest hit group for doing texture lookups + { + shaderStages.push_back(loadShader(getShadersPath() + "raytracinggltf/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.intersectionShader = VK_SHADER_UNUSED_KHR; + // This group also uses an anyhit shader for doing transparency (see anyhit.rahit for details) + shaderStages.push_back(loadShader(getShadersPath() + "raytracinggltf/anyhit.rahit.spv", VK_SHADER_STAGE_ANY_HIT_BIT_KHR)); + shaderGroup.anyHitShader = static_cast(shaderStages.size()) - 1; + shaderGroups.push_back(shaderGroup); + } + + /* + Create the ray tracing pipeline + */ + VkRayTracingPipelineCreateInfoKHR rayTracingPipelineCI{}; + rayTracingPipelineCI.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR; + rayTracingPipelineCI.stageCount = static_cast(shaderStages.size()); + rayTracingPipelineCI.pStages = shaderStages.data(); + rayTracingPipelineCI.groupCount = static_cast(shaderGroups.size()); + rayTracingPipelineCI.pGroups = shaderGroups.data(); + rayTracingPipelineCI.maxPipelineRayRecursionDepth = 1; + rayTracingPipelineCI.layout = pipelineLayout; + VK_CHECK_RESULT(vkCreateRayTracingPipelinesKHR(device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &rayTracingPipelineCI, nullptr, &pipeline)); + } + + /* + Create the descriptor sets used for the ray tracing dispatch + */ + void createDescriptorSets() + { + // @todo + uint32_t imageCount{ 0 }; + imageCount = static_cast(model.textures.size()); + + 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_COMBINED_IMAGE_SAMPLER, 1 } + }; + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); + + VkDescriptorSetVariableDescriptorCountAllocateInfoEXT variableDescriptorCountAllocInfo{}; + uint32_t variableDescCounts[] = { imageCount }; + variableDescriptorCountAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT; + variableDescriptorCountAllocInfo.descriptorSetCount = 1; + variableDescriptorCountAllocInfo.pDescriptorCounts = variableDescCounts; + + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + descriptorSetAllocateInfo.pNext = &variableDescriptorCountAllocInfo; + 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 }; + + 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 4: Geometry node information SSBO + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4, &geometryNodesBuffer.descriptor), + }; + + // Image descriptors for the image array + std::vector textureDescriptors{}; + for (auto texture : model.textures) { + VkDescriptorImageInfo descriptor{}; + descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + descriptor.sampler = texture.sampler;; + descriptor.imageView = texture.view; + textureDescriptors.push_back(descriptor); + } + + VkWriteDescriptorSet writeDescriptorImgArray{}; + writeDescriptorImgArray.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorImgArray.dstBinding = 5; + writeDescriptorImgArray.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorImgArray.descriptorCount = imageCount; + writeDescriptorImgArray.dstSet = descriptorSet; + writeDescriptorImgArray.pImageInfo = textureDescriptors.data(); + writeDescriptorSets.push_back(writeDescriptorImgArray); + + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, VK_NULL_HANDLE); + } + + /* + 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); + resized = false; + } + + /* + 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); + + 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); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void updateUniformBuffers() + { + uniformData.projInverse = glm::inverse(camera.matrices.perspective); + uniformData.viewInverse = glm::inverse(camera.matrices.view); + uniformData.frame++; + 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; + + enabledFeatures.samplerAnisotropy = VK_TRUE; + } + + void loadAssets() + { + 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; + model.loadFromFile(getAssetPath() + "models/FlightHelmet/glTF/FlightHelmet.gltf", vulkanDevice, queue); + } + + void prepare() + { + VulkanRaytracingSample::prepare(); + + loadAssets(); + + // 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; + updateUniformBuffers(); + draw(); + } + + virtual void viewChanged() + { + uniformData.frame = -1; + } +}; + +VULKAN_EXAMPLE_MAIN() diff --git a/examples/texturesparseresidency/texturesparseresidency.cpp b/examples/texturesparseresidency/texturesparseresidency.cpp index 9bc069d2..5602013c 100644 --- a/examples/texturesparseresidency/texturesparseresidency.cpp +++ b/examples/texturesparseresidency/texturesparseresidency.cpp @@ -7,7 +7,7 @@ */ /* -* Note : This sample is work-in-progress and works basically, but it's not yet finished +* Important note : This sample is work-in-progress and works basically, but it's not finished */ #include "texturesparseresidency.h" diff --git a/shaders/glsl/raytracinggltf/anyhit.rahit b/shaders/glsl/raytracinggltf/anyhit.rahit new file mode 100644 index 00000000..da54723c --- /dev/null +++ b/shaders/glsl/raytracinggltf/anyhit.rahit @@ -0,0 +1,49 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ +#version 460 + +#extension GL_EXT_ray_tracing : require +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_nonuniform_qualifier : require +#extension GL_EXT_buffer_reference2 : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require + +layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 3) rayPayloadInEXT uint payloadSeed; + +hitAttributeEXT vec2 attribs; + +layout(binding = 3, set = 0) uniform sampler2D image; + +struct GeometryNode { + uint64_t vertexBufferDeviceAddress; + uint64_t indexBufferDeviceAddress; + int textureIndexBaseColor; + int textureIndexOcclusion; +}; +layout(binding = 4, set = 0) buffer GeometryNodes { GeometryNode nodes[]; } geometryNodes; + +layout(binding = 5, set = 0) uniform sampler2D textures[]; + +#include "bufferreferences.glsl" +#include "geometrytypes.glsl" +#include "random.glsl" + +void main() +{ + Triangle tri = unpackTriangle(gl_PrimitiveID, 112); + GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT]; + vec4 color = texture(textures[nonuniformEXT(geometryNode.textureIndexBaseColor)], tri.uv); + // If the alpha value of the texture at the current UV coordinates is below a given threshold, we'll ignore this intersection + // That way ray traversal will be stopped and the miss shader will be invoked +// if (color.a < 0.9) { + //if (((gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x) % 4) == 0) { + if(rnd(payloadSeed) > color.a) { + ignoreIntersectionEXT; + } +// } +} \ No newline at end of file diff --git a/shaders/glsl/raytracinggltf/anyhit.rahit.spv b/shaders/glsl/raytracinggltf/anyhit.rahit.spv new file mode 100644 index 0000000000000000000000000000000000000000..61b05eb83f169b7b4d54567c6eb005f0c2ee815b GIT binary patch literal 7052 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 literal 0 HcmV?d00001 diff --git a/shaders/glsl/raytracinggltf/bufferreferences.glsl b/shaders/glsl/raytracinggltf/bufferreferences.glsl new file mode 100644 index 00000000..e2b0771f --- /dev/null +++ b/shaders/glsl/raytracinggltf/bufferreferences.glsl @@ -0,0 +1,15 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +layout(push_constant) uniform BufferReferences { + uint64_t vertices; + uint64_t indices; + uint64_t bufferAddress; +} bufferReferences; + +layout(buffer_reference, scalar) buffer Vertices {vec4 v[]; }; +layout(buffer_reference, scalar) buffer Indices {uint i[]; }; +layout(buffer_reference, scalar) buffer Data {vec4 f[]; }; \ No newline at end of file diff --git a/shaders/glsl/raytracinggltf/closesthit.rchit b/shaders/glsl/raytracinggltf/closesthit.rchit new file mode 100644 index 00000000..981b2c8b --- /dev/null +++ b/shaders/glsl/raytracinggltf/closesthit.rchit @@ -0,0 +1,63 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 460 + +#extension GL_EXT_ray_tracing : require +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_nonuniform_qualifier : require +#extension GL_EXT_buffer_reference2 : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require + +layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 2) rayPayloadEXT bool shadowed; +hitAttributeEXT vec2 attribs; + +layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; +layout(binding = 3, set = 0) uniform sampler2D image; + +struct GeometryNode { + uint64_t vertexBufferDeviceAddress; + uint64_t indexBufferDeviceAddress; + int textureIndexBaseColor; + int textureIndexOcclusion; +}; +layout(binding = 4, set = 0) buffer GeometryNodes { GeometryNode nodes[]; } geometryNodes; + +layout(binding = 5, set = 0) uniform sampler2D textures[]; + +#include "bufferreferences.glsl" +#include "geometrytypes.glsl" + +void main() +{ + Triangle tri = unpackTriangle(gl_PrimitiveID, 112); + hitValue = vec3(tri.normal); + + GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT]; + + vec3 color = texture(textures[nonuniformEXT(geometryNode.textureIndexBaseColor)], tri.uv).rgb; + if (geometryNode.textureIndexOcclusion > -1) { + float occlusion = texture(textures[nonuniformEXT(geometryNode.textureIndexOcclusion)], tri.uv).r; + color *= occlusion; + } + + hitValue = color; + + // Shadow casting + float tmin = 0.001; + float tmax = 10000.0; + float epsilon = 0.001; + vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT + tri.normal * epsilon; + shadowed = true; + vec3 lightVector = vec3(-5.0, -2.5, -5.0); + // Trace shadow ray and offset indices to match shadow hit/miss shader group indices +// traceRayEXT(topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT, 0xFF, 0, 0, 1, origin, tmin, lightVector, tmax, 2); +// if (shadowed) { +// hitValue *= 0.7; +// } +} diff --git a/shaders/glsl/raytracinggltf/closesthit.rchit.spv b/shaders/glsl/raytracinggltf/closesthit.rchit.spv new file mode 100644 index 0000000000000000000000000000000000000000..0fb5193b59cd9cde07189a549777dbbb8e47ba35 GIT binary patch literal 7808 zcma)<3z$_^701ua+&jZVQ68cs5QF9mL@+>*K!%5dNe%*yP}b#g@0ppCckaEOdj}c9 zgs^v+z3g4|e%hC&SlR>2EFYPcWoc5r%8E=f6)hkC-#L51=6?3k=3C!d>%X3RoxS%R zTAGh(3o=c?@L))ASW}R!kwLTcAb?*oGUeBfN%;=PZ*cq}@nMQxwW2$>bj|8qF{~Bh zs9LYYxu{$WH%8@N*@q_fvzM*RRr>~OQ6XQ-)#Q@z4Rh-UdV0cGKCSzF;{0H)9_I_h z4i`>aoy%7(pH{0Er_G!-ohC;FEy2U8jqR{tRB)5zjO6}&RL0&eI##qmKsoBE z#QnJo2lAz;C(>Rs+LO!eZ`F^hX+-%rx4u*V5rTJu2ktgVnI6;n!!+OnMncOlMbD=klewLba;F50=7QDJqA#VibpkdbAKOv5Xd7N z94>iv9OcWsC3S&MYl0zfQ0}M@)>1t6ZOljf{(M6M9fM$?T+J6Qu&gHx%$OI=m?xZ` zM~No~GtslbXmRR<8&^e})B!z>6;_X$PNnF29hTtuq&XHP zhj~f3L2Y*wi*Z<^`Bt57;^{r9{nBpo8=CmBA2n1Th{L3ZMfqB|xKgUbNv_nVrNO7G zpf6U7Dja_L9WUP7&@cD|;V-ohaMr>0f#3hL5Bv_c4}1=`kC)5G?>C)iHUMXp^)0~I zTf4Zw=M-;C-htjy&e9|^)mx>QOfXe30+M6K8AfMCBJ9(Uot?9Bn)L1i&SfUS1P40O z^2i2rkv*TRb~#^|FKG3y3^QJN-yeFLtcwKBbD=#bq4KF;@xqhEJ`o=x7% zzNp@vFAXGTo%!ADt?QmAF(~nIG&*Ong`^?7Z`yeM$P-Rm$nN z&nCYCvM-D3tCJ}D;Rb$PfUEOo3(=fLks z)UO}4q<&j)i1hTTue9HeWFEY6*jWBK;mricWM{5b-yV#+l>QLye^)pc{BEWAfi;#X z7x#;GOv(bo$PYF|WElCucux)EZi4Zqc540;#al&3iSV&Ih!3k%=NF7z__vEs7O_kR z<97u8SaJT&cvJ<@r${D0cJkX@^z(zI`CF2^x!c9gcCpq!_mt(wf>|tg)MBreOdo9` z`r!QfeWTNNHnFz^cZgq;)NKjw^yIrd`2kOU(32n9CufrLOWj-C9yqV)w>o{V)9-LP zXU^{noxWJ~&H3}~=-69C%+t=C&BQRd9dI7(yy3?@`L2eI)&!e{wF@IJ@)4@dcbg<) zO_I(mA#m)RwMimsVIL}v9ek`Z4NLGKI8rudfKGn&L!3^niP{H$ci;nIn<5|Tj1Uns zUix9;_<<3_KEN!dUD!zJ;PgR1^f^v^p@=n6S^3=tgXlHgMP!}~@aJ1JiNNtk=l+0UKTe!{)H+_ZP7;4a=Y}4p zNM=6tFjX9#81n%`KUthTfm;u!NVXc&ot=GBW2QJY_(roN^Ub)s)Z{*bQImUzP7R9z zL!To~O>p~;3na6~=yWX=$8Whfwb+OGbxB6Yey%uqsEOV!jz7BjpD($|+1H8V&-cfl zHyfSvf!^nI_GGi*?=Q|E_5o*S=GgJuBnpBpXJCJ`r2AAKJKqbP|BHZOzu3u^pM6{* zBENlK-lZmy)xTD9RYd&v#CItd{o;>)y~D=oH2+L8xrz6e=|}6k{9Fe7_+Gb&qm%nq zaTXZ4(QgCL!v4!tO#E%{cRS2_y+=BC(e!&Izo2FQ|Bzl8we57%|0z9KIQ~rX|4Sl$ z+TPIltp}qY^e3H;|I(?Z?=-zN z(I@Sks?$bKZPM`phu=S@IT#{Nt)b#9bZVjVPP5RN9q%%WcZqk^-VHc84i(0GgpC+< ze&@kvD~2<|J!3KBy+X3$_^uPbO2k=4hwt!oa$M)>ykj?dI``maPv;)&YS4E#=#Mq% zk9#`#_B81HJw;#h)!z{D?}#|-o27FPu5s7`?T+5jk?ET|FjrpO+wY3>2@@Y|_^@~I z4o!sGH;BMa5mArt_k9ugx$@!6{J>#i`>-E6Os($2e&jIbzrvk`osx;iA3Hs84|j@e ze?OMYTiv0UzT495}PMThyg5$!vcs z!Va;RA3p4XbA{dZ@H@%u;U1Cg;rEikuT(!v#qSe=@qRq59QTW}o>l+Y{vZP54j^0Z zKT2-Q{U>4IHlGJ1gV}r@l+1je-$ye%QO%4zc~y)n<>~cs$_G3Tm0W86OS+F5*?X4%o~i%{pI~d z=I(-9{J$mh_L}~bhPt97FN{ikPE+FQ0QbwaERv zh<+^h3zG2xx7-{s@?)d_J>p=<&EmZ4$jO@{`8gTqh_Pd{1mG*6!+d9J4uH@`ve^z?HjuWZoQdipxac~57SgHC5| zt&)!vv4=~wNA#l{HbR_P&_5XdyuYsy`R~*5(%VIooejSUl1~s}v-jylhmn&Rp!0pF zi_Z{&+4mhM`BaB*RKAlWztUmEGc$CHKV35M^gw;$=ZV0HpDF$-hY>&5>B!7?frvYC zhB)63{*>Z6#21NJ*cXdqhui#?BytdZTYFh5`AiWQ^W)t?Unn9MvB<=-r)47cV!6(e z%)Cv1bs`7B7WZw=mW~fNzYojBUn3$9d&fRsIN$Lc5w>PgMr7Z2g=B19BKFUD0;48# zS|wsW*28Ma_*f6Gl?=wr=%HKuJP~p9fSn#@iLVi1qX+H}^*FEKR_}F^{d%vL?$>*R zbg=V9R_~3HiK8BNtCy3E&9ApsI=Jm;eIlniJr+dtWWPy8hk>)2MC9wxE$068i1H$8 zlMBwiz=^dwy@^a*Hu?WSB%L~(3w(x&zexm+PA?aT=(kI?_$@6t44g$T_S?`e89U3c zvScv)>~D^WWa{9L-S$zHZ2O>B?jHL9C&u=1VIuGEu_oQ`u`V49Vooh0Y7B^P5LpfK z#3E{d6UUueB+k8nTg{6SdH_jtjKsvb9_@HFsz_+@(ACgRMY*zQflC3T^{kk8Kj@{~hR5Eq15ZSl-m}GFP z`*F#{fm_{ANTxP6a&b;SDWWd*m^nIr^u?Y3l*syG=FEhe!^J->!ft(iMlyZ*Gykk~ zaQoY2yJX_Pt;XjhTMhF2H9jvLyVdxDWNP>`|DtqotMMht#DTMSdi}C!y9k@r{fcC( z%l`ekUzLvC>V8c!b^V!tT{^hc{f1=Xz^(2#B~u$4xtQsdBI;6)nWN*!Tn`ujmI(Xv KdK-}WJM2Z2*^;{e literal 0 HcmV?d00001 diff --git a/shaders/glsl/raytracinggltf/geometrytypes.glsl b/shaders/glsl/raytracinggltf/geometrytypes.glsl new file mode 100644 index 00000000..cfff9f14 --- /dev/null +++ b/shaders/glsl/raytracinggltf/geometrytypes.glsl @@ -0,0 +1,50 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +struct Vertex +{ + vec3 pos; + vec3 normal; + vec2 uv; +}; + +struct Triangle { + Vertex vertices[3]; + vec3 normal; + vec2 uv; +}; + +// This function will unpack our vertex buffer data into a single triangle and calculates uv coordinates +Triangle unpackTriangle(uint index, int vertexSize) { + Triangle tri; + const uint triIndex = index * 3; + + GeometryNode geometryNode = geometryNodes.nodes[gl_GeometryIndexEXT]; + + Indices indices = Indices(geometryNode.indexBufferDeviceAddress); + Vertices vertices = Vertices(geometryNode.vertexBufferDeviceAddress); + + // Unpack vertices + // Data is packed as vec4 so we can map to the glTF vertex structure from the host side + // We match vkglTF::Vertex: pos.xyz+normal.x, normalyz+uv.xy + // glm::vec3 pos; + // glm::vec3 normal; + // glm::vec2 uv; + // ... + for (uint i = 0; i < 3; i++) { + const uint offset = indices.i[triIndex + i] * 6; + vec4 d0 = vertices.v[offset + 0]; // pos.xyz, n.x + vec4 d1 = vertices.v[offset + 1]; // n.yz, uv.xy + tri.vertices[i].pos = d0.xyz; + tri.vertices[i].normal = vec3(d0.w, d1.xy); + tri.vertices[i].uv = d1.zw; + } + // Calculate values at barycentric coordinates + vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); + tri.uv = tri.vertices[0].uv * barycentricCoords.x + tri.vertices[1].uv * barycentricCoords.y + tri.vertices[2].uv * barycentricCoords.z; + tri.normal = tri.vertices[0].normal * barycentricCoords.x + tri.vertices[1].normal * barycentricCoords.y + tri.vertices[2].normal * barycentricCoords.z; + return tri; +} \ No newline at end of file diff --git a/shaders/glsl/raytracinggltf/miss.rmiss b/shaders/glsl/raytracinggltf/miss.rmiss new file mode 100644 index 00000000..dacf6eac --- /dev/null +++ b/shaders/glsl/raytracinggltf/miss.rmiss @@ -0,0 +1,15 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 460 +#extension GL_EXT_ray_tracing : enable + +layout(location = 0) rayPayloadInEXT vec3 hitValue; + +void main() +{ + hitValue = vec3(1.0); +} \ No newline at end of file diff --git a/shaders/glsl/raytracinggltf/miss.rmiss.spv b/shaders/glsl/raytracinggltf/miss.rmiss.spv new file mode 100644 index 0000000000000000000000000000000000000000..6c508af2226db156858cfcd36fe5388347c6d64d GIT binary patch literal 352 zcmY*VPpbiO6g}pjzme$Kk|oIkDaA^ZS-g|DCf-c>bbve z_nvdlJ#!n=sK#N%kVXO-{6-!IzVko|4rZSVZ!(^GC;#GwCw~> 5) + 0xC8013EA4); + v1 += ((v0 << 4) + 0xAD90777D) ^ (v0 + sum) ^ ((v0 >> 5) + 0x7E95761E); + } + return v0; +} + +// Linear congruential generator based on the previous RNG state +// See https://en.wikipedia.org/wiki/Linear_congruential_generator +uint lcg(inout uint previous) +{ + const uint multiplier = 1664525u; + const uint increment = 1013904223u; + previous = (multiplier * previous + increment); + return previous & 0x00FFFFFF; +} + +// Generate a random float in [0, 1) given the previous RNG state +float rnd(inout uint previous) +{ + return (float(lcg(previous)) / float(0x01000000)); +} \ No newline at end of file diff --git a/shaders/glsl/raytracinggltf/raygen.rgen b/shaders/glsl/raytracinggltf/raygen.rgen new file mode 100644 index 00000000..e3b47295 --- /dev/null +++ b/shaders/glsl/raytracinggltf/raygen.rgen @@ -0,0 +1,77 @@ +/* Copyright (c) 2023, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 460 +#extension GL_EXT_ray_tracing : enable +#extension GL_GOOGLE_include_directive : require + +layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; +layout(binding = 1, set = 0, rgba8) uniform image2D image; +layout(binding = 2, set = 0) uniform CameraProperties +{ + mat4 viewInverse; + mat4 projInverse; + uint frame; +} cam; + +layout(location = 0) rayPayloadEXT vec3 hitValue; +layout(location = 3) rayPayloadEXT uint payloadSeed; + +#include "random.glsl" + +void main() +{ + uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, cam.frame); + + float r1 = rnd(seed); + float r2 = rnd(seed); + + // Subpixel jitter: send the ray through a different position inside the pixel + // each time, to provide antialiasing. + vec2 subpixel_jitter = cam.frame == 0 ? vec2(0.5f, 0.5f) : vec2(r1, r2); + const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + subpixel_jitter; + const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); + vec2 d = inUV * 2.0 - 1.0; + +// const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); +// const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy); +// vec2 d = inUV * 2.0 - 1.0; + + vec4 origin = cam.viewInverse * vec4(0,0,0,1); + vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1) ; + vec4 direction = cam.viewInverse*vec4(normalize(target.xyz), 0.0) ; + + float tmin = 0.001; + float tmax = 10000.0; + + hitValue = vec3(0.0); + vec3 hitValues = vec3(0); + + const int samples = 4; + + // Trace multiple rays for e.g. transparency + for(int smpl = 0; smpl < samples; smpl++) { + payloadSeed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, cam.frame); + traceRayEXT(topLevelAS, gl_RayFlagsNoneEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); + hitValues += hitValue; + } + +// imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValues / float(samples), 0.0)); + + vec3 hitVal = hitValues / float(samples); + + if(cam.frame > 0) + { + float a = 1.0f / float(cam.frame + 1); + vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz; + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, hitVal, a), 1.f)); + } + else + { + // First frame, replace the value in the buffer + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitVal, 1.f)); + } +} diff --git a/shaders/glsl/raytracinggltf/raygen.rgen.spv b/shaders/glsl/raytracinggltf/raygen.rgen.spv new file mode 100644 index 0000000000000000000000000000000000000000..6ad714286eee6d2fc0912f44675b3acf7506fb2e GIT binary patch literal 7260 zcmZXY37nN>6^B1&16f31WKl#ATtO0ZUm?Z8n0}0+)X%cpb>=c$y|Z*@K}>B-Ez>g1 zY|kvsj9kks+e|aHP}6L+G+VVYvn;n%dY(J)k&oLehx0%Gv%K5+zVBjV!~C&X)0k{R zHZEH{CaaApSwr^jDOol?Yspr3tSPKqaeSd#+)x;-7CXzm-Nd$JhPJh@ZeKhw*tK}S z{r53(+pMvcuWxcTF}tHR%bLJF#d5EXwa976F!CVs81ghSnS@6EYXIK|ZlZo$dtv!o zPS|Q%`0?^>t5&tOFE4cV_ZJ2RH&jZ6O1ZaG=qguBorC4!lKJCuapm65%1~E6pqYsL zx|*|Q_LOCVrQ#k#OAh5v2Gf*n12EbFh9hRc0J z0};a!WYylT*J7sqUrb{*9d2N#hYF3^Ozigyq*n|LKi2YsA5m4TI{Iy^_QxH-o0D7rhOO+z z?A_Sa+P<5zkviXb`u4SEQ+EDVzLxAF?17rU_nnmYYwJg4)2L#ddCb{}7H>~Z3wkc% zn2YUt75RDWa9qN{&m(41j+;(3Uc0p#`lf=ljbE7R&j35ZcK@DbE2~(L<97q!w&B1l zT-RRUxyTsAJne-@Q?gy(-biK1va7Am zCVOq-qF#5hjbE3rH6q^rwTAWWdFS*zX7absqMY`e)XQ>;HLN*=_TKF|Yiiqba&ZRh zlWqJN2}l0f*xsx2x`y{BY`*aqqV>yL&$tgGj3XDZCi%+{ImH^EL|Z@BV9vD&V`DB8Ml_u9Tw zYNo3ot&D=DO!jL>;yto4oCJHEjFM?`qiUk({mnyqvB7 z{G5Fu_@X-dLv^;_*{E;)m38(tIosa5q0YXw&h|SS^<3YkI{W^dZ67?Gv+V=FrID|G zqR#fa8GiHqMuzQtej~%y?{`vr8Moj&rHrgax{<@V6MN)TL_T8tko&Ijy&W+fvug->&KQKNqO%Cn=5|=9pe4VS;P0uyAa<=zK3G&GZSyT{h@s@t8g7> zArlbm=rhJ1laKs!z;gOV(AozxpU=a45FV=g5 zD^%1V zWyp(g#_Kc2^?4STBknQmkAhuW2Xp%F{upA6eAJW6*R!tq#(x}fPGfxzwe@wOKZV#U ztLRR@0iRBsyo0u-M@H*>2CP5oUIDh=IcZ&=1sfypTt3rs5&t=`T+ID>u>Bo#Uj>#^ z9QnDg#@2s9n(GT->&ZLUm1wz`>x*Eyi&OlUz;f3jagJXGZ%lG-in|WmSbf&L1}$e# z+=RXvu_tasoNEC66-540Vyft`CGHlqHEu{;)c88KHC7@~;~QXkV|;(yhB)^O#2EYH zn}~eGd<$$$Hxe=52FrJFpRVOQhiF4p-yaQ$BIfRlGV>wh0H zZxz2#??m5~IC+P6*nmvrEusGhiCc~~_Bi(I-tI>9IfA3n>cI69)Acn-+29g z-(!zI0=s5o+~e(NIq@d+eF@)>ejwoo(GMl;cOkIf1n=GF?B{5oTlI6C#V^snLLB-Z zLF-q0R(qj;jacJm_PhZ7o5U?d`@Ycr1!668#oom_^ly>1NZfS`_;Ex&Vtxlc4T+vH z=J$wvd@ue0ejJgHyg!1iozMFdwtRdq{tPx(-m`lGEoY50(0@VfmGy}Gb?(0+^2R@z z?D$^%4SND&9eu_;hL$(~DfB;(r;}~3{S)z7ieCFy;^ZCCYvxbEeiqq;Xq*3U#QTr@ z|0GV{QJ?=DoPBZfl!>*qcs|b{>JZo9Jk~Pa-f=#A{rNik1#J829L`Mti-??Y?q8pr z{bMiL18VEpKQAHnk9VT|-^8s%o3BqU-=FqUtmhT5b9AIUf}C+qVZ+>d^H*q*UJ?2|aFUBUUY+6_B@R=Z=%DGpVf)gBo7<81Z>dp5Dwy|CpR z!7WUjba0Cjm*3OgaONAYKlbE$omYQ;Pwwp%R(BBMzt?>cXt*~zvK&Gq^8Je+?P;(Yq7qaD5-!OpMGn%ZLPS;M{Ugm!&u zzll4e=OPaM^U(U$QFj-xHEv4pja|WV^AX<_+H(+VnJbRm-NEi5-V1wx<#t12e{vDO zC)l;$mCkz+SZ*N_HSBl!y^+X!9oW3E_W|eUU4m_l{N1VczF=$Vi@eu^t#fCJ*&i&o z9}@E)2$nwpiCPDNt*0+)9Sru4BJU8eoUw6#auI(h*!bA{VPLuZ-jBeRKfETRJ#i%1 zyr{DjoZtI0Y-8kOy>9?pOJC$21-4G?{b;aUe(%R%%P&WwRvXxQ`l8kfu;&4)1jbM8&^4r03@g3DCm+xu&FY@06b}z3|SDb%_Cu2J%A@K}v3w90L zp`*reVEOo6c09NPk&ktq0G3~kxG&@7qQ;3}xqDJSy%{XG261iLv9>ATR>UzCiM34w zySDAo@eVl&JQj(0-U>F(HR#j!-`Bn~-iEkm`G`L`*~Xiv9pCAE