From fea879083467fea0e7e99fe59526aa5c0065f09f Mon Sep 17 00:00:00 2001 From: Lars Ivar Hatledal Date: Tue, 16 Jun 2026 10:51:23 +0200 Subject: [PATCH] vulkan - pipeline cache --- src/threepp/renderers/VulkanRenderer.cpp | 28 +++---- src/threepp/renderers/vulkan/BloomPass.cpp | 10 +-- .../renderers/vulkan/DeferredShade.cpp | 4 +- src/threepp/renderers/vulkan/Denoiser.cpp | 4 +- src/threepp/renderers/vulkan/EnvPrefilter.cpp | 2 +- .../renderers/vulkan/EventCameraDetector.cpp | 2 +- .../renderers/vulkan/FoamWorldPipeline.cpp | 2 +- .../renderers/vulkan/GrassWindPipeline.cpp | 2 +- src/threepp/renderers/vulkan/OverlayPass.cpp | 12 +-- .../renderers/vulkan/SkinningPipeline.cpp | 2 +- src/threepp/renderers/vulkan/TaaResolve.cpp | 4 +- .../renderers/vulkan/TetSkinningPipeline.cpp | 2 +- .../renderers/vulkan/VulkanContext.cpp | 82 +++++++++++++++++++ .../renderers/vulkan/VulkanContext.hpp | 14 ++++ .../vulkan/WaterDisplacePipeline.cpp | 2 +- .../renderers/vulkan/water/OceanFFT.cpp | 2 +- 16 files changed, 135 insertions(+), 39 deletions(-) diff --git a/src/threepp/renderers/VulkanRenderer.cpp b/src/threepp/renderers/VulkanRenderer.cpp index 648ea5485..d9ffa4c0b 100644 --- a/src/threepp/renderers/VulkanRenderer.cpp +++ b/src/threepp/renderers/VulkanRenderer.cpp @@ -8915,7 +8915,7 @@ namespace threepp { gpci.layout = rasterPipelineLayout; gpci.renderPass = rasterGbufRenderPass; gpci.subpass = 0; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpci, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpci, nullptr, &rasterGbufPipeline), "vkCreateGraphicsPipelines(rasterGbuf)"); @@ -8964,7 +8964,7 @@ namespace threepp { gpciInd.pStages = stagesInd; gpciInd.pVertexInputState = &viInd; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciInd, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciInd, nullptr, &rasterGbufIndirectPipeline), "vkCreateGraphicsPipelines(rasterGbufIndirect)"); @@ -9010,7 +9010,7 @@ namespace threepp { gpciDecal.pStages = stagesDecal; gpciDecal.pDepthStencilState = &dsDecal; gpciDecal.pColorBlendState = &cbDecal; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciDecal, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciDecal, nullptr, &rasterGbufDecalPipeline), "vkCreateGraphicsPipelines(rasterGbufDecal)"); @@ -9170,7 +9170,7 @@ namespace threepp { gpci.pColorBlendState = &cb; gpci.pDynamicState = &dyn; gpci.layout = overlayPipelineLayout; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpci, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpci, nullptr, &overlayWireframePipeline), "vkCreateGraphicsPipelines(overlayWireframe)"); @@ -9184,7 +9184,7 @@ namespace threepp { rsBasic.cullMode = VK_CULL_MODE_BACK_BIT; VkGraphicsPipelineCreateInfo gpciBasic = gpci; gpciBasic.pRasterizationState = &rsBasic; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciBasic, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciBasic, nullptr, &overlayBasicPipeline), "vkCreateGraphicsPipelines(overlayBasic)"); @@ -9213,7 +9213,7 @@ namespace threepp { cbBlend.pAttachments = &cbasBlend; VkGraphicsPipelineCreateInfo gpciBasicTr = gpciBasic; gpciBasicTr.pColorBlendState = &cbBlend; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciBasicTr, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciBasicTr, nullptr, &overlayBasicTransparentPipeline), "vkCreateGraphicsPipelines(overlayBasicTransparent)"); @@ -9233,7 +9233,7 @@ namespace threepp { VkGraphicsPipelineCreateInfo gpciLineList = gpci; gpciLineList.pInputAssemblyState = &iaLineList; gpciLineList.pRasterizationState = &rsLine; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciLineList, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciLineList, nullptr, &overlayLineListPipeline), "vkCreateGraphicsPipelines(overlayLineList)"); @@ -9243,7 +9243,7 @@ namespace threepp { VkGraphicsPipelineCreateInfo gpciLineStrip = gpci; gpciLineStrip.pInputAssemblyState = &iaLineStrip; gpciLineStrip.pRasterizationState = &rsLine; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciLineStrip, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciLineStrip, nullptr, &overlayLineStripPipeline), "vkCreateGraphicsPipelines(overlayLineStrip)"); @@ -9303,7 +9303,7 @@ namespace threepp { gpciLineListColored.stageCount = 2; gpciLineListColored.pStages = cStages; gpciLineListColored.pVertexInputState = &cvi; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciLineListColored, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciLineListColored, nullptr, &overlayLineListColoredPipeline), "vkCreateGraphicsPipelines(overlayLineListColored)"); @@ -9311,7 +9311,7 @@ namespace threepp { gpciLineStripColored.stageCount = 2; gpciLineStripColored.pStages = cStages; gpciLineStripColored.pVertexInputState = &cvi; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciLineStripColored, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciLineStripColored, nullptr, &overlayLineStripColoredPipeline), "vkCreateGraphicsPipelines(overlayLineStripColored)"); @@ -9357,7 +9357,7 @@ namespace threepp { gpciPointList.pStages = pStages; gpciPointList.pVertexInputState = &cvi; gpciPointList.pInputAssemblyState = &iaPointList; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &gpciPointList, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &gpciPointList, nullptr, &overlayPointListPipeline), "vkCreateGraphicsPipelines(overlayPointList)"); @@ -9481,7 +9481,7 @@ namespace threepp { dgpci.pColorBlendState = &dcb; dgpci.pDynamicState = &ddyn; dgpci.layout = rasterPipelineLayout; - check(vkCreateGraphicsPipelines(ctx->device(), VK_NULL_HANDLE, 1, &dgpci, nullptr, + check(vkCreateGraphicsPipelines(ctx->device(), ctx->pipelineCache(), 1, &dgpci, nullptr, &overlayDepthPrepassPipeline), "vkCreateGraphicsPipelines(overlayDepthPrepass)"); @@ -11234,7 +11234,7 @@ namespace threepp { const uint32_t idx = rtVariantIndex(useSer, restirDISpec); check(ctx->rt().createRayTracingPipelines( - ctx->device(), VK_NULL_HANDLE, VK_NULL_HANDLE, + ctx->device(), VK_NULL_HANDLE, ctx->pipelineCache(), 1, &rci, nullptr, &rtVariants_[idx].pipeline), "vkCreateRayTracingPipelinesKHR (single)"); createShaderBindingTable(useSer, restirDISpec); @@ -13737,7 +13737,7 @@ namespace threepp { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = ssci; cpci.layout = eventShadePipelineLayout_; - check(vkCreateComputePipelines(ctx->device(), VK_NULL_HANDLE, 1, &cpci, nullptr, &eventShadePipeline_), + check(vkCreateComputePipelines(ctx->device(), ctx->pipelineCache(), 1, &cpci, nullptr, &eventShadePipeline_), "vkCreateComputePipelines(event_shade)"); vkDestroyShaderModule(ctx->device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/BloomPass.cpp b/src/threepp/renderers/vulkan/BloomPass.cpp index f744b4128..70eb99133 100644 --- a/src/threepp/renderers/vulkan/BloomPass.cpp +++ b/src/threepp/renderers/vulkan/BloomPass.cpp @@ -137,7 +137,7 @@ namespace threepp::vulkan { img = createStorageSampledImage(halfW_, halfH_, "vmaCreateImage(bloom.B)"); } - static VkPipeline makeComputePipe(VkDevice d, VkPipelineLayout layout, + static VkPipeline makeComputePipe(VkDevice d, VkPipelineCache cache, VkPipelineLayout layout, const uint32_t* spv, size_t spvBytes, const char* label) { VkShaderModuleCreateInfo smci{}; @@ -158,7 +158,7 @@ namespace threepp::vulkan { cpci.stage = stage; cpci.layout = layout; VkPipeline pipe = VK_NULL_HANDLE; - check(vkCreateComputePipelines(d, VK_NULL_HANDLE, 1, &cpci, nullptr, &pipe), label); + check(vkCreateComputePipelines(d, cache, 1, &cpci, nullptr, &pipe), label); vkDestroyShaderModule(d, mod, nullptr); return pipe; } @@ -244,11 +244,11 @@ namespace threepp::vulkan { "vkCreatePipelineLayout(composite)"); } - downPipe_ = makeComputePipe(d, bloomPipeLayout_, kBloomDownCompSpv, + downPipe_ = makeComputePipe(d, ctx_.pipelineCache(), bloomPipeLayout_, kBloomDownCompSpv, sizeof(kBloomDownCompSpv), "vkCreateComputePipelines(bloom_down)"); - blurPipe_ = makeComputePipe(d, bloomPipeLayout_, kBloomBlurCompSpv, + blurPipe_ = makeComputePipe(d, ctx_.pipelineCache(), bloomPipeLayout_, kBloomBlurCompSpv, sizeof(kBloomBlurCompSpv), "vkCreateComputePipelines(bloom_blur)"); - compPipe_ = makeComputePipe(d, compPipeLayout_, kCompositeCompSpv, + compPipe_ = makeComputePipe(d, ctx_.pipelineCache(), compPipeLayout_, kCompositeCompSpv, sizeof(kCompositeCompSpv), "vkCreateComputePipelines(composite)"); } diff --git a/src/threepp/renderers/vulkan/DeferredShade.cpp b/src/threepp/renderers/vulkan/DeferredShade.cpp index 349e93850..b81cc513d 100644 --- a/src/threepp/renderers/vulkan/DeferredShade.cpp +++ b/src/threepp/renderers/vulkan/DeferredShade.cpp @@ -129,7 +129,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipeLayout_; - check(vkCreateComputePipelines(d, VK_NULL_HANDLE, 1, &cpci, nullptr, &pipe_), + check(vkCreateComputePipelines(d, ctx_.pipelineCache(), 1, &cpci, nullptr, &pipe_), "vkCreateComputePipelines(deferred_shade)"); // Second pipeline — spatial denoise + recombine — shares the descriptor @@ -142,7 +142,7 @@ namespace threepp::vulkan { check(vkCreateShaderModule(d, &smciD, nullptr, &modD), "vkCreateShaderModule(deferred_denoise)"); VkComputePipelineCreateInfo cpciD = cpci; cpciD.stage.module = modD; - check(vkCreateComputePipelines(d, VK_NULL_HANDLE, 1, &cpciD, nullptr, &denoisePipe_), + check(vkCreateComputePipelines(d, ctx_.pipelineCache(), 1, &cpciD, nullptr, &denoisePipe_), "vkCreateComputePipelines(deferred_denoise)"); vkDestroyShaderModule(d, mod, nullptr); diff --git a/src/threepp/renderers/vulkan/Denoiser.cpp b/src/threepp/renderers/vulkan/Denoiser.cpp index e1ee54f96..789676ce3 100644 --- a/src/threepp/renderers/vulkan/Denoiser.cpp +++ b/src/threepp/renderers/vulkan/Denoiser.cpp @@ -199,7 +199,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &finalizePipeline_), "vkCreateComputePipelines(denoise)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); @@ -225,7 +225,7 @@ namespace threepp::vulkan { acpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; acpci.stage = astage; acpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &acpci, nullptr, &atrousPipeline_), "vkCreateComputePipelines(denoise_atrous)"); vkDestroyShaderModule(ctx_.device(), amod, nullptr); diff --git a/src/threepp/renderers/vulkan/EnvPrefilter.cpp b/src/threepp/renderers/vulkan/EnvPrefilter.cpp index 6f6020572..a0cbddc57 100644 --- a/src/threepp/renderers/vulkan/EnvPrefilter.cpp +++ b/src/threepp/renderers/vulkan/EnvPrefilter.cpp @@ -94,7 +94,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(prefilter)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/EventCameraDetector.cpp b/src/threepp/renderers/vulkan/EventCameraDetector.cpp index 19959ca0e..ac2455722 100644 --- a/src/threepp/renderers/vulkan/EventCameraDetector.cpp +++ b/src/threepp/renderers/vulkan/EventCameraDetector.cpp @@ -143,7 +143,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = ssci; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, 1, &cpci, nullptr, &pipeline_), + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(event_detect)"); vkDestroyShaderModule(ctx_.device(), shader, nullptr); diff --git a/src/threepp/renderers/vulkan/FoamWorldPipeline.cpp b/src/threepp/renderers/vulkan/FoamWorldPipeline.cpp index b9d1437ea..1c715f57c 100644 --- a/src/threepp/renderers/vulkan/FoamWorldPipeline.cpp +++ b/src/threepp/renderers/vulkan/FoamWorldPipeline.cpp @@ -80,7 +80,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(foamWorld)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/GrassWindPipeline.cpp b/src/threepp/renderers/vulkan/GrassWindPipeline.cpp index 937b89769..8a79d8f61 100644 --- a/src/threepp/renderers/vulkan/GrassWindPipeline.cpp +++ b/src/threepp/renderers/vulkan/GrassWindPipeline.cpp @@ -55,7 +55,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(grass_wind)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/OverlayPass.cpp b/src/threepp/renderers/vulkan/OverlayPass.cpp index ae2c915d9..599704b81 100644 --- a/src/threepp/renderers/vulkan/OverlayPass.cpp +++ b/src/threepp/renderers/vulkan/OverlayPass.cpp @@ -194,13 +194,13 @@ void OverlayPass::createOrthoLinePipelines() { gpci.pColorBlendState = &cb; gpci.pDynamicState = &dyn; gpci.layout = orthoLinePipelineLayout_; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpci, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpci, nullptr, &orthoLineListPipeline_), "vkCreateGraphicsPipelines(orthoLineList)"); VkGraphicsPipelineCreateInfo gpciStrip = gpci; gpciStrip.pInputAssemblyState = &iaStrip; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpciStrip, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpciStrip, nullptr, &orthoLineStripPipeline_), "vkCreateGraphicsPipelines(orthoLineStrip)"); @@ -212,7 +212,7 @@ void OverlayPass::createOrthoLinePipelines() { VkGraphicsPipelineCreateInfo gpciMesh = gpci; gpciMesh.pInputAssemblyState = &iaTri; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpciMesh, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpciMesh, nullptr, &orthoMeshPipeline_), "vkCreateGraphicsPipelines(orthoMesh)"); @@ -228,7 +228,7 @@ void OverlayPass::createOrthoLinePipelines() { cbT.pAttachments = &cbasT; VkGraphicsPipelineCreateInfo gpciMeshT = gpciMesh; gpciMeshT.pColorBlendState = &cbT; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpciMeshT, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpciMeshT, nullptr, &orthoMeshTransparentPipeline_), "vkCreateGraphicsPipelines(orthoMeshTransparent)"); @@ -343,7 +343,7 @@ void OverlayPass::createOrthoPointPipeline() { gpci.pColorBlendState = &cb; gpci.pDynamicState = &dyn; gpci.layout = orthoLinePipelineLayout_; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpci, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpci, nullptr, &orthoPointListPipeline_), "vkCreateGraphicsPipelines(orthoPointList)"); @@ -512,7 +512,7 @@ void OverlayPass::createSpriteOverlayPipeline() { gpci.pColorBlendState = &cb; gpci.pDynamicState = &dyn; gpci.layout = spritePipelineLayout_; - check(vkCreateGraphicsPipelines(ctx_.device(), VK_NULL_HANDLE, 1, &gpci, nullptr, + check(vkCreateGraphicsPipelines(ctx_.device(), ctx_.pipelineCache(), 1, &gpci, nullptr, &overlaySpritePipeline_), "vkCreateGraphicsPipelines(overlaySprite)"); diff --git a/src/threepp/renderers/vulkan/SkinningPipeline.cpp b/src/threepp/renderers/vulkan/SkinningPipeline.cpp index 64947ea08..ca08f310f 100644 --- a/src/threepp/renderers/vulkan/SkinningPipeline.cpp +++ b/src/threepp/renderers/vulkan/SkinningPipeline.cpp @@ -73,7 +73,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(skinning)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/TaaResolve.cpp b/src/threepp/renderers/vulkan/TaaResolve.cpp index 1d7c0d05d..3369776ae 100644 --- a/src/threepp/renderers/vulkan/TaaResolve.cpp +++ b/src/threepp/renderers/vulkan/TaaResolve.cpp @@ -233,7 +233,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(taa)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); @@ -286,7 +286,7 @@ namespace threepp::vulkan { rcpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; rcpci.stage = rstage; rcpci.layout = rcasPipeLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, 1, &rcpci, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &rcpci, nullptr, &rcasPipe_), "vkCreateComputePipelines(rcas)"); vkDestroyShaderModule(ctx_.device(), rmod, nullptr); diff --git a/src/threepp/renderers/vulkan/TetSkinningPipeline.cpp b/src/threepp/renderers/vulkan/TetSkinningPipeline.cpp index 1cd5ec5a7..b16fa8d4d 100644 --- a/src/threepp/renderers/vulkan/TetSkinningPipeline.cpp +++ b/src/threepp/renderers/vulkan/TetSkinningPipeline.cpp @@ -73,7 +73,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(tetSkinning)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/VulkanContext.cpp b/src/threepp/renderers/vulkan/VulkanContext.cpp index 3fea63d9d..1b1fb136a 100644 --- a/src/threepp/renderers/vulkan/VulkanContext.cpp +++ b/src/threepp/renderers/vulkan/VulkanContext.cpp @@ -5,10 +5,13 @@ #include #include #include +#include +#include #include #include #include #include +#include namespace threepp::vulkan { @@ -91,6 +94,7 @@ namespace threepp::vulkan { createSurface(); pickPhysicalDevice(); createLogicalDevice(); + createPipelineCache(); createAllocator(); createSwapchain(); createSwapchainImageViews(); @@ -102,6 +106,10 @@ namespace threepp::vulkan { destroySwapchainResources(); if (allocator_ != VK_NULL_HANDLE) vmaDestroyAllocator(allocator_); + if (pipelineCache_ != VK_NULL_HANDLE) { + savePipelineCache(); + vkDestroyPipelineCache(device_, pipelineCache_, nullptr); + } if (device_ != VK_NULL_HANDLE) vkDestroyDevice(device_, nullptr); if (surface_ != VK_NULL_HANDLE) vkDestroySurfaceKHR(instance_, surface_, nullptr); @@ -113,6 +121,80 @@ namespace threepp::vulkan { if (instance_ != VK_NULL_HANDLE) vkDestroyInstance(instance_, nullptr); } + namespace { + std::filesystem::path pipelineCachePath() { + std::error_code ec; + auto dir = std::filesystem::temp_directory_path(ec); + if (ec) return {}; + return dir / "threepp_pipeline_cache.bin"; + } + }// namespace + + void VulkanContext::createPipelineCache() { + // Read the previous run's blob, if any. + std::vector initial; + const auto path = pipelineCachePath(); + if (!path.empty()) { + std::ifstream f(path, std::ios::binary | std::ios::ate); + if (f) { + const std::streamsize sz = f.tellg(); + if (sz > 0) { + initial.resize(static_cast(sz)); + f.seekg(0); + f.read(initial.data(), sz); + } + } + } + + // Validate the cache header against THIS device — vendor/device/UUID + // must match or the blob is from another GPU/driver and must be dropped + // (some drivers reject foreign data outright). Header layout + // (VkPipelineCacheHeaderVersionOne): u32 size, u32 version, u32 vendorID, + // u32 deviceID, u8 uuid[VK_UUID_SIZE] — 32 bytes total. + bool usable = false; + if (initial.size() >= 32) { + uint32_t hdrVersion = 0, vendorID = 0, deviceID = 0; + std::memcpy(&hdrVersion, initial.data() + 4, 4); + std::memcpy(&vendorID, initial.data() + 8, 4); + std::memcpy(&deviceID, initial.data() + 12, 4); + VkPhysicalDeviceProperties props{}; + vkGetPhysicalDeviceProperties(physicalDevice_, &props); + usable = hdrVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE && + vendorID == props.vendorID && deviceID == props.deviceID && + std::memcmp(initial.data() + 16, props.pipelineCacheUUID, VK_UUID_SIZE) == 0; + } + + VkPipelineCacheCreateInfo ci{}; + ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + if (usable) { + ci.initialDataSize = initial.size(); + ci.pInitialData = initial.data(); + } + if (vkCreatePipelineCache(device_, &ci, nullptr, &pipelineCache_) != VK_SUCCESS) { + pipelineCache_ = VK_NULL_HANDLE;// non-fatal: pipelines just compile cold + } + std::cout << "[VulkanContext] pipeline cache: " + << (usable ? "WARM - loaded " : "COLD - ignoring ") + << initial.size() << " bytes from " << path.string() + << (usable ? " (pipelines reused)" : " (recompiling all pipelines)") << std::endl; + } + + void VulkanContext::savePipelineCache() { + if (pipelineCache_ == VK_NULL_HANDLE) return; + size_t sz = 0; + if (vkGetPipelineCacheData(device_, pipelineCache_, &sz, nullptr) != VK_SUCCESS || sz == 0) return; + std::vector data(sz); + if (vkGetPipelineCacheData(device_, pipelineCache_, &sz, data.data()) != VK_SUCCESS) return; + const auto path = pipelineCachePath(); + if (path.empty()) return; + std::ofstream f(path, std::ios::binary | std::ios::trunc); + if (f) { + f.write(data.data(), static_cast(sz)); + std::cout << "[VulkanContext] pipeline cache: saved " << sz + << " bytes to " << path.string() << std::endl; + } + } + void VulkanContext::createInstance(bool enableValidation) { VkApplicationInfo app{}; app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; diff --git a/src/threepp/renderers/vulkan/VulkanContext.hpp b/src/threepp/renderers/vulkan/VulkanContext.hpp index 5f5f9f0ce..a31d6ee08 100644 --- a/src/threepp/renderers/vulkan/VulkanContext.hpp +++ b/src/threepp/renderers/vulkan/VulkanContext.hpp @@ -44,6 +44,14 @@ namespace threepp::vulkan { VkDevice device() const { return device_; } VmaAllocator allocator() const { return allocator_; } + // Shared pipeline cache, loaded from disk at construction and saved at + // destruction. Pass to every vkCreate{Graphics,Compute,RayTracing} + // Pipelines call so repeat launches skip the (multi-second, esp. for + // the RT megakernel) cold pipeline compile. Purely an optimization — + // the driver validates the on-disk blob against the device and it is + // discarded if incompatible, so it can never affect correctness. + VkPipelineCache pipelineCache() const { return pipelineCache_; } + VkSurfaceKHR surface() const { return surface_; } VkQueue graphicsQueue() const { return graphicsQueue_; } VkQueue presentQueue() const { return presentQueue_; } @@ -146,12 +154,18 @@ namespace threepp::vulkan { // is on). Loaded via vkGetDeviceProcAddr at device creation. PFN_vkSetDebugUtilsObjectNameEXT setObjectNameFn_ = nullptr; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; + void createInstance(bool enableValidation); void createDebugMessenger(); void createSurface(); void pickPhysicalDevice(); void createLogicalDevice(); void createAllocator(); + // Load the on-disk pipeline cache (validated against this device) at + // startup; persist it back to disk at shutdown. + void createPipelineCache(); + void savePipelineCache(); void createSwapchain(); void createSwapchainImageViews(); void destroySwapchainResources(); diff --git a/src/threepp/renderers/vulkan/WaterDisplacePipeline.cpp b/src/threepp/renderers/vulkan/WaterDisplacePipeline.cpp index 68779e530..10143fae9 100644 --- a/src/threepp/renderers/vulkan/WaterDisplacePipeline.cpp +++ b/src/threepp/renderers/vulkan/WaterDisplacePipeline.cpp @@ -75,7 +75,7 @@ namespace threepp::vulkan { cpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; cpci.stage = stage; cpci.layout = pipelineLayout_; - check(vkCreateComputePipelines(ctx_.device(), VK_NULL_HANDLE, + check(vkCreateComputePipelines(ctx_.device(), ctx_.pipelineCache(), 1, &cpci, nullptr, &pipeline_), "vkCreateComputePipelines(displace)"); vkDestroyShaderModule(ctx_.device(), mod, nullptr); diff --git a/src/threepp/renderers/vulkan/water/OceanFFT.cpp b/src/threepp/renderers/vulkan/water/OceanFFT.cpp index 1ccecd684..a5b36112a 100644 --- a/src/threepp/renderers/vulkan/water/OceanFFT.cpp +++ b/src/threepp/renderers/vulkan/water/OceanFFT.cpp @@ -204,7 +204,7 @@ namespace threepp::water { cpci.layout = layout; VkPipeline p = VK_NULL_HANDLE; - check(vkCreateComputePipelines(ctx.device(), VK_NULL_HANDLE, 1, &cpci, nullptr, &p), + check(vkCreateComputePipelines(ctx.device(), ctx.pipelineCache(), 1, &cpci, nullptr, &p), "vkCreateComputePipelines"); return p; }