#include "PipelineCache.h" #include "Descriptor.h" #include "Enums.h" #include "Graphics.h" #include "RenderPass.h" #include "Shader.h" #include #include using namespace Seele; using namespace Seele::Vulkan; PipelineCache::PipelineCache(PGraphics graphics, const std::string& cacheFilePath) : graphics(graphics), cacheFile(cacheFilePath) { Array cacheData; std::ifstream stream(cacheFilePath, std::ios::binary | std::ios::ate); if (stream.good()) { uint32 fileSize = static_cast(stream.tellg()); cacheData.resize(fileSize); stream.seekg(0); stream.read((char*)cacheData.data(), fileSize); std::cout << "Loaded " << fileSize << " bytes from pipeline cache" << std::endl; } VkPipelineCacheCreateInfo cacheCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, .pNext = nullptr, .flags = 0, .initialDataSize = cacheData.size(), .pInitialData = cacheData.data(), }; VK_CHECK(vkCreatePipelineCache(graphics->getDevice(), &cacheCreateInfo, nullptr, &cache)); } PipelineCache::~PipelineCache() { size_t cacheSize; VK_CHECK(vkGetPipelineCacheData(graphics->getDevice(), cache, &cacheSize, nullptr)); Array cacheData(cacheSize); VK_CHECK(vkGetPipelineCacheData(graphics->getDevice(), cache, &cacheSize, cacheData.data())); std::ofstream stream(cacheFile, std::ios::binary); stream.write((char*)cacheData.data(), cacheSize); stream.flush(); stream.close(); vkDestroyPipelineCache(graphics->getDevice(), cache, nullptr); std::cout << "Written " << cacheSize << " bytes to cache" << std::endl; } PGraphicsPipeline PipelineCache::createPipeline(Gfx::LegacyPipelineCreateInfo gfxInfo) { uint32 hash = CRC::Calculate(&gfxInfo, sizeof(Gfx::LegacyPipelineCreateInfo), CRC::CRC_32()); if (graphicsPipelines.contains(hash)) { return graphicsPipelines[hash]; } PPipelineLayout layout = gfxInfo.pipelineLayout.cast(); Array bindings; Array attributes; if (gfxInfo.vertexInput != nullptr) { const VertexInputStateCreateInfo& vertexInputDesc = gfxInfo.vertexInput->getInfo(); for (const auto& b : vertexInputDesc.bindings) { bindings.add() = { .binding = b.binding, .stride = b.stride, .inputRate = VkVertexInputRate(b.inputRate), }; } for (const auto& a : vertexInputDesc.attributes) { attributes.add() = { .location = a.location, .binding = a.binding, .format = cast(a.format), .offset = a.offset, }; } } VkPipelineVertexInputStateCreateInfo vertexInput = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .vertexBindingDescriptionCount = (uint32)bindings.size(), .pVertexBindingDescriptions = bindings.data(), .vertexAttributeDescriptionCount = (uint32)attributes.size(), .pVertexAttributeDescriptions = attributes.data(), }; uint32 stageCount = 0; VkPipelineShaderStageCreateInfo stageInfos[2]; std::memset(stageInfos, 0, sizeof(stageInfos)); PVertexShader vertexShader = gfxInfo.vertexShader.cast(); stageInfos[stageCount++] = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vertexShader->getModuleHandle(), .pName = vertexShader->getEntryPointName(), .pSpecializationInfo = nullptr, }; if (gfxInfo.fragmentShader != nullptr) { PFragmentShader fragment = gfxInfo.fragmentShader.cast(); stageInfos[stageCount++] = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fragment->getModuleHandle(), .pName = fragment->getEntryPointName(), .pSpecializationInfo = nullptr, }; } VkPipelineInputAssemblyStateCreateInfo assemblyInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .topology = cast(gfxInfo.topology), .primitiveRestartEnable = false, }; VkPipelineViewportStateCreateInfo viewportInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .viewportCount = 1, .pViewports = nullptr, .scissorCount = 1, .pScissors = nullptr, }; VkPipelineRasterizationStateCreateInfo rasterizationState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthClampEnable = gfxInfo.rasterizationState.depthClampEnable, .rasterizerDiscardEnable = gfxInfo.rasterizationState.rasterizerDiscardEnable, .polygonMode = cast(gfxInfo.rasterizationState.polygonMode), .cullMode = gfxInfo.rasterizationState.cullMode, .frontFace = (VkFrontFace)gfxInfo.rasterizationState.frontFace, .depthBiasEnable = gfxInfo.rasterizationState.depthBiasEnable, .depthBiasConstantFactor = gfxInfo.rasterizationState.depthBiasConstantFactor, .depthBiasClamp = gfxInfo.rasterizationState.depthBiasClamp, .depthBiasSlopeFactor = gfxInfo.rasterizationState.depthBiasSlopeFactor, .lineWidth = gfxInfo.rasterizationState.lineWidth, }; VkPipelineMultisampleStateCreateInfo multisampleState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .rasterizationSamples = (VkSampleCountFlagBits)gfxInfo.multisampleState.samples, .sampleShadingEnable = gfxInfo.multisampleState.sampleShadingEnable, .minSampleShading = gfxInfo.multisampleState.minSampleShading, .alphaToCoverageEnable = gfxInfo.multisampleState.alphaCoverageEnable, .alphaToOneEnable = gfxInfo.multisampleState.alphaToOneEnable, }; VkPipelineDepthStencilStateCreateInfo depthStencilState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthTestEnable = gfxInfo.depthStencilState.depthTestEnable, .depthWriteEnable = gfxInfo.depthStencilState.depthWriteEnable, .depthCompareOp = cast(gfxInfo.depthStencilState.depthCompareOp), .depthBoundsTestEnable = gfxInfo.depthStencilState.depthBoundsTestEnable, .front = {(VkStencilOp)gfxInfo.depthStencilState.front}, .back = {(VkStencilOp)gfxInfo.depthStencilState.back}, .minDepthBounds = gfxInfo.depthStencilState.minDepthBounds, .maxDepthBounds = gfxInfo.depthStencilState.maxDepthBounds, }; Array blendAttachments; for (uint32 i = 0; i < gfxInfo.colorBlend.attachmentCount; ++i) { const Gfx::ColorBlendState::BlendAttachment& attachment = gfxInfo.colorBlend.blendAttachments[i]; blendAttachments.add() = { .blendEnable = attachment.blendEnable, .srcColorBlendFactor = (VkBlendFactor)attachment.srcColorBlendFactor, .dstColorBlendFactor = (VkBlendFactor)attachment.dstColorBlendFactor, .colorBlendOp = (VkBlendOp)attachment.colorBlendOp, .srcAlphaBlendFactor = (VkBlendFactor)attachment.srcAlphaBlendFactor, .dstAlphaBlendFactor = (VkBlendFactor)attachment.dstAlphaBlendFactor, .alphaBlendOp = (VkBlendOp)attachment.alphaBlendOp, .colorWriteMask = attachment.colorWriteMask, }; } VkPipelineColorBlendStateCreateInfo blendState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .logicOpEnable = gfxInfo.colorBlend.logicOpEnable, .logicOp = (VkLogicOp)gfxInfo.colorBlend.logicOp, .attachmentCount = (uint32)blendAttachments.size(), .pAttachments = blendAttachments.data(), }; std::memcpy(blendState.blendConstants, gfxInfo.colorBlend.blendConstants.data(), sizeof(blendState.blendConstants)); uint32 numDynamicEnabled = 0; StaticArray dynamicEnabled; dynamicEnabled[numDynamicEnabled++] = VK_DYNAMIC_STATE_VIEWPORT; dynamicEnabled[numDynamicEnabled++] = VK_DYNAMIC_STATE_SCISSOR; VkPipelineDynamicStateCreateInfo dynamicState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .dynamicStateCount = (uint32)dynamicEnabled.size(), .pDynamicStates = dynamicEnabled.data(), }; VkPipeline pipelineHandle; VkGraphicsPipelineCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = 0, .flags = 0, .stageCount = stageCount, .pStages = stageInfos, .pVertexInputState = &vertexInput, .pInputAssemblyState = &assemblyInfo, .pViewportState = &viewportInfo, .pRasterizationState = &rasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &depthStencilState, .pColorBlendState = &blendState, .pDynamicState = &dynamicState, .layout = layout->getHandle(), .renderPass = gfxInfo.renderPass.cast()->getHandle(), .subpass = 0, }; auto beginTime = std::chrono::high_resolution_clock::now(); VK_CHECK(vkCreateGraphicsPipelines(graphics->getDevice(), cache, 1, &createInfo, nullptr, &pipelineHandle)); auto endTime = std::chrono::high_resolution_clock::now(); int64 delta = std::chrono::duration_cast(endTime - beginTime).count(); std::cout << "Gfx creation time: " << delta << std::endl; OGraphicsPipeline pipeline = new GraphicsPipeline(graphics, pipelineHandle, gfxInfo.pipelineLayout); PGraphicsPipeline result = pipeline; graphicsPipelines[hash] = std::move(pipeline); return result; } PGraphicsPipeline PipelineCache::createPipeline(Gfx::MeshPipelineCreateInfo gfxInfo) { uint32 hash = CRC::Calculate(&gfxInfo, sizeof(Gfx::MeshPipelineCreateInfo), CRC::CRC_32()); std::cout << hash << std::endl; if (graphicsPipelines.contains(hash)) { std::cout << "found exisiting" << std::endl; return graphicsPipelines[hash]; } PPipelineLayout layout = Gfx::PPipelineLayout(gfxInfo.pipelineLayout).cast(); // uint32 hash = layout->getHash(); uint32 stageCount = 0; VkPipelineShaderStageCreateInfo stageInfos[3]; std::memset(stageInfos, 0, sizeof(stageInfos)); if (gfxInfo.taskShader != nullptr) { PTaskShader taskShader = gfxInfo.taskShader.cast(); stageInfos[stageCount++] = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_TASK_BIT_EXT, .module = taskShader->getModuleHandle(), .pName = taskShader->getEntryPointName(), .pSpecializationInfo = nullptr, }; } PMeshShader meshShader = gfxInfo.meshShader.cast(); stageInfos[stageCount++] = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_MESH_BIT_EXT, .module = meshShader->getModuleHandle(), .pName = meshShader->getEntryPointName(), .pSpecializationInfo = nullptr, }; if (gfxInfo.fragmentShader != nullptr) { PFragmentShader fragment = gfxInfo.fragmentShader.cast(); stageInfos[stageCount++] = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fragment->getModuleHandle(), .pName = fragment->getEntryPointName(), .pSpecializationInfo = nullptr, }; } // hash = CRC::Calculate(stageInfos, sizeof(stageInfos), CRC::CRC_32(), hash); VkPipelineViewportStateCreateInfo viewportInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .viewportCount = 1, .pViewports = nullptr, .scissorCount = 1, .pScissors = nullptr, }; // hash = CRC::Calculate(&viewportInfo, sizeof(VkPipelineViewportStateCreateInfo), CRC::CRC_32(), hash); VkPipelineRasterizationStateCreateInfo rasterizationState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthClampEnable = gfxInfo.rasterizationState.depthClampEnable, .rasterizerDiscardEnable = gfxInfo.rasterizationState.rasterizerDiscardEnable, .polygonMode = cast(gfxInfo.rasterizationState.polygonMode), .cullMode = gfxInfo.rasterizationState.cullMode, .frontFace = (VkFrontFace)gfxInfo.rasterizationState.frontFace, .depthBiasEnable = gfxInfo.rasterizationState.depthBiasEnable, .depthBiasConstantFactor = gfxInfo.rasterizationState.depthBiasConstantFactor, .depthBiasClamp = gfxInfo.rasterizationState.depthBiasClamp, .depthBiasSlopeFactor = gfxInfo.rasterizationState.depthBiasSlopeFactor, .lineWidth = 0, }; // hash = CRC::Calculate(&rasterizationState, sizeof(VkPipelineRasterizationStateCreateInfo), CRC::CRC_32(), hash); VkPipelineMultisampleStateCreateInfo multisampleState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .rasterizationSamples = (VkSampleCountFlagBits)gfxInfo.multisampleState.samples, .sampleShadingEnable = gfxInfo.multisampleState.sampleShadingEnable, .minSampleShading = gfxInfo.multisampleState.minSampleShading, .pSampleMask = nullptr, .alphaToCoverageEnable = gfxInfo.multisampleState.alphaCoverageEnable, .alphaToOneEnable = gfxInfo.multisampleState.alphaToOneEnable, }; // hash = CRC::Calculate(&multisampleState, sizeof(VkPipelineMultisampleStateCreateInfo), CRC::CRC_32(), hash); VkPipelineDepthStencilStateCreateInfo depthStencilState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthTestEnable = gfxInfo.depthStencilState.depthTestEnable, .depthWriteEnable = gfxInfo.depthStencilState.depthWriteEnable, .depthCompareOp = cast(gfxInfo.depthStencilState.depthCompareOp), .depthBoundsTestEnable = gfxInfo.depthStencilState.depthBoundsTestEnable, .stencilTestEnable = gfxInfo.depthStencilState.stencilTestEnable, .front = VkStencilOpState{ .failOp = VK_STENCIL_OP_ZERO, .passOp = (VkStencilOp)gfxInfo.depthStencilState.front, .depthFailOp = VK_STENCIL_OP_ZERO, .compareOp = VK_COMPARE_OP_ALWAYS, .compareMask = 0, .writeMask = 0, .reference = 0, }, .back = VkStencilOpState{ .failOp = VK_STENCIL_OP_ZERO, .passOp = (VkStencilOp)gfxInfo.depthStencilState.back, .depthFailOp = VK_STENCIL_OP_ZERO, .compareOp = VK_COMPARE_OP_ALWAYS, .compareMask = 0, .writeMask = 0, .reference = 0, }, .minDepthBounds = gfxInfo.depthStencilState.minDepthBounds, .maxDepthBounds = gfxInfo.depthStencilState.maxDepthBounds, }; // hash = CRC::Calculate(&depthStencilState, sizeof(VkPipelineDepthStencilStateCreateInfo), CRC::CRC_32(), hash); Array blendAttachments; for (uint32 i = 0; i < gfxInfo.colorBlend.attachmentCount; ++i) { const Gfx::ColorBlendState::BlendAttachment& attachment = gfxInfo.colorBlend.blendAttachments[i]; blendAttachments.add() = { .blendEnable = attachment.blendEnable, .srcColorBlendFactor = (VkBlendFactor)attachment.srcColorBlendFactor, .dstColorBlendFactor = (VkBlendFactor)attachment.dstColorBlendFactor, .colorBlendOp = (VkBlendOp)attachment.colorBlendOp, .srcAlphaBlendFactor = (VkBlendFactor)attachment.srcAlphaBlendFactor, .dstAlphaBlendFactor = (VkBlendFactor)attachment.dstAlphaBlendFactor, .alphaBlendOp = (VkBlendOp)attachment.alphaBlendOp, .colorWriteMask = attachment.colorWriteMask, }; } // hash = CRC::Calculate(blendAttachments.data(), blendAttachments.size() * sizeof(VkPipelineColorBlendAttachmentState), CRC::CRC_32(), // hash); VkPipelineColorBlendStateCreateInfo blendState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .logicOpEnable = gfxInfo.colorBlend.logicOpEnable, .logicOp = (VkLogicOp)gfxInfo.colorBlend.logicOp, .attachmentCount = (uint32)blendAttachments.size(), .pAttachments = blendAttachments.data(), }; std::memcpy(blendState.blendConstants, gfxInfo.colorBlend.blendConstants.data(), sizeof(blendState.blendConstants)); uint32 numDynamicEnabled = 0; StaticArray dynamicEnabled; dynamicEnabled[numDynamicEnabled++] = VK_DYNAMIC_STATE_VIEWPORT; dynamicEnabled[numDynamicEnabled++] = VK_DYNAMIC_STATE_SCISSOR; // hash = CRC::Calculate(dynamicEnabled.data(), sizeof(dynamicEnabled), CRC::CRC_32(), hash); VkPipelineDynamicStateCreateInfo dynamicState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, .dynamicStateCount = (uint32)dynamicEnabled.size(), .pDynamicStates = dynamicEnabled.data(), }; VkPipeline pipelineHandle; VkGraphicsPipelineCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = 0, .flags = 0, .stageCount = stageCount, .pStages = stageInfos, .pVertexInputState = nullptr, .pInputAssemblyState = nullptr, .pViewportState = &viewportInfo, .pRasterizationState = &rasterizationState, .pMultisampleState = &multisampleState, .pDepthStencilState = &depthStencilState, .pColorBlendState = &blendState, .pDynamicState = &dynamicState, .layout = layout->getHandle(), .renderPass = gfxInfo.renderPass.cast()->getHandle(), .subpass = 0, }; auto beginTime = std::chrono::high_resolution_clock::now(); VK_CHECK(vkCreateGraphicsPipelines(graphics->getDevice(), cache, 1, &createInfo, nullptr, &pipelineHandle)); auto endTime = std::chrono::high_resolution_clock::now(); int64 delta = std::chrono::duration_cast(endTime - beginTime).count(); std::cout << "Gfx creation time: " << delta << std::endl; OGraphicsPipeline pipeline = new GraphicsPipeline(graphics, pipelineHandle, gfxInfo.pipelineLayout); PGraphicsPipeline result = pipeline; graphicsPipelines[hash] = std::move(pipeline); return result; } PComputePipeline PipelineCache::createPipeline(Gfx::ComputePipelineCreateInfo computeInfo) { PPipelineLayout layout = computeInfo.pipelineLayout.cast(); auto computeStage = computeInfo.computeShader.cast(); uint32 hash = layout->getHash(); VkComputePipelineCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, .pNext = 0, .flags = 0, .stage = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_COMPUTE_BIT, .module = computeStage->getModuleHandle(), .pName = computeStage->getEntryPointName(), }, .layout = layout->getHandle(), .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0, }; hash = CRC::Calculate(&createInfo, sizeof(createInfo), CRC::CRC_32(), hash); VkPipeline pipelineHandle; auto beginTime = std::chrono::high_resolution_clock::now(); VK_CHECK(vkCreateComputePipelines(graphics->getDevice(), cache, 1, &createInfo, nullptr, &pipelineHandle)); auto endTime = std::chrono::high_resolution_clock::now(); int64 delta = std::chrono::duration_cast(endTime - beginTime).count(); std::cout << "Compute creation time: " << delta << std::endl; OComputePipeline pipeline = new ComputePipeline(graphics, pipelineHandle, computeInfo.pipelineLayout); PComputePipeline result = pipeline; graphicsPipelines[hash] = std::move(pipeline); return result; } PRayTracingPipeline PipelineCache::createPipeline(Gfx::RayTracingPipelineCreateInfo createInfo) { Array shaderStages; Array shaderGroups; { auto rayGen = createInfo.rayGenGroup.shader.cast(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR, .module = rayGen->getModuleHandle(), .pName = rayGen->getEntryPointName(), .pSpecializationInfo = nullptr, }); shaderGroups.add(VkRayTracingShaderGroupCreateInfoKHR{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .pNext = nullptr, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, .generalShader = static_cast(shaderStages.size() - 1), .closestHitShader = VK_SHADER_UNUSED_KHR, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR, .pShaderGroupCaptureReplayHandle = nullptr, }); } { for (const auto& hitgroup : createInfo.hitGroups) { auto hit = hitgroup.closestHitShader.cast(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, .module = hit->getModuleHandle(), .pName = hit->getEntryPointName(), .pSpecializationInfo = nullptr, }); uint32 hitIndex = static_cast(shaderStages.size() - 1); uint32 anyHitIndex = VK_SHADER_UNUSED_KHR; uint32 intersectionIndex = VK_SHADER_UNUSED_KHR; if (hitgroup.anyHitShader != nullptr) { auto anyHit = hitgroup.anyHitShader.cast(); anyHitIndex = (uint32)shaderStages.size(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_ANY_HIT_BIT_KHR, .module = anyHit->getModuleHandle(), .pName = anyHit->getEntryPointName(), .pSpecializationInfo = nullptr, }); } if (hitgroup.intersectionShader != nullptr) { auto intersect = hitgroup.intersectionShader.cast(); intersectionIndex = (uint32)shaderGroups.size(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_INTERSECTION_BIT_KHR, .module = intersect->getModuleHandle(), .pName = intersect->getEntryPointName(), .pSpecializationInfo = nullptr, }); } shaderGroups.add(VkRayTracingShaderGroupCreateInfoKHR{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .pNext = nullptr, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR, .generalShader = VK_SHADER_UNUSED_KHR, .closestHitShader = hitIndex, .anyHitShader = anyHitIndex, .intersectionShader = intersectionIndex, .pShaderGroupCaptureReplayHandle = nullptr, }); } } { for (auto gfxMiss : createInfo.missGroups) { auto miss = gfxMiss.shader.cast(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_MISS_BIT_KHR, .module = miss->getModuleHandle(), .pName = miss->getEntryPointName(), .pSpecializationInfo = nullptr, }); shaderGroups.add(VkRayTracingShaderGroupCreateInfoKHR{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .pNext = nullptr, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, .generalShader = static_cast(shaderStages.size() - 1), .closestHitShader = VK_SHADER_UNUSED_KHR, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR, .pShaderGroupCaptureReplayHandle = nullptr, }); } } { for (auto gfxCallable : createInfo.callableGroups) { auto call = gfxCallable.shader.cast(); shaderStages.add(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR, .module = call->getModuleHandle(), .pName = call->getEntryPointName(), .pSpecializationInfo = nullptr, }); shaderGroups.add(VkRayTracingShaderGroupCreateInfoKHR{ .sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR, .pNext = nullptr, .type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR, .generalShader = static_cast(shaderStages.size() - 1), .closestHitShader = VK_SHADER_UNUSED_KHR, .anyHitShader = VK_SHADER_UNUSED_KHR, .intersectionShader = VK_SHADER_UNUSED_KHR, .pShaderGroupCaptureReplayHandle = nullptr, }); } } uint32 hash = CRC::Calculate(shaderStages.data(), sizeof(VkPipelineShaderStageCreateInfo) * shaderStages.size(), CRC::CRC_32(), createInfo.pipelineLayout->getHash()); hash = CRC::Calculate(shaderGroups.data(), sizeof(VkRayTracingShaderGroupCreateInfoKHR) * shaderGroups.size(), CRC::CRC_32(), hash); if (rayTracingPipelines.contains(hash)) { return rayTracingPipelines[hash]; } VkRayTracingPipelineCreateInfoKHR pipelineInfo = { .sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR, .pNext = nullptr, .stageCount = static_cast(shaderStages.size()), .pStages = shaderStages.data(), .groupCount = static_cast(shaderGroups.size()), .pGroups = shaderGroups.data(), .maxPipelineRayRecursionDepth = 12, .layout = createInfo.pipelineLayout.cast()->getHandle(), }; VkPipeline pipelineHandle; auto beginTime = std::chrono::high_resolution_clock::now(); VK_CHECK(vkCreateRayTracingPipelinesKHR(graphics->getDevice(), VK_NULL_HANDLE, cache, 1, &pipelineInfo, nullptr, &pipelineHandle)); auto endTime = std::chrono::high_resolution_clock::now(); int64 delta = std::chrono::duration_cast(endTime - beginTime).count(); std::cout << "RT creation time: " << delta << std::endl; const uint32_t handleSize = graphics->getRayTracingProperties().shaderGroupHandleSize; const uint32_t handleSizeAligned = align(graphics->getRayTracingProperties().shaderGroupHandleSize, graphics->getRayTracingProperties().shaderGroupHandleAlignment); const uint32_t handleAlignment = graphics->getRayTracingProperties().shaderGroupHandleAlignment; const uint32_t sbtAlignment = graphics->getRayTracingProperties().shaderGroupBaseAlignment; const uint32_t groupCount = static_cast(shaderGroups.size()); const uint32_t sbtSize = groupCount * handleSizeAligned; const VkBufferUsageFlags sbtBufferUsage = VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; const VmaMemoryUsage sbtMemoryUsage = VMA_MEMORY_USAGE_AUTO; uint64 rayGenStride = align(handleSize + createInfo.rayGenGroup.parameters.size(), handleAlignment); uint64 hitStride = handleSize; for (const auto& h : createInfo.hitGroups) { hitStride = std::max(hitStride, align(handleSize + h.parameters.size(), handleAlignment)); } uint64 missStride = handleSize; for (const auto& m : createInfo.missGroups) { missStride = std::max(missStride, align(handleSize + m.parameters.size(), handleAlignment)); } OBufferAllocation rayGenBuffer = new BufferAllocation(graphics, "RayGenSBT", VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = rayGenStride, .usage = sbtBufferUsage, }, VmaAllocationCreateInfo{ .usage = sbtMemoryUsage, }, Gfx::QueueType::GRAPHICS, sbtAlignment); OBufferAllocation hitBuffer = new BufferAllocation(graphics, "HitSBT", VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = hitStride * createInfo.hitGroups.size(), .usage = sbtBufferUsage, }, VmaAllocationCreateInfo{ .usage = sbtMemoryUsage, }, Gfx::QueueType::GRAPHICS, sbtAlignment); OBufferAllocation missBuffer = new BufferAllocation(graphics, "MissSBT", VkBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = missStride * createInfo.missGroups.size(), .usage = sbtBufferUsage, }, VmaAllocationCreateInfo{ .usage = sbtMemoryUsage, }, Gfx::QueueType::GRAPHICS, sbtAlignment); Array sbt(sbtSize); vkGetRayTracingShaderGroupHandlesKHR(graphics->getDevice(), pipelineHandle, 0, (uint32)shaderGroups.size(), sbtSize, sbt.data()); uint64 sbtOffset = 0; Array rayGenSbt(rayGenStride); std::memcpy(rayGenSbt.data(), sbt.data() + sbtOffset, handleSize); std::memcpy(rayGenSbt.data() + handleSize, createInfo.rayGenGroup.parameters.data(), createInfo.rayGenGroup.parameters.size()); sbtOffset += handleSizeAligned; rayGenBuffer->updateContents(0, rayGenSbt.size(), rayGenSbt.data()); rayGenBuffer->pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); Array hitSbt(hitStride * createInfo.hitGroups.size()); for (uint64 i = 0; i < createInfo.hitGroups.size(); ++i) { std::memcpy(hitSbt.data() + i * hitStride, sbt.data() + sbtOffset, handleSize); std::memcpy(hitSbt.data() + i * hitStride + handleSize, createInfo.hitGroups[i].parameters.data(), createInfo.hitGroups[i].parameters.size()); sbtOffset += handleSizeAligned; } hitBuffer->updateContents(0, hitSbt.size(), hitSbt.data()); hitBuffer->pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); Array missSbt(missStride * createInfo.missGroups.size()); for (uint64 i = 0; i < createInfo.missGroups.size(); ++i) { std::memcpy(missSbt.data() + i * missStride, sbt.data() + sbtOffset, handleSize); std::memcpy(missSbt.data() + i * missStride + handleSize, createInfo.missGroups[i].parameters.data(), createInfo.missGroups[i].parameters.size()); sbtOffset += handleSizeAligned; } missBuffer->updateContents(0, missSbt.size(), missSbt.data()); missBuffer->pipelineBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); ORayTracingPipeline pipeline = new RayTracingPipeline(graphics, pipelineHandle, std::move(rayGenBuffer), rayGenStride, std::move(hitBuffer), hitStride, std::move(missBuffer), missStride, nullptr, 0, createInfo.pipelineLayout); PRayTracingPipeline handle = pipeline; rayTracingPipelines[hash] = std::move(pipeline); return handle; }