diff --git a/data/shaders/nv_ray_tracing_reflections/closesthit.rchit b/data/shaders/nv_ray_tracing_reflections/closesthit.rchit new file mode 100644 index 00000000..41c72e2a --- /dev/null +++ b/data/shaders/nv_ray_tracing_reflections/closesthit.rchit @@ -0,0 +1,70 @@ +#version 460 +#extension GL_NV_ray_tracing : require +#extension GL_EXT_nonuniform_qualifier : enable + +struct RayPayload { + vec3 color; + float distance; + vec3 normal; + float reflector; +}; + +layout(location = 0) rayPayloadInNV RayPayload rayPayload; + +layout(location = 2) rayPayloadNV bool shadowed; +hitAttributeNV vec3 attribs; + +layout(binding = 0, set = 0) uniform accelerationStructureNV topLevelAS; +layout(binding = 2, set = 0) uniform CameraProperties +{ + mat4 viewInverse; + mat4 projInverse; + vec4 lightPos; +} cam; +layout(binding = 3, set = 0) buffer Vertices { vec4 v[]; } vertices; +layout(binding = 4, set = 0) buffer Indices { uint i[]; } indices; + +struct Vertex +{ + vec3 pos; + vec3 normal; + vec3 color; + vec2 uv; + float _pad0; +}; + +Vertex unpack(uint index) +{ + vec4 d0 = vertices.v[3 * index + 0]; + vec4 d1 = vertices.v[3 * index + 1]; + vec4 d2 = vertices.v[3 * index + 2]; + + Vertex v; + v.pos = d0.xyz; + v.normal = vec3(d0.w, d1.x, d1.y); + v.color = vec3(d1.z, d1.w, d2.x); + return v; +} + +void main() +{ + ivec3 index = ivec3(indices.i[3 * gl_PrimitiveID], indices.i[3 * gl_PrimitiveID + 1], indices.i[3 * gl_PrimitiveID + 2]); + + Vertex v0 = unpack(index.x); + Vertex v1 = unpack(index.y); + Vertex v2 = unpack(index.z); + + // Interpolate normal + const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); + vec3 normal = normalize(v0.normal * barycentricCoords.x + v1.normal * barycentricCoords.y + v2.normal * barycentricCoords.z); + + // Basic lighting + vec3 lightVector = normalize(cam.lightPos.xyz); + float dot_product = max(dot(lightVector, normal), 0.6); + rayPayload.color = v0.color * vec3(dot_product); + rayPayload.distance = gl_RayTmaxNV; + rayPayload.normal = normal; + + // Objects with full white vertex color are treated as reflectors + rayPayload.reflector = ((v0.color.r == 1.0f) && (v0.color.g == 1.0f) && (v0.color.b == 1.0f)) ? 1.0f : 0.0f; +} diff --git a/data/shaders/nv_ray_tracing_reflections/closesthit.rchit.spv b/data/shaders/nv_ray_tracing_reflections/closesthit.rchit.spv new file mode 100644 index 00000000..a4c11809 Binary files /dev/null and b/data/shaders/nv_ray_tracing_reflections/closesthit.rchit.spv differ diff --git a/data/shaders/nv_ray_tracing_reflections/miss.rmiss b/data/shaders/nv_ray_tracing_reflections/miss.rmiss new file mode 100644 index 00000000..0037ff10 --- /dev/null +++ b/data/shaders/nv_ray_tracing_reflections/miss.rmiss @@ -0,0 +1,25 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct RayPayload { + vec3 color; + float distance; + vec3 normal; + float reflector; +}; + +layout(location = 0) rayPayloadInNV RayPayload rayPayload; + +void main() +{ + // View-independent background gradient to simulate a basic sky background + const vec3 gradientStart = vec3(0.5, 0.6, 1.0); + const vec3 gradientEnd = vec3(1.0); + vec3 unitDir = normalize(gl_WorldRayDirectionNV); + float t = 0.5 * (unitDir.y + 1.0); + rayPayload.color = (1.0-t) * gradientStart + t * gradientEnd; + + rayPayload.distance = -1.0f; + rayPayload.normal = vec3(0.0f); + rayPayload.reflector = 0.0f; +} \ No newline at end of file diff --git a/data/shaders/nv_ray_tracing_reflections/miss.rmiss.spv b/data/shaders/nv_ray_tracing_reflections/miss.rmiss.spv new file mode 100644 index 00000000..f190c331 Binary files /dev/null and b/data/shaders/nv_ray_tracing_reflections/miss.rmiss.spv differ diff --git a/data/shaders/nv_ray_tracing_reflections/raygen.rgen b/data/shaders/nv_ray_tracing_reflections/raygen.rgen new file mode 100644 index 00000000..1d51a742 --- /dev/null +++ b/data/shaders/nv_ray_tracing_reflections/raygen.rgen @@ -0,0 +1,62 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(binding = 0, set = 0) uniform accelerationStructureNV topLevelAS; +layout(binding = 1, set = 0, rgba8) uniform image2D image; +layout(binding = 2, set = 0) uniform CameraProperties +{ + mat4 viewInverse; + mat4 projInverse; + vec4 lightPos; +} cam; + + +struct RayPayload { + vec3 color; + float distance; + vec3 normal; + float reflector; +}; + +layout(location = 0) rayPayloadNV RayPayload rayPayload; + +// Max. number of recursion is passed via a specialization constant +layout (constant_id = 0) const int MAX_RECURSION = 0; + +void main() +{ + const vec2 pixelCenter = vec2(gl_LaunchIDNV.xy) + vec2(0.5); + const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeNV.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); + + uint rayFlags = gl_RayFlagsOpaqueNV; + uint cullMask = 0xff; + float tmin = 0.001; + float tmax = 10000.0; + + vec3 color = vec3(0.0); + + for (int i = 0; i < MAX_RECURSION; i++) { + traceNV(topLevelAS, rayFlags, cullMask, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); + vec3 hitColor = rayPayload.color; + + if (rayPayload.distance < 0.0f) { + color += hitColor; + break; + } else if (rayPayload.reflector == 1.0f) { + const vec4 hitPos = origin + direction * rayPayload.distance; + origin.xyz = hitPos.xyz + rayPayload.normal * 0.001f; + direction.xyz = reflect(direction.xyz, rayPayload.normal); + } else { + color += hitColor; + break; + } + + } + + imageStore(image, ivec2(gl_LaunchIDNV.xy), vec4(color, 0.0)); +} diff --git a/data/shaders/nv_ray_tracing_reflections/raygen.rgen.spv b/data/shaders/nv_ray_tracing_reflections/raygen.rgen.spv new file mode 100644 index 00000000..4127505e Binary files /dev/null and b/data/shaders/nv_ray_tracing_reflections/raygen.rgen.spv differ diff --git a/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp b/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp new file mode 100644 index 00000000..13d985bb --- /dev/null +++ b/examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp @@ -0,0 +1,761 @@ +/* +* Vulkan Example - Advanced example for doing reflections with ray tracing using VK_NV_ray_tracing +* +* Renders a complex scene doing recursion inside the shaders for creating reflections +* +* Copyright (C) by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#include +#include +#include +#include +#include + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#include +#include "vulkanexamplebase.h" +#include "VulkanDevice.hpp" +#include "VulkanBuffer.hpp" +#include "VulkanModel.hpp" + +// Ray tracing acceleration structure +struct AccelerationStructure { + VkDeviceMemory memory; + VkAccelerationStructureNV accelerationStructure; + uint64_t handle; +}; + +// Ray tracing geometry instance +struct GeometryInstance { + glm::mat3x4 transform; + uint32_t instanceId : 24; + uint32_t mask : 8; + uint32_t instanceOffset : 24; + uint32_t flags : 8; + uint64_t accelerationStructureHandle; +}; + +// Indices for the different ray tracing groups used in this example +#define INDEX_RAYGEN 0 +#define INDEX_MISS 1 +#define INDEX_CLOSEST_HIT 2 + +#define NUM_SHADER_GROUPS 3 + +class VulkanExample : public VulkanExampleBase +{ +public: + PFN_vkCreateAccelerationStructureNV vkCreateAccelerationStructureNV; + PFN_vkDestroyAccelerationStructureNV vkDestroyAccelerationStructureNV; + PFN_vkBindAccelerationStructureMemoryNV vkBindAccelerationStructureMemoryNV; + PFN_vkGetAccelerationStructureHandleNV vkGetAccelerationStructureHandleNV; + PFN_vkGetAccelerationStructureMemoryRequirementsNV vkGetAccelerationStructureMemoryRequirementsNV; + PFN_vkCmdBuildAccelerationStructureNV vkCmdBuildAccelerationStructureNV; + PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV; + PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV; + PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV; + + VkPhysicalDeviceRayTracingPropertiesNV rayTracingProperties{}; + + AccelerationStructure bottomLevelAS; + AccelerationStructure topLevelAS; + + vks::Buffer shaderBindingTable; + + struct StorageImage { + VkDeviceMemory memory; + VkImage image; + VkImageView view; + VkFormat format; + } storageImage; + + struct UniformData { + glm::mat4 viewInverse; + glm::mat4 projInverse; + glm::vec4 lightPos; + } uniformData; + vks::Buffer ubo; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; + VkDescriptorSetLayout descriptorSetLayout; + + vks::VertexLayout vertexLayout = vks::VertexLayout({ + vks::VERTEX_COMPONENT_POSITION, + vks::VERTEX_COMPONENT_NORMAL, + vks::VERTEX_COMPONENT_COLOR, + vks::VERTEX_COMPONENT_UV, + vks::VERTEX_COMPONENT_DUMMY_FLOAT + }); + vks::Model scene; + + VulkanExample() : VulkanExampleBase() + { + title = "VK_NV_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.0f, -1.5f)); + // Enable instance and device extensions required to use VK_NV_ray_tracing + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_NV_RAY_TRACING_EXTENSION_NAME); + } + + ~VulkanExample() + { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + vkDestroyImageView(device, storageImage.view, nullptr); + vkDestroyImage(device, storageImage.image, nullptr); + vkFreeMemory(device, storageImage.memory, nullptr); + vkFreeMemory(device, bottomLevelAS.memory, nullptr); + vkFreeMemory(device, topLevelAS.memory, nullptr); + vkDestroyAccelerationStructureNV(device, bottomLevelAS.accelerationStructure, nullptr); + vkDestroyAccelerationStructureNV(device, topLevelAS.accelerationStructure, nullptr); + shaderBindingTable.destroy(); + ubo.destroy(); + scene.destroy(); + } + + /* + Set up a storage image that the ray generation shader will be writing to + */ + void createStorageImage() + { + VkImageCreateInfo image = vks::initializers::imageCreateInfo(); + image.imageType = VK_IMAGE_TYPE_2D; + image.format = swapChain.colorFormat; + image.extent.width = width; + image.extent.height = height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + image.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &storageImage.image)); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, storageImage.image, &memReqs); + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memReqs.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &storageImage.memory)); + VK_CHECK_RESULT(vkBindImageMemory(device, storageImage.image, storageImage.memory, 0)); + + VkImageViewCreateInfo colorImageView = vks::initializers::imageViewCreateInfo(); + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = swapChain.colorFormat; + colorImageView.subresourceRange = {}; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = storageImage.image; + VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &storageImage.view)); + + VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(cmdBuffer, storageImage.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + vulkanDevice->flushCommandBuffer(cmdBuffer, queue); + } + + /* + The bottom level acceleration structure contains the scene's geometry (vertices, triangles) + */ + void createBottomLevelAccelerationStructure(const VkGeometryNV* geometries) + { + VkAccelerationStructureInfoNV accelerationStructureInfo{}; + accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + accelerationStructureInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV; + accelerationStructureInfo.instanceCount = 0; + accelerationStructureInfo.geometryCount = 1; + accelerationStructureInfo.pGeometries = geometries; + + VkAccelerationStructureCreateInfoNV accelerationStructureCI{}; + accelerationStructureCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; + accelerationStructureCI.info = accelerationStructureInfo; + VK_CHECK_RESULT(vkCreateAccelerationStructureNV(device, &accelerationStructureCI, nullptr, &bottomLevelAS.accelerationStructure)); + + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + + VkMemoryRequirements2 memoryRequirements2{}; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memoryRequirements2); + + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memoryRequirements2.memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &bottomLevelAS.memory)); + + VkBindAccelerationStructureMemoryInfoNV accelerationStructureMemoryInfo{}; + accelerationStructureMemoryInfo.sType = VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV; + accelerationStructureMemoryInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + accelerationStructureMemoryInfo.memory = bottomLevelAS.memory; + VK_CHECK_RESULT(vkBindAccelerationStructureMemoryNV(device, 1, &accelerationStructureMemoryInfo)); + + VK_CHECK_RESULT(vkGetAccelerationStructureHandleNV(device, bottomLevelAS.accelerationStructure, sizeof(uint64_t), &bottomLevelAS.handle)); + } + + /* + The top level acceleration structure contains the scene's object instances + */ + void createTopLevelAccelerationStructure() + { + VkAccelerationStructureInfoNV accelerationStructureInfo{}; + accelerationStructureInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + accelerationStructureInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV; + accelerationStructureInfo.instanceCount = 1; + accelerationStructureInfo.geometryCount = 0; + + VkAccelerationStructureCreateInfoNV accelerationStructureCI{}; + accelerationStructureCI.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; + accelerationStructureCI.info = accelerationStructureInfo; + VK_CHECK_RESULT(vkCreateAccelerationStructureNV(device, &accelerationStructureCI, nullptr, &topLevelAS.accelerationStructure)); + + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.accelerationStructure = topLevelAS.accelerationStructure; + + VkMemoryRequirements2 memoryRequirements2{}; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memoryRequirements2); + + VkMemoryAllocateInfo memoryAllocateInfo = vks::initializers::memoryAllocateInfo(); + memoryAllocateInfo.allocationSize = memoryRequirements2.memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memoryRequirements2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &topLevelAS.memory)); + + VkBindAccelerationStructureMemoryInfoNV accelerationStructureMemoryInfo{}; + accelerationStructureMemoryInfo.sType = VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV; + accelerationStructureMemoryInfo.accelerationStructure = topLevelAS.accelerationStructure; + accelerationStructureMemoryInfo.memory = topLevelAS.memory; + VK_CHECK_RESULT(vkBindAccelerationStructureMemoryNV(device, 1, &accelerationStructureMemoryInfo)); + + VK_CHECK_RESULT(vkGetAccelerationStructureHandleNV(device, topLevelAS.accelerationStructure, sizeof(uint64_t), &topLevelAS.handle)); + } + + /* + Create scene geometry and ray tracing acceleration structures + */ + void createScene() + { + // Instead of a simple triangle, we'll be loading a more complex scene for this example + vks::ModelCreateInfo modelCI{}; + modelCI.scale = glm::vec3(0.25f); + // The shaders are accessing the vertex and index buffers of the scene, so the proper usage flag has to be set + modelCI.memoryPropertyFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + scene.loadFromFile(getAssetPath() + "models/reflection_test.dae", vertexLayout, &modelCI, vulkanDevice, queue); + //scene.loadFromFile(getAssetPath() + "models/Mini_Diorama_01.dae", vertexLayout, &modelCI, vulkanDevice, queue); + + + /* + Create the bottom level acceleration structure containing the actual scene geometry + */ + VkGeometryNV geometry{}; + geometry.sType = VK_STRUCTURE_TYPE_GEOMETRY_NV; + geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_NV; + geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV; + geometry.geometry.triangles.vertexData = scene.vertices.buffer; + geometry.geometry.triangles.vertexOffset = 0; + geometry.geometry.triangles.vertexCount = static_cast(scene.vertexCount); + geometry.geometry.triangles.vertexStride = vertexLayout.stride(); + geometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; + geometry.geometry.triangles.indexData = scene.indices.buffer; + geometry.geometry.triangles.indexOffset = 0; + geometry.geometry.triangles.indexCount = scene.indexCount; + geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; + geometry.geometry.triangles.transformData = VK_NULL_HANDLE; + geometry.geometry.triangles.transformOffset = 0; + geometry.geometry.aabbs = {}; + geometry.geometry.aabbs.sType = { VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV }; + geometry.flags = VK_GEOMETRY_OPAQUE_BIT_NV; + + createBottomLevelAccelerationStructure(&geometry); + + /* + Create the top-level acceleration structure that contains geometry instance information + */ + + // Single instance with a 3x4 transform matrix for the ray traced triangle + vks::Buffer instanceBuffer; + + glm::mat3x4 transform = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + }; + + GeometryInstance geometryInstance{}; + geometryInstance.transform = transform; + geometryInstance.instanceId = 0; + geometryInstance.mask = 0xff; + geometryInstance.instanceOffset = 0; + geometryInstance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV; + geometryInstance.accelerationStructureHandle = bottomLevelAS.handle; + + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &instanceBuffer, + sizeof(GeometryInstance), + &geometryInstance)); + + createTopLevelAccelerationStructure(); + + /* + Build the acceleration structure + */ + + // Acceleration structure build requires some scratch space to store temporary information + VkAccelerationStructureMemoryRequirementsInfoNV memoryRequirementsInfo{}; + memoryRequirementsInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV; + memoryRequirementsInfo.type = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV; + + VkMemoryRequirements2 memReqBottomLevelAS; + memoryRequirementsInfo.accelerationStructure = bottomLevelAS.accelerationStructure; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memReqBottomLevelAS); + + VkMemoryRequirements2 memReqTopLevelAS; + memoryRequirementsInfo.accelerationStructure = topLevelAS.accelerationStructure; + vkGetAccelerationStructureMemoryRequirementsNV(device, &memoryRequirementsInfo, &memReqTopLevelAS); + + const VkDeviceSize scratchBufferSize = std::max(memReqBottomLevelAS.memoryRequirements.size, memReqTopLevelAS.memoryRequirements.size); + + vks::Buffer scratchBuffer; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &scratchBuffer, + scratchBufferSize)); + + VkCommandBuffer cmdBuffer = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + + /* + Build bottom level acceleration structure + */ + VkAccelerationStructureInfoNV buildInfo{}; + buildInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; + buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV; + buildInfo.geometryCount = 1; + buildInfo.pGeometries = &geometry; + + vkCmdBuildAccelerationStructureNV( + cmdBuffer, + &buildInfo, + VK_NULL_HANDLE, + 0, + VK_FALSE, + bottomLevelAS.accelerationStructure, + VK_NULL_HANDLE, + scratchBuffer.buffer, + 0); + + VkMemoryBarrier memoryBarrier = vks::initializers::memoryBarrier(); + memoryBarrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV; + memoryBarrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV; + vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, 0, 1, &memoryBarrier, 0, 0, 0, 0); + + /* + Build top-level acceleration structure + */ + buildInfo.pGeometries = 0; + buildInfo.geometryCount = 0; + buildInfo.instanceCount = 1; + + vkCmdBuildAccelerationStructureNV( + cmdBuffer, + &buildInfo, + instanceBuffer.buffer, + 0, + VK_FALSE, + topLevelAS.accelerationStructure, + VK_NULL_HANDLE, + scratchBuffer.buffer, + 0); + + vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, 0, 1, &memoryBarrier, 0, 0, 0, 0); + + vulkanDevice->flushCommandBuffer(cmdBuffer, queue); + + scratchBuffer.destroy(); + instanceBuffer.destroy(); + } + + VkDeviceSize copyShaderIdentifier(uint8_t* data, const uint8_t* shaderHandleStorage, uint32_t groupIndex) { + const uint32_t shaderGroupHandleSize = rayTracingProperties.shaderGroupHandleSize; + memcpy(data, shaderHandleStorage + groupIndex * shaderGroupHandleSize, shaderGroupHandleSize); + data += shaderGroupHandleSize; + return shaderGroupHandleSize; + } + + /* + Create the Shader Binding Table that binds the programs and top-level acceleration structure + */ + void createShaderBindingTable() { + // Create buffer for the shader binding table + const uint32_t sbtSize = rayTracingProperties.shaderGroupHandleSize * NUM_SHADER_GROUPS; + VK_CHECK_RESULT(vulkanDevice->createBuffer( + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &shaderBindingTable, + sbtSize)); + shaderBindingTable.map(); + + auto shaderHandleStorage = new uint8_t[sbtSize]; + // Get shader identifiers + VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesNV(device, pipeline, 0, NUM_SHADER_GROUPS, sbtSize, shaderHandleStorage)); + auto* data = static_cast(shaderBindingTable.mapped); + // Copy the shader identifiers to the shader binding table + VkDeviceSize offset = 0; + data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_RAYGEN); + data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_MISS); + data += copyShaderIdentifier(data, shaderHandleStorage, INDEX_CLOSEST_HIT); + shaderBindingTable.unmap(); + } + + /* + Create the descriptor sets used for the ray tracing dispatch + */ + void createDescriptorSets() + { + std::vector poolSizes = { + { VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 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)); + + VkWriteDescriptorSetAccelerationStructureNV descriptorAccelerationStructureInfo{}; + descriptorAccelerationStructureInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV; + descriptorAccelerationStructureInfo.accelerationStructureCount = 1; + descriptorAccelerationStructureInfo.pAccelerationStructures = &topLevelAS.accelerationStructure; + + 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_NV; + + VkDescriptorImageInfo storageImageDescriptor{}; + storageImageDescriptor.imageView = storageImage.view; + storageImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkDescriptorBufferInfo vertexBufferDescriptor{}; + vertexBufferDescriptor.buffer = scene.vertices.buffer; + vertexBufferDescriptor.range = VK_WHOLE_SIZE; + + VkDescriptorBufferInfo indexBufferDescriptor{}; + indexBufferDescriptor.buffer = scene.indices.buffer; + indexBufferDescriptor.range = VK_WHOLE_SIZE; + + VkWriteDescriptorSet resultImageWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, &storageImageDescriptor); + VkWriteDescriptorSet uniformBufferWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, &ubo.descriptor); + VkWriteDescriptorSet vertexBufferWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &vertexBufferDescriptor); + VkWriteDescriptorSet indexBufferWrite = vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4, &indexBufferDescriptor); + + std::vector writeDescriptorSets = { + accelerationStructureWrite, + resultImageWrite, + uniformBufferWrite, + vertexBufferWrite, + indexBufferWrite + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, VK_NULL_HANDLE); + } + + /* + Create our ray tracing pipeline + */ + void createRayTracingPipeline() + { + VkDescriptorSetLayoutBinding accelerationStructureLayoutBinding{}; + accelerationStructureLayoutBinding.binding = 0; + accelerationStructureLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV; + accelerationStructureLayoutBinding.descriptorCount = 1; + accelerationStructureLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV | VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV; + + VkDescriptorSetLayoutBinding resultImageLayoutBinding{}; + resultImageLayoutBinding.binding = 1; + resultImageLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + resultImageLayoutBinding.descriptorCount = 1; + resultImageLayoutBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV; + + VkDescriptorSetLayoutBinding uniformBufferBinding{}; + uniformBufferBinding.binding = 2; + uniformBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniformBufferBinding.descriptorCount = 1; + uniformBufferBinding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_NV | VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV | VK_SHADER_STAGE_MISS_BIT_NV; + + VkDescriptorSetLayoutBinding vertexBufferBinding{}; + vertexBufferBinding.binding = 3; + vertexBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vertexBufferBinding.descriptorCount = 1; + vertexBufferBinding.stageFlags = VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV; + + VkDescriptorSetLayoutBinding indexBufferBinding{}; + indexBufferBinding.binding = 4; + indexBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + indexBufferBinding.descriptorCount = 1; + indexBufferBinding.stageFlags = VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV; + + std::vector bindings({ + accelerationStructureLayoutBinding, + resultImageLayoutBinding, + uniformBufferBinding, + vertexBufferBinding, + indexBufferBinding + }); + + VkDescriptorSetLayoutCreateInfo layoutInfo{}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout)); + + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = 1; + pipelineLayoutCreateInfo.pSetLayouts = &descriptorSetLayout; + + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + + const uint32_t shaderIndexRaygen = 0; + const uint32_t shaderIndexMiss = 1; + const uint32_t shaderIndexClosestHit = 2; + + std::array shaderStages; + shaderStages[shaderIndexRaygen] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_reflections/raygen.rgen.spv", VK_SHADER_STAGE_RAYGEN_BIT_NV); + shaderStages[shaderIndexMiss] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_reflections/miss.rmiss.spv", VK_SHADER_STAGE_MISS_BIT_NV); + shaderStages[shaderIndexClosestHit] = loadShader(getAssetPath() + "shaders/nv_ray_tracing_reflections/closesthit.rchit.spv", VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV); + + // 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[shaderIndexRaygen].pSpecializationInfo = &specializationInfo; + + /* + Setup ray tracing shader groups + */ + std::array groups{}; + for (auto& group : groups) { + // Init all groups with some default values + group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; + group.generalShader = VK_SHADER_UNUSED_NV; + group.closestHitShader = VK_SHADER_UNUSED_NV; + group.anyHitShader = VK_SHADER_UNUSED_NV; + group.intersectionShader = VK_SHADER_UNUSED_NV; + } + + // Links shaders and types to ray tracing shader groups + // Ray generation shader group + groups[INDEX_RAYGEN].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + groups[INDEX_RAYGEN].generalShader = shaderIndexRaygen; + // Scene miss shader group + groups[INDEX_MISS].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; + groups[INDEX_MISS].generalShader = shaderIndexMiss; + // Scene closest hit shader group + groups[INDEX_CLOSEST_HIT].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV; + groups[INDEX_CLOSEST_HIT].generalShader = VK_SHADER_UNUSED_NV; + groups[INDEX_CLOSEST_HIT].closestHitShader = shaderIndexClosestHit; + + VkRayTracingPipelineCreateInfoNV rayPipelineInfo{}; + rayPipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV; + rayPipelineInfo.stageCount = static_cast(shaderStages.size()); + rayPipelineInfo.pStages = shaderStages.data(); + rayPipelineInfo.groupCount = static_cast(groups.size()); + rayPipelineInfo.pGroups = groups.data(); + rayPipelineInfo.maxRecursionDepth = 1; + rayPipelineInfo.layout = pipelineLayout; + VK_CHECK_RESULT(vkCreateRayTracingPipelinesNV(device, VK_NULL_HANDLE, 1, &rayPipelineInfo, 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(); + } + + /* + Command buffer generation + */ + void buildCommandBuffers() + { + 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_NV, pipeline); + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, pipelineLayout, 0, 1, &descriptorSet, 0, 0); + + // Calculate shader binding offsets, which is pretty straight forward in our example + VkDeviceSize bindingOffsetRayGenShader = rayTracingProperties.shaderGroupHandleSize * INDEX_RAYGEN; + VkDeviceSize bindingOffsetMissShader = rayTracingProperties.shaderGroupHandleSize * INDEX_MISS; + VkDeviceSize bindingOffsetHitShader = rayTracingProperties.shaderGroupHandleSize * INDEX_CLOSEST_HIT; + VkDeviceSize bindingStride = rayTracingProperties.shaderGroupHandleSize; + + vkCmdTraceRaysNV(drawCmdBuffers[i], + shaderBindingTable.buffer, bindingOffsetRayGenShader, + shaderBindingTable.buffer, bindingOffsetMissShader, bindingStride, + shaderBindingTable.buffer, bindingOffsetHitShader, bindingStride, + VK_NULL_HANDLE, 0, 0, + width, height, 1); + + /* + Copy raytracing output to swap chain image + */ + + // Prepare current swapchain 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 willl 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)) * 60.0f, -60.0f + sin(glm::radians(timer * 360.0f)) * 20.0f, 60.0f + sin(glm::radians(timer * 360.0f)) * 5.0f, 0.0f); + 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); +// uniformData.lightPos = glm::vec4(0.0f, -5.0f, 0.0f, 0.0f); + memcpy(ubo.mapped, &uniformData, sizeof(uniformData)); + } + + void prepare() + { + VulkanExampleBase::prepare(); + + // Query the ray tracing properties of the current implementation, we will need them later on + rayTracingProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV; + VkPhysicalDeviceProperties2 deviceProps2{}; + deviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + deviceProps2.pNext = &rayTracingProperties; + vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProps2); + + // Get VK_NV_ray_tracing related function pointers + vkCreateAccelerationStructureNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateAccelerationStructureNV")); + vkDestroyAccelerationStructureNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkDestroyAccelerationStructureNV")); + vkBindAccelerationStructureMemoryNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkBindAccelerationStructureMemoryNV")); + vkGetAccelerationStructureHandleNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureHandleNV")); + vkGetAccelerationStructureMemoryRequirementsNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetAccelerationStructureMemoryRequirementsNV")); + vkCmdBuildAccelerationStructureNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructureNV")); + vkCreateRayTracingPipelinesNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCreateRayTracingPipelinesNV")); + vkGetRayTracingShaderGroupHandlesNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetRayTracingShaderGroupHandlesNV")); + vkCmdTraceRaysNV = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdTraceRaysNV")); + + createScene(); + createStorageImage(); + createUniformBuffer(); + createRayTracingPipeline(); + createShaderBindingTable(); + 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()