From 63607298ea5b1dd8a4263a64028a1fd52d03332b Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 30 Sep 2026 22:21:06 +0300 Subject: [PATCH] Execute Windows DLSS K and M at recorded D3D12 command-list boundaries --- CMakeLists.txt | 19 +- docs/windows-rdna4-port.md | 58 +- scripts/windows/build-windows-rdna4.ps1 | 10 + scripts/windows/test-windows-rdna4.ps1 | 5 + tools/windows/d3d12_command_hooks.cpp | 320 +++++++++ tools/windows/d3d12_command_hooks.generated.h | 653 ++++++++++++++++++ tools/windows/d3d12_command_hooks.h | 22 + tools/windows/d3d12_command_probe.cpp | 163 +++++ tools/windows/d3d12_dlss_probe.cpp | 39 +- tools/windows/d4r_nvngx_windows.cpp | 44 +- tools/windows/generate_d3d12_hooks.py | 101 +++ tools/windows/ngx_windows_runtime.h | 2 + 12 files changed, 1421 insertions(+), 15 deletions(-) create mode 100644 tools/windows/d3d12_command_hooks.cpp create mode 100644 tools/windows/d3d12_command_hooks.generated.h create mode 100644 tools/windows/d3d12_command_hooks.h create mode 100644 tools/windows/d3d12_command_probe.cpp create mode 100644 tools/windows/generate_d3d12_hooks.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ae40fe..0149548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.24) -project(d4r_windows_diagnostics LANGUAGES CXX) +project(d4r_windows_diagnostics LANGUAGES C CXX) if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8) message(FATAL_ERROR "These diagnostics require native Windows x64") endif() @@ -68,7 +68,7 @@ foreach(family k m) endforeach() endforeach() add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects}) -foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_probe cuda_driver_probe cuda_mma_probe cuda_image_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe d3d12_dlss_probe) +foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_probe cuda_driver_probe cuda_mma_probe cuda_image_probe d3d12_command_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe d3d12_dlss_probe) add_executable(d4r_${probe} tools/windows/${probe}.cpp) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include") @@ -84,11 +84,17 @@ foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_pr endif() install(TARGETS d4r_${probe} RUNTIME DESTINATION bin) endforeach() -add_library(d4r_nvngx_windows SHARED tools/windows/d4r_nvngx_windows.cpp) +set(D4R_MINHOOK_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/MinHook" CACHE PATH "MinHook 1.3.4 source checkout") +if(NOT EXISTS "${D4R_MINHOOK_ROOT}/include/MinHook.h") + message(FATAL_ERROR "Clone MinHook v1.3.4 into external/MinHook or set D4R_MINHOOK_ROOT") +endif() +add_subdirectory("${D4R_MINHOOK_ROOT}" "${CMAKE_CURRENT_BINARY_DIR}/minhook" EXCLUDE_FROM_ALL) +add_library(d4r_nvngx_windows SHARED tools/windows/d4r_nvngx_windows.cpp tools/windows/d3d12_command_hooks.cpp) set_target_properties(d4r_nvngx_windows PROPERTIES PREFIX "" OUTPUT_NAME "d4r_nvngx") target_compile_definitions(d4r_nvngx_windows PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_include_directories(d4r_nvngx_windows PRIVATE "${D4R_HIP_ROOT}/include") -target_link_libraries(d4r_nvngx_windows PRIVATE version dbghelp d3d12 dxguid) +target_link_libraries(d4r_nvngx_windows PRIVATE version dbghelp d3d12 dxguid minhook) +install(FILES "${D4R_MINHOOK_ROOT}/LICENSE.txt" DESTINATION licenses RENAME MinHook.txt) if(MINGW) target_link_options(d4r_nvngx_windows PRIVATE -static) endif() @@ -132,6 +138,8 @@ endif() install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat) target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid) target_link_libraries(d4r_d3d12_dlss_probe PRIVATE d3d12 dxgi dxguid) +target_link_libraries(d4r_d3d12_command_probe PRIVATE d3d12 dxgi dxguid) +add_dependencies(d4r_d3d12_command_probe d4r_nvngx_windows) add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object) add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object) add_dependencies(d4r_k_module_probe d4r_native_gfx1201_compile) @@ -155,6 +163,9 @@ install(FILES kernels/tools/pwin_model.py kernels/tools/pwin_check.py kernels/to install(FILES kernels/tools/model_enc3.py kernels/tools/swin_model.py kernels/tools/swin_check.py DESTINATION reference) install(FILES docs/windows-rdna4-port.md DESTINATION docs) include(CTest) +add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe + --hip-root "${D4R_HIP_ROOT}" --module "$" --iterations 32) +set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime") add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}") set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201") diff --git a/docs/windows-rdna4-port.md b/docs/windows-rdna4-port.md index 22ad744..2ba78ea 100644 --- a/docs/windows-rdna4-port.md +++ b/docs/windows-rdna4-port.md @@ -13,7 +13,10 @@ frames on Windows. M now passes all five synthetic and real-weight layers agains NumPy and independent PTX, and four native CUDA frames. The standalone D3D12 harness now produces four K and four M frames using imported VRAM and shared fences, with bit-exact RGB agreement against the corresponding CUDA harnesses. -Game command-list/queue integration remains in progress. A full temporal M scan +Game integration remains in progress. The ordinary NGX D3D12 Evaluate API now +records a boundary in an open command list; a native queue interception backend +executes prefix, CUDA evaluation, then suffix in the same submitted frame. +A full temporal M scan found rare attention P/V accumulation mismatches that the earlier twelve-block sampling missed. `SWIN_EXACT_PV` fixes the native baseline; ZLUDA patch `0013` adds the corresponding opt-in f16 reference accumulation. All spatial blocks in @@ -31,7 +34,7 @@ Do not integrate NGX until the integer PTX workload is stable on the real GPU. | M5: independently validated gfx12 WMMA backend | PASS: raw + legacy adapter + upstream layout, max abs/relative error 0 | | M6: K layers, full transformer and image validation | PASS: 11 real-weight layers, 44 native launches, four D3D12 frames bit-exact against native CUDA, 81.85–92.91 dB versus translated K | | M7: M FP16-equivalent baseline and full transformer | PASS: all blocks of 40 native launches exactly match NumPy; targeted independent PTX regression matches; four D3D12 frames bit-exact against corrected translated M | -| M8: standalone Windows D3D12 NGX harness | PASS at explicit submitted queue boundaries: K/M, four frames each, no shim CPU image copies, bit-exact RGB against CUDA harness | +| M8: standalone Windows D3D12 NGX harness | PASS for ordinary recorded EvaluateFeature and explicit boundaries: K/M, four frames each, no shim CPU image copies; both bit-exact against explicit boundary baselines | | M9: OptiScaler integration, profiling, installation | Pending | ## M temporal precision regression (2026-09-30) @@ -93,6 +96,57 @@ The game selected for the next integration test is Silent Hill 2 at `D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe`. No game files have been modified at this milestone. +## Open D3D12 command lists (2026-09-30) + +The backend intercepts documented D3D12 COM methods using pinned open-source +[MinHook 1.3.4](https://github.com/TsudaKageyu/minhook/releases/tag/v1.3.4), commit +`c3fcafdc10146beb5919319d0683e44e3c30d537`. No NVIDIA instructions, proprietary +DLL contents or private driver interfaces are patched. The typed forwarding +code is generated from public SDK CommandList0..7 declarations. The additional +CommandList8..10 declarations were audited in Microsoft's current public +DirectX-Headers, `adbd6f3ba40795c46a8d0f33af00bcb57ff0f0a4`; coverage of those +newer methods is the next implementation step. + +At NGX Evaluate, the caller's resource references and typed parameter values are +copied into an owned snapshot. The recorded prefix is closed. Subsequent calls +on the same logical object are forwarded into a fresh native suffix list with +its own allocator. State-setting calls are replayed with deep copies of host +arrays and retained COM references. Open PIX events are closed in the prefix +and reopened in the suffix. At ExecuteCommandLists, preceding lists in the +caller's batch execute first, followed by the prefix, actual CUDA evaluation +and output copy, then the suffix and following lists. All work completes in the +same frame. Completion waits currently stall the host; they do not age frames. + +The callback module remains loaded while public D3D12 method interceptions are +installed. Original command-list pointers in the routing map are weak; final +Release removes metadata, suffixes, allocators and queued feature references. +This avoids a reference cycle that would keep every command list alive. +Failure at queue submission is logged and prevents submitting the consumer +suffix. No previous-frame fallback is substituted. + +`d3d12-command-foundation` passes 32 iterations with no NVIDIA binaries. Each +batch contains A, B and C. A produces shader inputs, B dispatches before a +boundary, the callback verifies that prefix and replaces inputs on the GPU, +the suffix dispatches without rebinding root state, and C reads the result. +All values match; a temporary root-constant array may change after recording; +live recording metadata returns to zero after every iteration. + +`d3d12-m-command-list.zip` passes four ordinary NGX EvaluateFeature frames with +forty native M launches. RGB is bit-exact to the earlier explicit boundary +harness. The initial producer remains unsubmitted until the same queue batch, +and the caller's Color/Reset parameters are deliberately changed after +Evaluate returns, testing snapshot ownership. `d3d12-k-command-list.zip` also +passes four finite frames and all forty-four native K launches; all four RGB +frames are bit-exact against `d3d12-k-final`. Complete CTest: 12/12 PASS +(`d3d12-command-final-ctest.log`). + +Enable this backend with `D4R_D3D12_COMMAND_BACKEND=1`; the diagnostic runner +sets it through `-CommandListBackend`. Before game testing, remaining gates are +newer command methods, indirect-state reconstruction, enhanced barrier layouts, +actual game texture formats and interaction with OptiScaler's own state hooks. +Closed lists, active render/query scopes, GPU predication and unknown indirect +state are currently explicitly rejected instead of guessing their semantics. + ## Source baseline (checked 2026-09-29) * d4r: `ed1ab60`, main, 14 commits; https://github.com/countervolts/d4r. diff --git a/scripts/windows/build-windows-rdna4.ps1 b/scripts/windows/build-windows-rdna4.ps1 index 10fa2d2..68debea 100644 --- a/scripts/windows/build-windows-rdna4.ps1 +++ b/scripts/windows/build-windows-rdna4.ps1 @@ -35,6 +35,15 @@ if (!(Test-Path $cmake) -or !(Test-Path $ninja)) { throw 'CMake >=3.24 and Ninja required. See docs/windows-rdna4-port.md for local tool setup.' } New-Item -ItemType Directory -Force $BuildDirectory | Out-Null +$minhook = Join-Path $repo 'external/MinHook' +if (!(Test-Path (Join-Path $minhook 'include/MinHook.h'))) { + & git clone --depth 1 --branch v1.3.4 https://github.com/TsudaKageyu/minhook.git $minhook + if ($LASTEXITCODE) { throw 'MinHook 1.3.4 source download failed.' } +} +$minhookCommit = (& git -C $minhook rev-parse HEAD).Trim() +if ($minhookCommit -ne 'c3fcafdc10146beb5919319d0683e44e3c30d537') { + throw "Expected audited MinHook 1.3.4 source, found $minhookCommit" +} $originalPath = $env:PATH try { $configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', @@ -44,6 +53,7 @@ try { if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" } $env:PATH = "$(Join-Path $ToolchainRoot 'bin');$originalPath" $configure += "-DCMAKE_CXX_COMPILER=$compiler" + $configure += "-DCMAKE_C_COMPILER=$(Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang.exe')" } if ($ZludaRoot) { $configure += "-DD4R_ZLUDA_ROOT=$ZludaRoot" } $ErrorActionPreference = 'Continue' diff --git a/scripts/windows/test-windows-rdna4.ps1 b/scripts/windows/test-windows-rdna4.ps1 index a056d02..f54aa96 100644 --- a/scripts/windows/test-windows-rdna4.ps1 +++ b/scripts/windows/test-windows-rdna4.ps1 @@ -12,6 +12,7 @@ param( [switch]$NgxOnly, [switch]$Trace, [switch]$RequireNativeNetwork, + [switch]$CommandListBackend, [int]$Iterations = 32, [int]$TimeoutSeconds = 180 ) @@ -210,6 +211,9 @@ try { if ($cudaOk) { $mapOk = $interopOk = $true if (!$NgxOnly) { + $commandOk = Invoke-Probe 'd3d12-command-backend' (Join-Path $bin 'd4r_d3d12_command_probe.exe') @( + '--hip-root', $HipRoot, '--module', (Join-Path $bin 'd4r_nvngx.dll'), '--iterations', "$Iterations") + if (!$commandOk) { throw 'D3D12 command-list order/state/lifetime validation failed.' } $mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( '--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64') $interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( @@ -250,6 +254,7 @@ try { $localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll' Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim $ngxArguments += @('--module', $localShim) + if ($CommandListBackend) { $ngxArguments += @('--interop-mode', 'command-list') } } $ngxName = if ($NgxMode -eq 'evaluate') { "ngx-evaluate-preset-$Preset" } elseif ($NgxMode -eq 'd3d12') { "d3d12-evaluate-preset-$Preset" } else { 'ngx-init' } $ngxOk = Invoke-Probe $ngxName $ngxExe $ngxArguments diff --git a/tools/windows/d3d12_command_hooks.cpp b/tools/windows/d3d12_command_hooks.cpp new file mode 100644 index 0000000..e4fc2e1 --- /dev/null +++ b/tools/windows/d3d12_command_hooks.cpp @@ -0,0 +1,320 @@ +#include "d3d12_command_hooks.h" +#include +#include +#include +#include +#include +#include + +namespace d4r::win::commands { +namespace { +thread_local bool internal = false; +std::mutex map_mutex, installation_mutex, error_mutex; +std::string last_error; +bool installed = false; +struct Event { UINT metadata; std::vector data; }; +using State = std::function; +struct Segment { + // The application's original list is a weak pointer; holding it in its + // own routing map would prevent final Release and leak the entire feature. + ID3D12GraphicsCommandList* list = nullptr; + ComPtr owner; + ComPtr allocator; + Boundary boundary; +}; +struct Recording { + std::recursive_mutex mutex; + ID3D12GraphicsCommandList* original; + ComPtr current; + ComPtr current_allocator; + std::vector segments; + std::vector state; + std::vector events; + std::unordered_map resource_states; + unsigned active_queries = 0; + bool render_pass = false, predication = false, indirect_state_unknown = false, closed = false; + explicit Recording(ID3D12GraphicsCommandList* list) : original(list) {} +}; +std::unordered_map> recordings; +std::shared_ptr get_recording(ID3D12GraphicsCommandList* self, bool create = true) { + if (internal) return {}; + std::lock_guard lock(map_mutex); + auto found = recordings.find(self); + if (found != recordings.end()) return found->second; + if (!create) return {}; + auto value = std::make_shared(self); + recordings.emplace(self, value); return value; +} +void forget(ID3D12GraphicsCommandList* self) { + std::shared_ptr released; + { std::lock_guard lock(map_mutex); + auto it = recordings.find(self); + if (it != recordings.end()) { released = std::move(it->second); recordings.erase(it); } } + // Release retained COM objects without holding the map mutex. +} +struct Access { + std::shared_ptr recording; + std::unique_lock lock; + ID3D12GraphicsCommandList* self; + explicit Access(ID3D12GraphicsCommandList* list) : recording(get_recording(list)), self(list) { + if (recording) lock = std::unique_lock(recording->mutex); + } + ID3D12GraphicsCommandList* target() const { + return recording && recording->current ? recording->current.Get() : self; + } +}; +template ComPtr retain(T* pointer) { return ComPtr(pointer); } +template std::vector copy_values(const T* pointer, size_t count) { + return pointer ? std::vector(pointer, pointer + count) : std::vector(); +} +template T* values_pointer(std::vector& values) { return values.empty() ? nullptr : values.data(); } +template struct RetainedArray { std::vector> owners; std::vector raw; }; +template RetainedArray retain_values(T* const* pointer, size_t count) { + RetainedArray result; + for (size_t i = 0; pointer && i < count; ++i) { result.owners.emplace_back(pointer[i]); result.raw.push_back(pointer[i]); } + return result; +} +struct Hook { void* target; void* replacement; }; +std::vector attached; +void attach_method(void* target, void* replacement, void** original, const char* name) { + for (const auto& hook : attached) if (hook.target == target) + throw std::runtime_error(std::string("Shared D3D12 implementation needs an explicit alias adapter: ") + name); + const auto result = MH_CreateHook(target, replacement, original); + if (result != MH_OK) throw std::runtime_error(std::string("Cannot intercept D3D12 ") + name + ": " + MH_StatusToString(result)); + attached.push_back({target, replacement}); + std::printf("D4R_D3D12_HOOK method=%s\n", name); +} + +#include "d3d12_command_hooks.generated.h" + +using ReleaseFn = ULONG(STDMETHODCALLTYPE*)(IUnknown*); +ReleaseFn original_release = nullptr; +ULONG STDMETHODCALLTYPE hook_release(IUnknown* self) { + const ULONG count = original_release(self); + if (!count) forget(reinterpret_cast(self)); + return count; +} +HRESULT STDMETHODCALLTYPE hook_Close(ID3D12GraphicsCommandList* self) { + Access access(self); + const auto result = original_Close(access.target()); + if (SUCCEEDED(result) && access.recording) access.recording->closed = true; + return result; +} +HRESULT STDMETHODCALLTYPE hook_Reset(ID3D12GraphicsCommandList* self, ID3D12CommandAllocator* allocator, ID3D12PipelineState* initial_state) { + if (internal) return original_Reset(self, allocator, initial_state); + forget(self); + const auto result = original_Reset(self, allocator, initial_state); + if (SUCCEEDED(result)) { + auto recording = get_recording(self); + auto pipeline = retain(initial_state); + recording->state.emplace_back([pipeline](ID3D12GraphicsCommandList* target) { + if (pipeline) original_SetPipelineState(target, pipeline.Get()); + }); + } + return result; +} +void STDMETHODCALLTYPE hook_ClearState(ID3D12GraphicsCommandList* self, ID3D12PipelineState* pipeline_state) { + Access access(self); + if (access.recording) { + access.recording->state.clear(); access.recording->indirect_state_unknown = false; + auto pipeline = retain(pipeline_state); + access.recording->state.emplace_back([pipeline](ID3D12GraphicsCommandList* target) { original_ClearState(target, pipeline.Get()); }); + } + original_ClearState(access.target(), pipeline_state); +} +void STDMETHODCALLTYPE hook_ResourceBarrier(ID3D12GraphicsCommandList* self, UINT barrier_count, const D3D12_RESOURCE_BARRIER* barriers) { + Access access(self); + if (access.recording) for (UINT i = 0; i < barrier_count; ++i) + if (barriers[i].Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION && barriers[i].Flags != D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY) + access.recording->resource_states[barriers[i].Transition.pResource] = barriers[i].Transition.StateAfter; + original_ResourceBarrier(access.target(), barrier_count, barriers); +} +void STDMETHODCALLTYPE hook_Barrier(ID3D12GraphicsCommandList* self, UINT32 barrier_groups_count, const D3D12_BARRIER_GROUP* barrier_groups) { + Access access(self); + original_Barrier(access.target(), barrier_groups_count, barrier_groups); + // Enhanced layout-to-legacy conversion is an explicit subsequent gate. +} +void STDMETHODCALLTYPE hook_BeginEvent(ID3D12GraphicsCommandList* self, UINT metadata, const void* data, UINT size) { + Access access(self); + if (access.recording) access.recording->events.push_back({metadata, copy_values(static_cast(data), size)}); + original_BeginEvent(access.target(), metadata, data, size); +} +void STDMETHODCALLTYPE hook_EndEvent(ID3D12GraphicsCommandList* self) { + Access access(self); + if (access.recording && !access.recording->events.empty()) access.recording->events.pop_back(); + original_EndEvent(access.target()); +} +void STDMETHODCALLTYPE hook_BeginRenderPass(ID3D12GraphicsCommandList* self, UINT render_target_count, + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* render_targets, const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* depth_stencil, D3D12_RENDER_PASS_FLAGS flags) { + Access access(self); if (access.recording) access.recording->render_pass = true; + original_BeginRenderPass(access.target(), render_target_count, render_targets, depth_stencil, flags); +} +void STDMETHODCALLTYPE hook_EndRenderPass(ID3D12GraphicsCommandList* self) { + Access access(self); if (access.recording) access.recording->render_pass = false; + original_EndRenderPass(access.target()); +} +void STDMETHODCALLTYPE hook_BeginQuery(ID3D12GraphicsCommandList* self, ID3D12QueryHeap* heap, D3D12_QUERY_TYPE type, UINT index) { + Access access(self); if (access.recording) ++access.recording->active_queries; + original_BeginQuery(access.target(), heap, type, index); +} +void STDMETHODCALLTYPE hook_EndQuery(ID3D12GraphicsCommandList* self, ID3D12QueryHeap* heap, D3D12_QUERY_TYPE type, UINT index) { + Access access(self); if (access.recording && access.recording->active_queries) --access.recording->active_queries; + original_EndQuery(access.target(), heap, type, index); +} +void STDMETHODCALLTYPE hook_SetPredication(ID3D12GraphicsCommandList* self, ID3D12Resource* buffer, UINT64 aligned_buffer_offset, D3D12_PREDICATION_OP operation) { + Access access(self); + if (access.recording) { + access.recording->predication = buffer != nullptr; + auto retained = retain(buffer); + access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) { original_SetPredication(target, retained.Get(), aligned_buffer_offset, operation); }); + } + original_SetPredication(access.target(), buffer, aligned_buffer_offset, operation); +} +void STDMETHODCALLTYPE hook_ExecuteBundle(ID3D12GraphicsCommandList* self, ID3D12GraphicsCommandList* command_list) { + Access access(self); + auto bundle = get_recording(command_list, false); + if (access.recording && bundle) { + std::lock_guard lock(bundle->mutex); + access.recording->state.insert(access.recording->state.end(), bundle->state.begin(), bundle->state.end()); + access.recording->indirect_state_unknown |= bundle->indirect_state_unknown; + } else if (access.recording) access.recording->indirect_state_unknown = true; + original_ExecuteBundle(access.target(), command_list); +} +void STDMETHODCALLTYPE hook_ExecuteIndirect(ID3D12GraphicsCommandList* self, ID3D12CommandSignature* command_signature, + UINT max_command_count, ID3D12Resource* arg_buffer, UINT64 arg_buffer_offset, ID3D12Resource* count_buffer, UINT64 count_buffer_offset) { + Access access(self); + if (access.recording) access.recording->indirect_state_unknown = true; + original_ExecuteIndirect(access.target(), command_signature, max_command_count, arg_buffer, arg_buffer_offset, count_buffer, count_buffer_offset); +} + +using ExecuteFn = void(STDMETHODCALLTYPE*)(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*); +ExecuteFn original_execute = nullptr; +void complete(ID3D12CommandQueue* queue) { + ComPtr device; dx(queue->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Queue device(submit drain)"); + ComPtr fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())), "Queue fence(submit drain)"); + Handle event; event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if (!event.value) throw std::runtime_error("CreateEvent(submit drain)"); + dx(queue->Signal(fence.Get(), 1), "Queue Signal(submit drain)"); + if (fence->GetCompletedValue() < 1) { + dx(fence->SetEventOnCompletion(1, event.value), "Queue event(submit drain)"); + if (WaitForSingleObject(event.value, 30000) != WAIT_OBJECT_0) throw std::runtime_error("GPU submit boundary timed out"); + } +} +void STDMETHODCALLTYPE hook_execute(ID3D12CommandQueue* queue, UINT count, ID3D12CommandList* const* lists) { + if (internal) { original_execute(queue, count, lists); return; } + std::vector> owners; + std::vector pending; + InternalScope scope; + auto flush = [&] { if (!pending.empty()) { original_execute(queue, UINT(pending.size()), pending.data()); pending.clear(); } }; + bool boundaries = false; + try { + for (UINT i = 0; i < count; ++i) { + std::shared_ptr recording; + { std::lock_guard lock(map_mutex); + auto found = recordings.find(static_cast(lists[i])); + if (found != recordings.end()) recording = found->second; } + if (!recording || recording->segments.empty()) { pending.push_back(lists[i]); continue; } + std::lock_guard lock(recording->mutex); + if (!recording->closed) throw std::runtime_error("Logical command list submitted before Close"); + owners.push_back(recording); boundaries = true; + for (const auto& segment : recording->segments) { + pending.push_back(segment.list); flush(); + segment.boundary(queue); + } + pending.push_back(recording->current.Get()); + } + flush(); + if (boundaries) complete(queue); + } catch (const std::exception& failure) { + { std::lock_guard lock(error_mutex); last_error = failure.what(); } + std::fprintf(stderr, "D4R_D3D12_SUBMIT_FAILURE %s\n", failure.what()); + try { complete(queue); } catch (...) {} + // Do not submit a consumer suffix after a failed DLSS boundary. + } +} +} + +InternalScope::InternalScope() : previous(internal) { internal = true; } +InternalScope::~InternalScope() { internal = previous; } +std::string error() { std::lock_guard lock(error_mutex); return last_error; } +size_t live_recordings() { std::lock_guard lock(map_mutex); return recordings.size(); } +D3D12_RESOURCE_STATES resource_state(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, D3D12_RESOURCE_STATES fallback) { + auto recording = get_recording(list, false); + if (!recording) return fallback; + std::lock_guard lock(recording->mutex); + auto found = recording->resource_states.find(resource); + return found == recording->resource_states.end() ? fallback : found->second; +} +void install(ID3D12Device* device) { + std::lock_guard lock(installation_mutex); + if (installed) return; + InternalScope scope; + // An application may unload its NGX handle while native D3D12 objects are + // still alive. Public method callbacks must remain callable until exit. + HMODULE owner = nullptr; + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + reinterpret_cast(&install), &owner)) throw std::runtime_error("Cannot retain the D3D12 callback module"); + const auto init = MH_Initialize(); + if (init != MH_OK && init != MH_ERROR_ALREADY_INITIALIZED) throw std::runtime_error(MH_StatusToString(init)); + ComPtr allocator; ComPtr list; + dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.GetAddressOf())), "Create hook allocator"); + dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(list.GetAddressOf())), "Create hook command list"); + try { + // The public COM interface inherits one ordered method table. + unsigned version = 0; + ComPtr newest; + const GUID* ids[] = {&IID_ID3D12GraphicsCommandList, &IID_ID3D12GraphicsCommandList1, &IID_ID3D12GraphicsCommandList2, + &IID_ID3D12GraphicsCommandList3, &IID_ID3D12GraphicsCommandList4, &IID_ID3D12GraphicsCommandList5, + &IID_ID3D12GraphicsCommandList6, &IID_ID3D12GraphicsCommandList7}; + for (unsigned n = 0; n < 8; ++n) if (SUCCEEDED(list->QueryInterface(*ids[n], reinterpret_cast(newest.ReleaseAndGetAddressOf())))) version = n; + auto table = *reinterpret_cast(list.Get()); + attach_recording_methods(table, version); + attach_method(table[2], reinterpret_cast(hook_release), reinterpret_cast(&original_release), "Release"); + D3D12_COMMAND_QUEUE_DESC desc{}; desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + ComPtr queue; dx(device->CreateCommandQueue(&desc, IID_PPV_ARGS(queue.GetAddressOf())), "Create hook queue"); + auto queueTable = *reinterpret_cast(queue.Get()); + // Queue inherits IUnknown, Object, DeviceChild; UpdateTileMappings, + // CopyTileMappings precede ExecuteCommandLists in the public SDK. + attach_method(queueTable[10], reinterpret_cast(hook_execute), reinterpret_cast(&original_execute), "ExecuteCommandLists"); + for (const auto& hook : attached) { + const auto result = MH_QueueEnableHook(hook.target); + if (result != MH_OK) throw std::runtime_error(MH_StatusToString(result)); + } + const auto result = MH_ApplyQueued(); + if (result != MH_OK) throw std::runtime_error(MH_StatusToString(result)); + installed = true; + std::printf("D4R_D3D12_COMMAND_BACKEND installed=1 interface=%u hooks=%zu frame_age=0\n", version, attached.size()); + } catch (...) { + for (const auto& hook : attached) { (void)MH_DisableHook(hook.target); (void)MH_RemoveHook(hook.target); } + attached.clear(); throw; + } +} +void record_boundary(ID3D12GraphicsCommandList* list, Boundary callback) { + if (!installed || !list || !callback) throw std::runtime_error("D3D12 command backend is not initialized"); + if (!error().empty()) throw std::runtime_error(error()); + if (list->GetType() != D3D12_COMMAND_LIST_TYPE_DIRECT) throw std::runtime_error("DLSS requires a direct command list"); + Access access(list); + auto& recording = *access.recording; + if (recording.closed || recording.render_pass || recording.active_queries || recording.predication || recording.indirect_state_unknown) + throw std::runtime_error("Cannot split a closed list or an active render/query/predication/unknown indirect state scope"); + // Construct/replay the suffix before modifying the original list so an + // allocation failure leaves the caller's recording intact. + InternalScope scope; + ComPtr device; dx(list->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Boundary device"); + Segment segment; segment.list = access.target(); segment.owner = recording.current; + segment.allocator = recording.current_allocator; segment.boundary = std::move(callback); + ComPtr nextAllocator; ComPtr next; + dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(nextAllocator.GetAddressOf())), "Suffix allocator"); + dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, nextAllocator.Get(), nullptr, IID_PPV_ARGS(next.GetAddressOf())), "Suffix command list"); + for (auto& state : recording.state) state(next.Get()); + recording.segments.reserve(recording.segments.size() + 1); + for (const auto& event : recording.events) original_BeginEvent(next.Get(), event.metadata, event.data.data(), UINT(event.data.size())); + for (size_t n = 0; n < recording.events.size(); ++n) original_EndEvent(segment.list); + dx(original_Close(segment.list), "Close boundary prefix"); + // Each suffix owns an independent allocator until GPU completion/reset. + recording.segments.push_back(std::move(segment)); + recording.current_allocator = std::move(nextAllocator); + recording.current = std::move(next); + std::printf("D4R_D3D12_RECORD boundary=%zu state_calls=%zu frame_age=0\n", recording.segments.size(), recording.state.size()); +} +} diff --git a/tools/windows/d3d12_command_hooks.generated.h b/tools/windows/d3d12_command_hooks.generated.h new file mode 100644 index 0000000..e68ccda --- /dev/null +++ b/tools/windows/d3d12_command_hooks.generated.h @@ -0,0 +1,653 @@ +// Generated by generate_d3d12_hooks.py from the public SDK. Do not edit. + +using Fn_Close = HRESULT(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self); +static Fn_Close original_Close = nullptr; +static HRESULT STDMETHODCALLTYPE hook_Close(ID3D12GraphicsCommandList* self); + +using Fn_Reset = HRESULT(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12CommandAllocator *allocator, ID3D12PipelineState *initial_state); +static Fn_Reset original_Reset = nullptr; +static HRESULT STDMETHODCALLTYPE hook_Reset(ID3D12GraphicsCommandList* self, ID3D12CommandAllocator *allocator, ID3D12PipelineState *initial_state); + +using Fn_ClearState = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12PipelineState *pipeline_state); +static Fn_ClearState original_ClearState = nullptr; +static void STDMETHODCALLTYPE hook_ClearState(ID3D12GraphicsCommandList* self, ID3D12PipelineState *pipeline_state); + +using Fn_DrawInstanced = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT vertex_count_per_instance, UINT instance_count, UINT start_vertex_location, UINT start_instance_location); +static Fn_DrawInstanced original_DrawInstanced = nullptr; +static void STDMETHODCALLTYPE hook_DrawInstanced(ID3D12GraphicsCommandList* self, UINT vertex_count_per_instance, UINT instance_count, UINT start_vertex_location, UINT start_instance_location) { + Access access(self); + return original_DrawInstanced(access.target(), vertex_count_per_instance, instance_count, start_vertex_location, start_instance_location); +} + +using Fn_DrawIndexedInstanced = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT index_count_per_instance, UINT instance_count, UINT start_vertex_location, INT base_vertex_location, UINT start_instance_location); +static Fn_DrawIndexedInstanced original_DrawIndexedInstanced = nullptr; +static void STDMETHODCALLTYPE hook_DrawIndexedInstanced(ID3D12GraphicsCommandList* self, UINT index_count_per_instance, UINT instance_count, UINT start_vertex_location, INT base_vertex_location, UINT start_instance_location) { + Access access(self); + return original_DrawIndexedInstanced(access.target(), index_count_per_instance, instance_count, start_vertex_location, base_vertex_location, start_instance_location); +} + +using Fn_Dispatch = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT x, UINT u, UINT z); +static Fn_Dispatch original_Dispatch = nullptr; +static void STDMETHODCALLTYPE hook_Dispatch(ID3D12GraphicsCommandList* self, UINT x, UINT u, UINT z) { + Access access(self); + return original_Dispatch(access.target(), x, u, z); +} + +using Fn_CopyBufferRegion = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT64 byte_count); +static Fn_CopyBufferRegion original_CopyBufferRegion = nullptr; +static void STDMETHODCALLTYPE hook_CopyBufferRegion(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT64 byte_count) { + Access access(self); + return original_CopyBufferRegion(access.target(), dst_buffer, dst_offset, src_buffer, src_offset, byte_count); +} + +using Fn_CopyTextureRegion = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const D3D12_TEXTURE_COPY_LOCATION *dst, UINT dst_x, UINT dst_y, UINT dst_z, const D3D12_TEXTURE_COPY_LOCATION *src, const D3D12_BOX *src_box); +static Fn_CopyTextureRegion original_CopyTextureRegion = nullptr; +static void STDMETHODCALLTYPE hook_CopyTextureRegion(ID3D12GraphicsCommandList* self, const D3D12_TEXTURE_COPY_LOCATION *dst, UINT dst_x, UINT dst_y, UINT dst_z, const D3D12_TEXTURE_COPY_LOCATION *src, const D3D12_BOX *src_box) { + Access access(self); + return original_CopyTextureRegion(access.target(), dst, dst_x, dst_y, dst_z, src, src_box); +} + +using Fn_CopyResource = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, ID3D12Resource *src_resource); +static Fn_CopyResource original_CopyResource = nullptr; +static void STDMETHODCALLTYPE hook_CopyResource(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, ID3D12Resource *src_resource) { + Access access(self); + return original_CopyResource(access.target(), dst_resource, src_resource); +} + +using Fn_CopyTiles = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *tiled_resource, const D3D12_TILED_RESOURCE_COORDINATE *tile_region_start_coordinate, const D3D12_TILE_REGION_SIZE *tile_region_size, ID3D12Resource *buffer, UINT64 buffer_offset, D3D12_TILE_COPY_FLAGS flags); +static Fn_CopyTiles original_CopyTiles = nullptr; +static void STDMETHODCALLTYPE hook_CopyTiles(ID3D12GraphicsCommandList* self, ID3D12Resource *tiled_resource, const D3D12_TILED_RESOURCE_COORDINATE *tile_region_start_coordinate, const D3D12_TILE_REGION_SIZE *tile_region_size, ID3D12Resource *buffer, UINT64 buffer_offset, D3D12_TILE_COPY_FLAGS flags) { + Access access(self); + return original_CopyTiles(access.target(), tiled_resource, tile_region_start_coordinate, tile_region_size, buffer, buffer_offset, flags); +} + +using Fn_ResolveSubresource = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, UINT dst_sub_resource, ID3D12Resource *src_resource, UINT src_sub_resource, DXGI_FORMAT format); +static Fn_ResolveSubresource original_ResolveSubresource = nullptr; +static void STDMETHODCALLTYPE hook_ResolveSubresource(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, UINT dst_sub_resource, ID3D12Resource *src_resource, UINT src_sub_resource, DXGI_FORMAT format) { + Access access(self); + return original_ResolveSubresource(access.target(), dst_resource, dst_sub_resource, src_resource, src_sub_resource, format); +} + +using Fn_IASetPrimitiveTopology = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_PRIMITIVE_TOPOLOGY primitive_topology); +static Fn_IASetPrimitiveTopology original_IASetPrimitiveTopology = nullptr; +static void STDMETHODCALLTYPE hook_IASetPrimitiveTopology(ID3D12GraphicsCommandList* self, D3D12_PRIMITIVE_TOPOLOGY primitive_topology) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_IASetPrimitiveTopology(target, primitive_topology); + }); + return original_IASetPrimitiveTopology(access.target(), primitive_topology); +} + +using Fn_RSSetViewports = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT viewport_count, const D3D12_VIEWPORT *viewports); +static Fn_RSSetViewports original_RSSetViewports = nullptr; +static void STDMETHODCALLTYPE hook_RSSetViewports(ID3D12GraphicsCommandList* self, UINT viewport_count, const D3D12_VIEWPORT *viewports) { + Access access(self); + auto saved_viewports = copy_values(viewports, viewport_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_RSSetViewports(target, viewport_count, values_pointer(saved_viewports)); + }); + return original_RSSetViewports(access.target(), viewport_count, viewports); +} + +using Fn_RSSetScissorRects = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT rect_count, const D3D12_RECT *rects); +static Fn_RSSetScissorRects original_RSSetScissorRects = nullptr; +static void STDMETHODCALLTYPE hook_RSSetScissorRects(ID3D12GraphicsCommandList* self, UINT rect_count, const D3D12_RECT *rects) { + Access access(self); + auto saved_rects = copy_values(rects, rect_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_RSSetScissorRects(target, rect_count, values_pointer(saved_rects)); + }); + return original_RSSetScissorRects(access.target(), rect_count, rects); +} + +using Fn_OMSetBlendFactor = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const FLOAT blend_factor[4]); +static Fn_OMSetBlendFactor original_OMSetBlendFactor = nullptr; +static void STDMETHODCALLTYPE hook_OMSetBlendFactor(ID3D12GraphicsCommandList* self, const FLOAT blend_factor[4]) { + Access access(self); + auto saved_blend_factor = copy_values(blend_factor, 4); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_OMSetBlendFactor(target, values_pointer(saved_blend_factor)); + }); + return original_OMSetBlendFactor(access.target(), blend_factor); +} + +using Fn_OMSetStencilRef = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT stencil_ref); +static Fn_OMSetStencilRef original_OMSetStencilRef = nullptr; +static void STDMETHODCALLTYPE hook_OMSetStencilRef(ID3D12GraphicsCommandList* self, UINT stencil_ref) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_OMSetStencilRef(target, stencil_ref); + }); + return original_OMSetStencilRef(access.target(), stencil_ref); +} + +using Fn_SetPipelineState = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12PipelineState *pipeline_state); +static Fn_SetPipelineState original_SetPipelineState = nullptr; +static void STDMETHODCALLTYPE hook_SetPipelineState(ID3D12GraphicsCommandList* self, ID3D12PipelineState *pipeline_state) { + Access access(self); + auto saved_pipeline_state = retain(pipeline_state); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetPipelineState(target, saved_pipeline_state.Get()); + }); + return original_SetPipelineState(access.target(), pipeline_state); +} + +using Fn_ResourceBarrier = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT barrier_count, const D3D12_RESOURCE_BARRIER *barriers); +static Fn_ResourceBarrier original_ResourceBarrier = nullptr; +static void STDMETHODCALLTYPE hook_ResourceBarrier(ID3D12GraphicsCommandList* self, UINT barrier_count, const D3D12_RESOURCE_BARRIER *barriers); + +using Fn_ExecuteBundle = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12GraphicsCommandList *command_list); +static Fn_ExecuteBundle original_ExecuteBundle = nullptr; +static void STDMETHODCALLTYPE hook_ExecuteBundle(ID3D12GraphicsCommandList* self, ID3D12GraphicsCommandList *command_list); + +using Fn_SetDescriptorHeaps = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT heap_count, ID3D12DescriptorHeap *const *heaps); +static Fn_SetDescriptorHeaps original_SetDescriptorHeaps = nullptr; +static void STDMETHODCALLTYPE hook_SetDescriptorHeaps(ID3D12GraphicsCommandList* self, UINT heap_count, ID3D12DescriptorHeap *const *heaps) { + Access access(self); + auto saved_heaps = retain_values(heaps, heap_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetDescriptorHeaps(target, heap_count, saved_heaps.raw.data()); + }); + return original_SetDescriptorHeaps(access.target(), heap_count, heaps); +} + +using Fn_SetComputeRootSignature = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12RootSignature *root_signature); +static Fn_SetComputeRootSignature original_SetComputeRootSignature = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRootSignature(ID3D12GraphicsCommandList* self, ID3D12RootSignature *root_signature) { + Access access(self); + auto saved_root_signature = retain(root_signature); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRootSignature(target, saved_root_signature.Get()); + }); + return original_SetComputeRootSignature(access.target(), root_signature); +} + +using Fn_SetGraphicsRootSignature = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12RootSignature *root_signature); +static Fn_SetGraphicsRootSignature original_SetGraphicsRootSignature = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRootSignature(ID3D12GraphicsCommandList* self, ID3D12RootSignature *root_signature) { + Access access(self); + auto saved_root_signature = retain(root_signature); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRootSignature(target, saved_root_signature.Get()); + }); + return original_SetGraphicsRootSignature(access.target(), root_signature); +} + +using Fn_SetComputeRootDescriptorTable = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor); +static Fn_SetComputeRootDescriptorTable original_SetComputeRootDescriptorTable = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRootDescriptorTable(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRootDescriptorTable(target, root_parameter_index, base_descriptor); + }); + return original_SetComputeRootDescriptorTable(access.target(), root_parameter_index, base_descriptor); +} + +using Fn_SetGraphicsRootDescriptorTable = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor); +static Fn_SetGraphicsRootDescriptorTable original_SetGraphicsRootDescriptorTable = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRootDescriptorTable(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRootDescriptorTable(target, root_parameter_index, base_descriptor); + }); + return original_SetGraphicsRootDescriptorTable(access.target(), root_parameter_index, base_descriptor); +} + +using Fn_SetComputeRoot32BitConstant = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT data, UINT dst_offset); +static Fn_SetComputeRoot32BitConstant original_SetComputeRoot32BitConstant = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRoot32BitConstant(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT data, UINT dst_offset) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRoot32BitConstant(target, root_parameter_index, data, dst_offset); + }); + return original_SetComputeRoot32BitConstant(access.target(), root_parameter_index, data, dst_offset); +} + +using Fn_SetGraphicsRoot32BitConstant = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT data, UINT dst_offset); +static Fn_SetGraphicsRoot32BitConstant original_SetGraphicsRoot32BitConstant = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRoot32BitConstant(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT data, UINT dst_offset) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRoot32BitConstant(target, root_parameter_index, data, dst_offset); + }); + return original_SetGraphicsRoot32BitConstant(access.target(), root_parameter_index, data, dst_offset); +} + +using Fn_SetComputeRoot32BitConstants = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset); +static Fn_SetComputeRoot32BitConstants original_SetComputeRoot32BitConstants = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRoot32BitConstants(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset) { + Access access(self); + auto saved_data = copy_values(static_cast(data), constant_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRoot32BitConstants(target, root_parameter_index, constant_count, values_pointer(saved_data), dst_offset); + }); + return original_SetComputeRoot32BitConstants(access.target(), root_parameter_index, constant_count, data, dst_offset); +} + +using Fn_SetGraphicsRoot32BitConstants = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset); +static Fn_SetGraphicsRoot32BitConstants original_SetGraphicsRoot32BitConstants = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRoot32BitConstants(ID3D12GraphicsCommandList* self, UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset) { + Access access(self); + auto saved_data = copy_values(static_cast(data), constant_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRoot32BitConstants(target, root_parameter_index, constant_count, values_pointer(saved_data), dst_offset); + }); + return original_SetGraphicsRoot32BitConstants(access.target(), root_parameter_index, constant_count, data, dst_offset); +} + +using Fn_SetComputeRootConstantBufferView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetComputeRootConstantBufferView original_SetComputeRootConstantBufferView = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRootConstantBufferView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRootConstantBufferView(target, root_parameter_index, address); + }); + return original_SetComputeRootConstantBufferView(access.target(), root_parameter_index, address); +} + +using Fn_SetGraphicsRootConstantBufferView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetGraphicsRootConstantBufferView original_SetGraphicsRootConstantBufferView = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRootConstantBufferView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRootConstantBufferView(target, root_parameter_index, address); + }); + return original_SetGraphicsRootConstantBufferView(access.target(), root_parameter_index, address); +} + +using Fn_SetComputeRootShaderResourceView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetComputeRootShaderResourceView original_SetComputeRootShaderResourceView = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRootShaderResourceView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRootShaderResourceView(target, root_parameter_index, address); + }); + return original_SetComputeRootShaderResourceView(access.target(), root_parameter_index, address); +} + +using Fn_SetGraphicsRootShaderResourceView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetGraphicsRootShaderResourceView original_SetGraphicsRootShaderResourceView = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRootShaderResourceView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRootShaderResourceView(target, root_parameter_index, address); + }); + return original_SetGraphicsRootShaderResourceView(access.target(), root_parameter_index, address); +} + +using Fn_SetComputeRootUnorderedAccessView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetComputeRootUnorderedAccessView original_SetComputeRootUnorderedAccessView = nullptr; +static void STDMETHODCALLTYPE hook_SetComputeRootUnorderedAccessView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetComputeRootUnorderedAccessView(target, root_parameter_index, address); + }); + return original_SetComputeRootUnorderedAccessView(access.target(), root_parameter_index, address); +} + +using Fn_SetGraphicsRootUnorderedAccessView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address); +static Fn_SetGraphicsRootUnorderedAccessView original_SetGraphicsRootUnorderedAccessView = nullptr; +static void STDMETHODCALLTYPE hook_SetGraphicsRootUnorderedAccessView(ID3D12GraphicsCommandList* self, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetGraphicsRootUnorderedAccessView(target, root_parameter_index, address); + }); + return original_SetGraphicsRootUnorderedAccessView(access.target(), root_parameter_index, address); +} + +using Fn_IASetIndexBuffer = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const D3D12_INDEX_BUFFER_VIEW *view); +static Fn_IASetIndexBuffer original_IASetIndexBuffer = nullptr; +static void STDMETHODCALLTYPE hook_IASetIndexBuffer(ID3D12GraphicsCommandList* self, const D3D12_INDEX_BUFFER_VIEW *view) { + Access access(self); + auto saved_view = copy_values(view, 1); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_IASetIndexBuffer(target, values_pointer(saved_view)); + }); + return original_IASetIndexBuffer(access.target(), view); +} + +using Fn_IASetVertexBuffers = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT start_slot, UINT view_count, const D3D12_VERTEX_BUFFER_VIEW *views); +static Fn_IASetVertexBuffers original_IASetVertexBuffers = nullptr; +static void STDMETHODCALLTYPE hook_IASetVertexBuffers(ID3D12GraphicsCommandList* self, UINT start_slot, UINT view_count, const D3D12_VERTEX_BUFFER_VIEW *views) { + Access access(self); + auto saved_views = copy_values(views, view_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_IASetVertexBuffers(target, start_slot, view_count, values_pointer(saved_views)); + }); + return original_IASetVertexBuffers(access.target(), start_slot, view_count, views); +} + +using Fn_SOSetTargets = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT start_slot, UINT view_count, const D3D12_STREAM_OUTPUT_BUFFER_VIEW *views); +static Fn_SOSetTargets original_SOSetTargets = nullptr; +static void STDMETHODCALLTYPE hook_SOSetTargets(ID3D12GraphicsCommandList* self, UINT start_slot, UINT view_count, const D3D12_STREAM_OUTPUT_BUFFER_VIEW *views) { + Access access(self); + auto saved_views = copy_values(views, view_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SOSetTargets(target, start_slot, view_count, values_pointer(saved_views)); + }); + return original_SOSetTargets(access.target(), start_slot, view_count, views); +} + +using Fn_OMSetRenderTargets = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, WINBOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor); +static Fn_OMSetRenderTargets original_OMSetRenderTargets = nullptr; +static void STDMETHODCALLTYPE hook_OMSetRenderTargets(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, WINBOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor) { + Access access(self); + auto saved_render_target_descriptors = copy_values(render_target_descriptors, (single_descriptor_handle ? 1 : render_target_descriptor_count)); + auto saved_depth_stencil_descriptor = copy_values(depth_stencil_descriptor, 1); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_OMSetRenderTargets(target, render_target_descriptor_count, values_pointer(saved_render_target_descriptors), single_descriptor_handle, values_pointer(saved_depth_stencil_descriptor)); + }); + return original_OMSetRenderTargets(access.target(), render_target_descriptor_count, render_target_descriptors, single_descriptor_handle, depth_stencil_descriptor); +} + +using Fn_ClearDepthStencilView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_CPU_DESCRIPTOR_HANDLE dsv, D3D12_CLEAR_FLAGS flags, FLOAT depth, UINT8 stencil, UINT rect_count, const D3D12_RECT *rects); +static Fn_ClearDepthStencilView original_ClearDepthStencilView = nullptr; +static void STDMETHODCALLTYPE hook_ClearDepthStencilView(ID3D12GraphicsCommandList* self, D3D12_CPU_DESCRIPTOR_HANDLE dsv, D3D12_CLEAR_FLAGS flags, FLOAT depth, UINT8 stencil, UINT rect_count, const D3D12_RECT *rects) { + Access access(self); + return original_ClearDepthStencilView(access.target(), dsv, flags, depth, stencil, rect_count, rects); +} + +using Fn_ClearRenderTargetView = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_CPU_DESCRIPTOR_HANDLE rtv, const FLOAT color[4], UINT rect_count, const D3D12_RECT *rects); +static Fn_ClearRenderTargetView original_ClearRenderTargetView = nullptr; +static void STDMETHODCALLTYPE hook_ClearRenderTargetView(ID3D12GraphicsCommandList* self, D3D12_CPU_DESCRIPTOR_HANDLE rtv, const FLOAT color[4], UINT rect_count, const D3D12_RECT *rects) { + Access access(self); + return original_ClearRenderTargetView(access.target(), rtv, color, rect_count, rects); +} + +using Fn_ClearUnorderedAccessViewUint = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource, const UINT values[4], UINT rect_count, const D3D12_RECT *rects); +static Fn_ClearUnorderedAccessViewUint original_ClearUnorderedAccessViewUint = nullptr; +static void STDMETHODCALLTYPE hook_ClearUnorderedAccessViewUint(ID3D12GraphicsCommandList* self, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource, const UINT values[4], UINT rect_count, const D3D12_RECT *rects) { + Access access(self); + return original_ClearUnorderedAccessViewUint(access.target(), gpu_handle, cpu_handle, resource, values, rect_count, rects); +} + +using Fn_ClearUnorderedAccessViewFloat = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource, const float values[4], UINT rect_count, const D3D12_RECT *rects); +static Fn_ClearUnorderedAccessViewFloat original_ClearUnorderedAccessViewFloat = nullptr; +static void STDMETHODCALLTYPE hook_ClearUnorderedAccessViewFloat(ID3D12GraphicsCommandList* self, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource, const float values[4], UINT rect_count, const D3D12_RECT *rects) { + Access access(self); + return original_ClearUnorderedAccessViewFloat(access.target(), gpu_handle, cpu_handle, resource, values, rect_count, rects); +} + +using Fn_DiscardResource = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *resource, const D3D12_DISCARD_REGION *region); +static Fn_DiscardResource original_DiscardResource = nullptr; +static void STDMETHODCALLTYPE hook_DiscardResource(ID3D12GraphicsCommandList* self, ID3D12Resource *resource, const D3D12_DISCARD_REGION *region) { + Access access(self); + return original_DiscardResource(access.target(), resource, region); +} + +using Fn_BeginQuery = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index); +static Fn_BeginQuery original_BeginQuery = nullptr; +static void STDMETHODCALLTYPE hook_BeginQuery(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index); + +using Fn_EndQuery = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index); +static Fn_EndQuery original_EndQuery = nullptr; +static void STDMETHODCALLTYPE hook_EndQuery(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index); + +using Fn_ResolveQueryData = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT start_index, UINT query_count, ID3D12Resource *dst_buffer, UINT64 aligned_dst_buffer_offset); +static Fn_ResolveQueryData original_ResolveQueryData = nullptr; +static void STDMETHODCALLTYPE hook_ResolveQueryData(ID3D12GraphicsCommandList* self, ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT start_index, UINT query_count, ID3D12Resource *dst_buffer, UINT64 aligned_dst_buffer_offset) { + Access access(self); + return original_ResolveQueryData(access.target(), heap, type, start_index, query_count, dst_buffer, aligned_dst_buffer_offset); +} + +using Fn_SetPredication = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *buffer, UINT64 aligned_buffer_offset, D3D12_PREDICATION_OP operation); +static Fn_SetPredication original_SetPredication = nullptr; +static void STDMETHODCALLTYPE hook_SetPredication(ID3D12GraphicsCommandList* self, ID3D12Resource *buffer, UINT64 aligned_buffer_offset, D3D12_PREDICATION_OP operation); + +using Fn_SetMarker = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT metadata, const void *data, UINT size); +static Fn_SetMarker original_SetMarker = nullptr; +static void STDMETHODCALLTYPE hook_SetMarker(ID3D12GraphicsCommandList* self, UINT metadata, const void *data, UINT size) { + Access access(self); + return original_SetMarker(access.target(), metadata, data, size); +} + +using Fn_BeginEvent = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT metadata, const void *data, UINT size); +static Fn_BeginEvent original_BeginEvent = nullptr; +static void STDMETHODCALLTYPE hook_BeginEvent(ID3D12GraphicsCommandList* self, UINT metadata, const void *data, UINT size); + +using Fn_EndEvent = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self); +static Fn_EndEvent original_EndEvent = nullptr; +static void STDMETHODCALLTYPE hook_EndEvent(ID3D12GraphicsCommandList* self); + +using Fn_ExecuteIndirect = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12CommandSignature *command_signature, UINT max_command_count, ID3D12Resource *arg_buffer, UINT64 arg_buffer_offset, ID3D12Resource *count_buffer, UINT64 count_buffer_offset); +static Fn_ExecuteIndirect original_ExecuteIndirect = nullptr; +static void STDMETHODCALLTYPE hook_ExecuteIndirect(ID3D12GraphicsCommandList* self, ID3D12CommandSignature *command_signature, UINT max_command_count, ID3D12Resource *arg_buffer, UINT64 arg_buffer_offset, ID3D12Resource *count_buffer, UINT64 count_buffer_offset); + +using Fn_AtomicCopyBufferUINT = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT dependent_resource_count, ID3D12Resource *const *dependent_resources, const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges); +static Fn_AtomicCopyBufferUINT original_AtomicCopyBufferUINT = nullptr; +static void STDMETHODCALLTYPE hook_AtomicCopyBufferUINT(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT dependent_resource_count, ID3D12Resource *const *dependent_resources, const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges) { + Access access(self); + return original_AtomicCopyBufferUINT(access.target(), dst_buffer, dst_offset, src_buffer, src_offset, dependent_resource_count, dependent_resources, dependent_sub_resource_ranges); +} + +using Fn_AtomicCopyBufferUINT64 = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT dependent_resource_count, ID3D12Resource *const *dependent_resources, const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges); +static Fn_AtomicCopyBufferUINT64 original_AtomicCopyBufferUINT64 = nullptr; +static void STDMETHODCALLTYPE hook_AtomicCopyBufferUINT64(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_buffer, UINT64 dst_offset, ID3D12Resource *src_buffer, UINT64 src_offset, UINT dependent_resource_count, ID3D12Resource *const *dependent_resources, const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges) { + Access access(self); + return original_AtomicCopyBufferUINT64(access.target(), dst_buffer, dst_offset, src_buffer, src_offset, dependent_resource_count, dependent_resources, dependent_sub_resource_ranges); +} + +using Fn_OMSetDepthBounds = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, FLOAT min, FLOAT max); +static Fn_OMSetDepthBounds original_OMSetDepthBounds = nullptr; +static void STDMETHODCALLTYPE hook_OMSetDepthBounds(ID3D12GraphicsCommandList* self, FLOAT min, FLOAT max) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_OMSetDepthBounds(target, min, max); + }); + return original_OMSetDepthBounds(access.target(), min, max); +} + +using Fn_SetSamplePositions = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT sample_count, UINT pixel_count, D3D12_SAMPLE_POSITION *sample_positions); +static Fn_SetSamplePositions original_SetSamplePositions = nullptr; +static void STDMETHODCALLTYPE hook_SetSamplePositions(ID3D12GraphicsCommandList* self, UINT sample_count, UINT pixel_count, D3D12_SAMPLE_POSITION *sample_positions) { + Access access(self); + auto saved_sample_positions = copy_values(sample_positions, sample_count * pixel_count); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetSamplePositions(target, sample_count, pixel_count, values_pointer(saved_sample_positions)); + }); + return original_SetSamplePositions(access.target(), sample_count, pixel_count, sample_positions); +} + +using Fn_ResolveSubresourceRegion = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, UINT dst_sub_resource_idx, UINT dst_x, UINT dst_y, ID3D12Resource *src_resource, UINT src_sub_resource_idx, D3D12_RECT *src_rect, DXGI_FORMAT format, D3D12_RESOLVE_MODE mode); +static Fn_ResolveSubresourceRegion original_ResolveSubresourceRegion = nullptr; +static void STDMETHODCALLTYPE hook_ResolveSubresourceRegion(ID3D12GraphicsCommandList* self, ID3D12Resource *dst_resource, UINT dst_sub_resource_idx, UINT dst_x, UINT dst_y, ID3D12Resource *src_resource, UINT src_sub_resource_idx, D3D12_RECT *src_rect, DXGI_FORMAT format, D3D12_RESOLVE_MODE mode) { + Access access(self); + return original_ResolveSubresourceRegion(access.target(), dst_resource, dst_sub_resource_idx, dst_x, dst_y, src_resource, src_sub_resource_idx, src_rect, format, mode); +} + +using Fn_SetViewInstanceMask = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT mask); +static Fn_SetViewInstanceMask original_SetViewInstanceMask = nullptr; +static void STDMETHODCALLTYPE hook_SetViewInstanceMask(ID3D12GraphicsCommandList* self, UINT mask) { + Access access(self); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetViewInstanceMask(target, mask); + }); + return original_SetViewInstanceMask(access.target(), mask); +} + +using Fn_WriteBufferImmediate = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT count, const D3D12_WRITEBUFFERIMMEDIATE_PARAMETER *parameters, const D3D12_WRITEBUFFERIMMEDIATE_MODE *modes); +static Fn_WriteBufferImmediate original_WriteBufferImmediate = nullptr; +static void STDMETHODCALLTYPE hook_WriteBufferImmediate(ID3D12GraphicsCommandList* self, UINT count, const D3D12_WRITEBUFFERIMMEDIATE_PARAMETER *parameters, const D3D12_WRITEBUFFERIMMEDIATE_MODE *modes) { + Access access(self); + return original_WriteBufferImmediate(access.target(), count, parameters, modes); +} + +using Fn_SetProtectedResourceSession = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12ProtectedResourceSession *protected_resource_session); +static Fn_SetProtectedResourceSession original_SetProtectedResourceSession = nullptr; +static void STDMETHODCALLTYPE hook_SetProtectedResourceSession(ID3D12GraphicsCommandList* self, ID3D12ProtectedResourceSession *protected_resource_session) { + Access access(self); + auto saved_protected_resource_session = retain(protected_resource_session); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetProtectedResourceSession(target, saved_protected_resource_session.Get()); + }); + return original_SetProtectedResourceSession(access.target(), protected_resource_session); +} + +using Fn_BeginRenderPass = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT render_targets_count, const D3D12_RENDER_PASS_RENDER_TARGET_DESC *render_targets, const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC *depth_stencil, D3D12_RENDER_PASS_FLAGS flags); +static Fn_BeginRenderPass original_BeginRenderPass = nullptr; +static void STDMETHODCALLTYPE hook_BeginRenderPass(ID3D12GraphicsCommandList* self, UINT render_targets_count, const D3D12_RENDER_PASS_RENDER_TARGET_DESC *render_targets, const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC *depth_stencil, D3D12_RENDER_PASS_FLAGS flags); + +using Fn_EndRenderPass = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self); +static Fn_EndRenderPass original_EndRenderPass = nullptr; +static void STDMETHODCALLTYPE hook_EndRenderPass(ID3D12GraphicsCommandList* self); + +using Fn_InitializeMetaCommand = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12MetaCommand *meta_command, const void *initialization_parameters_data, SIZE_T initialization_parameters_data_size_in_bytes); +static Fn_InitializeMetaCommand original_InitializeMetaCommand = nullptr; +static void STDMETHODCALLTYPE hook_InitializeMetaCommand(ID3D12GraphicsCommandList* self, ID3D12MetaCommand *meta_command, const void *initialization_parameters_data, SIZE_T initialization_parameters_data_size_in_bytes) { + Access access(self); + return original_InitializeMetaCommand(access.target(), meta_command, initialization_parameters_data, initialization_parameters_data_size_in_bytes); +} + +using Fn_ExecuteMetaCommand = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12MetaCommand *meta_command, const void *execution_parameters_data, SIZE_T execution_parameters_data_size_in_bytes); +static Fn_ExecuteMetaCommand original_ExecuteMetaCommand = nullptr; +static void STDMETHODCALLTYPE hook_ExecuteMetaCommand(ID3D12GraphicsCommandList* self, ID3D12MetaCommand *meta_command, const void *execution_parameters_data, SIZE_T execution_parameters_data_size_in_bytes) { + Access access(self); + return original_ExecuteMetaCommand(access.target(), meta_command, execution_parameters_data, execution_parameters_data_size_in_bytes); +} + +using Fn_BuildRaytracingAccelerationStructure = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC *desc, UINT postbuild_info_descs_count, const D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC *postbuild_info_descs); +static Fn_BuildRaytracingAccelerationStructure original_BuildRaytracingAccelerationStructure = nullptr; +static void STDMETHODCALLTYPE hook_BuildRaytracingAccelerationStructure(ID3D12GraphicsCommandList* self, const D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC *desc, UINT postbuild_info_descs_count, const D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC *postbuild_info_descs) { + Access access(self); + return original_BuildRaytracingAccelerationStructure(access.target(), desc, postbuild_info_descs_count, postbuild_info_descs); +} + +using Fn_EmitRaytracingAccelerationStructurePostbuildInfo = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC *desc, UINT src_acceleration_structures_count, const D3D12_GPU_VIRTUAL_ADDRESS *src_acceleration_structure_data); +static Fn_EmitRaytracingAccelerationStructurePostbuildInfo original_EmitRaytracingAccelerationStructurePostbuildInfo = nullptr; +static void STDMETHODCALLTYPE hook_EmitRaytracingAccelerationStructurePostbuildInfo(ID3D12GraphicsCommandList* self, const D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC *desc, UINT src_acceleration_structures_count, const D3D12_GPU_VIRTUAL_ADDRESS *src_acceleration_structure_data) { + Access access(self); + return original_EmitRaytracingAccelerationStructurePostbuildInfo(access.target(), desc, src_acceleration_structures_count, src_acceleration_structure_data); +} + +using Fn_CopyRaytracingAccelerationStructure = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_GPU_VIRTUAL_ADDRESS dst_acceleration_structure_data, D3D12_GPU_VIRTUAL_ADDRESS src_acceleration_structure_data, D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE mode); +static Fn_CopyRaytracingAccelerationStructure original_CopyRaytracingAccelerationStructure = nullptr; +static void STDMETHODCALLTYPE hook_CopyRaytracingAccelerationStructure(ID3D12GraphicsCommandList* self, D3D12_GPU_VIRTUAL_ADDRESS dst_acceleration_structure_data, D3D12_GPU_VIRTUAL_ADDRESS src_acceleration_structure_data, D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE mode) { + Access access(self); + return original_CopyRaytracingAccelerationStructure(access.target(), dst_acceleration_structure_data, src_acceleration_structure_data, mode); +} + +using Fn_SetPipelineState1 = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12StateObject *state_object); +static Fn_SetPipelineState1 original_SetPipelineState1 = nullptr; +static void STDMETHODCALLTYPE hook_SetPipelineState1(ID3D12GraphicsCommandList* self, ID3D12StateObject *state_object) { + Access access(self); + auto saved_state_object = retain(state_object); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_SetPipelineState1(target, saved_state_object.Get()); + }); + return original_SetPipelineState1(access.target(), state_object); +} + +using Fn_DispatchRays = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const D3D12_DISPATCH_RAYS_DESC *desc); +static Fn_DispatchRays original_DispatchRays = nullptr; +static void STDMETHODCALLTYPE hook_DispatchRays(ID3D12GraphicsCommandList* self, const D3D12_DISPATCH_RAYS_DESC *desc) { + Access access(self); + return original_DispatchRays(access.target(), desc); +} + +using Fn_RSSetShadingRate = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_SHADING_RATE base_shading_rate, const D3D12_SHADING_RATE_COMBINER *combiners); +static Fn_RSSetShadingRate original_RSSetShadingRate = nullptr; +static void STDMETHODCALLTYPE hook_RSSetShadingRate(ID3D12GraphicsCommandList* self, D3D12_SHADING_RATE base_shading_rate, const D3D12_SHADING_RATE_COMBINER *combiners) { + Access access(self); + auto saved_combiners = copy_values(combiners, 2); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_RSSetShadingRate(target, base_shading_rate, values_pointer(saved_combiners)); + }); + return original_RSSetShadingRate(access.target(), base_shading_rate, combiners); +} + +using Fn_RSSetShadingRateImage = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, ID3D12Resource *shading_rate_image); +static Fn_RSSetShadingRateImage original_RSSetShadingRateImage = nullptr; +static void STDMETHODCALLTYPE hook_RSSetShadingRateImage(ID3D12GraphicsCommandList* self, ID3D12Resource *shading_rate_image) { + Access access(self); + auto saved_shading_rate_image = retain(shading_rate_image); + if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable { + original_RSSetShadingRateImage(target, saved_shading_rate_image.Get()); + }); + return original_RSSetShadingRateImage(access.target(), shading_rate_image); +} + +using Fn_DispatchMesh = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT thread_group_count_x, UINT thread_group_count_y, UINT thread_group_count_z); +static Fn_DispatchMesh original_DispatchMesh = nullptr; +static void STDMETHODCALLTYPE hook_DispatchMesh(ID3D12GraphicsCommandList* self, UINT thread_group_count_x, UINT thread_group_count_y, UINT thread_group_count_z) { + Access access(self); + return original_DispatchMesh(access.target(), thread_group_count_x, thread_group_count_y, thread_group_count_z); +} + +using Fn_Barrier = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT32 barrier_groups_count, const D3D12_BARRIER_GROUP *barrier_groups); +static Fn_Barrier original_Barrier = nullptr; +static void STDMETHODCALLTYPE hook_Barrier(ID3D12GraphicsCommandList* self, UINT32 barrier_groups_count, const D3D12_BARRIER_GROUP *barrier_groups); + +static void attach_recording_methods(void** table, unsigned version) { + if (version >= 0) attach_method(table[9], reinterpret_cast(hook_Close), reinterpret_cast(&original_Close), "Close"); + if (version >= 0) attach_method(table[10], reinterpret_cast(hook_Reset), reinterpret_cast(&original_Reset), "Reset"); + if (version >= 0) attach_method(table[11], reinterpret_cast(hook_ClearState), reinterpret_cast(&original_ClearState), "ClearState"); + if (version >= 0) attach_method(table[12], reinterpret_cast(hook_DrawInstanced), reinterpret_cast(&original_DrawInstanced), "DrawInstanced"); + if (version >= 0) attach_method(table[13], reinterpret_cast(hook_DrawIndexedInstanced), reinterpret_cast(&original_DrawIndexedInstanced), "DrawIndexedInstanced"); + if (version >= 0) attach_method(table[14], reinterpret_cast(hook_Dispatch), reinterpret_cast(&original_Dispatch), "Dispatch"); + if (version >= 0) attach_method(table[15], reinterpret_cast(hook_CopyBufferRegion), reinterpret_cast(&original_CopyBufferRegion), "CopyBufferRegion"); + if (version >= 0) attach_method(table[16], reinterpret_cast(hook_CopyTextureRegion), reinterpret_cast(&original_CopyTextureRegion), "CopyTextureRegion"); + if (version >= 0) attach_method(table[17], reinterpret_cast(hook_CopyResource), reinterpret_cast(&original_CopyResource), "CopyResource"); + if (version >= 0) attach_method(table[18], reinterpret_cast(hook_CopyTiles), reinterpret_cast(&original_CopyTiles), "CopyTiles"); + if (version >= 0) attach_method(table[19], reinterpret_cast(hook_ResolveSubresource), reinterpret_cast(&original_ResolveSubresource), "ResolveSubresource"); + if (version >= 0) attach_method(table[20], reinterpret_cast(hook_IASetPrimitiveTopology), reinterpret_cast(&original_IASetPrimitiveTopology), "IASetPrimitiveTopology"); + if (version >= 0) attach_method(table[21], reinterpret_cast(hook_RSSetViewports), reinterpret_cast(&original_RSSetViewports), "RSSetViewports"); + if (version >= 0) attach_method(table[22], reinterpret_cast(hook_RSSetScissorRects), reinterpret_cast(&original_RSSetScissorRects), "RSSetScissorRects"); + if (version >= 0) attach_method(table[23], reinterpret_cast(hook_OMSetBlendFactor), reinterpret_cast(&original_OMSetBlendFactor), "OMSetBlendFactor"); + if (version >= 0) attach_method(table[24], reinterpret_cast(hook_OMSetStencilRef), reinterpret_cast(&original_OMSetStencilRef), "OMSetStencilRef"); + if (version >= 0) attach_method(table[25], reinterpret_cast(hook_SetPipelineState), reinterpret_cast(&original_SetPipelineState), "SetPipelineState"); + if (version >= 0) attach_method(table[26], reinterpret_cast(hook_ResourceBarrier), reinterpret_cast(&original_ResourceBarrier), "ResourceBarrier"); + if (version >= 0) attach_method(table[27], reinterpret_cast(hook_ExecuteBundle), reinterpret_cast(&original_ExecuteBundle), "ExecuteBundle"); + if (version >= 0) attach_method(table[28], reinterpret_cast(hook_SetDescriptorHeaps), reinterpret_cast(&original_SetDescriptorHeaps), "SetDescriptorHeaps"); + if (version >= 0) attach_method(table[29], reinterpret_cast(hook_SetComputeRootSignature), reinterpret_cast(&original_SetComputeRootSignature), "SetComputeRootSignature"); + if (version >= 0) attach_method(table[30], reinterpret_cast(hook_SetGraphicsRootSignature), reinterpret_cast(&original_SetGraphicsRootSignature), "SetGraphicsRootSignature"); + if (version >= 0) attach_method(table[31], reinterpret_cast(hook_SetComputeRootDescriptorTable), reinterpret_cast(&original_SetComputeRootDescriptorTable), "SetComputeRootDescriptorTable"); + if (version >= 0) attach_method(table[32], reinterpret_cast(hook_SetGraphicsRootDescriptorTable), reinterpret_cast(&original_SetGraphicsRootDescriptorTable), "SetGraphicsRootDescriptorTable"); + if (version >= 0) attach_method(table[33], reinterpret_cast(hook_SetComputeRoot32BitConstant), reinterpret_cast(&original_SetComputeRoot32BitConstant), "SetComputeRoot32BitConstant"); + if (version >= 0) attach_method(table[34], reinterpret_cast(hook_SetGraphicsRoot32BitConstant), reinterpret_cast(&original_SetGraphicsRoot32BitConstant), "SetGraphicsRoot32BitConstant"); + if (version >= 0) attach_method(table[35], reinterpret_cast(hook_SetComputeRoot32BitConstants), reinterpret_cast(&original_SetComputeRoot32BitConstants), "SetComputeRoot32BitConstants"); + if (version >= 0) attach_method(table[36], reinterpret_cast(hook_SetGraphicsRoot32BitConstants), reinterpret_cast(&original_SetGraphicsRoot32BitConstants), "SetGraphicsRoot32BitConstants"); + if (version >= 0) attach_method(table[37], reinterpret_cast(hook_SetComputeRootConstantBufferView), reinterpret_cast(&original_SetComputeRootConstantBufferView), "SetComputeRootConstantBufferView"); + if (version >= 0) attach_method(table[38], reinterpret_cast(hook_SetGraphicsRootConstantBufferView), reinterpret_cast(&original_SetGraphicsRootConstantBufferView), "SetGraphicsRootConstantBufferView"); + if (version >= 0) attach_method(table[39], reinterpret_cast(hook_SetComputeRootShaderResourceView), reinterpret_cast(&original_SetComputeRootShaderResourceView), "SetComputeRootShaderResourceView"); + if (version >= 0) attach_method(table[40], reinterpret_cast(hook_SetGraphicsRootShaderResourceView), reinterpret_cast(&original_SetGraphicsRootShaderResourceView), "SetGraphicsRootShaderResourceView"); + if (version >= 0) attach_method(table[41], reinterpret_cast(hook_SetComputeRootUnorderedAccessView), reinterpret_cast(&original_SetComputeRootUnorderedAccessView), "SetComputeRootUnorderedAccessView"); + if (version >= 0) attach_method(table[42], reinterpret_cast(hook_SetGraphicsRootUnorderedAccessView), reinterpret_cast(&original_SetGraphicsRootUnorderedAccessView), "SetGraphicsRootUnorderedAccessView"); + if (version >= 0) attach_method(table[43], reinterpret_cast(hook_IASetIndexBuffer), reinterpret_cast(&original_IASetIndexBuffer), "IASetIndexBuffer"); + if (version >= 0) attach_method(table[44], reinterpret_cast(hook_IASetVertexBuffers), reinterpret_cast(&original_IASetVertexBuffers), "IASetVertexBuffers"); + if (version >= 0) attach_method(table[45], reinterpret_cast(hook_SOSetTargets), reinterpret_cast(&original_SOSetTargets), "SOSetTargets"); + if (version >= 0) attach_method(table[46], reinterpret_cast(hook_OMSetRenderTargets), reinterpret_cast(&original_OMSetRenderTargets), "OMSetRenderTargets"); + if (version >= 0) attach_method(table[47], reinterpret_cast(hook_ClearDepthStencilView), reinterpret_cast(&original_ClearDepthStencilView), "ClearDepthStencilView"); + if (version >= 0) attach_method(table[48], reinterpret_cast(hook_ClearRenderTargetView), reinterpret_cast(&original_ClearRenderTargetView), "ClearRenderTargetView"); + if (version >= 0) attach_method(table[49], reinterpret_cast(hook_ClearUnorderedAccessViewUint), reinterpret_cast(&original_ClearUnorderedAccessViewUint), "ClearUnorderedAccessViewUint"); + if (version >= 0) attach_method(table[50], reinterpret_cast(hook_ClearUnorderedAccessViewFloat), reinterpret_cast(&original_ClearUnorderedAccessViewFloat), "ClearUnorderedAccessViewFloat"); + if (version >= 0) attach_method(table[51], reinterpret_cast(hook_DiscardResource), reinterpret_cast(&original_DiscardResource), "DiscardResource"); + if (version >= 0) attach_method(table[52], reinterpret_cast(hook_BeginQuery), reinterpret_cast(&original_BeginQuery), "BeginQuery"); + if (version >= 0) attach_method(table[53], reinterpret_cast(hook_EndQuery), reinterpret_cast(&original_EndQuery), "EndQuery"); + if (version >= 0) attach_method(table[54], reinterpret_cast(hook_ResolveQueryData), reinterpret_cast(&original_ResolveQueryData), "ResolveQueryData"); + if (version >= 0) attach_method(table[55], reinterpret_cast(hook_SetPredication), reinterpret_cast(&original_SetPredication), "SetPredication"); + if (version >= 0) attach_method(table[56], reinterpret_cast(hook_SetMarker), reinterpret_cast(&original_SetMarker), "SetMarker"); + if (version >= 0) attach_method(table[57], reinterpret_cast(hook_BeginEvent), reinterpret_cast(&original_BeginEvent), "BeginEvent"); + if (version >= 0) attach_method(table[58], reinterpret_cast(hook_EndEvent), reinterpret_cast(&original_EndEvent), "EndEvent"); + if (version >= 0) attach_method(table[59], reinterpret_cast(hook_ExecuteIndirect), reinterpret_cast(&original_ExecuteIndirect), "ExecuteIndirect"); + if (version >= 1) attach_method(table[60], reinterpret_cast(hook_AtomicCopyBufferUINT), reinterpret_cast(&original_AtomicCopyBufferUINT), "AtomicCopyBufferUINT"); + if (version >= 1) attach_method(table[61], reinterpret_cast(hook_AtomicCopyBufferUINT64), reinterpret_cast(&original_AtomicCopyBufferUINT64), "AtomicCopyBufferUINT64"); + if (version >= 1) attach_method(table[62], reinterpret_cast(hook_OMSetDepthBounds), reinterpret_cast(&original_OMSetDepthBounds), "OMSetDepthBounds"); + if (version >= 1) attach_method(table[63], reinterpret_cast(hook_SetSamplePositions), reinterpret_cast(&original_SetSamplePositions), "SetSamplePositions"); + if (version >= 1) attach_method(table[64], reinterpret_cast(hook_ResolveSubresourceRegion), reinterpret_cast(&original_ResolveSubresourceRegion), "ResolveSubresourceRegion"); + if (version >= 1) attach_method(table[65], reinterpret_cast(hook_SetViewInstanceMask), reinterpret_cast(&original_SetViewInstanceMask), "SetViewInstanceMask"); + if (version >= 2) attach_method(table[66], reinterpret_cast(hook_WriteBufferImmediate), reinterpret_cast(&original_WriteBufferImmediate), "WriteBufferImmediate"); + if (version >= 3) attach_method(table[67], reinterpret_cast(hook_SetProtectedResourceSession), reinterpret_cast(&original_SetProtectedResourceSession), "SetProtectedResourceSession"); + if (version >= 4) attach_method(table[68], reinterpret_cast(hook_BeginRenderPass), reinterpret_cast(&original_BeginRenderPass), "BeginRenderPass"); + if (version >= 4) attach_method(table[69], reinterpret_cast(hook_EndRenderPass), reinterpret_cast(&original_EndRenderPass), "EndRenderPass"); + if (version >= 4) attach_method(table[70], reinterpret_cast(hook_InitializeMetaCommand), reinterpret_cast(&original_InitializeMetaCommand), "InitializeMetaCommand"); + if (version >= 4) attach_method(table[71], reinterpret_cast(hook_ExecuteMetaCommand), reinterpret_cast(&original_ExecuteMetaCommand), "ExecuteMetaCommand"); + if (version >= 4) attach_method(table[72], reinterpret_cast(hook_BuildRaytracingAccelerationStructure), reinterpret_cast(&original_BuildRaytracingAccelerationStructure), "BuildRaytracingAccelerationStructure"); + if (version >= 4) attach_method(table[73], reinterpret_cast(hook_EmitRaytracingAccelerationStructurePostbuildInfo), reinterpret_cast(&original_EmitRaytracingAccelerationStructurePostbuildInfo), "EmitRaytracingAccelerationStructurePostbuildInfo"); + if (version >= 4) attach_method(table[74], reinterpret_cast(hook_CopyRaytracingAccelerationStructure), reinterpret_cast(&original_CopyRaytracingAccelerationStructure), "CopyRaytracingAccelerationStructure"); + if (version >= 4) attach_method(table[75], reinterpret_cast(hook_SetPipelineState1), reinterpret_cast(&original_SetPipelineState1), "SetPipelineState1"); + if (version >= 4) attach_method(table[76], reinterpret_cast(hook_DispatchRays), reinterpret_cast(&original_DispatchRays), "DispatchRays"); + if (version >= 5) attach_method(table[77], reinterpret_cast(hook_RSSetShadingRate), reinterpret_cast(&original_RSSetShadingRate), "RSSetShadingRate"); + if (version >= 5) attach_method(table[78], reinterpret_cast(hook_RSSetShadingRateImage), reinterpret_cast(&original_RSSetShadingRateImage), "RSSetShadingRateImage"); + if (version >= 6) attach_method(table[79], reinterpret_cast(hook_DispatchMesh), reinterpret_cast(&original_DispatchMesh), "DispatchMesh"); + if (version >= 7) attach_method(table[80], reinterpret_cast(hook_Barrier), reinterpret_cast(&original_Barrier), "Barrier"); +} diff --git a/tools/windows/d3d12_command_hooks.h b/tools/windows/d3d12_command_hooks.h new file mode 100644 index 0000000..2d10d0b --- /dev/null +++ b/tools/windows/d3d12_command_hooks.h @@ -0,0 +1,22 @@ +#pragma once +#include "d3d12_external.h" +#include +#include + +namespace d4r::win::commands { +// Intercept only documented D3D12 COM methods on this device's runtime. +// A boundary becomes prefix -> callback -> suffix at ExecuteCommandLists, +// preserving preceding lists in the caller's batch and current-frame output. +using Boundary = std::function; +void install(ID3D12Device* device); +void record_boundary(ID3D12GraphicsCommandList* list, Boundary callback); +std::string error(); +size_t live_recordings(); +D3D12_RESOURCE_STATES resource_state(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, D3D12_RESOURCE_STATES fallback); +// Internal copies and callback submissions must bypass logical list routing. +struct InternalScope { + bool previous; + InternalScope(); + ~InternalScope(); +}; +} diff --git a/tools/windows/d3d12_command_probe.cpp b/tools/windows/d3d12_command_probe.cpp new file mode 100644 index 0000000..97f4f80 --- /dev/null +++ b/tools/windows/d3d12_command_probe.cpp @@ -0,0 +1,163 @@ +#include "d3d12_external.h" +#include +#include +#include + +using namespace d4r::diag; +using namespace d4r::win; +namespace { +constexpr unsigned count = 256, bytes = count * sizeof(uint32_t), seed = 0x12345; +void drain(ID3D12Device* device, ID3D12CommandQueue* queue) { + ComPtr fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())), "Probe fence"); + Handle event; event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if (!event.value) throw std::runtime_error("Probe event"); + dx(queue->Signal(fence.Get(), 1), "Probe signal"); + if (fence->GetCompletedValue() < 1) { + dx(fence->SetEventOnCompletion(1, event.value), "Probe fence event"); + if (WaitForSingleObject(event.value, 30000) != WAIT_OBJECT_0) throw std::runtime_error("Probe GPU timeout"); + } +} +struct NativeList { + ComPtr allocator; + ComPtr list; + explicit NativeList(ID3D12Device* device) { + dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.GetAddressOf())), "Probe allocator"); + dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(list.GetAddressOf())), "Probe command list"); + } + void submit(ID3D12Device* device, ID3D12CommandQueue* queue) { + dx(list->Close(), "Close probe list"); ID3D12CommandList* lists[] = {list.Get()}; + queue->ExecuteCommandLists(1, lists); drain(device, queue); + } +}; +ComPtr output_buffer(ID3D12Device* device) { + D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT; heap.CreationNodeMask = heap.VisibleNodeMask = 1; + D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Width = bytes; + desc.Height = desc.DepthOrArraySize = desc.MipLevels = desc.SampleDesc.Count = 1; + desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + ComPtr result; + dx(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + nullptr, IID_PPV_ARGS(result.GetAddressOf())), "Probe UAV"); + return result; +} +void upload(ID3D12Resource* buffer, uint32_t multiplier, uint32_t addend) { + void* data = nullptr; D3D12_RANGE noRead{}; dx(buffer->Map(0, &noRead, &data), "Probe upload map"); + auto* values = static_cast(data); + for (unsigned i = 0; i < count; ++i) values[i] = i * multiplier + addend; + buffer->Unmap(0, nullptr); +} +bool verify(ID3D12Resource* buffer, uint32_t multiplier, uint32_t addend) { + void* data = nullptr; D3D12_RANGE range{0, bytes}; dx(buffer->Map(0, &range, &data), "Probe verification map"); + const auto* values = static_cast(data); + bool passed = true; + for (unsigned i = 0; i < count; ++i) if (values[i] != i * multiplier + addend + seed) { + std::fprintf(stderr, "COMMAND_DIFFERENT index=%u expected=%u actual=%u\n", i, i * multiplier + addend + seed, values[i]); + passed = false; break; + } + D3D12_RANGE noWrite{}; buffer->Unmap(0, &noWrite); return passed; +} +struct Callback { + ID3D12Device* device; + ID3D12Resource* input; + ID3D12Resource* output; + ID3D12Resource* replacement; + ID3D12Resource* readback; + bool prefix_verified = false; +}; +void WINAPI boundary(ID3D12CommandQueue* queue, void* context) { + auto& callback = *static_cast(context); + NativeList list(callback.device); + transition(list.list.Get(), callback.output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE); + list.list->CopyResource(callback.readback, callback.output); + transition(list.list.Get(), callback.output, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + transition(list.list.Get(), callback.input, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST); + list.list->CopyResource(callback.input, callback.replacement); + transition(list.list.Get(), callback.input, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + list.submit(callback.device, queue); + callback.prefix_verified = verify(callback.readback, 17, 10); +} +} + +int main(int argc, char** argv) { + start(); + try { + Args args(argc, argv); + if (args.module.empty()) throw std::runtime_error("--module d4r_nvngx.dll is required; NVIDIA DLLs are not needed"); + HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_gfx1201(args.device, props); + ComPtr factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI"); + ComPtr adapter; + for (UINT i = 0; ; ++i) { + dx(factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()), "Probe adapter"); + DXGI_ADAPTER_DESC1 desc{}; dx(adapter->GetDesc1(&desc), "Probe adapter desc"); + if (!std::memcmp(&desc.AdapterLuid, props.luid, sizeof(LUID))) break; + } + ComPtr device; + dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "Probe D3D12"); + ComPtr queue; + D3D12_COMMAND_QUEUE_DESC qd{}; qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + dx(device->CreateCommandQueue(&qd, IID_PPV_ARGS(queue.GetAddressOf())), "Probe queue"); + wchar_t system[32768]{}; if (!GetSystemDirectoryW(system, 32768)) throw std::runtime_error("System directory"); + Library compiler(std::filesystem::path(system) / L"d3dcompiler_47.dll"); + auto compile = compiler.symbol("D3DCompile"); + static constexpr char shader[] = "RWStructuredBuffer Output : register(u0); StructuredBuffer Input : register(t0); cbuffer Constants : register(b0) { uint Seed; }; [numthreads(64,1,1)] void main(uint3 t:SV_DispatchThreadID) { Output[t.x] = Input[t.x] + Seed; }"; + ComPtr code, errors; + dx(compile(shader, sizeof(shader)-1, nullptr, nullptr, nullptr, "main", "cs_5_1", D3DCOMPILE_OPTIMIZATION_LEVEL3, + 0, code.GetAddressOf(), errors.GetAddressOf()), "Probe HLSL"); + D3D12_ROOT_PARAMETER roots[3]{}; + roots[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; roots[0].Descriptor.ShaderRegister = 0; + roots[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; roots[1].Descriptor.ShaderRegister = 0; + roots[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; roots[2].Constants.Num32BitValues = 1; + D3D12_ROOT_SIGNATURE_DESC signature{}; signature.NumParameters = 3; signature.pParameters = roots; + ComPtr serialized; + dx(D3D12SerializeRootSignature(&signature, D3D_ROOT_SIGNATURE_VERSION_1, serialized.GetAddressOf(), errors.ReleaseAndGetAddressOf()), "Probe root signature serialize"); + ComPtr root; + dx(device->CreateRootSignature(0, serialized->GetBufferPointer(), serialized->GetBufferSize(), IID_PPV_ARGS(root.GetAddressOf())), "Probe root signature"); + D3D12_COMPUTE_PIPELINE_STATE_DESC pipeline{}; + pipeline.pRootSignature = root.Get(); pipeline.CS = {code->GetBufferPointer(), code->GetBufferSize()}; + ComPtr pso; dx(device->CreateComputePipelineState(&pipeline, IID_PPV_ARGS(pso.GetAddressOf())), "Probe PSO"); + Library shim(wide(args.module)); + using Install = unsigned(*)(ID3D12Device*); + using Record = unsigned(*)(ID3D12GraphicsCommandList*, void(WINAPI*)(ID3D12CommandQueue*, void*), void*); + using Live = unsigned long long(*)(); + if (shim.symbol("d4r_D3D12_InstallCommandBackend")(device.Get()) != 1) throw std::runtime_error("Command backend installation failed"); + auto record = shim.symbol("d4r_D3D12_RecordDiagnosticBoundary"); + auto live = shim.symbol("d4r_D3D12_LiveRecordings"); + for (unsigned iteration = 0; iteration < args.iterations; ++iteration) { + auto input = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST); + auto output = output_buffer(device.Get()); + auto first = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ); + auto second = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ); + auto readback = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST); + auto prefix = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST); + upload(first.Get(), 17, 10); upload(second.Get(), 25, 100); + { + NativeList a(device.Get()), b(device.Get()), c(device.Get()); + a.list->CopyResource(input.Get(), first.Get()); + transition(a.list.Get(), input.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + dx(a.list->Close(), "Close predecessor A"); + b.list->SetComputeRootSignature(root.Get()); b.list->SetPipelineState(pso.Get()); + b.list->SetComputeRootUnorderedAccessView(0, output->GetGPUVirtualAddress()); + b.list->SetComputeRootShaderResourceView(1, input->GetGPUVirtualAddress()); + uint32_t temporary = seed; + b.list->SetComputeRoot32BitConstants(2, 1, &temporary, 0); + temporary = 0xdeadbeef; // Deep-copy check: replay must retain Seed. + b.list->Dispatch(count / 64, 1, 1); + Callback callback{device.Get(), input.Get(), output.Get(), second.Get(), prefix.Get()}; + if (record(b.list.Get(), boundary, &callback) != 1) throw std::runtime_error("Record split failed"); + b.list->Dispatch(count / 64, 1, 1); // No rebinding after the split. + dx(b.list->Close(), "Close logical list B"); + transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE); + c.list->CopyResource(readback.Get(), output.Get()); + transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + dx(c.list->Close(), "Close consumer C"); + ID3D12CommandList* batch[] = {a.list.Get(), b.list.Get(), c.list.Get()}; + queue->ExecuteCommandLists(3, batch); drain(device.Get(), queue.Get()); + if (!callback.prefix_verified || !verify(readback.Get(), 25, 100)) throw std::runtime_error("Queue order or suffix root state mismatch"); + } + if (live() != 0) throw std::runtime_error("D3D12 recording metadata leaked after command-list Release"); + } + std::printf("PASS D3D12_COMMAND_BACKEND architecture=gfx1201 iterations=%u batch_order=1 root_state=1 deep_copy=1 live_recordings=0 frame_age=0\n", args.iterations); + return 0; + } catch (const std::exception& failure) { + std::fprintf(stderr, "FAIL D3D12_COMMAND_BACKEND %s\n", failure.what()); loaded_modules(); return 4; + } +} diff --git a/tools/windows/d3d12_dlss_probe.cpp b/tools/windows/d3d12_dlss_probe.cpp index 71117ae..84de1f4 100644 --- a/tools/windows/d3d12_dlss_probe.cpp +++ b/tools/windows/d3d12_dlss_probe.cpp @@ -16,6 +16,8 @@ int main(int argc, char** argv) { if (!diagDir && args.interop_mode != "images") throw std::runtime_error("Set D4R_DIAG_DIR"); const std::filesystem::path directory(diagDir ? wide(diagDir) : std::filesystem::current_path().wstring()); std::filesystem::create_directories(directory); + const bool commandBackend = args.interop_mode == "command-list"; + if (commandBackend && _putenv_s("D4R_D3D12_COMMAND_BACKEND", "1")) throw std::runtime_error("Cannot enable command backend"); for (const auto& pair : {std::make_pair("D4R_HIP_ROOT", args.hip_root), std::make_pair("D4R_NVCUDA_DLL", args.cuda_dll), std::make_pair("D4R_NVAPI_DLL", args.nvapi_dll), std::make_pair("D4R_NGX_CORE", args.ngx_core), std::make_pair("D4R_DLSS_DLL", args.dlss_dll)}) @@ -93,7 +95,7 @@ int main(int argc, char** argv) { i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); } dx(list->Close(), "Close(synthetic producers)"); ID3D12CommandList* producer[] = {list.Get()}; - queue->ExecuteCommandLists(1, producer); drain(); + if (!commandBackend) { queue->ExecuteCommandLists(1, producer); drain(); } if (args.interop_mode == "images") { CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit(image interop)"); CUdevice ordinal = 0; CUcontext context = nullptr; @@ -164,17 +166,43 @@ int main(int argc, char** argv) { for (unsigned i = 0; i < 5; ++i) d4r_ngx_set_d3d12_resource(parameters, names[i], textures[i].image.Get()); d4r_ngx_set_int(parameters, "Disable.Watermark", 1); auto readback = make_buffer(device.Get(), textures[3].bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST); + ComPtr predecessor; + ComPtr predecessorAllocator; + if (commandBackend) { + // Keep initial synthetic producers unsubmitted until the first + // batch, before the list which contains the NGX evaluation. + predecessor = list; predecessorAllocator = allocator; + dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.ReleaseAndGetAddressOf())), "Create game-style allocator"); + dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, + IID_PPV_ARGS(list.ReleaseAndGetAddressOf())), "Create game-style command list"); + dx(list->Close(), "Close initial game-style list"); + } for (unsigned frame = 0; frame < args.iterations; ++frame) { d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0); - check(shim.symbol("d4r_D3D12_EvaluateAtBoundary")(queue.Get(), handle, parameters), "D3D12 same-frame Evaluate"); - dx(allocator->Reset(), "Reset(harness allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(harness list)"); + if (commandBackend) { + dx(allocator->Reset(), "Reset(game-style allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(game-style list)"); + using RecordedEvaluate = unsigned(*)(ID3D12GraphicsCommandList*, void*, void*, void*); + check(shim.symbol("NVSDK_NGX_D3D12_EvaluateFeature")(list.Get(), handle, parameters, nullptr), "D3D12 recorded same-frame Evaluate"); + // The caller may reuse/mutate its parameter object as soon as + // Evaluate returns. The queued snapshot must remain immutable. + d4r_ngx_set_d3d12_resource(parameters, "Color", nullptr); + d4r_ngx_set_int(parameters, "Reset", 0); + } else { + check(shim.symbol("d4r_D3D12_EvaluateAtBoundary")(queue.Get(), handle, parameters), "D3D12 same-frame Evaluate"); + dx(allocator->Reset(), "Reset(harness allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(harness list)"); + } transition(list.Get(), textures[3].image.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE); D3D12_TEXTURE_COPY_LOCATION src{}, dst{}; src.pResource = textures[3].image.Get(); src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dst.pResource = readback.Get(); dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst.PlacedFootprint = textures[3].footprint; list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); transition(list.Get(), textures[3].image.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - dx(list->Close(), "Close(verification)"); ID3D12CommandList* verify[] = {list.Get()}; queue->ExecuteCommandLists(1, verify); drain(); + dx(list->Close(), "Close(verification)"); + if (commandBackend && frame == 0) { + ID3D12CommandList* batch[] = {predecessor.Get(), list.Get()}; queue->ExecuteCommandLists(2, batch); + } else { ID3D12CommandList* verify[] = {list.Get()}; queue->ExecuteCommandLists(1, verify); } + drain(); + if (commandBackend) d4r_ngx_set_d3d12_resource(parameters, "Color", textures[0].image.Get()); void* raw = nullptr; D3D12_RANGE range{0, size_t(textures[3].bytes)}; dx(readback->Map(0, &range, &raw), "Map(verification only)"); std::vector pixels(size_t(outWidth) * outHeight * 4); @@ -193,7 +221,8 @@ int main(int argc, char** argv) { if (variance < 1e-6) throw std::runtime_error("D3D12 output lost the synthetic pattern"); } loaded_modules(); - std::printf("PASS D3D12_DLSS architecture=gfx1201 preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 queue_integration=explicit_harness\n", args.preset, args.iterations); + std::printf("PASS D3D12_DLSS architecture=gfx1201 preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 queue_integration=%s\n", + args.preset, args.iterations, commandBackend ? "recorded_command_list" : "explicit_harness"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; } } diff --git a/tools/windows/d4r_nvngx_windows.cpp b/tools/windows/d4r_nvngx_windows.cpp index 51e8aaf..a3372f1 100644 --- a/tools/windows/d4r_nvngx_windows.cpp +++ b/tools/windows/d4r_nvngx_windows.cpp @@ -44,6 +44,8 @@ API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* dat std::lock_guard lock(apiMutex); return call([&] { if (!runtime) runtime = std::make_shared(device, app, data, sdk); + if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1") + d4r::win::commands::install(device); return 1u; }); } @@ -84,10 +86,33 @@ API unsigned d4r_D3D12_EvaluateAtBoundary(ID3D12CommandQueue* queue, Handle* han auto it = features.find(handle); if (it == features.end() || !p) return invalid; feature = it->second; } return call([&] { feature->evaluate_boundary(queue, p); return 1u; }); } -API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList*, const Handle*, void*, void*) { - // Explicitly refuse an unsubmitted game list until queue integration exists. - std::fprintf(stderr, "D4R_WINDOWS_FAILURE command-list queue backend pending; no frame-age fallback\n"); - return failure; +API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, const Handle* handle, void* p, void*) { + std::shared_ptr feature; + { std::lock_guard lock(apiMutex); + auto it = features.find(const_cast(handle)); + if (it == features.end() || !p || !list) return invalid; feature = it->second; } + return call([&] { + struct Snapshot { + void* parameters = d4r_ngx_parameters_create(); + std::vector> resources; + ~Snapshot() { if (parameters) d4r_ngx_parameters_destroy(parameters); } + }; + auto snapshot = std::make_shared(); + if (!snapshot->parameters) return failure; + d4r::ngx::copy_create(p, snapshot->parameters); d4r::ngx::copy_frame(p, snapshot->parameters); + const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; + const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"}; + for (unsigned i = 0; i < 5; ++i) { + ID3D12Resource* resource = nullptr; (void)d4r_ngx_get_d3d12_resource(p, names[i], &resource); + snapshot->resources.emplace_back(resource); + d4r_ngx_set_d3d12_resource(snapshot->parameters, names[i], resource); + const auto state = d4r::win::commands::resource_state(list, resource, D3D12_RESOURCE_STATES( + d4r::ngx::uint_value(p, states[i], i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE))); + d4r_ngx_set_uint(snapshot->parameters, states[i], unsigned(state)); + } + d4r::win::commands::record_boundary(list, [feature, snapshot](ID3D12CommandQueue* queue) { feature->evaluate_boundary(queue, snapshot->parameters); }); + return 1u; + }); } API unsigned NVSDK_NGX_D3D12_EvaluateFeature_C(ID3D12GraphicsCommandList* list, const Handle* handle, void* p, void* callback) { return NVSDK_NGX_D3D12_EvaluateFeature(list, handle, p, callback); @@ -109,3 +134,14 @@ API unsigned NVSDK_NGX_D3D12_Shutdown() { }); } API unsigned NVSDK_NGX_D3D12_Shutdown1(ID3D12Device*) { return NVSDK_NGX_D3D12_Shutdown(); } + +// Public diagnostics exercise queue/list integration without NVIDIA binaries. +API unsigned d4r_D3D12_InstallCommandBackend(ID3D12Device* device) { + return call([&] { d4r::win::commands::install(device); return 1u; }); +} +using DiagnosticBoundary = void(WINAPI*)(ID3D12CommandQueue*, void*); +API unsigned d4r_D3D12_RecordDiagnosticBoundary(ID3D12GraphicsCommandList* list, DiagnosticBoundary callback, void* context) { + if (!callback) return invalid; + return call([&] { d4r::win::commands::record_boundary(list, [=](ID3D12CommandQueue* queue) { callback(queue, context); }); return 1u; }); +} +API unsigned long long d4r_D3D12_LiveRecordings() { return d4r::win::commands::live_recordings(); } diff --git a/tools/windows/generate_d3d12_hooks.py b/tools/windows/generate_d3d12_hooks.py new file mode 100644 index 0000000..d6a6e5f --- /dev/null +++ b/tools/windows/generate_d3d12_hooks.py @@ -0,0 +1,101 @@ +"""Generate typed command forwarding from public MinGW D3D12 SDK declarations. + +Run with --header --output d3d12_command_hooks.generated.h. +Every recording method through CommandList7 is forwarded. State setters copy +their host arguments; COM object arguments retain a reference. SDK slot numbers +are derived from the public interface inheritance, never a driver binary. +""" +import argparse +import pathlib +import re + +POINTER_COUNTS = { + 'RSSetViewports': {'viewports': 'viewport_count'}, + 'RSSetScissorRects': {'rects': 'rect_count'}, + 'OMSetBlendFactor': {'blend_factor': '4'}, + 'SetDescriptorHeaps': {'heaps': 'heap_count'}, + 'SetComputeRoot32BitConstants': {'data': 'constant_count'}, + 'SetGraphicsRoot32BitConstants': {'data': 'constant_count'}, + 'IASetIndexBuffer': {'view': '1'}, + 'IASetVertexBuffers': {'views': 'view_count'}, + 'SOSetTargets': {'views': 'view_count'}, + 'OMSetRenderTargets': {'render_target_descriptors': '(single_descriptor_handle ? 1 : render_target_descriptor_count)', 'depth_stencil_descriptor': '1'}, + 'SetSamplePositions': {'sample_positions': 'sample_count * pixel_count'}, + 'RSSetShadingRate': {'combiners': '2'}, +} +SPECIAL = {'Close', 'Reset', 'ClearState', 'ResourceBarrier', 'Barrier', + 'BeginEvent', 'EndEvent', 'BeginRenderPass', 'EndRenderPass', + 'BeginQuery', 'EndQuery', 'ExecuteBundle', 'ExecuteIndirect', 'SetPredication'} + + +def generate(source): + classes = [] + for match in re.finditer(r'\n(ID3D12GraphicsCommandList\d*) : public \w+\s*\{(.*?)\n\};', source, re.S): + name, body = match.groups() + methods = re.findall(r'virtual\s+(.*?)\s+STDMETHODCALLTYPE\s+(\w+)\s*\((.*?)\)\s*= 0;', body, re.S) + classes.append((int(name[len('ID3D12GraphicsCommandList'):] or '0'), name, methods)) + if [version for version, _, _ in sorted(classes)] != list(range(8)): + raise RuntimeError('Expected exactly the public CommandList0..7 interfaces; review a changed SDK') + lines = ['// Generated by generate_d3d12_hooks.py from the public SDK. Do not edit.', ''] + registrations = [] + slot = 9 # IUnknown(3), ID3D12Object(4), DeviceChild(1), CommandList(1). + for version, interface, methods in sorted(classes): + for result, name, arguments in methods: + result = ' '.join(result.split()) + arguments = ' '.join(arguments.split()) + declarations = [arg.strip() for arg in arguments.split(',')] if arguments else [] + names = [] + for declaration in declarations: + match = re.search(r'(\w+)\s*(?:\[\d+\])?$', declaration) + if not match: + raise RuntimeError(f'Unparsed SDK argument: {name}: {declaration}') + names.append(match[1]) + signature = ', '.join(['ID3D12GraphicsCommandList* self'] + declarations) + call = ', '.join(['access.target()'] + names) + lines.append(f'using Fn_{name} = {result}(STDMETHODCALLTYPE*)({signature});') + lines.append(f'static Fn_{name} original_{name} = nullptr;') + if name in SPECIAL: + lines.append(f'static {result} STDMETHODCALLTYPE hook_{name}({signature});') + else: + lines += [f'static {result} STDMETHODCALLTYPE hook_{name}({signature}) {{', ' Access access(self);'] + stateful = name.startswith(('Set', 'IASet', 'RSSet', 'OMSet', 'SOSet')) and name != 'SetMarker' + if stateful: + captured = [] + for declaration, argument in zip(declarations, names): + pointer = '*' in declaration or '[' in declaration + if pointer and 'ID3D12' in declaration: + if declaration.count('*') > 1: + count = POINTER_COUNTS.get(name, {}).get(argument) + if count is None: + raise RuntimeError(f'No COM array copy rule: {name}: {declaration}') + lines.append(f' auto saved_{argument} = retain_values({argument}, {count});') + captured.append(f'saved_{argument}.raw.data()') + else: + lines.append(f' auto saved_{argument} = retain({argument});') + captured.append(f'saved_{argument}.Get()') + elif pointer: + count = POINTER_COUNTS.get(name, {}).get(argument) + if count is None: + raise RuntimeError(f'No deep-copy rule for {name}: {declaration}') + value = f'static_cast({argument})' if argument == 'data' else argument + lines.append(f' auto saved_{argument} = copy_values({value}, {count});') + captured.append(f'values_pointer(saved_{argument})') + else: + captured.append(argument) + lines += [' if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable {', + f' original_{name}(' + ', '.join(['target'] + captured) + ');', ' });'] + lines += [f' return original_{name}({call});', '}'] + registrations.append(f' attach_method(table[{slot}], reinterpret_cast(hook_{name}), reinterpret_cast(&original_{name}), "{name}");') + registrations[-1] = f' if (version >= {version})' + registrations[-1][3:] + slot += 1 + lines.append('') + lines += ['static void attach_recording_methods(void** table, unsigned version) {'] + registrations + ['}', ''] + return '\n'.join(lines) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--header', type=pathlib.Path, required=True) + parser.add_argument('--output', type=pathlib.Path, required=True) + args = parser.parse_args() + args.output.write_text(generate(args.header.read_text()), encoding='utf-8') diff --git a/tools/windows/ngx_windows_runtime.h b/tools/windows/ngx_windows_runtime.h index 29e5691..bdf5a29 100644 --- a/tools/windows/ngx_windows_runtime.h +++ b/tools/windows/ngx_windows_runtime.h @@ -2,6 +2,7 @@ #include "d3d12_external.h" #include "cuda_image_api.h" #include "ngx_parameters.h" +#include "d3d12_command_hooks.h" #include #include #include @@ -160,6 +161,7 @@ public: // Explicit harness boundary: all earlier producers must already be queued. // The forthcoming command-list backend calls the same stages at queue submit. void evaluate_boundary(ID3D12CommandQueue* queue, void* parameters) { + commands::InternalScope internal; std::lock_guard lock(rt_->mutex); if (!queue) throw std::runtime_error("Null D3D12 queue"); if (queue_ && queue_.Get() != queue) throw std::runtime_error("A feature cannot switch queues without an explicit drain");