/* Crafter®.Graphics Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #ifdef CRAFTER_GRAPHICS_VULKAN #include #endif export module Crafter.Graphics:PipelineRTVulkan; #ifdef CRAFTER_GRAPHICS_VULKAN import std; import :VulkanDevice; import :DescriptorLayoutVulkan; import :VulkanBuffer; import :Types; export namespace Crafter { template class PipelineRTVulkan { public: inline static VkPipeline pipeline; inline static VkPipelineLayout pipelineLayout; inline static std::vector shaderHandles; inline static VulkanBuffer sbtBuffer; inline static VkStridedDeviceAddressRegionKHR raygenRegion; inline static VkStridedDeviceAddressRegionKHR missRegion; inline static VkStridedDeviceAddressRegionKHR hitRegion; inline static VkStridedDeviceAddressRegionKHR callableRegion; static void Init() { VkPipelineLayoutCreateInfo pipelineLayoutInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = sizeof...(Shaders), .pSetLayouts = DescriptorLayoutVulkan::descriptorSetLayout }; VulkanDevice::CheckVkResult(vkCreatePipelineLayout(VulkanDevice::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); std::array shaderStages; shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[0].stage = Raygen::_stage; shaderStages[0].module = Raygen::shader; shaderStages[0].pName = Raygen::_entrypoint.value; shaderStages[0].flags = 0; shaderStages[0].pSpecializationInfo = nullptr; shaderStages[0].pNext = nullptr; shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[1].stage = Miss::_stage; shaderStages[1].module = Miss::shader; shaderStages[1].pName = Miss::_entrypoint.value; shaderStages[1].flags = 0; shaderStages[1].pSpecializationInfo = nullptr; shaderStages[1].pNext = nullptr; shaderStages[2].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[2].stage = ClosestHit::_stage; shaderStages[2].module = ClosestHit::shader; shaderStages[2].pName = ClosestHit::_entrypoint.value; shaderStages[2].flags = 0; shaderStages[2].pSpecializationInfo = nullptr; shaderStages[2].pNext = nullptr; std::array groups {{ { .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, .generalShader = 0, .closestHitShader = VK_SHADER_UNUSED_KHR, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR }, { .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, .generalShader = 1, .closestHitShader = VK_SHADER_UNUSED_KHR, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR }, { .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR, .generalShader = VK_SHADER_UNUSED_KHR, .closestHitShader = 2, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR } }}; VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, .stageCount = static_cast(shaderStages.size()), .pStages = shaderStages.data(), .groupCount = static_cast(groups.size()), .pGroups = groups.data(), .maxPipelineRayRecursionDepth = 1, .layout = pipelineLayout }; VulkanDevice::CheckVkResult(VulkanDevice::vkCreateRayTracingPipelinesKHR(VulkanDevice::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); std::uint32_t handleSize = VulkanDevice::rayTracingProperties.shaderGroupHandleSize; std::uint32_t handleAlignment = VulkanDevice::rayTracingProperties.shaderGroupHandleAlignment; std::uint32_t baseAlignment = VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment; std::uint32_t groupCount = rtPipelineInfo.groupCount; std::size_t dataSize = handleSize * groupCount; shaderHandles.resize(dataSize); VulkanDevice::CheckVkResult(VulkanDevice::vkGetRayTracingShaderGroupHandlesKHR(VulkanDevice::device, pipeline, 0, groupCount, dataSize, shaderHandles.data())); std::uint32_t raygenSize = AlignUp(handleSize, handleAlignment); std::uint32_t missSize = AlignUp(handleSize, handleAlignment); std::uint32_t hitSize = AlignUp(handleSize, handleAlignment); std::uint32_t callableSize = 0; std::uint32_t raygenOffset = 0; std::uint32_t missOffset = AlignUp(raygenSize, baseAlignment); std::uint32_t hitOffset = AlignUp(missOffset + missSize, baseAlignment); std::uint32_t callableOffset = AlignUp(hitOffset + hitSize, baseAlignment); std::size_t bufferSize = callableOffset + callableSize; sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize); // Ray generation shader (group 0) std::memcpy(sbtBuffer.value + raygenOffset, shaderHandles.data() + 0 * handleSize, handleSize); raygenRegion.deviceAddress = sbtBuffer.address + raygenOffset; raygenRegion.stride = raygenSize; raygenRegion.size = raygenSize; std::memcpy(sbtBuffer.value + missOffset, shaderHandles.data() + 1 * handleSize, handleSize); missRegion.deviceAddress = sbtBuffer.address + missOffset; missRegion.stride = missSize; missRegion.size = missSize; std::memcpy(sbtBuffer.value + hitOffset, shaderHandles.data() + 2 * handleSize, handleSize); hitRegion.deviceAddress = sbtBuffer.address + hitOffset; hitRegion.stride = hitSize; hitRegion.size = hitSize; callableRegion.deviceAddress = 0; callableRegion.stride = 0; callableRegion.size = 0; } }; } #endif