From 8fdabb00195d758356a3865acde43c30e45c60e7 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Sat, 27 Apr 2019 11:05:29 +0200 Subject: [PATCH] Added VK_NV_ray_tracing reflections example --- .../closesthit.rchit | 70 ++ .../closesthit.rchit.spv | Bin 0 -> 5512 bytes .../nv_ray_tracing_reflections/miss.rmiss | 25 + .../nv_ray_tracing_reflections/miss.rmiss.spv | Bin 0 -> 1296 bytes .../nv_ray_tracing_reflections/raygen.rgen | 62 ++ .../raygen.rgen.spv | Bin 0 -> 4544 bytes .../nv_ray_tracing_reflections.cpp | 761 ++++++++++++++++++ 7 files changed, 918 insertions(+) create mode 100644 data/shaders/nv_ray_tracing_reflections/closesthit.rchit create mode 100644 data/shaders/nv_ray_tracing_reflections/closesthit.rchit.spv create mode 100644 data/shaders/nv_ray_tracing_reflections/miss.rmiss create mode 100644 data/shaders/nv_ray_tracing_reflections/miss.rmiss.spv create mode 100644 data/shaders/nv_ray_tracing_reflections/raygen.rgen create mode 100644 data/shaders/nv_ray_tracing_reflections/raygen.rgen.spv create mode 100644 examples/nv_ray_tracing_reflections/nv_ray_tracing_reflections.cpp 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 0000000000000000000000000000000000000000..a4c1180951b939e524f0570ff87446746eb968ba GIT binary patch literal 5512 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4127505ebd9ccbaaf287d3613ff83c57e14d99ce GIT binary patch literal 4544 zcmZve>vx=06^Gv@nH1VmE-hEFq=a%U1q&1ep)G-?)ifEJ-V`vtOkUD~NhVCDrA3yc zlp=Cd6uf}KQto$Hiw}I|lYa0Y&?Wx{ifgg(dEPl2#?jOD%ijBU_St)%ea?HP{e8nr zvusJWG&>>tAnS}%v%c&ftFvrbHkeIT_fC!PooeMrrrNE%R-c>6vgPnga2*|+9NVza zp5AcvHCMWFW!7H?aPDLxenN}^^x3>V=fp|iOwiB2KIC6<93a-{*#D%+D$I1Rt-zef zn)&+SqA^m;wTl+aU>S3!F*TMSoU0w!am)B#d|Z1vvOc#*v-C6XSALWA2MRnZ)NwBS zbm=pYtwT0j^%<+JQ2#~PBl&F6%B!vBe9>yxiv=jM4wiH3hw8=sJLV1*t%c$%-u=(F zn)meb?zvH)Inb^)%Q)u01g@6PmNjogZ|ALEHI z7W1s7rhcUQ8EAvZA#f(~+>5qzL)a%E<;3aO%SxU<4E^o3obmKufi#!=4axS52<~YZ8n# z(eJ0x`@q@!GQEt{cM0eo z&c~g4B~9pS4cdNd4$t6Cq4&*z}|)JZ(g_UZ(GA-Z9Bav~Lf%C8?3J9c^*;m1MXs~ZeJ>){+30e@(VgoY zZ08?KxxRsJJb80@4swy}o9J>+C;#)%<$RB-iCc?4lyEF@=VR;ZoN+x*xvk9mR$LA) z0lp#Tx)8Ym$UlS6F!Ec8yAo*(?|@vyxEkFUSAmFe4Z6HOp7V9U+?#+t-t%t*`S7^` zT_4|+@c9n9e3gz{%Zn8X*Zbsgc zIC;ks&<9qq1Lto|oKoN0*spur2IL&UjiQHtaN84S%-G`&bl2Ae~p* zH{Y8(fiXU0|5L~x0Xg5{f8(S5ePArts=GEvPJ>#qmmv$_UYysy8@Qi1qZ#ydAkOFj zx}0#t8R_p?)PX&S_4lCLw^)BKx}0$Iu5ZBEx4*4kQPygG*5q9dSc7?trN6!O-ra|6 zf%(K)gZE}3@t(tMvh{DH9|Zcx_v9W9CH~{o-}@8i-S{UljvyWO+PTBvC%|5-N8Sg} z%@_8M(Y*&piECXC0y+KSj>viLadyUyS{Ko+^&wzPYds1U!AGgqhZ8r1wAM$E4r_Jp z82CM~R(0bHBOe8xbJ#yc_uL})W9Yqo`Wc+O@1!vv2d{;_k0&njJc%tA z_v0yaIpK)=VI7|D&w+LLTW$>d_zNI!9oo+%u8On{xnBZfI~Tcsh3=jw_+_!yXMvo) z1E2!zaX+wjef5d;&!KnU?O$Wd2}f`3zk%zm{kL%PD|mCQ^LM~?qriC1N51FLJNY{G zzJM*~n*Ud?zWR8dya%4SXCHn01A6!Ud=b0*e!he)r(fJpx$gUETy4Jz<14jqPqxy0 zmm>cN_JKt@tNo|M`Ry6sIXTzwM;g!X!1XU9UjwfudpYuTa1x0B1H6$qd53*`khO0j z-vZ9Z-rq)dukqXP4!WEpxOdUzj1$~@iR(kgZ^--TuGimr--#M|t=GKHe?gS%z<&Ux CV}peN literal 0 HcmV?d00001 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()