Skip to content

D3D12: enable persistent PSO cache reuse, Fix #811 - #812

Open
hzqst wants to merge 3 commits into
DiligentGraphics:masterfrom
hzqst:master
Open

hzqst wants to merge 3 commits into
DiligentGraphics:masterfrom
hzqst:master

Conversation

@hzqst

@hzqst hzqst commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Restore and own initial library data. Copy supplied bytes into an owned IDataBlob and pass them to CreatePipelineLibrary. Retain the blob with the library through deferred release. If the data is rejected as invalid/incompatible, retry with an empty library. If pipeline libraries are unavailable, allow PSO creation to continue without native caching.
  • Forward the cache during archive unpacking. Set UnpackInfo.pCache = PSOCreateInfo.pPSOCache.
  • Add mesh library support. Query ID3D12PipelineLibrary1, try LoadPipeline for mesh PSO streams, and store newly created mesh PSOs. Populate an explicit primitive-topology subobject so creation and lookup use consistent descriptions.
  • Use deterministic content-based names. Generate PSO_2_<hash> from the root-signature hash, graphics pipeline description when applicable, shader stages, and effective shader bytecode after binding remapping. Hash strings by content rather than pointer address. This removes dependence on display names and supports same-name variants and unnamed pipelines.
  • Serialize library operations. Protect load/store/serialization with one mutex, including parallel PSO creation and cache snapshots.
  • Request ID3D12PipelineState on graphics and compute cache hits. Use a concrete PSO pointer for IID_PPV_ARGS, then explicitly upcast the raw pointer into the existing ID3D12DeviceChild storage, matching the mesh path.

Why the cache load must request ID3D12PipelineState

IID_PPV_ARGS(&d3d12PSO) derives the requested IID from the pointer's declared type. With CComPtr<ID3D12DeviceChild>, the library is asked for IID_ID3D12DeviceChild, not IID_ID3D12PipelineState.

Requesting a base COM interface can succeed, but COM does not guarantee that the returned interface pointer has the same address/layout as the base subobject of a separately obtained ID3D12PipelineState interface. The canonical pointer identity guarantee applies to queries for IUnknown; it does not make arbitrary interface pointers interchangeable. See Microsoft's QueryInterface rules.

This matters because Diligent stores native pipeline objects in CComPtr<ID3D12DeviceChild> and GetD3D12PipelineState() later uses static_cast<ID3D12PipelineState*>(m_pd3d12PSO.p) before binding. That cast is valid for the base subobject of a real ID3D12PipelineState pointer; it does not perform QueryInterface and cannot turn an independently returned DeviceChild interface into the required PSO interface. The creation path already requests ID3D12PipelineState explicitly.

The graphics/compute cache load paths therefore must request ID3D12PipelineState directly (or explicitly query it before use). They now use CComPtr<ID3D12PipelineState> and return CComPtr<ID3D12DeviceChild>{d3d12PSO.p}. The ordinary C++ upcast preserves the correct PSO interface's base subobject and retains its reference, without querying the object again for a potentially different DeviceChild interface. This follows the existing mesh-cache implementation and keeps the shared storage/API unchanged. Microsoft's LoadGraphicsPipeline documentation describes the requested PSO interface.

On the affected Windows/NVIDIA configuration, a successful cache hit followed by binding the wrong interface caused a node-mask mismatch at SetPipelineState, E_INVALIDARG from command-list Close, and device removal after submission. A later texture allocation reported the fatal error, obscuring the original failure. Validation-layer wrappers could mask the interface-layout problem, so validation-disabled coverage is necessary.

Validation

The four-line interface fix was built and exercised in the downstream HLND2T-DiligentGraphics integration checkout; this PR adds the same source change (the file differs only in formatting). These are downstream results, not a run of the entire upstream DiligentCore test suite:

  • D3D12 backend builds: x86 RelWithDebInfo, x64 RelWithDebInfo, and x64 Debug.
  • Downstream d3d12_pipeline_cache and vulkan_pipeline_cache: both passed. D3D12 covers validation explicitly disabled and enabled, warm graphics PSO binding/submission, compute GPU readback, native graphics/compute/mesh cache hits, concurrent serialization, and corrupt-data fallback. The old backend failed when the restored graphics PSO was bound.
  • 16 x86 game startup runs reached 200 Present calls each, including six warm-cache runs with successful graphics pipeline-library loads; no runtime IID substitution was used.
  • In-process rendering and the IPC x64 render server both loaded de_dust2, rendered the map/player HUD, and exited normally.
  • git diff --check passed for this commit. The upstream full suite was not run in the isolated PR worktree.

hzqst and others added 3 commits October 25, 2026 14:44
…nes.

Retain serialized pipeline library data, use stable content keys, forward caches during archive unpacking, and cache mesh pipelines with explicit topology.
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 <noreply@anthropic.com>
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 <codex@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant