From ddc66c021f6983a0da1abbc1193e4641208a15e9 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 17 Oct 2026 08:42:40 +0800 Subject: [PATCH 1/8] D3D12: enable persistent PSO cache reuse for archived and mesh pipelines. Retain serialized pipeline library data, use stable content keys, forward caches during archive unpacking, and cache mesh pipelines with explicit topology. --- .../include/PipelineStateCacheD3D12Impl.hpp | 9 +- .../src/PipelineStateCacheD3D12Impl.cpp | 75 ++++++++-- .../src/PipelineStateD3D12Impl.cpp | 133 ++++++++++++++---- .../src/RenderStateCacheImpl.cpp | 1 + 4 files changed, 175 insertions(+), 43 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp index 11d7c6e7bd..ab78bc6558 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp @@ -31,6 +31,7 @@ #include "EngineD3D12ImplTraits.hpp" #include "PipelineStateCacheBase.hpp" +#include namespace Diligent { @@ -53,11 +54,17 @@ class PipelineStateCacheD3D12Impl final : public PipelineStateCacheBase LoadComputePipeline(const wchar_t* Name, const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc); CComPtr LoadGraphicsPipeline(const wchar_t* Name, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc); +#ifdef D3D12_H_HAS_MESH_SHADER + CComPtr LoadPipeline(const wchar_t* Name, const D3D12_PIPELINE_STATE_STREAM_DESC& Desc); +#endif bool StorePipeline(const wchar_t* Name, ID3D12DeviceChild* pPSO); private: - CComPtr m_pLibrary; + RefCntAutoPtr m_pInitialData; + CComPtr m_pLibrary; + CComPtr m_pLibrary1; + std::mutex m_LibraryMtx; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp index 7a8736babe..3e860b7dd4 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp @@ -46,15 +46,39 @@ PipelineStateCacheD3D12Impl::PipelineStateCacheD3D12Impl(IReferenceCounters* } // clang-format on { - HRESULT hr = pRenderDeviceD3D12->GetD3D12Device1()->CreatePipelineLibrary(nullptr, 0, IID_PPV_ARGS(&m_pLibrary)); + auto* pDevice1 = pRenderDeviceD3D12->GetD3D12Device1(); + if (pDevice1 == nullptr) + { + LOG_WARNING_MESSAGE("D3D12 pipeline libraries are unavailable; native caching is disabled."); + return; + } + + // D3D12 does not copy the serialized data: retain our own copy until the library is released. + if (CreateInfo.pCacheData != nullptr && CreateInfo.CacheDataSize != 0) + { + m_pInitialData = DataBlobImpl::Create(CreateInfo.CacheDataSize, CreateInfo.pCacheData); + const HRESULT LoadResult = pDevice1->CreatePipelineLibrary(m_pInitialData->GetConstDataPtr(), m_pInitialData->GetSize(), IID_PPV_ARGS(&m_pLibrary)); + if (FAILED(LoadResult)) + { + LOG_WARNING_MESSAGE("D3D12 pipeline library data is invalid or incompatible; using an empty cache (HRESULT ", LoadResult, ")."); + m_pInitialData.Release(); + } + } + + HRESULT hr = m_pLibrary ? S_OK : pDevice1->CreatePipelineLibrary(nullptr, 0, IID_PPV_ARGS(&m_pLibrary)); if (FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create D3D12 pipeline library"); + { + LOG_WARNING_MESSAGE("D3D12 pipeline library is unavailable; native caching is disabled (HRESULT ", hr, ")."); + return; + } + m_pLibrary->QueryInterface(IID_PPV_ARGS(&m_pLibrary1)); } PipelineStateCacheD3D12Impl::~PipelineStateCacheD3D12Impl() { // D3D12 object can only be destroyed when it is no longer used by the GPU - GetDevice()->SafeReleaseDeviceObject(std::move(m_pLibrary), ~Uint64{0}); + m_pLibrary1.Release(); + GetDevice()->SafeReleaseDeviceObject(std::make_pair(std::move(m_pInitialData), std::move(m_pLibrary)), ~Uint64{0}); } CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(const wchar_t* Name, const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) @@ -66,11 +90,12 @@ CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(cons } CComPtr d3d12PSO; - if ((m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) + if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) { - HRESULT hr = m_pLibrary->LoadComputePipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); - if (FAILED(hr) && (m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_ERROR_MESSAGE("Failed to load compute pipeline '", NarrowString(Name), "' from the library"); + std::lock_guard Lock{m_LibraryMtx}; + HRESULT hr = m_pLibrary->LoadComputePipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); + if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) + LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (compute): " : "PSO cache miss (compute): ", NarrowString(Name)); } return d3d12PSO; } @@ -84,24 +109,42 @@ CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(con } CComPtr d3d12PSO; - if ((m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) + if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) { - HRESULT hr = m_pLibrary->LoadGraphicsPipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); - if (FAILED(hr) && (m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_ERROR_MESSAGE("Failed to load graphics pipeline '", NarrowString(Name), "' from the library"); + std::lock_guard Lock{m_LibraryMtx}; + HRESULT hr = m_pLibrary->LoadGraphicsPipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); + if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) + LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (graphics): " : "PSO cache miss (graphics): ", NarrowString(Name)); } return d3d12PSO; } +#ifdef D3D12_H_HAS_MESH_SHADER +CComPtr PipelineStateCacheD3D12Impl::LoadPipeline(const wchar_t* Name, const D3D12_PIPELINE_STATE_STREAM_DESC& Desc) +{ + VERIFY_EXPR(Name != nullptr); + CComPtr d3d12PSO; + if (m_pLibrary1 && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) + { + std::lock_guard Lock{m_LibraryMtx}; + const HRESULT hr = m_pLibrary1->LoadPipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); + if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) + LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (mesh): " : "PSO cache miss (mesh): ", NarrowString(Name), " (HRESULT ", hr, ")"); + } + return CComPtr{d3d12PSO.p}; +} +#endif + bool PipelineStateCacheD3D12Impl::StorePipeline(const wchar_t* Name, ID3D12DeviceChild* pPSO) { VERIFY_EXPR(Name != nullptr); - if ((m_Desc.Mode & PSO_CACHE_MODE_STORE) == 0) + if (!m_pLibrary || (m_Desc.Mode & PSO_CACHE_MODE_STORE) == 0) return false; - HRESULT hr = m_pLibrary->StorePipeline(Name, static_cast(pPSO)); + std::lock_guard Lock{m_LibraryMtx}; + HRESULT hr = m_pLibrary->StorePipeline(Name, static_cast(pPSO)); if (FAILED(hr) && (m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_ERROR_MESSAGE("Failed to add pipeline '", NarrowString(Name), "' to the library"); + LOG_INFO_MESSAGE("PSO cache store failed: ", NarrowString(Name), " (HRESULT ", hr, "; it may already be stored)."); return SUCCEEDED(hr); } @@ -110,6 +153,10 @@ void PipelineStateCacheD3D12Impl::GetData(IDataBlob** ppBlob) { DEV_CHECK_ERR(ppBlob != nullptr, "ppBlob must not be null"); *ppBlob = nullptr; + if (!m_pLibrary) + return; + + std::lock_guard Lock{m_LibraryMtx}; RefCntAutoPtr pDataBlob = DataBlobImpl::Create(m_pLibrary->GetSerializedSize()); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index fc696006e3..008e6481f5 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -51,6 +51,7 @@ #include "DynamicLinearAllocator.hpp" #include "D3DShaderResourceValidation.hpp" #include "DataBlobImpl.hpp" +#include "HashUtils.hpp" #include "DXBCUtils.hpp" #include "DXCompiler.hpp" @@ -63,6 +64,66 @@ constexpr INTERFACE_ID PipelineStateD3D12Impl::IID_InternalImpl; namespace { +struct PipelineCacheHasher +{ + template + void Update(const T& Value) + { + if constexpr (std::is_fundamental::value || std::is_enum::value) + UpdateRaw(&Value, sizeof(Value)); + else + HashCombiner{*this}(Value); + } + + void Update(const char* Value) + { + const Uint64 Length = Value != nullptr ? std::strlen(Value) : 0; + Update(Length); + UpdateRaw(Value, Length); + } + + template + void operator()(const Args&... Values) + { + (Update(Values), ...); + } + + void UpdateRaw(const void* pData, Uint64 Size) + { + const auto* Bytes = static_cast(pData); + for (Uint64 i = 0; i < Size; ++i) + { + m_Hash ^= Bytes[i]; + m_Hash *= FNVPrime; + } + } + + Uint64 Get() const { return m_Hash; } + +private: + static constexpr Uint64 FNVOffsetBasis = 14695981039346656037ull; + static constexpr Uint64 FNVPrime = 1099511628211ull; + Uint64 m_Hash = FNVOffsetBasis; +}; + +template +std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, const ShaderStagesType& ShaderStages, const GraphicsPipelineDesc* pGraphics = nullptr) +{ + // Hash the effective pipeline after binding remapping. Archive unpacking changes + // create-info flags/signatures and shader bindings, but not the native pipeline. + PipelineCacheHasher Hasher; + Hasher(RootSig.GetHash(), pGraphics != nullptr); + if (pGraphics != nullptr) + Hasher(*pGraphics); + for (const auto& Stage : ShaderStages) + { + Hasher(Stage.Type); + for (const auto& Bytecode : Stage.ByteCodes) + Hasher.UpdateRaw(Bytecode->GetConstDataPtr(), Bytecode->GetSize()); + } + return L"PSO_2_" + std::to_wstring(Hasher.Get()); +} + #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4324) // warning C4324: structure was padded due to alignment specifier @@ -641,6 +702,9 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat TShaderStages ShaderStages; InitInternalObjects(CreateInfo, ShaderStages); + PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); + const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages, &m_pGraphicsPipelineData->Desc) : std::wstring{}; + ID3D12Device* pd3d12Device = m_pDevice->GetD3D12Device(); if (m_Desc.PipelineType == PIPELINE_TYPE_GRAPHICS) { @@ -727,9 +791,8 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat d3d12PSODesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; // Try to load from the cache - PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); - if (pPSOCacheD3D12 != nullptr && !WName.empty()) - m_pd3d12PSO = pPSOCacheD3D12->LoadGraphicsPipeline(WName.c_str(), d3d12PSODesc); + if (pPSOCacheD3D12 != nullptr) + m_pd3d12PSO = pPSOCacheD3D12->LoadGraphicsPipeline(CacheName.c_str(), d3d12PSODesc); if (!m_pd3d12PSO) { // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested @@ -738,8 +801,8 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat LOG_ERROR_AND_THROW("Failed to create pipeline state"); // Add to the cache - if (pPSOCacheD3D12 != nullptr && !WName.empty()) - pPSOCacheD3D12->StorePipeline(WName.c_str(), m_pd3d12PSO); + if (pPSOCacheD3D12 != nullptr) + pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); } } #ifdef D3D12_H_HAS_MESH_SHADER @@ -749,20 +812,21 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat struct MESH_SHADER_PIPELINE_STATE_DESC { - PSS_SubObject Flags; - PSS_SubObject NodeMask; - PSS_SubObject pRootSignature; - PSS_SubObject PS; - PSS_SubObject AS; - PSS_SubObject MS; - PSS_SubObject BlendState; - PSS_SubObject DepthStencilState; - PSS_SubObject RasterizerState; - PSS_SubObject SampleDesc; - PSS_SubObject SampleMask; - PSS_SubObject DSVFormat; - PSS_SubObject RTVFormatArray; - PSS_SubObject CachedPSO; + PSS_SubObject Flags; + PSS_SubObject NodeMask; + PSS_SubObject pRootSignature; + PSS_SubObject PS; + PSS_SubObject AS; + PSS_SubObject MS; + PSS_SubObject BlendState; + PSS_SubObject DepthStencilState; + PSS_SubObject RasterizerState; + PSS_SubObject SampleDesc; + PSS_SubObject SampleMask; + PSS_SubObject PrimitiveTopologyType; + PSS_SubObject DSVFormat; + PSS_SubObject RTVFormatArray; + PSS_SubObject CachedPSO; }; MESH_SHADER_PIPELINE_STATE_DESC d3d12PSODesc = {}; @@ -791,6 +855,11 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat BlendStateDesc_To_D3D12_BLEND_DESC(GraphicsPipeline.BlendDesc, *d3d12PSODesc.BlendState); d3d12PSODesc.SampleMask = GraphicsPipeline.SampleMask; + // Explicit topology keeps CreatePipelineState and PipelineLibrary::LoadPipeline + // descriptions identical; creation may infer topology from the mesh shader. + static const PrimitiveTopology_To_D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimTopologyToD3D12TopologyType; + d3d12PSODesc.PrimitiveTopologyType = PrimTopologyToD3D12TopologyType[GraphicsPipeline.PrimitiveTopology]; + RasterizerStateDesc_To_D3D12_RASTERIZER_DESC(GraphicsPipeline.RasterizerDesc, *d3d12PSODesc.RasterizerState); DepthStencilStateDesc_To_D3D12_DEPTH_STENCIL_DESC(GraphicsPipeline.DepthStencilDesc, *d3d12PSODesc.DepthStencilState); @@ -819,11 +888,18 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat streamDesc.SizeInBytes = sizeof(d3d12PSODesc); streamDesc.pPipelineStateSubobjectStream = &d3d12PSODesc; - ID3D12Device2* pd3d12Device2 = m_pDevice->GetD3D12Device2(); - // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested - HRESULT hr = pd3d12Device2->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&m_pd3d12PSO)); - if (FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create pipeline state"); + if (pPSOCacheD3D12 != nullptr) + m_pd3d12PSO = pPSOCacheD3D12->LoadPipeline(CacheName.c_str(), streamDesc); + if (!m_pd3d12PSO) + { + ID3D12Device2* pd3d12Device2 = m_pDevice->GetD3D12Device2(); + // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested + HRESULT hr = pd3d12Device2->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&m_pd3d12PSO)); + if (FAILED(hr)) + LOG_ERROR_AND_THROW("Failed to create pipeline state"); + if (pPSOCacheD3D12 != nullptr) + pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); + } } #endif // D3D12_H_HAS_MESH_SHADER else @@ -868,8 +944,9 @@ void PipelineStateD3D12Impl::InitializePipeline(const ComputePipelineStateCreate // Try to load from the cache const std::wstring WName = WidenString(m_Desc.Name); PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); - if (pPSOCacheD3D12 != nullptr && !WName.empty()) - m_pd3d12PSO = pPSOCacheD3D12->LoadComputePipeline(WName.c_str(), d3d12PSODesc); + const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages) : std::wstring{}; + if (pPSOCacheD3D12 != nullptr) + m_pd3d12PSO = pPSOCacheD3D12->LoadComputePipeline(CacheName.c_str(), d3d12PSODesc); if (!m_pd3d12PSO) { // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested @@ -878,8 +955,8 @@ void PipelineStateD3D12Impl::InitializePipeline(const ComputePipelineStateCreate LOG_ERROR_AND_THROW("Failed to create pipeline state"); // Add to the cache - if (pPSOCacheD3D12 != nullptr && !WName.empty()) - pPSOCacheD3D12->StorePipeline(WName.c_str(), m_pd3d12PSO); + if (pPSOCacheD3D12 != nullptr) + pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); } if (!WName.empty()) diff --git a/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp b/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp index 58c87b7ca7..4c178c7b1b 100644 --- a/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp +++ b/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp @@ -834,6 +834,7 @@ bool RenderStateCacheImpl::CreatePipelineStateInternal(const CreateInfoType& PSO UnpackInfo.PipelineType = PSOCreateInfo.PSODesc.PipelineType; UnpackInfo.Name = HashStr.c_str(); UnpackInfo.pDevice = m_pDevice; + UnpackInfo.pCache = PSOCreateInfo.pPSOCache; UnpackInfo.ModifyPipelineStateCreateInfo = Callback; UnpackInfo.pUserData = Callback; RefCntAutoPtr pPSO; From 158e9acf3b4f8539770390ffcafc23e3578582ca Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 25 Oct 2026 14:43:07 +0800 Subject: [PATCH 2/8] D3D12: request pipeline state interface on native cache hits Load cached graphics and compute PSOs as ID3D12PipelineState, then explicitly upcast the returned raw pointer for shared DeviceChild storage, matching the mesh pipeline path. A separately queried DeviceChild interface is not guaranteed to be the base subobject of the PipelineState interface expected by downstream static casts and SetPipelineState. --- .../src/PipelineStateCacheD3D12Impl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp index 3e860b7dd4..79a52aa37f 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp @@ -89,7 +89,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(cons return {}; } - CComPtr d3d12PSO; + CComPtr d3d12PSO; if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) { std::lock_guard Lock{m_LibraryMtx}; @@ -97,7 +97,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(cons if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (compute): " : "PSO cache miss (compute): ", NarrowString(Name)); } - return d3d12PSO; + return CComPtr{d3d12PSO.p}; } CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(const wchar_t* Name, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) @@ -108,7 +108,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(con return {}; } - CComPtr d3d12PSO; + CComPtr d3d12PSO; if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) { std::lock_guard Lock{m_LibraryMtx}; @@ -116,7 +116,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(con if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (graphics): " : "PSO cache miss (graphics): ", NarrowString(Name)); } - return d3d12PSO; + return CComPtr{d3d12PSO.p}; } #ifdef D3D12_H_HAS_MESH_SHADER From f690fd599f1ed57c0fffd492775c17a0ef59eb2e Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 21:05:55 +0000 Subject: [PATCH 3/8] D3D12: Use XXH128 for pipeline cache keys --- .../src/PipelineStateD3D12Impl.cpp | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 008e6481f5..3644da38e4 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,7 +51,7 @@ #include "DynamicLinearAllocator.hpp" #include "D3DShaderResourceValidation.hpp" #include "DataBlobImpl.hpp" -#include "HashUtils.hpp" +#include "XXH128Hasher.hpp" #include "DXBCUtils.hpp" #include "DXCompiler.hpp" @@ -64,54 +64,12 @@ constexpr INTERFACE_ID PipelineStateD3D12Impl::IID_InternalImpl; namespace { -struct PipelineCacheHasher -{ - template - void Update(const T& Value) - { - if constexpr (std::is_fundamental::value || std::is_enum::value) - UpdateRaw(&Value, sizeof(Value)); - else - HashCombiner{*this}(Value); - } - - void Update(const char* Value) - { - const Uint64 Length = Value != nullptr ? std::strlen(Value) : 0; - Update(Length); - UpdateRaw(Value, Length); - } - - template - void operator()(const Args&... Values) - { - (Update(Values), ...); - } - - void UpdateRaw(const void* pData, Uint64 Size) - { - const auto* Bytes = static_cast(pData); - for (Uint64 i = 0; i < Size; ++i) - { - m_Hash ^= Bytes[i]; - m_Hash *= FNVPrime; - } - } - - Uint64 Get() const { return m_Hash; } - -private: - static constexpr Uint64 FNVOffsetBasis = 14695981039346656037ull; - static constexpr Uint64 FNVPrime = 1099511628211ull; - Uint64 m_Hash = FNVOffsetBasis; -}; - template std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, const ShaderStagesType& ShaderStages, const GraphicsPipelineDesc* pGraphics = nullptr) { // Hash the effective pipeline after binding remapping. Archive unpacking changes // create-info flags/signatures and shader bindings, but not the native pipeline. - PipelineCacheHasher Hasher; + XXH128State Hasher; Hasher(RootSig.GetHash(), pGraphics != nullptr); if (pGraphics != nullptr) Hasher(*pGraphics); @@ -121,7 +79,7 @@ std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, const Shade for (const auto& Bytecode : Stage.ByteCodes) Hasher.UpdateRaw(Bytecode->GetConstDataPtr(), Bytecode->GetSize()); } - return L"PSO_2_" + std::to_wstring(Hasher.Get()); + return L"PSO_2_" + WidenString(Hasher.Digest().ToString()); } #ifdef _MSC_VER From 54cd74a736cab2d735b4c091fa7daa4767fec6dc Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 21:30:35 +0000 Subject: [PATCH 4/8] D3D12: Simplify pipeline cache name helper --- .../GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 3644da38e4..63ba13af2f 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -64,13 +64,18 @@ constexpr INTERFACE_ID PipelineStateD3D12Impl::IID_InternalImpl; namespace { -template -std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, const ShaderStagesType& ShaderStages, const GraphicsPipelineDesc* pGraphics = nullptr) +std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, + const PipelineStateD3D12Impl::TShaderStages& ShaderStages, + const GraphicsPipelineDesc* pGraphics = nullptr) { // Hash the effective pipeline after binding remapping. Archive unpacking changes // create-info flags/signatures and shader bindings, but not the native pipeline. XXH128State Hasher; Hasher(RootSig.GetHash(), pGraphics != nullptr); + + // Graphics and mesh pipelines have additional fixed-function state. Compute + // pipelines are fully described by the root signature and shader bytecode. + // Ray tracing uses a separate state-object creation path and is not cached here. if (pGraphics != nullptr) Hasher(*pGraphics); for (const auto& Stage : ShaderStages) From 2f77fab21627c51c027cd3984554ee5ac86fc3e9 Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 21:38:42 +0000 Subject: [PATCH 5/8] D3D12: Extract shared pipeline cache creation flow Use LoadOrCreatePipeline for graphics, compute, and mesh pipelines. Centralize cache lookup, creation fallback, error handling, and storage while preserving the interface requirement for RenderDoc capture. --- .../src/PipelineStateD3D12Impl.cpp | 115 ++++++++++-------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 63ba13af2f..4839d9bb99 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -64,20 +64,23 @@ constexpr INTERFACE_ID PipelineStateD3D12Impl::IID_InternalImpl; namespace { -std::wstring GetPipelineCacheName(const RootSignatureD3D12& RootSig, - const PipelineStateD3D12Impl::TShaderStages& ShaderStages, - const GraphicsPipelineDesc* pGraphics = nullptr) +std::wstring GetPSOKey(const RootSignatureD3D12& RootSig, + const PipelineStateD3D12Impl::TShaderStages& ShaderStages, + const GraphicsPipelineDesc* pGraphics = nullptr) { // Hash the effective pipeline after binding remapping. Archive unpacking changes // create-info flags/signatures and shader bindings, but not the native pipeline. XXH128State Hasher; Hasher(RootSig.GetHash(), pGraphics != nullptr); - // Graphics and mesh pipelines have additional fixed-function state. Compute - // pipelines are fully described by the root signature and shader bytecode. - // Ray tracing uses a separate state-object creation path and is not cached here. if (pGraphics != nullptr) + { + // Graphics and mesh pipelines have additional fixed-function state. Compute + // pipelines are fully described by the root signature and shader bytecode. + // Ray tracing uses a separate state-object creation path and is not cached here. Hasher(*pGraphics); + } + for (const auto& Stage : ShaderStages) { Hasher(Stage.Type); @@ -87,6 +90,37 @@ std::wstring GetPipelineCacheName(const RootSignatureD3D12& R return L"PSO_2_" + WidenString(Hasher.Digest().ToString()); } +template +CComPtr LoadOrCreatePipeline(IPipelineStateCache* pPSOCache, + const RootSignatureD3D12& RootSig, + const PipelineStateD3D12Impl::TShaderStages& ShaderStages, + const GraphicsPipelineDesc* pGraphics, + const LoadPipelineType& LoadPipeline, + const CreatePipelineType& CreatePipeline) +{ + PipelineStateCacheD3D12Impl* const pCacheD3D12 = ClassPtrCast(pPSOCache); + + std::wstring PSOKey; + CComPtr pPSO; + if (pCacheD3D12 != nullptr) + { + PSOKey = GetPSOKey(RootSig, ShaderStages, pGraphics); + pPSO = LoadPipeline(*pCacheD3D12, PSOKey.c_str()); + } + + if (!pPSO) + { + // RenderDoc frame capture requires requesting IID_ID3D12PipelineState. + const HRESULT hr = CreatePipeline(__uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&pPSO)); + if (FAILED(hr)) + LOG_ERROR_AND_THROW("Failed to create pipeline state"); + + if (pCacheD3D12 != nullptr) + pCacheD3D12->StorePipeline(PSOKey.c_str(), pPSO); + } + return pPSO; +} + #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4324) // warning C4324: structure was padded due to alignment specifier @@ -665,9 +699,6 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat TShaderStages ShaderStages; InitInternalObjects(CreateInfo, ShaderStages); - PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); - const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages, &m_pGraphicsPipelineData->Desc) : std::wstring{}; - ID3D12Device* pd3d12Device = m_pDevice->GetD3D12Device(); if (m_Desc.PipelineType == PIPELINE_TYPE_GRAPHICS) { @@ -753,20 +784,14 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat // The only valid bit is D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG, which can only be set on WARP devices. d3d12PSODesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; - // Try to load from the cache - if (pPSOCacheD3D12 != nullptr) - m_pd3d12PSO = pPSOCacheD3D12->LoadGraphicsPipeline(CacheName.c_str(), d3d12PSODesc); - if (!m_pd3d12PSO) - { - // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested - HRESULT hr = pd3d12Device->CreateGraphicsPipelineState(&d3d12PSODesc, __uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&m_pd3d12PSO)); - if (FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create pipeline state"); - - // Add to the cache - if (pPSOCacheD3D12 != nullptr) - pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); - } + m_pd3d12PSO = LoadOrCreatePipeline( + CreateInfo.pPSOCache, *m_RootSig, ShaderStages, &GraphicsPipeline, + [&](PipelineStateCacheD3D12Impl& Cache, const wchar_t* Name) { + return Cache.LoadGraphicsPipeline(Name, d3d12PSODesc); + }, + [&](REFIID IID, void** ppPSO) { + return pd3d12Device->CreateGraphicsPipelineState(&d3d12PSODesc, IID, ppPSO); + }); } #ifdef D3D12_H_HAS_MESH_SHADER else if (m_Desc.PipelineType == PIPELINE_TYPE_MESH) @@ -851,18 +876,14 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat streamDesc.SizeInBytes = sizeof(d3d12PSODesc); streamDesc.pPipelineStateSubobjectStream = &d3d12PSODesc; - if (pPSOCacheD3D12 != nullptr) - m_pd3d12PSO = pPSOCacheD3D12->LoadPipeline(CacheName.c_str(), streamDesc); - if (!m_pd3d12PSO) - { - ID3D12Device2* pd3d12Device2 = m_pDevice->GetD3D12Device2(); - // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested - HRESULT hr = pd3d12Device2->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&m_pd3d12PSO)); - if (FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create pipeline state"); - if (pPSOCacheD3D12 != nullptr) - pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); - } + m_pd3d12PSO = LoadOrCreatePipeline( + CreateInfo.pPSOCache, *m_RootSig, ShaderStages, &GraphicsPipeline, + [&](PipelineStateCacheD3D12Impl& Cache, const wchar_t* Name) { + return Cache.LoadPipeline(Name, streamDesc); + }, + [&](REFIID IID, void** ppPSO) { + return m_pDevice->GetD3D12Device2()->CreatePipelineState(&streamDesc, IID, ppPSO); + }); } #endif // D3D12_H_HAS_MESH_SHADER else @@ -905,22 +926,16 @@ void PipelineStateD3D12Impl::InitializePipeline(const ComputePipelineStateCreate d3d12PSODesc.pRootSignature = m_RootSig->GetD3D12RootSignature(); // Try to load from the cache - const std::wstring WName = WidenString(m_Desc.Name); - PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); - const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages) : std::wstring{}; - if (pPSOCacheD3D12 != nullptr) - m_pd3d12PSO = pPSOCacheD3D12->LoadComputePipeline(CacheName.c_str(), d3d12PSODesc); - if (!m_pd3d12PSO) - { - // Note: renderdoc frame capture fails if any interface but IID_ID3D12PipelineState is requested - HRESULT hr = pd3d12Device->CreateComputePipelineState(&d3d12PSODesc, __uuidof(ID3D12PipelineState), IID_PPV_ARGS_Helper(&m_pd3d12PSO)); - if (FAILED(hr)) - LOG_ERROR_AND_THROW("Failed to create pipeline state"); + const std::wstring WName = WidenString(m_Desc.Name); - // Add to the cache - if (pPSOCacheD3D12 != nullptr) - pPSOCacheD3D12->StorePipeline(CacheName.c_str(), m_pd3d12PSO); - } + m_pd3d12PSO = LoadOrCreatePipeline( + CreateInfo.pPSOCache, *m_RootSig, ShaderStages, nullptr, + [&](PipelineStateCacheD3D12Impl& Cache, const wchar_t* Name) { + return Cache.LoadComputePipeline(Name, d3d12PSODesc); + }, + [&](REFIID IID, void** ppPSO) { + return pd3d12Device->CreateComputePipelineState(&d3d12PSODesc, IID, ppPSO); + }); if (!WName.empty()) { From 4d06106b894c4d191f42093f78e3c7631ea6e50f Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 22:10:39 +0000 Subject: [PATCH 6/8] D3D12: Clarify pipeline library data lifetime --- .../include/PipelineStateCacheD3D12Impl.hpp | 9 ++++++--- .../src/PipelineStateCacheD3D12Impl.cpp | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp index ab78bc6558..38b5b96ae4 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,10 +61,13 @@ class PipelineStateCacheD3D12Impl final : public PipelineStateCacheBase m_pInitialData; + // CreatePipelineLibrary retains the raw data pointer but neither copies the bytes + // nor takes ownership of the memory. + RefCntAutoPtr m_pInitialData; + + std::mutex m_LibraryMtx; CComPtr m_pLibrary; CComPtr m_pLibrary1; - std::mutex m_LibraryMtx; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp index 79a52aa37f..def68d732a 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp @@ -46,17 +46,20 @@ PipelineStateCacheD3D12Impl::PipelineStateCacheD3D12Impl(IReferenceCounters* } // clang-format on { - auto* pDevice1 = pRenderDeviceD3D12->GetD3D12Device1(); + ID3D12Device1* pDevice1 = pRenderDeviceD3D12->GetD3D12Device1(); if (pDevice1 == nullptr) { LOG_WARNING_MESSAGE("D3D12 pipeline libraries are unavailable; native caching is disabled."); return; } - // D3D12 does not copy the serialized data: retain our own copy until the library is released. if (CreateInfo.pCacheData != nullptr && CreateInfo.CacheDataSize != 0) { - m_pInitialData = DataBlobImpl::Create(CreateInfo.CacheDataSize, CreateInfo.pCacheData); + // CreatePipelineLibrary retains the raw data pointer but neither copies the bytes + // nor takes ownership of the memory. Keeping the library alive does not keep the + // IDataBlob alive, so own a copy and retain it until the library is destroyed. + m_pInitialData = DataBlobImpl::Create(CreateInfo.CacheDataSize, CreateInfo.pCacheData); + const HRESULT LoadResult = pDevice1->CreatePipelineLibrary(m_pInitialData->GetConstDataPtr(), m_pInitialData->GetSize(), IID_PPV_ARGS(&m_pLibrary)); if (FAILED(LoadResult)) { @@ -78,6 +81,7 @@ PipelineStateCacheD3D12Impl::~PipelineStateCacheD3D12Impl() { // D3D12 object can only be destroyed when it is no longer used by the GPU m_pLibrary1.Release(); + // Keep the backing data alive through deferred destruction of the library. GetDevice()->SafeReleaseDeviceObject(std::make_pair(std::move(m_pInitialData), std::move(m_pLibrary)), ~Uint64{0}); } From b5a5dc1cddd71b001c5c4029e5ef610bba96804d Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 22:23:58 +0000 Subject: [PATCH 7/8] D3D12: Preserve shader-declared mesh topology Omit the primitive topology subobject from mesh pipeline streams and document why GraphicsPipeline.PrimitiveTopology is ignored. --- .../src/PipelineStateD3D12Impl.cpp | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 4839d9bb99..9ce0c2d73f 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -798,23 +798,24 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat { const GraphicsPipelineDesc& GraphicsPipeline = m_pGraphicsPipelineData->Desc; + // Omit the primitive topology subobject: mesh shaders declare their output topology + // with [outputtopology], and GraphicsPipeline.PrimitiveTopology is ignored. struct MESH_SHADER_PIPELINE_STATE_DESC { - PSS_SubObject Flags; - PSS_SubObject NodeMask; - PSS_SubObject pRootSignature; - PSS_SubObject PS; - PSS_SubObject AS; - PSS_SubObject MS; - PSS_SubObject BlendState; - PSS_SubObject DepthStencilState; - PSS_SubObject RasterizerState; - PSS_SubObject SampleDesc; - PSS_SubObject SampleMask; - PSS_SubObject PrimitiveTopologyType; - PSS_SubObject DSVFormat; - PSS_SubObject RTVFormatArray; - PSS_SubObject CachedPSO; + PSS_SubObject Flags; + PSS_SubObject NodeMask; + PSS_SubObject pRootSignature; + PSS_SubObject PS; + PSS_SubObject AS; + PSS_SubObject MS; + PSS_SubObject BlendState; + PSS_SubObject DepthStencilState; + PSS_SubObject RasterizerState; + PSS_SubObject SampleDesc; + PSS_SubObject SampleMask; + PSS_SubObject DSVFormat; + PSS_SubObject RTVFormatArray; + PSS_SubObject CachedPSO; }; MESH_SHADER_PIPELINE_STATE_DESC d3d12PSODesc = {}; @@ -843,11 +844,6 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat BlendStateDesc_To_D3D12_BLEND_DESC(GraphicsPipeline.BlendDesc, *d3d12PSODesc.BlendState); d3d12PSODesc.SampleMask = GraphicsPipeline.SampleMask; - // Explicit topology keeps CreatePipelineState and PipelineLibrary::LoadPipeline - // descriptions identical; creation may infer topology from the mesh shader. - static const PrimitiveTopology_To_D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimTopologyToD3D12TopologyType; - d3d12PSODesc.PrimitiveTopologyType = PrimTopologyToD3D12TopologyType[GraphicsPipeline.PrimitiveTopology]; - RasterizerStateDesc_To_D3D12_RASTERIZER_DESC(GraphicsPipeline.RasterizerDesc, *d3d12PSODesc.RasterizerState); DepthStencilStateDesc_To_D3D12_DEPTH_STENCIL_DESC(GraphicsPipeline.DepthStencilDesc, *d3d12PSODesc.DepthStencilState); From dadb93b2babc33641468732e8d1ddf2699c44bdd Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 27 Oct 2026 22:31:53 +0000 Subject: [PATCH 8/8] D3D12: Consolidate pipeline cache loading Extract shared validation, locking, and logging into LoadPipelineImpl for graphics, compute, and mesh pipelines. --- .../include/PipelineStateCacheD3D12Impl.hpp | 6 +++ .../src/PipelineStateCacheD3D12Impl.cpp | 52 ++++++++----------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp index 38b5b96ae4..8ad318584c 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp @@ -61,6 +61,12 @@ class PipelineStateCacheD3D12Impl final : public PipelineStateCacheBase + CComPtr LoadPipelineImpl(ID3D12PipelineLibrary* pLibrary, + const wchar_t* Name, + const char* PipelineType, + const LoadPipelineType& LoadPipeline); + // CreatePipelineLibrary retains the raw data pointer but neither copies the bytes // nor takes ownership of the memory. RefCntAutoPtr m_pInitialData; diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp index def68d732a..d77c098218 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,7 +85,11 @@ PipelineStateCacheD3D12Impl::~PipelineStateCacheD3D12Impl() GetDevice()->SafeReleaseDeviceObject(std::make_pair(std::move(m_pInitialData), std::move(m_pLibrary)), ~Uint64{0}); } -CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(const wchar_t* Name, const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) +template +CComPtr PipelineStateCacheD3D12Impl::LoadPipelineImpl(ID3D12PipelineLibrary* pLibrary, + const wchar_t* Name, + const char* PipelineType, + const LoadPipelineType& LoadPipeline) { if (Name == nullptr) { @@ -94,48 +98,36 @@ CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(cons } CComPtr d3d12PSO; - if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) + if (pLibrary != nullptr && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) { std::lock_guard Lock{m_LibraryMtx}; - HRESULT hr = m_pLibrary->LoadComputePipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); + const HRESULT hr = LoadPipeline(IID_PPV_ARGS(&d3d12PSO)); if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (compute): " : "PSO cache miss (compute): ", NarrowString(Name)); + LOG_INFO_MESSAGE("PSO cache ", SUCCEEDED(hr) ? "hit" : "miss", " (", PipelineType, "): ", NarrowString(Name), " (HRESULT ", hr, ")"); } return CComPtr{d3d12PSO.p}; } -CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(const wchar_t* Name, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) +CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(const wchar_t* Name, const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) { - if (Name == nullptr) - { - DEV_ERROR("Pipeline name must not be null"); - return {}; - } + return LoadPipelineImpl(m_pLibrary, Name, "compute", [&](REFIID IID, void** ppPSO) { + return m_pLibrary->LoadComputePipeline(Name, &Desc, IID, ppPSO); + }); +} - CComPtr d3d12PSO; - if (m_pLibrary && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) - { - std::lock_guard Lock{m_LibraryMtx}; - HRESULT hr = m_pLibrary->LoadGraphicsPipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); - if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (graphics): " : "PSO cache miss (graphics): ", NarrowString(Name)); - } - return CComPtr{d3d12PSO.p}; +CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(const wchar_t* Name, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) +{ + return LoadPipelineImpl(m_pLibrary, Name, "graphics", [&](REFIID IID, void** ppPSO) { + return m_pLibrary->LoadGraphicsPipeline(Name, &Desc, IID, ppPSO); + }); } #ifdef D3D12_H_HAS_MESH_SHADER CComPtr PipelineStateCacheD3D12Impl::LoadPipeline(const wchar_t* Name, const D3D12_PIPELINE_STATE_STREAM_DESC& Desc) { - VERIFY_EXPR(Name != nullptr); - CComPtr d3d12PSO; - if (m_pLibrary1 && (m_Desc.Mode & PSO_CACHE_MODE_LOAD) != 0) - { - std::lock_guard Lock{m_LibraryMtx}; - const HRESULT hr = m_pLibrary1->LoadPipeline(Name, &Desc, IID_PPV_ARGS(&d3d12PSO)); - if ((m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) - LOG_INFO_MESSAGE(SUCCEEDED(hr) ? "PSO cache hit (mesh): " : "PSO cache miss (mesh): ", NarrowString(Name), " (HRESULT ", hr, ")"); - } - return CComPtr{d3d12PSO.p}; + return LoadPipelineImpl(m_pLibrary1, Name, "mesh", [&](REFIID IID, void** ppPSO) { + return m_pLibrary1->LoadPipeline(Name, &Desc, IID, ppPSO); + }); } #endif