Execute Windows DLSS K and M at recorded D3D12 command-list boundaries

This commit is contained in:
Codex committed 2026-09-30 22:21:06 +03:00
1 parent b8d801ae4c
commit 63607298ea
12 files changed
+1421 -15

No files matched your search

+15 -4
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.24) 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) if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "These diagnostics require native Windows x64") message(FATAL_ERROR "These diagnostics require native Windows x64")
endif() endif()
@@ -68,7 +68,7 @@ foreach(family k m)
endforeach() endforeach()
endforeach() endforeach()
add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects}) 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) add_executable(d4r_${probe} tools/windows/${probe}.cpp)
target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include") 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() endif()
install(TARGETS d4r_${probe} RUNTIME DESTINATION bin) install(TARGETS d4r_${probe} RUNTIME DESTINATION bin)
endforeach() 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") 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_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_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) if(MINGW)
target_link_options(d4r_nvngx_windows PRIVATE -static) target_link_options(d4r_nvngx_windows PRIVATE -static)
endif() endif()
@@ -132,6 +138,8 @@ endif()
install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat) 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_hip_interop_probe PRIVATE d3d12 dxgi dxguid)
target_link_libraries(d4r_d3d12_dlss_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_hip_gfx1201_probe d4r_probe_code_object)
add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object) add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object)
add_dependencies(d4r_k_module_probe d4r_native_gfx1201_compile) 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 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) install(FILES docs/windows-rdna4-port.md DESTINATION docs)
include(CTest) include(CTest)
add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --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 add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}") --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}")
set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201") set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201")
+56 -2
View File
@@ -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 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 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. 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 found rare attention P/V accumulation mismatches that the earlier twelve-block
sampling missed. `SWIN_EXACT_PV` fixes the native baseline; ZLUDA patch `0013` sampling missed. `SWIN_EXACT_PV` fixes the native baseline; ZLUDA patch `0013`
adds the corresponding opt-in f16 reference accumulation. All spatial blocks in 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 | | 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 | | 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 | | 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 | | M9: OptiScaler integration, profiling, installation | Pending |
## M temporal precision regression (2026-09-30) ## 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`. `D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe`.
No game files have been modified at this milestone. 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) ## Source baseline (checked 2026-09-29)
* d4r: `ed1ab60`, main, 14 commits; https://github.com/countervolts/d4r. * d4r: `ed1ab60`, main, 14 commits; https://github.com/countervolts/d4r.
+10
View File
@@ -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.' 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 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 $originalPath = $env:PATH
try { try {
$configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', $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" } if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" }
$env:PATH = "$(Join-Path $ToolchainRoot 'bin');$originalPath" $env:PATH = "$(Join-Path $ToolchainRoot 'bin');$originalPath"
$configure += "-DCMAKE_CXX_COMPILER=$compiler" $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" } if ($ZludaRoot) { $configure += "-DD4R_ZLUDA_ROOT=$ZludaRoot" }
$ErrorActionPreference = 'Continue' $ErrorActionPreference = 'Continue'
+5
View File
@@ -12,6 +12,7 @@ param(
[switch]$NgxOnly, [switch]$NgxOnly,
[switch]$Trace, [switch]$Trace,
[switch]$RequireNativeNetwork, [switch]$RequireNativeNetwork,
[switch]$CommandListBackend,
[int]$Iterations = 32, [int]$Iterations = 32,
[int]$TimeoutSeconds = 180 [int]$TimeoutSeconds = 180
) )
@@ -210,6 +211,9 @@ try {
if ($cudaOk) { if ($cudaOk) {
$mapOk = $interopOk = $true $mapOk = $interopOk = $true
if (!$NgxOnly) { 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') @( $mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64') '--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64')
$interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( $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' $localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll'
Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim
$ngxArguments += @('--module', $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' } $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 $ngxOk = Invoke-Probe $ngxName $ngxExe $ngxArguments
+320
View File
@@ -0,0 +1,320 @@
#include "d3d12_command_hooks.h"
#include <MinHook.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
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<uint8_t> data; };
using State = std::function<void(ID3D12GraphicsCommandList*)>;
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<ID3D12GraphicsCommandList> owner;
ComPtr<ID3D12CommandAllocator> allocator;
Boundary boundary;
};
struct Recording {
std::recursive_mutex mutex;
ID3D12GraphicsCommandList* original;
ComPtr<ID3D12GraphicsCommandList> current;
ComPtr<ID3D12CommandAllocator> current_allocator;
std::vector<Segment> segments;
std::vector<State> state;
std::vector<Event> events;
std::unordered_map<ID3D12Resource*, D3D12_RESOURCE_STATES> 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<ID3D12GraphicsCommandList*, std::shared_ptr<Recording>> recordings;
std::shared_ptr<Recording> get_recording(ID3D12GraphicsCommandList* self, bool create = true) {
if (internal) return {};
std::lock_guard<std::mutex> lock(map_mutex);
auto found = recordings.find(self);
if (found != recordings.end()) return found->second;
if (!create) return {};
auto value = std::make_shared<Recording>(self);
recordings.emplace(self, value); return value;
}
void forget(ID3D12GraphicsCommandList* self) {
std::shared_ptr<Recording> released;
{ std::lock_guard<std::mutex> 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> recording;
std::unique_lock<std::recursive_mutex> lock;
ID3D12GraphicsCommandList* self;
explicit Access(ID3D12GraphicsCommandList* list) : recording(get_recording(list)), self(list) {
if (recording) lock = std::unique_lock<std::recursive_mutex>(recording->mutex);
}
ID3D12GraphicsCommandList* target() const {
return recording && recording->current ? recording->current.Get() : self;
}
};
template<class T> ComPtr<T> retain(T* pointer) { return ComPtr<T>(pointer); }
template<class T> std::vector<T> copy_values(const T* pointer, size_t count) {
return pointer ? std::vector<T>(pointer, pointer + count) : std::vector<T>();
}
template<class T> T* values_pointer(std::vector<T>& values) { return values.empty() ? nullptr : values.data(); }
template<class T> struct RetainedArray { std::vector<ComPtr<T>> owners; std::vector<T*> raw; };
template<class T> RetainedArray<T> retain_values(T* const* pointer, size_t count) {
RetainedArray<T> 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<Hook> 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<ID3D12GraphicsCommandList*>(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<const uint8_t*>(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<std::recursive_mutex> 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<ID3D12Device> device; dx(queue->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Queue device(submit drain)");
ComPtr<ID3D12Fence> 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<std::shared_ptr<Recording>> owners;
std::vector<ID3D12CommandList*> 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> recording;
{ std::lock_guard<std::mutex> lock(map_mutex);
auto found = recordings.find(static_cast<ID3D12GraphicsCommandList*>(lists[i]));
if (found != recordings.end()) recording = found->second; }
if (!recording || recording->segments.empty()) { pending.push_back(lists[i]); continue; }
std::lock_guard<std::recursive_mutex> 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<std::mutex> 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<std::mutex> lock(error_mutex); return last_error; }
size_t live_recordings() { std::lock_guard<std::mutex> 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<std::recursive_mutex> 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<std::mutex> 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<LPCWSTR>(&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<ID3D12CommandAllocator> allocator; ComPtr<ID3D12GraphicsCommandList> 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<IUnknown> 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<void**>(newest.ReleaseAndGetAddressOf())))) version = n;
auto table = *reinterpret_cast<void***>(list.Get());
attach_recording_methods(table, version);
attach_method(table[2], reinterpret_cast<void*>(hook_release), reinterpret_cast<void**>(&original_release), "Release");
D3D12_COMMAND_QUEUE_DESC desc{}; desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
ComPtr<ID3D12CommandQueue> queue; dx(device->CreateCommandQueue(&desc, IID_PPV_ARGS(queue.GetAddressOf())), "Create hook queue");
auto queueTable = *reinterpret_cast<void***>(queue.Get());
// Queue inherits IUnknown, Object, DeviceChild; UpdateTileMappings,
// CopyTileMappings precede ExecuteCommandLists in the public SDK.
attach_method(queueTable[10], reinterpret_cast<void*>(hook_execute), reinterpret_cast<void**>(&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<ID3D12Device> 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<ID3D12CommandAllocator> nextAllocator; ComPtr<ID3D12GraphicsCommandList> 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());
}
}
@@ -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<const UINT*>(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<const UINT*>(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<void*>(hook_Close), reinterpret_cast<void**>(&original_Close), "Close");
if (version >= 0) attach_method(table[10], reinterpret_cast<void*>(hook_Reset), reinterpret_cast<void**>(&original_Reset), "Reset");
if (version >= 0) attach_method(table[11], reinterpret_cast<void*>(hook_ClearState), reinterpret_cast<void**>(&original_ClearState), "ClearState");
if (version >= 0) attach_method(table[12], reinterpret_cast<void*>(hook_DrawInstanced), reinterpret_cast<void**>(&original_DrawInstanced), "DrawInstanced");
if (version >= 0) attach_method(table[13], reinterpret_cast<void*>(hook_DrawIndexedInstanced), reinterpret_cast<void**>(&original_DrawIndexedInstanced), "DrawIndexedInstanced");
if (version >= 0) attach_method(table[14], reinterpret_cast<void*>(hook_Dispatch), reinterpret_cast<void**>(&original_Dispatch), "Dispatch");
if (version >= 0) attach_method(table[15], reinterpret_cast<void*>(hook_CopyBufferRegion), reinterpret_cast<void**>(&original_CopyBufferRegion), "CopyBufferRegion");
if (version >= 0) attach_method(table[16], reinterpret_cast<void*>(hook_CopyTextureRegion), reinterpret_cast<void**>(&original_CopyTextureRegion), "CopyTextureRegion");
if (version >= 0) attach_method(table[17], reinterpret_cast<void*>(hook_CopyResource), reinterpret_cast<void**>(&original_CopyResource), "CopyResource");
if (version >= 0) attach_method(table[18], reinterpret_cast<void*>(hook_CopyTiles), reinterpret_cast<void**>(&original_CopyTiles), "CopyTiles");
if (version >= 0) attach_method(table[19], reinterpret_cast<void*>(hook_ResolveSubresource), reinterpret_cast<void**>(&original_ResolveSubresource), "ResolveSubresource");
if (version >= 0) attach_method(table[20], reinterpret_cast<void*>(hook_IASetPrimitiveTopology), reinterpret_cast<void**>(&original_IASetPrimitiveTopology), "IASetPrimitiveTopology");
if (version >= 0) attach_method(table[21], reinterpret_cast<void*>(hook_RSSetViewports), reinterpret_cast<void**>(&original_RSSetViewports), "RSSetViewports");
if (version >= 0) attach_method(table[22], reinterpret_cast<void*>(hook_RSSetScissorRects), reinterpret_cast<void**>(&original_RSSetScissorRects), "RSSetScissorRects");
if (version >= 0) attach_method(table[23], reinterpret_cast<void*>(hook_OMSetBlendFactor), reinterpret_cast<void**>(&original_OMSetBlendFactor), "OMSetBlendFactor");
if (version >= 0) attach_method(table[24], reinterpret_cast<void*>(hook_OMSetStencilRef), reinterpret_cast<void**>(&original_OMSetStencilRef), "OMSetStencilRef");
if (version >= 0) attach_method(table[25], reinterpret_cast<void*>(hook_SetPipelineState), reinterpret_cast<void**>(&original_SetPipelineState), "SetPipelineState");
if (version >= 0) attach_method(table[26], reinterpret_cast<void*>(hook_ResourceBarrier), reinterpret_cast<void**>(&original_ResourceBarrier), "ResourceBarrier");
if (version >= 0) attach_method(table[27], reinterpret_cast<void*>(hook_ExecuteBundle), reinterpret_cast<void**>(&original_ExecuteBundle), "ExecuteBundle");
if (version >= 0) attach_method(table[28], reinterpret_cast<void*>(hook_SetDescriptorHeaps), reinterpret_cast<void**>(&original_SetDescriptorHeaps), "SetDescriptorHeaps");
if (version >= 0) attach_method(table[29], reinterpret_cast<void*>(hook_SetComputeRootSignature), reinterpret_cast<void**>(&original_SetComputeRootSignature), "SetComputeRootSignature");
if (version >= 0) attach_method(table[30], reinterpret_cast<void*>(hook_SetGraphicsRootSignature), reinterpret_cast<void**>(&original_SetGraphicsRootSignature), "SetGraphicsRootSignature");
if (version >= 0) attach_method(table[31], reinterpret_cast<void*>(hook_SetComputeRootDescriptorTable), reinterpret_cast<void**>(&original_SetComputeRootDescriptorTable), "SetComputeRootDescriptorTable");
if (version >= 0) attach_method(table[32], reinterpret_cast<void*>(hook_SetGraphicsRootDescriptorTable), reinterpret_cast<void**>(&original_SetGraphicsRootDescriptorTable), "SetGraphicsRootDescriptorTable");
if (version >= 0) attach_method(table[33], reinterpret_cast<void*>(hook_SetComputeRoot32BitConstant), reinterpret_cast<void**>(&original_SetComputeRoot32BitConstant), "SetComputeRoot32BitConstant");
if (version >= 0) attach_method(table[34], reinterpret_cast<void*>(hook_SetGraphicsRoot32BitConstant), reinterpret_cast<void**>(&original_SetGraphicsRoot32BitConstant), "SetGraphicsRoot32BitConstant");
if (version >= 0) attach_method(table[35], reinterpret_cast<void*>(hook_SetComputeRoot32BitConstants), reinterpret_cast<void**>(&original_SetComputeRoot32BitConstants), "SetComputeRoot32BitConstants");
if (version >= 0) attach_method(table[36], reinterpret_cast<void*>(hook_SetGraphicsRoot32BitConstants), reinterpret_cast<void**>(&original_SetGraphicsRoot32BitConstants), "SetGraphicsRoot32BitConstants");
if (version >= 0) attach_method(table[37], reinterpret_cast<void*>(hook_SetComputeRootConstantBufferView), reinterpret_cast<void**>(&original_SetComputeRootConstantBufferView), "SetComputeRootConstantBufferView");
if (version >= 0) attach_method(table[38], reinterpret_cast<void*>(hook_SetGraphicsRootConstantBufferView), reinterpret_cast<void**>(&original_SetGraphicsRootConstantBufferView), "SetGraphicsRootConstantBufferView");
if (version >= 0) attach_method(table[39], reinterpret_cast<void*>(hook_SetComputeRootShaderResourceView), reinterpret_cast<void**>(&original_SetComputeRootShaderResourceView), "SetComputeRootShaderResourceView");
if (version >= 0) attach_method(table[40], reinterpret_cast<void*>(hook_SetGraphicsRootShaderResourceView), reinterpret_cast<void**>(&original_SetGraphicsRootShaderResourceView), "SetGraphicsRootShaderResourceView");
if (version >= 0) attach_method(table[41], reinterpret_cast<void*>(hook_SetComputeRootUnorderedAccessView), reinterpret_cast<void**>(&original_SetComputeRootUnorderedAccessView), "SetComputeRootUnorderedAccessView");
if (version >= 0) attach_method(table[42], reinterpret_cast<void*>(hook_SetGraphicsRootUnorderedAccessView), reinterpret_cast<void**>(&original_SetGraphicsRootUnorderedAccessView), "SetGraphicsRootUnorderedAccessView");
if (version >= 0) attach_method(table[43], reinterpret_cast<void*>(hook_IASetIndexBuffer), reinterpret_cast<void**>(&original_IASetIndexBuffer), "IASetIndexBuffer");
if (version >= 0) attach_method(table[44], reinterpret_cast<void*>(hook_IASetVertexBuffers), reinterpret_cast<void**>(&original_IASetVertexBuffers), "IASetVertexBuffers");
if (version >= 0) attach_method(table[45], reinterpret_cast<void*>(hook_SOSetTargets), reinterpret_cast<void**>(&original_SOSetTargets), "SOSetTargets");
if (version >= 0) attach_method(table[46], reinterpret_cast<void*>(hook_OMSetRenderTargets), reinterpret_cast<void**>(&original_OMSetRenderTargets), "OMSetRenderTargets");
if (version >= 0) attach_method(table[47], reinterpret_cast<void*>(hook_ClearDepthStencilView), reinterpret_cast<void**>(&original_ClearDepthStencilView), "ClearDepthStencilView");
if (version >= 0) attach_method(table[48], reinterpret_cast<void*>(hook_ClearRenderTargetView), reinterpret_cast<void**>(&original_ClearRenderTargetView), "ClearRenderTargetView");
if (version >= 0) attach_method(table[49], reinterpret_cast<void*>(hook_ClearUnorderedAccessViewUint), reinterpret_cast<void**>(&original_ClearUnorderedAccessViewUint), "ClearUnorderedAccessViewUint");
if (version >= 0) attach_method(table[50], reinterpret_cast<void*>(hook_ClearUnorderedAccessViewFloat), reinterpret_cast<void**>(&original_ClearUnorderedAccessViewFloat), "ClearUnorderedAccessViewFloat");
if (version >= 0) attach_method(table[51], reinterpret_cast<void*>(hook_DiscardResource), reinterpret_cast<void**>(&original_DiscardResource), "DiscardResource");
if (version >= 0) attach_method(table[52], reinterpret_cast<void*>(hook_BeginQuery), reinterpret_cast<void**>(&original_BeginQuery), "BeginQuery");
if (version >= 0) attach_method(table[53], reinterpret_cast<void*>(hook_EndQuery), reinterpret_cast<void**>(&original_EndQuery), "EndQuery");
if (version >= 0) attach_method(table[54], reinterpret_cast<void*>(hook_ResolveQueryData), reinterpret_cast<void**>(&original_ResolveQueryData), "ResolveQueryData");
if (version >= 0) attach_method(table[55], reinterpret_cast<void*>(hook_SetPredication), reinterpret_cast<void**>(&original_SetPredication), "SetPredication");
if (version >= 0) attach_method(table[56], reinterpret_cast<void*>(hook_SetMarker), reinterpret_cast<void**>(&original_SetMarker), "SetMarker");
if (version >= 0) attach_method(table[57], reinterpret_cast<void*>(hook_BeginEvent), reinterpret_cast<void**>(&original_BeginEvent), "BeginEvent");
if (version >= 0) attach_method(table[58], reinterpret_cast<void*>(hook_EndEvent), reinterpret_cast<void**>(&original_EndEvent), "EndEvent");
if (version >= 0) attach_method(table[59], reinterpret_cast<void*>(hook_ExecuteIndirect), reinterpret_cast<void**>(&original_ExecuteIndirect), "ExecuteIndirect");
if (version >= 1) attach_method(table[60], reinterpret_cast<void*>(hook_AtomicCopyBufferUINT), reinterpret_cast<void**>(&original_AtomicCopyBufferUINT), "AtomicCopyBufferUINT");
if (version >= 1) attach_method(table[61], reinterpret_cast<void*>(hook_AtomicCopyBufferUINT64), reinterpret_cast<void**>(&original_AtomicCopyBufferUINT64), "AtomicCopyBufferUINT64");
if (version >= 1) attach_method(table[62], reinterpret_cast<void*>(hook_OMSetDepthBounds), reinterpret_cast<void**>(&original_OMSetDepthBounds), "OMSetDepthBounds");
if (version >= 1) attach_method(table[63], reinterpret_cast<void*>(hook_SetSamplePositions), reinterpret_cast<void**>(&original_SetSamplePositions), "SetSamplePositions");
if (version >= 1) attach_method(table[64], reinterpret_cast<void*>(hook_ResolveSubresourceRegion), reinterpret_cast<void**>(&original_ResolveSubresourceRegion), "ResolveSubresourceRegion");
if (version >= 1) attach_method(table[65], reinterpret_cast<void*>(hook_SetViewInstanceMask), reinterpret_cast<void**>(&original_SetViewInstanceMask), "SetViewInstanceMask");
if (version >= 2) attach_method(table[66], reinterpret_cast<void*>(hook_WriteBufferImmediate), reinterpret_cast<void**>(&original_WriteBufferImmediate), "WriteBufferImmediate");
if (version >= 3) attach_method(table[67], reinterpret_cast<void*>(hook_SetProtectedResourceSession), reinterpret_cast<void**>(&original_SetProtectedResourceSession), "SetProtectedResourceSession");
if (version >= 4) attach_method(table[68], reinterpret_cast<void*>(hook_BeginRenderPass), reinterpret_cast<void**>(&original_BeginRenderPass), "BeginRenderPass");
if (version >= 4) attach_method(table[69], reinterpret_cast<void*>(hook_EndRenderPass), reinterpret_cast<void**>(&original_EndRenderPass), "EndRenderPass");
if (version >= 4) attach_method(table[70], reinterpret_cast<void*>(hook_InitializeMetaCommand), reinterpret_cast<void**>(&original_InitializeMetaCommand), "InitializeMetaCommand");
if (version >= 4) attach_method(table[71], reinterpret_cast<void*>(hook_ExecuteMetaCommand), reinterpret_cast<void**>(&original_ExecuteMetaCommand), "ExecuteMetaCommand");
if (version >= 4) attach_method(table[72], reinterpret_cast<void*>(hook_BuildRaytracingAccelerationStructure), reinterpret_cast<void**>(&original_BuildRaytracingAccelerationStructure), "BuildRaytracingAccelerationStructure");
if (version >= 4) attach_method(table[73], reinterpret_cast<void*>(hook_EmitRaytracingAccelerationStructurePostbuildInfo), reinterpret_cast<void**>(&original_EmitRaytracingAccelerationStructurePostbuildInfo), "EmitRaytracingAccelerationStructurePostbuildInfo");
if (version >= 4) attach_method(table[74], reinterpret_cast<void*>(hook_CopyRaytracingAccelerationStructure), reinterpret_cast<void**>(&original_CopyRaytracingAccelerationStructure), "CopyRaytracingAccelerationStructure");
if (version >= 4) attach_method(table[75], reinterpret_cast<void*>(hook_SetPipelineState1), reinterpret_cast<void**>(&original_SetPipelineState1), "SetPipelineState1");
if (version >= 4) attach_method(table[76], reinterpret_cast<void*>(hook_DispatchRays), reinterpret_cast<void**>(&original_DispatchRays), "DispatchRays");
if (version >= 5) attach_method(table[77], reinterpret_cast<void*>(hook_RSSetShadingRate), reinterpret_cast<void**>(&original_RSSetShadingRate), "RSSetShadingRate");
if (version >= 5) attach_method(table[78], reinterpret_cast<void*>(hook_RSSetShadingRateImage), reinterpret_cast<void**>(&original_RSSetShadingRateImage), "RSSetShadingRateImage");
if (version >= 6) attach_method(table[79], reinterpret_cast<void*>(hook_DispatchMesh), reinterpret_cast<void**>(&original_DispatchMesh), "DispatchMesh");
if (version >= 7) attach_method(table[80], reinterpret_cast<void*>(hook_Barrier), reinterpret_cast<void**>(&original_Barrier), "Barrier");
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "d3d12_external.h"
#include <functional>
#include <string>
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(ID3D12CommandQueue*)>;
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();
};
}
+163
View File
@@ -0,0 +1,163 @@
#include "d3d12_external.h"
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <array>
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<ID3D12Fence> 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<ID3D12CommandAllocator> allocator;
ComPtr<ID3D12GraphicsCommandList> 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<ID3D12Resource> 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<ID3D12Resource> 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<uint32_t*>(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<const uint32_t*>(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<Callback*>(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<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI");
ComPtr<IDXGIAdapter1> 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<ID3D12Device> device;
dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "Probe D3D12");
ComPtr<ID3D12CommandQueue> 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<decltype(&D3DCompile)>("D3DCompile");
static constexpr char shader[] = "RWStructuredBuffer<uint> Output : register(u0); StructuredBuffer<uint> 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<ID3DBlob> 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<ID3DBlob> serialized;
dx(D3D12SerializeRootSignature(&signature, D3D_ROOT_SIGNATURE_VERSION_1, serialized.GetAddressOf(), errors.ReleaseAndGetAddressOf()), "Probe root signature serialize");
ComPtr<ID3D12RootSignature> 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<ID3D12PipelineState> 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<Install>("d4r_D3D12_InstallCommandBackend")(device.Get()) != 1) throw std::runtime_error("Command backend installation failed");
auto record = shim.symbol<Record>("d4r_D3D12_RecordDiagnosticBoundary");
auto live = shim.symbol<Live>("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;
}
}
+34 -5
View File
@@ -16,6 +16,8 @@ int main(int argc, char** argv) {
if (!diagDir && args.interop_mode != "images") throw std::runtime_error("Set D4R_DIAG_DIR"); 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()); const std::filesystem::path directory(diagDir ? wide(diagDir) : std::filesystem::current_path().wstring());
std::filesystem::create_directories(directory); 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), 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_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)}) 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); i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
} }
dx(list->Close(), "Close(synthetic producers)"); ID3D12CommandList* producer[] = {list.Get()}; 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") { if (args.interop_mode == "images") {
CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit(image interop)"); CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit(image interop)");
CUdevice ordinal = 0; CUcontext context = nullptr; 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()); 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); 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); auto readback = make_buffer(device.Get(), textures[3].bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
ComPtr<ID3D12GraphicsCommandList> predecessor;
ComPtr<ID3D12CommandAllocator> 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) { for (unsigned frame = 0; frame < args.iterations; ++frame) {
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0); d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
check(shim.symbol<Evaluate>("d4r_D3D12_EvaluateAtBoundary")(queue.Get(), handle, parameters), "D3D12 same-frame Evaluate"); if (commandBackend) {
dx(allocator->Reset(), "Reset(harness allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(harness list)"); 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<RecordedEvaluate>("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<Evaluate>("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); transition(list.Get(), textures[3].image.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
D3D12_TEXTURE_COPY_LOCATION src{}, dst{}; D3D12_TEXTURE_COPY_LOCATION src{}, dst{};
src.pResource = textures[3].image.Get(); src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 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; 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); 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); 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)}; void* raw = nullptr; D3D12_RANGE range{0, size_t(textures[3].bytes)};
dx(readback->Map(0, &range, &raw), "Map(verification only)"); dx(readback->Map(0, &range, &raw), "Map(verification only)");
std::vector<uint16_t> pixels(size_t(outWidth) * outHeight * 4); std::vector<uint16_t> 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"); if (variance < 1e-6) throw std::runtime_error("D3D12 output lost the synthetic pattern");
} }
loaded_modules(); 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; return 0;
} catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; } } catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; }
} }
+40 -4
View File
@@ -44,6 +44,8 @@ API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* dat
std::lock_guard<std::mutex> lock(apiMutex); std::lock_guard<std::mutex> lock(apiMutex);
return call([&] { return call([&] {
if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, app, data, sdk); if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(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; 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; } auto it = features.find(handle); if (it == features.end() || !p) return invalid; feature = it->second; }
return call([&] { feature->evaluate_boundary(queue, p); return 1u; }); return call([&] { feature->evaluate_boundary(queue, p); return 1u; });
} }
API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList*, const Handle*, void*, void*) { API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, const Handle* handle, void* p, void*) {
// Explicitly refuse an unsubmitted game list until queue integration exists. std::shared_ptr<d4r::win::Feature> feature;
std::fprintf(stderr, "D4R_WINDOWS_FAILURE command-list queue backend pending; no frame-age fallback\n"); { std::lock_guard<std::mutex> lock(apiMutex);
return failure; auto it = features.find(const_cast<Handle*>(handle));
if (it == features.end() || !p || !list) return invalid; feature = it->second; }
return call([&] {
struct Snapshot {
void* parameters = d4r_ngx_parameters_create();
std::vector<d4r::win::ComPtr<ID3D12Resource>> resources;
~Snapshot() { if (parameters) d4r_ngx_parameters_destroy(parameters); }
};
auto snapshot = std::make_shared<Snapshot>();
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) { 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); 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(); } 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(); }
+101
View File
@@ -0,0 +1,101 @@
"""Generate typed command forwarding from public MinGW D3D12 SDK declarations.
Run with --header <SDK/include/d3d12.h> --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<const UINT*>({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<void*>(hook_{name}), reinterpret_cast<void**>(&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')
+2
View File
@@ -2,6 +2,7 @@
#include "d3d12_external.h" #include "d3d12_external.h"
#include "cuda_image_api.h" #include "cuda_image_api.h"
#include "ngx_parameters.h" #include "ngx_parameters.h"
#include "d3d12_command_hooks.h"
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -160,6 +161,7 @@ public:
// Explicit harness boundary: all earlier producers must already be queued. // Explicit harness boundary: all earlier producers must already be queued.
// The forthcoming command-list backend calls the same stages at queue submit. // The forthcoming command-list backend calls the same stages at queue submit.
void evaluate_boundary(ID3D12CommandQueue* queue, void* parameters) { void evaluate_boundary(ID3D12CommandQueue* queue, void* parameters) {
commands::InternalScope internal;
std::lock_guard<std::mutex> lock(rt_->mutex); std::lock_guard<std::mutex> lock(rt_->mutex);
if (!queue) throw std::runtime_error("Null D3D12 queue"); 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"); if (queue_ && queue_.Get() != queue) throw std::runtime_error("A feature cannot switch queues without an explicit drain");