From 1259e8d0fe1e40f73c2c3ccb11cc94e3700cb278 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 17 Oct 2026 08:34:26 +0800 Subject: [PATCH 1/3] 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 | 7 ++ .../src/PipelineStateCacheD3D12Impl.cpp | 69 ++++++++++-- .../src/PipelineStateD3D12Impl.cpp | 105 +++++++++++++++--- .../src/RenderStateCacheImpl.cpp | 1 + 4 files changed, 157 insertions(+), 25 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp index 11d7c6e7bd..0d6bd072df 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: + 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..5073925cf5 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) { + std::lock_guard Lock{m_LibraryMtx}; 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"); + 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) { + std::lock_guard Lock{m_LibraryMtx}; 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"); + 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; + 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..aab6c022aa 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 @@ -760,6 +823,7 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat PSS_SubObject RasterizerState; PSS_SubObject SampleDesc; PSS_SubObject SampleMask; + PSS_SubObject PrimitiveTopologyType; PSS_SubObject DSVFormat; PSS_SubObject RTVFormatArray; PSS_SubObject CachedPSO; @@ -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 abd42b858e..65d1fed688 100644 --- a/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp +++ b/Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp @@ -833,6 +833,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 df0ea3c4bfed0dd660feb714e67ec4c0fe9f5a69 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 17 Oct 2026 08:42:40 +0800 Subject: [PATCH 2/3] style(D3D12): fix clang-format violations in PSO cache code Align consecutive member and local declarations in PipelineStateCacheD3D12Impl.hpp/.cpp and PipelineStateD3D12Impl.cpp to satisfy the format validation pre-check. Whitespace only. Co-Authored-By: Claude Code --- .../include/PipelineStateCacheD3D12Impl.hpp | 6 ++-- .../src/PipelineStateCacheD3D12Impl.cpp | 10 +++--- .../src/PipelineStateD3D12Impl.cpp | 34 +++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp index 0d6bd072df..ab78bc6558 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/PipelineStateCacheD3D12Impl.hpp @@ -61,10 +61,10 @@ class PipelineStateCacheD3D12Impl final : public PipelineStateCacheBase m_pInitialData; - CComPtr m_pLibrary; + RefCntAutoPtr m_pInitialData; + CComPtr m_pLibrary; CComPtr m_pLibrary1; - std::mutex m_LibraryMtx; + std::mutex m_LibraryMtx; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp index 5073925cf5..3e860b7dd4 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateCacheD3D12Impl.cpp @@ -56,7 +56,7 @@ PipelineStateCacheD3D12Impl::PipelineStateCacheD3D12Impl(IReferenceCounters* // 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); + 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)) { @@ -93,7 +93,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadComputePipeline(cons if (m_pLibrary && (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)); + 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)); } @@ -112,7 +112,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadGraphicsPipeline(con 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)); + 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)); } @@ -127,7 +127,7 @@ CComPtr PipelineStateCacheD3D12Impl::LoadPipeline(const wchar 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)); + 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, ")"); } @@ -142,7 +142,7 @@ bool PipelineStateCacheD3D12Impl::StorePipeline(const wchar_t* Name, ID3D12Devic return false; std::lock_guard Lock{m_LibraryMtx}; - HRESULT hr = m_pLibrary->StorePipeline(Name, static_cast(pPSO)); + HRESULT hr = m_pLibrary->StorePipeline(Name, static_cast(pPSO)); if (FAILED(hr) && (m_Desc.Flags & PSO_CACHE_FLAG_VERBOSE) != 0) LOG_INFO_MESSAGE("PSO cache store failed: ", NarrowString(Name), " (HRESULT ", hr, "; it may already be stored)."); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index aab6c022aa..008e6481f5 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -103,7 +103,7 @@ struct PipelineCacheHasher private: static constexpr Uint64 FNVOffsetBasis = 14695981039346656037ull; static constexpr Uint64 FNVPrime = 1099511628211ull; - Uint64 m_Hash = FNVOffsetBasis; + Uint64 m_Hash = FNVOffsetBasis; }; template @@ -703,7 +703,7 @@ void PipelineStateD3D12Impl::InitializePipeline(const GraphicsPipelineStateCreat InitInternalObjects(CreateInfo, ShaderStages); PipelineStateCacheD3D12Impl* const pPSOCacheD3D12 = ClassPtrCast(CreateInfo.pPSOCache); - const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages, &m_pGraphicsPipelineData->Desc) : std::wstring{}; + 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) @@ -812,21 +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 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 DSVFormat; + PSS_SubObject RTVFormatArray; + PSS_SubObject CachedPSO; }; MESH_SHADER_PIPELINE_STATE_DESC d3d12PSODesc = {}; @@ -944,7 +944,7 @@ 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); - const std::wstring CacheName = pPSOCacheD3D12 ? GetPipelineCacheName(*m_RootSig, ShaderStages) : std::wstring{}; + 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) From 92e7100e590ddcf7f50f317a6827d9665c601384 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 25 Oct 2026 14:43:07 +0800 Subject: [PATCH 3/3] fix(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. Co-Authored-By: Codex --- .../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