/* * Vulkan Example - Using ray queries for hardware accelerated ray tracing queries in a fragment shader * * Copyright (C) 2020-2023 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ #include "vulkanexamplebase.h" #include "VulkanglTFModel.h" #include "VulkanRaytracingSample.h" #define ENABLE_VALIDATION false class VulkanExample : public VulkanRaytracingSample { public: glm::vec3 lightPos = glm::vec3(); struct UniformData { glm::mat4 projection; glm::mat4 view; glm::mat4 model; glm::vec3 lightPos; } uniformData; vks::Buffer ubo; vkglTF::Model scene; VkPipeline pipeline; VkPipelineLayout pipelineLayout; VkDescriptorSet descriptorSet; VkDescriptorSetLayout descriptorSetLayout; VulkanRaytracingSample::AccelerationStructure bottomLevelAS{}; VulkanRaytracingSample::AccelerationStructure topLevelAS{}; VkPhysicalDeviceRayQueryFeaturesKHR enabledRayQueryFeatures{}; VulkanExample() : VulkanRaytracingSample() { title = "Ray queries for ray traced shadows"; camera.type = Camera::CameraType::lookat; timerSpeed *= 0.25f; 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, 3.0f, -10.0f)); rayQueryOnly = true; enableExtensions(); enabledDeviceExtensions.push_back(VK_KHR_RAY_QUERY_EXTENSION_NAME); } ~VulkanExample() { vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); ubo.destroy(); deleteAccelerationStructure(bottomLevelAS); deleteAccelerationStructure(topLevelAS); } /* Create the bottom level acceleration structure contains the scene's actual geometry (vertices, triangles) */ void createBottomLevelAccelerationStructure() { VkDeviceOrHostAddressConstKHR vertexBufferDeviceAddress{}; VkDeviceOrHostAddressConstKHR indexBufferDeviceAddress{}; vertexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.vertices.buffer); indexBufferDeviceAddress.deviceAddress = getBufferDeviceAddress(scene.indices.buffer); uint32_t numTriangles = static_cast(scene.indices.count) / 3; // Build VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR(); accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; accelerationStructureGeometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; accelerationStructureGeometry.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; accelerationStructureGeometry.geometry.triangles.vertexData = vertexBufferDeviceAddress; accelerationStructureGeometry.geometry.triangles.maxVertex = scene.vertices.count - 1; accelerationStructureGeometry.geometry.triangles.vertexStride = sizeof(vkglTF::Vertex); accelerationStructureGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; accelerationStructureGeometry.geometry.triangles.indexData = indexBufferDeviceAddress; accelerationStructureGeometry.geometry.triangles.transformData.deviceAddress = 0; accelerationStructureGeometry.geometry.triangles.transformData.hostAddress = nullptr; // Get size info VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationStructureBuildGeometryInfo.geometryCount = 1; accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR(); vkGetAccelerationStructureBuildSizesKHR( device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &accelerationStructureBuildGeometryInfo, &numTriangles, &accelerationStructureBuildSizesInfo); createAccelerationStructure(bottomLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, accelerationStructureBuildSizesInfo); // Create a small scratch buffer used during build of the bottom level acceleration structure ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; accelerationBuildGeometryInfo.dstAccelerationStructure = bottomLevelAS.handle; accelerationBuildGeometryInfo.geometryCount = 1; accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; accelerationStructureBuildRangeInfo.primitiveCount = numTriangles; accelerationStructureBuildRangeInfo.primitiveOffset = 0; accelerationStructureBuildRangeInfo.firstVertex = 0; accelerationStructureBuildRangeInfo.transformOffset = 0; std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; // 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); deleteScratchBuffer(scratchBuffer); } /* The top level acceleration structure contains the scene's object instances */ void createTopLevelAccelerationStructure() { VkTransformMatrixKHR transformMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }; VkAccelerationStructureInstanceKHR instance{}; instance.transform = transformMatrix; instance.instanceCustomIndex = 0; instance.mask = 0xFF; instance.instanceShaderBindingTableRecordOffset = 0; instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; instance.accelerationStructureReference = bottomLevelAS.deviceAddress; // Buffer for instance data vks::Buffer instancesBuffer; VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &instancesBuffer, sizeof(VkAccelerationStructureInstanceKHR), &instance)); VkDeviceOrHostAddressConstKHR instanceDataDeviceAddress{}; instanceDataDeviceAddress.deviceAddress = getBufferDeviceAddress(instancesBuffer.buffer); VkAccelerationStructureGeometryKHR accelerationStructureGeometry = vks::initializers::accelerationStructureGeometryKHR(); accelerationStructureGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; accelerationStructureGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; accelerationStructureGeometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; accelerationStructureGeometry.geometry.instances.arrayOfPointers = VK_FALSE; accelerationStructureGeometry.geometry.instances.data = instanceDataDeviceAddress; // Get size info VkAccelerationStructureBuildGeometryInfoKHR accelerationStructureBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationStructureBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; accelerationStructureBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationStructureBuildGeometryInfo.geometryCount = 1; accelerationStructureBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; uint32_t primitive_count = 1; VkAccelerationStructureBuildSizesInfoKHR accelerationStructureBuildSizesInfo = vks::initializers::accelerationStructureBuildSizesInfoKHR(); vkGetAccelerationStructureBuildSizesKHR( device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &accelerationStructureBuildGeometryInfo, &primitive_count, &accelerationStructureBuildSizesInfo); createAccelerationStructure(topLevelAS, VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, accelerationStructureBuildSizesInfo); // Create a small scratch buffer used during build of the top level acceleration structure ScratchBuffer scratchBuffer = createScratchBuffer(accelerationStructureBuildSizesInfo.buildScratchSize); VkAccelerationStructureBuildGeometryInfoKHR accelerationBuildGeometryInfo = vks::initializers::accelerationStructureBuildGeometryInfoKHR(); accelerationBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; accelerationBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; accelerationBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; accelerationBuildGeometryInfo.dstAccelerationStructure = topLevelAS.handle; accelerationBuildGeometryInfo.geometryCount = 1; accelerationBuildGeometryInfo.pGeometries = &accelerationStructureGeometry; accelerationBuildGeometryInfo.scratchData.deviceAddress = scratchBuffer.deviceAddress; VkAccelerationStructureBuildRangeInfoKHR accelerationStructureBuildRangeInfo{}; accelerationStructureBuildRangeInfo.primitiveCount = 1; accelerationStructureBuildRangeInfo.primitiveOffset = 0; accelerationStructureBuildRangeInfo.firstVertex = 0; accelerationStructureBuildRangeInfo.transformOffset = 0; std::vector accelerationBuildStructureRangeInfos = { &accelerationStructureBuildRangeInfo }; // 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); deleteScratchBuffer(scratchBuffer); instancesBuffer.destroy(); } void buildCommandBuffers() { VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkClearValue clearValues[2]; VkViewport viewport; VkRect2D scissor; for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); /* Note: Explicit synchronization is not required between the render pass, as this is done implicit via sub pass dependencies */ /* Second pass: Scene rendering with applied shadow map */ clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 1.0f } };; clearValues[1].depthStencil = { 1.0f, 0 }; VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.framebuffer = frameBuffers[i]; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; renderPassBeginInfo.clearValueCount = 2; renderPassBeginInfo.pClearValues = clearValues; vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); scissor = vks::initializers::rect2D(width, height, 0, 0); vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); // 3D scene vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); scene.draw(drawCmdBuffers[i]); VulkanExampleBase::drawUI(drawCmdBuffers[i]); vkCmdEndRenderPass(drawCmdBuffers[i]); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); } } 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; const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; scene.loadFromFile(getAssetPath() + "models/vulkanscene_shadow.gltf", vulkanDevice, queue, glTFLoadingFlags); } void setupDescriptorPool() { std::vector poolSizes = { vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1) }; VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); } void setupDescriptorSetLayout() { // Shared pipeline layout for all pipelines used in this sample std::vector setLayoutBindings = { // Binding 0 : Vertex shader uniform buffer vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0), // Binding 1: Acceleration structure vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, VK_SHADER_STAGE_FRAGMENT_BIT, 1), }; VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1); VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout)); } void setupDescriptorSets() { std::vector writeDescriptorSets; // Debug display VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr); // Scene rendering with shadow map applied VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &ubo.descriptor) }; 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 = 1; accelerationStructureWrite.descriptorCount = 1; accelerationStructureWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; writeDescriptorSets.push_back(accelerationStructureWrite); vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr); } void preparePipelines() { VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), dynamicStateEnables.size(), 0); std::array shaderStages; VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; pipelineCI.pRasterizationState = &rasterizationStateCI; pipelineCI.pColorBlendState = &colorBlendStateCI; pipelineCI.pMultisampleState = &multisampleStateCI; pipelineCI.pViewportState = &viewportStateCI; pipelineCI.pDepthStencilState = &depthStencilStateCI; pipelineCI.pDynamicState = &dynamicStateCI; pipelineCI.stageCount = shaderStages.size(); pipelineCI.pStages = shaderStages.data(); // Scene rendering with ray traced shadows applied pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color, vkglTF::VertexComponent::Normal }); rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT; shaderStages[0] = loadShader(getShadersPath() + "rayquery/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getShadersPath() + "rayquery/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); } // Prepare and initialize uniform buffer containing shader uniforms void prepareUniformBuffers() { // Scene vertex shader uniform buffer block 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))); // Map persistent VK_CHECK_RESULT(ubo.map()); updateLight(); updateUniformBuffers(); } void updateLight() { // Animate the light source lightPos.x = cos(glm::radians(timer * 360.0f)) * 40.0f; lightPos.y = -50.0f + sin(glm::radians(timer * 360.0f)) * 20.0f; lightPos.z = 25.0f + sin(glm::radians(timer * 360.0f)) * 5.0f; } void updateUniformBuffers() { uniformData.projection = camera.matrices.perspective; uniformData.view = camera.matrices.view; uniformData.model = glm::mat4(1.0f); uniformData.lightPos = lightPos; 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; enabledRayQueryFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR; enabledRayQueryFeatures.rayQuery = VK_TRUE; enabledRayQueryFeatures.pNext = &enabledAccelerationStructureFeatures; deviceCreatepNextChain = &enabledRayQueryFeatures; } void draw() { VulkanExampleBase::prepareFrame(); // Command buffer to be submitted to the queue submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; // Submit to queue VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VulkanExampleBase::submitFrame(); } void prepare() { VulkanRaytracingSample::prepare(); loadAssets(); prepareUniformBuffers(); setupDescriptorSetLayout(); preparePipelines(); createBottomLevelAccelerationStructure(); createTopLevelAccelerationStructure(); setupDescriptorPool(); setupDescriptorSets(); buildCommandBuffers(); prepared = true; } virtual void render() { if (!prepared) return; draw(); if (!paused || camera.updated) { updateLight(); updateUniformBuffers(); } } }; VULKAN_EXAMPLE_MAIN()