Track Windows D3D12 indirect state and newer command interfaces
This commit is contained in:
1 parent
63607298ea
commit
312c6bb50d
8 files changed
+478
-24
No files matched your search
+19
-1
@@ -140,6 +140,20 @@ 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)
|
target_link_libraries(d4r_d3d12_command_probe PRIVATE d3d12 dxgi dxguid)
|
||||||
add_dependencies(d4r_d3d12_command_probe d4r_nvngx_windows)
|
add_dependencies(d4r_d3d12_command_probe d4r_nvngx_windows)
|
||||||
|
set(D4R_AGILITY_ROOT "" CACHE PATH "Optional unpacked Microsoft.Direct3D.D3D12 1.619.5 debug SDK")
|
||||||
|
if(EXISTS "${D4R_AGILITY_ROOT}/build/native/bin/x64/d3d12SDKLayers.dll")
|
||||||
|
add_executable(d4r_d3d12_command_debug_probe EXCLUDE_FROM_ALL tools/windows/d3d12_command_probe.cpp)
|
||||||
|
target_compile_definitions(d4r_d3d12_command_debug_probe PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__ D4R_DIAGNOSTIC_AGILITY)
|
||||||
|
target_include_directories(d4r_d3d12_command_debug_probe PRIVATE "${D4R_HIP_ROOT}/include")
|
||||||
|
target_link_libraries(d4r_d3d12_command_debug_probe PRIVATE version dbghelp d3d12 dxgi dxguid)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_d3d12_command_debug_probe PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
foreach(dll D3D12Core.dll d3d12SDKLayers.dll)
|
||||||
|
configure_file("${D4R_AGILITY_ROOT}/build/native/bin/x64/${dll}" "${CMAKE_CURRENT_BINARY_DIR}/debug-d3d12/${dll}" COPYONLY)
|
||||||
|
endforeach()
|
||||||
|
configure_file("${D4R_AGILITY_ROOT}/LICENSE.txt" "${CMAKE_CURRENT_BINARY_DIR}/debug-d3d12/LICENSE.txt" COPYONLY)
|
||||||
|
endif()
|
||||||
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)
|
||||||
@@ -164,8 +178,12 @@ install(FILES kernels/tools/model_enc3.py kernels/tools/swin_model.py kernels/to
|
|||||||
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
|
add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe
|
||||||
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --iterations 32)
|
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode indirect --iterations 32)
|
||||||
set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime")
|
set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime")
|
||||||
|
add_test(NAME d3d12_command_root_reset_warp COMMAND d4r_d3d12_command_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode indirect-root-reset --iterations 32)
|
||||||
|
set_tests_properties(d3d12_command_root_reset_warp PROPERTIES TIMEOUT 120 LABELS "software;d3d12;command_lists;lifetime"
|
||||||
|
ENVIRONMENT "D4R_COMMAND_PROBE_WARP=1;D4R_COMMAND_PROBE_NO_HIP=1")
|
||||||
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")
|
||||||
|
|||||||
@@ -102,10 +102,14 @@ 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
|
[MinHook 1.3.4](https://github.com/TsudaKageyu/minhook/releases/tag/v1.3.4), commit
|
||||||
`c3fcafdc10146beb5919319d0683e44e3c30d537`. No NVIDIA instructions, proprietary
|
`c3fcafdc10146beb5919319d0683e44e3c30d537`. No NVIDIA instructions, proprietary
|
||||||
DLL contents or private driver interfaces are patched. The typed forwarding
|
DLL contents or private driver interfaces are patched. The typed forwarding
|
||||||
code is generated from public SDK CommandList0..7 declarations. The additional
|
code is generated from public SDK CommandList0..7 declarations, with typed
|
||||||
CommandList8..10 declarations were audited in Microsoft's current public
|
CommandList8..10 additions audited in Microsoft's current public
|
||||||
DirectX-Headers, `adbd6f3ba40795c46a8d0f33af00bcb57ff0f0a4`; coverage of those
|
DirectX-Headers, `adbd6f3ba40795c46a8d0f33af00bcb57ff0f0a4`. The AMD runtime
|
||||||
newer methods is the next implementation step.
|
exposes CommandList10: eighty public-method hooks now install successfully.
|
||||||
|
SetProgram descriptors are copied by value. Work-graph replay omits INITIALIZE
|
||||||
|
to preserve already initialized backing memory; see the
|
||||||
|
[Microsoft work-graph specification](https://microsoft.github.io/DirectX-Specs/d3d/WorkGraphs.html#d3d12_set_work_graph_flags).
|
||||||
|
Full GPU work-graph execution is not a tested gate yet.
|
||||||
|
|
||||||
At NGX Evaluate, the caller's resource references and typed parameter values are
|
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
|
copied into an owned snapshot. The recorded prefix is closed. Subsequent calls
|
||||||
@@ -142,11 +146,61 @@ frames are bit-exact against `d3d12-k-final`. Complete CTest: 12/12 PASS
|
|||||||
|
|
||||||
Enable this backend with `D4R_D3D12_COMMAND_BACKEND=1`; the diagnostic runner
|
Enable this backend with `D4R_D3D12_COMMAND_BACKEND=1`; the diagnostic runner
|
||||||
sets it through `-CommandListBackend`. Before game testing, remaining gates are
|
sets it through `-CommandListBackend`. Before game testing, remaining gates are
|
||||||
newer command methods, indirect-state reconstruction, enhanced barrier layouts,
|
enhanced barrier layouts,
|
||||||
actual game texture formats and interaction with OptiScaler's own state hooks.
|
actual game texture formats and interaction with OptiScaler's own state hooks.
|
||||||
Closed lists, active render/query scopes, GPU predication and unknown indirect
|
Closed lists, active render/query scopes, GPU predication and unknown indirect
|
||||||
state are currently explicitly rejected instead of guessing their semantics.
|
state are currently explicitly rejected instead of guessing their semantics.
|
||||||
|
|
||||||
|
## Indirect commands and Windows diagnostics (2026-10-01)
|
||||||
|
|
||||||
|
CreateCommandSignature interception stores its public argument description
|
||||||
|
using ID3D12Object::SetPrivateData, owned by the original COM object. There is
|
||||||
|
no driver-private introspection or independent signature-lifetime table.
|
||||||
|
After ExecuteIndirect, replay restores exactly the documented zero/NULL values
|
||||||
|
for affected root constants, views, VB slots and IB; unaffected state remains
|
||||||
|
inherited. See [Microsoft's indirect drawing specification](https://learn.microsoft.com/en-us/windows/win32/direct3d12/indirect-drawing).
|
||||||
|
Signatures created before interception remain explicitly untracked.
|
||||||
|
|
||||||
|
The RX 9070 XT passes 32 queue/backend iterations alternating direct Dispatch
|
||||||
|
and dispatch-only ExecuteIndirect. Bindings, ordering, temporary host argument
|
||||||
|
copies and final Release are verified (`d3d12-indirect-plain-final`). Microsoft
|
||||||
|
WARP passes 32 backend iterations with an indirect signature that changes a
|
||||||
|
root constant, followed by direct Dispatch without rebinding: both prefix and
|
||||||
|
suffix observe the required zero (`d3d12-indirect-root-warp-backend.log`).
|
||||||
|
WARP is reported as software, never as gfx1201.
|
||||||
|
CTest now passes 13/13 (twelve physical GPU gates plus the WARP root-reset
|
||||||
|
regression), `d3d12-command-v10-final-ctest.log`. Four ordinary recorded M
|
||||||
|
frames still execute forty native transformer launches and remain bit-exact
|
||||||
|
against the previous CommandList7 implementation (`d3d12-m-command-v10.zip`,
|
||||||
|
`d3d12-m-command-v10-reference.log`).
|
||||||
|
|
||||||
|
**Unresolved native AMD root-mutating indirect regression:** that same valid
|
||||||
|
root-constant/Dispatch signature removes the AMD device (0x887a0006). It also
|
||||||
|
fails with d4r hooks disabled and with HIP omitted, under inbox D3D12Core and
|
||||||
|
Agility 1.619.5, with UPLOAD and DEFAULT argument buffers. DRED stops inside
|
||||||
|
ExecuteIndirect and reports 0x80015c000 when the argument-buffer GPU VA is
|
||||||
|
0x200057000. The exact fault address is four times that allocation's VA; this
|
||||||
|
is an observation, not a proven driver cause. This workload passes on WARP.
|
||||||
|
Do not classify native root-mutating indirect execution as validated. The
|
||||||
|
default physical test covers dispatch-only signatures; the root mutation case
|
||||||
|
is a separate reproducible diagnostic:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -NoProfile -File scripts/windows/test-d3d12-command.ps1 -NoHooks -NoHip -Mode indirect-root-reset -Iterations 2 -OutputDirectory test-results/amd-root-indirect-reproducer
|
||||||
|
```
|
||||||
|
|
||||||
|
Logs retain stdout/stderr, device-removed reason, DRED breadcrumbs/fault address,
|
||||||
|
loaded DLL versions and exception minidumps via D4R_DIAG_DIR. `-Warp` selects
|
||||||
|
the software comparison. No game or NVIDIA DLL is required.
|
||||||
|
|
||||||
|
Optional app-local debug target: set D4R_AGILITY_ROOT to unpacked official
|
||||||
|
Microsoft.Direct3D.D3D12 1.619.5. NuGet ZIP SHA256:
|
||||||
|
`0e9bcf32aac9a79343ede9b21e4864950ee54577e3d8e19bfcdf002bb4e9bfd6`.
|
||||||
|
On this machine, the public debug interfaces still return SDK_COMPONENT_MISSING
|
||||||
|
despite app-local SDKLayers loading. Installing Windows Graphics Tools failed
|
||||||
|
with DISM's component-store-corrupted error. No repair, reboot, security change
|
||||||
|
or game runtime dependency was introduced. DRED works without that layer.
|
||||||
|
|
||||||
## 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.
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
param(
|
||||||
|
[int]$Iterations = 32,
|
||||||
|
[switch]$NoHooks,
|
||||||
|
[switch]$SmallSeed,
|
||||||
|
[switch]$DebugLayer,
|
||||||
|
[switch]$Warp,
|
||||||
|
[switch]$NoHip,
|
||||||
|
[ValidateSet('basic','indirect','indirect-root-reset')][string]$Mode = 'indirect',
|
||||||
|
[string]$OutputDirectory = 'test-results/d3d12-command-development'
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = (Resolve-Path "$PSScriptRoot/../..").Path
|
||||||
|
Push-Location $repo
|
||||||
|
$names = @('D4R_COMMAND_PROBE_NO_HOOKS','D4R_COMMAND_PROBE_SMALL_SEED','D4R_COMMAND_DEBUG_SDK','D4R_DIAG_DIR','D4R_COMMAND_PROBE_WARP','D4R_COMMAND_PROBE_NO_HIP')
|
||||||
|
$saved = @{}
|
||||||
|
foreach ($name in $names) { $saved[$name] = [Environment]::GetEnvironmentVariable($name,'Process') }
|
||||||
|
try {
|
||||||
|
if ($Iterations -lt 1 -or $Iterations -gt 1024) { throw 'Iterations must be 1..1024' }
|
||||||
|
$cmake = "$repo/.tools/python/cmake/data/bin/cmake.exe"
|
||||||
|
$build = "$repo/build/windows-rdna4-therock"
|
||||||
|
$hip = "$repo/.tools/therock-10.2.0a20260929/_rocm_sdk_core"
|
||||||
|
$output = [IO.Path]::GetFullPath((Join-Path $repo $OutputDirectory))
|
||||||
|
New-Item -ItemType Directory -Force -Path $output | Out-Null
|
||||||
|
$target = 'd4r_d3d12_command_probe'
|
||||||
|
if ($DebugLayer) {
|
||||||
|
$sdk = "$repo/.tools/agility-1.619.5"
|
||||||
|
if (!(Test-Path "$sdk/build/native/bin/x64/d3d12SDKLayers.dll")) { throw 'Unpack Microsoft.Direct3D.D3D12 1.619.5 into .tools/agility-1.619.5 for this optional debug probe' }
|
||||||
|
& $cmake -S . -B $build "-DD4R_AGILITY_ROOT=$sdk" > "$output/configure.log"
|
||||||
|
if ($LASTEXITCODE) { throw 'Debug probe configuration failed' }
|
||||||
|
$target = 'd4r_d3d12_command_debug_probe'
|
||||||
|
}
|
||||||
|
& $cmake --build $build --target d4r_nvngx_windows $target > "$output/build.log"
|
||||||
|
if ($LASTEXITCODE) { throw "Build failed: $output/build.log" }
|
||||||
|
[Environment]::SetEnvironmentVariable($names[0], $(if ($NoHooks) { '1' } else { $null }), 'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($names[1], $(if ($SmallSeed) { '1' } else { $null }), 'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($names[2], $(if ($DebugLayer) { '1' } else { $null }), 'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($names[3], $output, 'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($names[4], $(if ($Warp) { '1' } else { $null }), 'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($names[5], $(if ($NoHip -or $Warp) { '1' } else { $null }), 'Process')
|
||||||
|
# PowerShell 5 treats native stderr as ErrorRecord: retain it without
|
||||||
|
# converting it into a terminating exception, then check the process code.
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& "$build/$target.exe" --hip-root $hip --module "$build/d4r_nvngx.dll" --interop-mode $Mode --iterations $Iterations > "$output/stdout.log" 2> "$output/stderr.log"
|
||||||
|
$code = $LASTEXITCODE
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Get-Content "$output/stdout.log", "$output/stderr.log" | Select-String 'D3D12_VALIDATION|DRED|DEVICE_REMOVED|PROBE_GPU|DIFFERENT|FAIL|PASS|installed'
|
||||||
|
Write-Host "D3D12 command diagnostic logs: $output"
|
||||||
|
if ($code) { throw "D3D12 probe exit=$code" }
|
||||||
|
} finally {
|
||||||
|
foreach ($name in $names) { [Environment]::SetEnvironmentVariable($name,$saved[$name],'Process') }
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "d3d12_command_hooks.h"
|
#include "d3d12_command_hooks.h"
|
||||||
|
#include "d3d12_newer_commands.h"
|
||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -87,6 +88,40 @@ void attach_method(void* target, void* replacement, void** original, const char*
|
|||||||
|
|
||||||
#include "d3d12_command_hooks.generated.h"
|
#include "d3d12_command_hooks.generated.h"
|
||||||
|
|
||||||
|
static constexpr GUID signature_description_tag = {0xbdda6148,0x46f0,0x4d82,{0xa1,0x90,0x08,0x47,0x7e,0xda,0x32,0x12}};
|
||||||
|
using SignatureFn = HRESULT(STDMETHODCALLTYPE*)(ID3D12Device*, const D3D12_COMMAND_SIGNATURE_DESC*, ID3D12RootSignature*, REFIID, void**);
|
||||||
|
SignatureFn original_signature = nullptr;
|
||||||
|
HRESULT STDMETHODCALLTYPE hook_signature(ID3D12Device* device, const D3D12_COMMAND_SIGNATURE_DESC* desc, ID3D12RootSignature* root, REFIID iid, void** out) {
|
||||||
|
const auto result = original_signature(device, desc, root, iid, out);
|
||||||
|
if (SUCCEEDED(result) && out && *out && desc && desc->NumArgumentDescs <= 4096) try {
|
||||||
|
ComPtr<ID3D12CommandSignature> signature;
|
||||||
|
if (SUCCEEDED(static_cast<IUnknown*>(*out)->QueryInterface(IID_PPV_ARGS(signature.GetAddressOf())))) {
|
||||||
|
std::vector<uint8_t> value(sizeof(UINT) + desc->NumArgumentDescs * sizeof(D3D12_INDIRECT_ARGUMENT_DESC));
|
||||||
|
std::memcpy(value.data(), &desc->NumArgumentDescs, sizeof(UINT));
|
||||||
|
if (desc->NumArgumentDescs)
|
||||||
|
std::memcpy(value.data() + sizeof(UINT), desc->pArgumentDescs, value.size() - sizeof(UINT));
|
||||||
|
const auto tagged = signature->SetPrivateData(signature_description_tag, UINT(value.size()), value.data());
|
||||||
|
if (FAILED(tagged)) std::fprintf(stderr, "D4R_SIGNATURE_TRACK_FAILURE result=0x%08x\n", unsigned(tagged));
|
||||||
|
}
|
||||||
|
} catch (const std::exception& failure) {
|
||||||
|
// Preserve the successful public API call. An untracked signature is
|
||||||
|
// rejected at a later split instead of unwinding into the application.
|
||||||
|
std::fprintf(stderr, "D4R_SIGNATURE_TRACK_FAILURE %s\n", failure.what());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
void STDMETHODCALLTYPE hook_SetProgram(ID3D12GraphicsCommandList* self, const NewProgramDescription* desc) {
|
||||||
|
Access access(self);
|
||||||
|
if (access.recording && desc) {
|
||||||
|
auto saved = *desc;
|
||||||
|
// INITIALIZE changes backing memory. Rebinding an already initialized
|
||||||
|
// work graph in the suffix must preserve its prefix's GPU state.
|
||||||
|
if (saved.type == 5) saved.work_graph.flags &= ~1u;
|
||||||
|
access.recording->state.emplace_back([saved](ID3D12GraphicsCommandList* target) { original_SetProgram(target, &saved); });
|
||||||
|
}
|
||||||
|
original_SetProgram(access.target(), desc);
|
||||||
|
}
|
||||||
|
|
||||||
using ReleaseFn = ULONG(STDMETHODCALLTYPE*)(IUnknown*);
|
using ReleaseFn = ULONG(STDMETHODCALLTYPE*)(IUnknown*);
|
||||||
ReleaseFn original_release = nullptr;
|
ReleaseFn original_release = nullptr;
|
||||||
ULONG STDMETHODCALLTYPE hook_release(IUnknown* self) {
|
ULONG STDMETHODCALLTYPE hook_release(IUnknown* self) {
|
||||||
@@ -183,7 +218,52 @@ void STDMETHODCALLTYPE hook_ExecuteBundle(ID3D12GraphicsCommandList* self, ID3D1
|
|||||||
void STDMETHODCALLTYPE hook_ExecuteIndirect(ID3D12GraphicsCommandList* self, ID3D12CommandSignature* command_signature,
|
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) {
|
UINT max_command_count, ID3D12Resource* arg_buffer, UINT64 arg_buffer_offset, ID3D12Resource* count_buffer, UINT64 count_buffer_offset) {
|
||||||
Access access(self);
|
Access access(self);
|
||||||
if (access.recording) access.recording->indirect_state_unknown = true;
|
if (access.recording) {
|
||||||
|
UINT bytes = 0;
|
||||||
|
(void)command_signature->GetPrivateData(signature_description_tag, &bytes, nullptr);
|
||||||
|
if (bytes < sizeof(UINT) || bytes > sizeof(UINT) + 4096 * sizeof(D3D12_INDIRECT_ARGUMENT_DESC)) {
|
||||||
|
access.recording->indirect_state_unknown = true;
|
||||||
|
} else {
|
||||||
|
std::vector<uint8_t> description(bytes);
|
||||||
|
UINT count = 0;
|
||||||
|
const auto result = command_signature->GetPrivateData(signature_description_tag, &bytes, description.data());
|
||||||
|
std::memcpy(&count, description.data(), sizeof(count));
|
||||||
|
if (FAILED(result) || count > 4096 || bytes != sizeof(UINT) + count * sizeof(D3D12_INDIRECT_ARGUMENT_DESC)) access.recording->indirect_state_unknown = true;
|
||||||
|
else {
|
||||||
|
std::vector<D3D12_INDIRECT_ARGUMENT_DESC> arguments(count);
|
||||||
|
std::memcpy(arguments.data(), description.data() + sizeof(UINT), count * sizeof(D3D12_INDIRECT_ARGUMENT_DESC));
|
||||||
|
bool compute = false;
|
||||||
|
for (const auto& arg : arguments) compute |= arg.Type == D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH || arg.Type == D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH_RAYS;
|
||||||
|
// Microsoft documents that only bindings modified by the
|
||||||
|
// signature are reset to zero/NULL after ExecuteIndirect.
|
||||||
|
access.recording->state.emplace_back([arguments, compute](ID3D12GraphicsCommandList* target) {
|
||||||
|
for (const auto& arg : arguments) switch (arg.Type) {
|
||||||
|
// Public SDK addition after the pinned MinGW header:
|
||||||
|
// INCREMENTING_CONSTANT has the same first two UINTs.
|
||||||
|
case static_cast<D3D12_INDIRECT_ARGUMENT_TYPE>(11):
|
||||||
|
(compute ? original_SetComputeRoot32BitConstant : original_SetGraphicsRoot32BitConstant)
|
||||||
|
(target, arg.Constant.RootParameterIndex, 0, arg.Constant.DestOffsetIn32BitValues); break;
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT: {
|
||||||
|
std::vector<UINT> zeros(arg.Constant.Num32BitValuesToSet);
|
||||||
|
auto setter = compute ? original_SetComputeRoot32BitConstants : original_SetGraphicsRoot32BitConstants;
|
||||||
|
setter(target, arg.Constant.RootParameterIndex, UINT(zeros.size()), zeros.data(), arg.Constant.DestOffsetIn32BitValues); break;
|
||||||
|
}
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW:
|
||||||
|
(compute ? original_SetComputeRootConstantBufferView : original_SetGraphicsRootConstantBufferView)(target, arg.ConstantBufferView.RootParameterIndex, 0); break;
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW:
|
||||||
|
(compute ? original_SetComputeRootShaderResourceView : original_SetGraphicsRootShaderResourceView)(target, arg.ShaderResourceView.RootParameterIndex, 0); break;
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW:
|
||||||
|
(compute ? original_SetComputeRootUnorderedAccessView : original_SetGraphicsRootUnorderedAccessView)(target, arg.UnorderedAccessView.RootParameterIndex, 0); break;
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW: original_IASetIndexBuffer(target, nullptr); break;
|
||||||
|
case D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW: {
|
||||||
|
D3D12_VERTEX_BUFFER_VIEW empty{}; original_IASetVertexBuffers(target, arg.VertexBuffer.Slot, 1, &empty); break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
original_ExecuteIndirect(access.target(), command_signature, max_command_count, arg_buffer, arg_buffer_offset, count_buffer, count_buffer_offset);
|
original_ExecuteIndirect(access.target(), command_signature, max_command_count, arg_buffer, arg_buffer_offset, count_buffer, count_buffer_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,8 +345,13 @@ void install(ID3D12Device* device) {
|
|||||||
ComPtr<IUnknown> newest;
|
ComPtr<IUnknown> newest;
|
||||||
const GUID* ids[] = {&IID_ID3D12GraphicsCommandList, &IID_ID3D12GraphicsCommandList1, &IID_ID3D12GraphicsCommandList2,
|
const GUID* ids[] = {&IID_ID3D12GraphicsCommandList, &IID_ID3D12GraphicsCommandList1, &IID_ID3D12GraphicsCommandList2,
|
||||||
&IID_ID3D12GraphicsCommandList3, &IID_ID3D12GraphicsCommandList4, &IID_ID3D12GraphicsCommandList5,
|
&IID_ID3D12GraphicsCommandList3, &IID_ID3D12GraphicsCommandList4, &IID_ID3D12GraphicsCommandList5,
|
||||||
&IID_ID3D12GraphicsCommandList6, &IID_ID3D12GraphicsCommandList7};
|
&IID_ID3D12GraphicsCommandList6, &IID_ID3D12GraphicsCommandList7,
|
||||||
for (unsigned n = 0; n < 8; ++n) if (SUCCEEDED(list->QueryInterface(*ids[n], reinterpret_cast<void**>(newest.ReleaseAndGetAddressOf())))) version = n;
|
&command_list8_iid, &command_list9_iid, &command_list10_iid};
|
||||||
|
for (unsigned n = 0; n < 11; ++n) {
|
||||||
|
ComPtr<IUnknown> candidate;
|
||||||
|
if (SUCCEEDED(list->QueryInterface(*ids[n], reinterpret_cast<void**>(candidate.GetAddressOf())))) { newest = std::move(candidate); version = n; }
|
||||||
|
}
|
||||||
|
if (newest.Get() != static_cast<IUnknown*>(list.Get())) throw std::runtime_error("D3D12 command-list versions need a separate interface identity adapter");
|
||||||
auto table = *reinterpret_cast<void***>(list.Get());
|
auto table = *reinterpret_cast<void***>(list.Get());
|
||||||
attach_recording_methods(table, version);
|
attach_recording_methods(table, version);
|
||||||
attach_method(table[2], reinterpret_cast<void*>(hook_release), reinterpret_cast<void**>(&original_release), "Release");
|
attach_method(table[2], reinterpret_cast<void*>(hook_release), reinterpret_cast<void**>(&original_release), "Release");
|
||||||
@@ -276,6 +361,8 @@ void install(ID3D12Device* device) {
|
|||||||
// Queue inherits IUnknown, Object, DeviceChild; UpdateTileMappings,
|
// Queue inherits IUnknown, Object, DeviceChild; UpdateTileMappings,
|
||||||
// CopyTileMappings precede ExecuteCommandLists in the public SDK.
|
// CopyTileMappings precede ExecuteCommandLists in the public SDK.
|
||||||
attach_method(queueTable[10], reinterpret_cast<void*>(hook_execute), reinterpret_cast<void**>(&original_execute), "ExecuteCommandLists");
|
attach_method(queueTable[10], reinterpret_cast<void*>(hook_execute), reinterpret_cast<void**>(&original_execute), "ExecuteCommandLists");
|
||||||
|
auto deviceTable = *reinterpret_cast<void***>(device);
|
||||||
|
attach_method(deviceTable[device_signature_slot], reinterpret_cast<void*>(hook_signature), reinterpret_cast<void**>(&original_signature), "CreateCommandSignature");
|
||||||
for (const auto& hook : attached) {
|
for (const auto& hook : attached) {
|
||||||
const auto result = MH_QueueEnableHook(hook.target);
|
const auto result = MH_QueueEnableHook(hook.target);
|
||||||
if (result != MH_OK) throw std::runtime_error(MH_StatusToString(result));
|
if (result != MH_OK) throw std::runtime_error(MH_StatusToString(result));
|
||||||
|
|||||||
@@ -577,6 +577,47 @@ using Fn_Barrier = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UIN
|
|||||||
static Fn_Barrier original_Barrier = nullptr;
|
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 STDMETHODCALLTYPE hook_Barrier(ID3D12GraphicsCommandList* self, UINT32 barrier_groups_count, const D3D12_BARRIER_GROUP *barrier_groups);
|
||||||
|
|
||||||
|
using Fn_OMSetFrontAndBackStencilRef = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT front_stencil, UINT back_stencil);
|
||||||
|
static Fn_OMSetFrontAndBackStencilRef original_OMSetFrontAndBackStencilRef = nullptr;
|
||||||
|
static void STDMETHODCALLTYPE hook_OMSetFrontAndBackStencilRef(ID3D12GraphicsCommandList* self, UINT front_stencil, UINT back_stencil) {
|
||||||
|
Access access(self);
|
||||||
|
if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) {
|
||||||
|
original_OMSetFrontAndBackStencilRef(target, front_stencil, back_stencil);
|
||||||
|
});
|
||||||
|
original_OMSetFrontAndBackStencilRef(access.target(), front_stencil, back_stencil);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Fn_RSSetDepthBias = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, FLOAT depth_bias, FLOAT clamp, FLOAT slope);
|
||||||
|
static Fn_RSSetDepthBias original_RSSetDepthBias = nullptr;
|
||||||
|
static void STDMETHODCALLTYPE hook_RSSetDepthBias(ID3D12GraphicsCommandList* self, FLOAT depth_bias, FLOAT clamp, FLOAT slope) {
|
||||||
|
Access access(self);
|
||||||
|
if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) {
|
||||||
|
original_RSSetDepthBias(target, depth_bias, clamp, slope);
|
||||||
|
});
|
||||||
|
original_RSSetDepthBias(access.target(), depth_bias, clamp, slope);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Fn_IASetIndexBufferStripCutValue = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, D3D12_INDEX_BUFFER_STRIP_CUT_VALUE value);
|
||||||
|
static Fn_IASetIndexBufferStripCutValue original_IASetIndexBufferStripCutValue = nullptr;
|
||||||
|
static void STDMETHODCALLTYPE hook_IASetIndexBufferStripCutValue(ID3D12GraphicsCommandList* self, D3D12_INDEX_BUFFER_STRIP_CUT_VALUE value) {
|
||||||
|
Access access(self);
|
||||||
|
if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) {
|
||||||
|
original_IASetIndexBufferStripCutValue(target, value);
|
||||||
|
});
|
||||||
|
original_IASetIndexBufferStripCutValue(access.target(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Fn_SetProgram = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const NewProgramDescription* desc);
|
||||||
|
static Fn_SetProgram original_SetProgram = nullptr;
|
||||||
|
static void STDMETHODCALLTYPE hook_SetProgram(ID3D12GraphicsCommandList* self, const NewProgramDescription* desc);
|
||||||
|
|
||||||
|
using Fn_DispatchGraph = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, const void* desc);
|
||||||
|
static Fn_DispatchGraph original_DispatchGraph = nullptr;
|
||||||
|
static void STDMETHODCALLTYPE hook_DispatchGraph(ID3D12GraphicsCommandList* self, const void* desc) {
|
||||||
|
Access access(self);
|
||||||
|
original_DispatchGraph(access.target(), desc);
|
||||||
|
}
|
||||||
|
|
||||||
static void attach_recording_methods(void** table, unsigned version) {
|
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[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[10], reinterpret_cast<void*>(hook_Reset), reinterpret_cast<void**>(&original_Reset), "Reset");
|
||||||
@@ -650,4 +691,11 @@ static void attach_recording_methods(void** table, unsigned version) {
|
|||||||
if (version >= 5) attach_method(table[78], reinterpret_cast<void*>(hook_RSSetShadingRateImage), reinterpret_cast<void**>(&original_RSSetShadingRateImage), "RSSetShadingRateImage");
|
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 >= 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");
|
if (version >= 7) attach_method(table[80], reinterpret_cast<void*>(hook_Barrier), reinterpret_cast<void**>(&original_Barrier), "Barrier");
|
||||||
|
if (version >= 8) attach_method(table[81], reinterpret_cast<void*>(hook_OMSetFrontAndBackStencilRef), reinterpret_cast<void**>(&original_OMSetFrontAndBackStencilRef), "OMSetFrontAndBackStencilRef");
|
||||||
|
if (version >= 9) attach_method(table[82], reinterpret_cast<void*>(hook_RSSetDepthBias), reinterpret_cast<void**>(&original_RSSetDepthBias), "RSSetDepthBias");
|
||||||
|
if (version >= 9) attach_method(table[83], reinterpret_cast<void*>(hook_IASetIndexBufferStripCutValue), reinterpret_cast<void**>(&original_IASetIndexBufferStripCutValue), "IASetIndexBufferStripCutValue");
|
||||||
|
if (version >= 10) attach_method(table[84], reinterpret_cast<void*>(hook_SetProgram), reinterpret_cast<void**>(&original_SetProgram), "SetProgram");
|
||||||
|
if (version >= 10) attach_method(table[85], reinterpret_cast<void*>(hook_DispatchGraph), reinterpret_cast<void**>(&original_DispatchGraph), "DispatchGraph");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned device_signature_slot = 41;
|
||||||
@@ -1,12 +1,31 @@
|
|||||||
#include "d3d12_external.h"
|
#include "d3d12_external.h"
|
||||||
#include <dxgi1_6.h>
|
#include <dxgi1_6.h>
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
|
#include <d3d12sdklayers.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
using namespace d4r::diag;
|
using namespace d4r::diag;
|
||||||
using namespace d4r::win;
|
using namespace d4r::win;
|
||||||
|
#if defined(D4R_DIAGNOSTIC_AGILITY)
|
||||||
|
extern "C" {
|
||||||
|
__declspec(dllexport) extern const UINT D3D12SDKVersion = 619;
|
||||||
|
__declspec(dllexport) extern const char* D3D12SDKPath = ".\\debug-d3d12\\";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
namespace {
|
namespace {
|
||||||
constexpr unsigned count = 256, bytes = count * sizeof(uint32_t), seed = 0x12345;
|
constexpr unsigned count = 256, bytes = count * sizeof(uint32_t), seed = 0x12345;
|
||||||
|
void messages(ID3D12Device* device) {
|
||||||
|
ComPtr<ID3D12InfoQueue> info;
|
||||||
|
if (FAILED(device->QueryInterface(IID_PPV_ARGS(info.GetAddressOf())))) return;
|
||||||
|
for (UINT64 i = 0; i < info->GetNumStoredMessagesAllowedByRetrievalFilter(); ++i) {
|
||||||
|
SIZE_T size = 0; (void)info->GetMessage(i, nullptr, &size);
|
||||||
|
std::vector<uint8_t> buffer(size);
|
||||||
|
auto* value = reinterpret_cast<D3D12_MESSAGE*>(buffer.data());
|
||||||
|
if (SUCCEEDED(info->GetMessage(i, value, &size)))
|
||||||
|
std::fprintf(stderr, "D3D12_VALIDATION severity=%u id=%u %s\n", unsigned(value->Severity), unsigned(value->ID), value->pDescription);
|
||||||
|
}
|
||||||
|
info->ClearStoredMessages();
|
||||||
|
}
|
||||||
void drain(ID3D12Device* device, ID3D12CommandQueue* queue) {
|
void drain(ID3D12Device* device, ID3D12CommandQueue* queue) {
|
||||||
ComPtr<ID3D12Fence> fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())), "Probe fence");
|
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);
|
Handle event; event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
@@ -45,12 +64,12 @@ void upload(ID3D12Resource* buffer, uint32_t multiplier, uint32_t addend) {
|
|||||||
for (unsigned i = 0; i < count; ++i) values[i] = i * multiplier + addend;
|
for (unsigned i = 0; i < count; ++i) values[i] = i * multiplier + addend;
|
||||||
buffer->Unmap(0, nullptr);
|
buffer->Unmap(0, nullptr);
|
||||||
}
|
}
|
||||||
bool verify(ID3D12Resource* buffer, uint32_t multiplier, uint32_t addend) {
|
bool verify(ID3D12Resource* buffer, uint32_t multiplier, uint32_t addend, uint32_t root_seed = seed) {
|
||||||
void* data = nullptr; D3D12_RANGE range{0, bytes}; dx(buffer->Map(0, &range, &data), "Probe verification map");
|
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);
|
const auto* values = static_cast<const uint32_t*>(data);
|
||||||
bool passed = true;
|
bool passed = true;
|
||||||
for (unsigned i = 0; i < count; ++i) if (values[i] != i * multiplier + addend + seed) {
|
for (unsigned i = 0; i < count; ++i) if (values[i] != i * multiplier + addend + root_seed) {
|
||||||
std::fprintf(stderr, "COMMAND_DIFFERENT index=%u expected=%u actual=%u\n", i, i * multiplier + addend + seed, values[i]);
|
std::fprintf(stderr, "COMMAND_DIFFERENT index=%u expected=%u actual=%u\n", i, i * multiplier + addend + root_seed, values[i]);
|
||||||
passed = false; break;
|
passed = false; break;
|
||||||
}
|
}
|
||||||
D3D12_RANGE noWrite{}; buffer->Unmap(0, &noWrite); return passed;
|
D3D12_RANGE noWrite{}; buffer->Unmap(0, &noWrite); return passed;
|
||||||
@@ -61,6 +80,7 @@ struct Callback {
|
|||||||
ID3D12Resource* output;
|
ID3D12Resource* output;
|
||||||
ID3D12Resource* replacement;
|
ID3D12Resource* replacement;
|
||||||
ID3D12Resource* readback;
|
ID3D12Resource* readback;
|
||||||
|
uint32_t expected_seed = seed;
|
||||||
bool prefix_verified = false;
|
bool prefix_verified = false;
|
||||||
};
|
};
|
||||||
void WINAPI boundary(ID3D12CommandQueue* queue, void* context) {
|
void WINAPI boundary(ID3D12CommandQueue* queue, void* context) {
|
||||||
@@ -73,24 +93,45 @@ void WINAPI boundary(ID3D12CommandQueue* queue, void* context) {
|
|||||||
list.list->CopyResource(callback.input, callback.replacement);
|
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);
|
transition(list.list.Get(), callback.input, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
|
||||||
list.submit(callback.device, queue);
|
list.submit(callback.device, queue);
|
||||||
callback.prefix_verified = verify(callback.readback, 17, 10);
|
callback.prefix_verified = verify(callback.readback, 17, 10, callback.expected_seed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
start();
|
start();
|
||||||
|
ComPtr<ID3D12Device> device;
|
||||||
|
std::unique_ptr<Library> debug_layers;
|
||||||
try {
|
try {
|
||||||
Args args(argc, argv);
|
Args args(argc, argv);
|
||||||
if (args.module.empty()) throw std::runtime_error("--module d4r_nvngx.dll is required; NVIDIA DLLs are not needed");
|
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);
|
if (std::getenv("D4R_COMMAND_DEBUG_SDK")) {
|
||||||
|
wchar_t exe[32768]{}; GetModuleFileNameW(nullptr, exe, 32768);
|
||||||
|
debug_layers = std::make_unique<Library>(std::filesystem::path(exe).parent_path() / L"debug-d3d12/d3d12SDKLayers.dll");
|
||||||
|
ComPtr<ID3D12Debug> debug;
|
||||||
|
auto get_interface = reinterpret_cast<decltype(&D3D12GetInterface)>(GetProcAddress(GetModuleHandleW(L"d3d12.dll"), "D3D12GetInterface"));
|
||||||
|
if (!get_interface) throw std::runtime_error("D3D12GetInterface is unavailable");
|
||||||
|
dx(get_interface(CLSID_D3D12Debug, IID_PPV_ARGS(debug.GetAddressOf())), "Diagnostic debug layer(public CLSID)");
|
||||||
|
debug->EnableDebugLayer();
|
||||||
|
}
|
||||||
|
ComPtr<ID3D12DeviceRemovedExtendedDataSettings> dred_settings;
|
||||||
|
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(dred_settings.GetAddressOf())))) {
|
||||||
|
dred_settings->SetAutoBreadcrumbsEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON);
|
||||||
|
dred_settings->SetPageFaultEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON);
|
||||||
|
}
|
||||||
|
std::unique_ptr<HipApi> hip;
|
||||||
|
hipDeviceProp_t props{};
|
||||||
|
const bool no_hip = std::getenv("D4R_COMMAND_PROBE_NO_HIP") != nullptr;
|
||||||
|
if (!no_hip) { hip = std::make_unique<HipApi>(args.hip_root); hip->select_gfx1201(args.device, props); }
|
||||||
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI");
|
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI");
|
||||||
ComPtr<IDXGIAdapter1> adapter;
|
ComPtr<IDXGIAdapter1> adapter;
|
||||||
for (UINT i = 0; ; ++i) {
|
for (UINT i = 0; ; ++i) {
|
||||||
dx(factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()), "Probe adapter");
|
dx(factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()), "Probe adapter");
|
||||||
DXGI_ADAPTER_DESC1 desc{}; dx(adapter->GetDesc1(&desc), "Probe adapter desc");
|
DXGI_ADAPTER_DESC1 desc{}; dx(adapter->GetDesc1(&desc), "Probe adapter desc");
|
||||||
if (!std::memcmp(&desc.AdapterLuid, props.luid, sizeof(LUID))) break;
|
if (no_hip ? desc.VendorId == 0x1002 : !std::memcmp(&desc.AdapterLuid, props.luid, sizeof(LUID))) break;
|
||||||
|
}
|
||||||
|
if (std::getenv("D4R_COMMAND_PROBE_WARP")) {
|
||||||
|
dx(factory->EnumWarpAdapter(IID_PPV_ARGS(adapter.ReleaseAndGetAddressOf())), "Probe WARP adapter");
|
||||||
}
|
}
|
||||||
ComPtr<ID3D12Device> device;
|
|
||||||
dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "Probe D3D12");
|
dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "Probe D3D12");
|
||||||
ComPtr<ID3D12CommandQueue> queue;
|
ComPtr<ID3D12CommandQueue> queue;
|
||||||
D3D12_COMMAND_QUEUE_DESC qd{}; qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
D3D12_COMMAND_QUEUE_DESC qd{}; qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||||
@@ -118,9 +159,37 @@ int main(int argc, char** argv) {
|
|||||||
using Install = unsigned(*)(ID3D12Device*);
|
using Install = unsigned(*)(ID3D12Device*);
|
||||||
using Record = unsigned(*)(ID3D12GraphicsCommandList*, void(WINAPI*)(ID3D12CommandQueue*, void*), void*);
|
using Record = unsigned(*)(ID3D12GraphicsCommandList*, void(WINAPI*)(ID3D12CommandQueue*, void*), void*);
|
||||||
using Live = unsigned long long(*)();
|
using Live = unsigned long long(*)();
|
||||||
if (shim.symbol<Install>("d4r_D3D12_InstallCommandBackend")(device.Get()) != 1) throw std::runtime_error("Command backend installation failed");
|
const bool no_hooks = std::getenv("D4R_COMMAND_PROBE_NO_HOOKS") != nullptr;
|
||||||
|
if (!no_hooks && 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 record = shim.symbol<Record>("d4r_D3D12_RecordDiagnosticBoundary");
|
||||||
auto live = shim.symbol<Live>("d4r_D3D12_LiveRecordings");
|
auto live = shim.symbol<Live>("d4r_D3D12_LiveRecordings");
|
||||||
|
const bool root_indirect = args.interop_mode == "indirect-root-reset";
|
||||||
|
const bool indirect_enabled = args.interop_mode != "basic";
|
||||||
|
if (args.interop_mode != "direct" && args.interop_mode != "basic" && args.interop_mode != "indirect" && !root_indirect)
|
||||||
|
throw std::runtime_error("Command probe mode must be basic, indirect, or indirect-root-reset");
|
||||||
|
D3D12_INDIRECT_ARGUMENT_DESC arguments[2]{};
|
||||||
|
arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT;
|
||||||
|
arguments[0].Constant.RootParameterIndex = 2;
|
||||||
|
arguments[0].Constant.Num32BitValuesToSet = 1;
|
||||||
|
arguments[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
|
||||||
|
D3D12_COMMAND_SIGNATURE_DESC indirect_desc{};
|
||||||
|
indirect_desc.ByteStride = (root_indirect ? 4 : 3) * sizeof(uint32_t);
|
||||||
|
indirect_desc.NumArgumentDescs = root_indirect ? 2 : 1;
|
||||||
|
indirect_desc.pArgumentDescs = arguments + (root_indirect ? 0 : 1);
|
||||||
|
ComPtr<ID3D12CommandSignature> indirect;
|
||||||
|
dx(device->CreateCommandSignature(&indirect_desc, root_indirect ? root.Get() : nullptr, IID_PPV_ARGS(indirect.GetAddressOf())), "Probe indirect signature");
|
||||||
|
auto indirect_upload = make_buffer(device.Get(), 4 * sizeof(uint32_t), D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
|
||||||
|
auto indirect_buffer = make_buffer(device.Get(), 4 * sizeof(uint32_t), D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||||
|
void* mapped = nullptr; D3D12_RANGE no_read{};
|
||||||
|
dx(indirect_upload->Map(0, &no_read, &mapped), "Probe indirect arguments");
|
||||||
|
const uint32_t indirect_seed = std::getenv("D4R_COMMAND_PROBE_SMALL_SEED") ? 1u : seed;
|
||||||
|
const uint32_t dispatch[] = {indirect_seed, count / 64, 1, 1};
|
||||||
|
std::memcpy(mapped, dispatch, sizeof(dispatch)); indirect_upload->Unmap(0, nullptr);
|
||||||
|
{
|
||||||
|
NativeList copy(device.Get()); copy.list->CopyResource(indirect_buffer.Get(), indirect_upload.Get());
|
||||||
|
transition(copy.list.Get(), indirect_buffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
|
||||||
|
copy.submit(device.Get(), queue.Get());
|
||||||
|
}
|
||||||
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
|
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 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 output = output_buffer(device.Get());
|
||||||
@@ -128,6 +197,9 @@ int main(int argc, char** argv) {
|
|||||||
auto second = 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 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);
|
auto prefix = make_buffer(device.Get(), bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||||
|
input->SetName(L"Probe input"); output->SetName(L"Probe output"); indirect_buffer->SetName(L"Probe indirect");
|
||||||
|
std::printf("PROBE_GPU iteration=%u input=0x%llx output=0x%llx indirect=0x%llx\n", iteration,
|
||||||
|
input->GetGPUVirtualAddress(), output->GetGPUVirtualAddress(), indirect_buffer->GetGPUVirtualAddress());
|
||||||
upload(first.Get(), 17, 10); upload(second.Get(), 25, 100);
|
upload(first.Get(), 17, 10); upload(second.Get(), 25, 100);
|
||||||
{
|
{
|
||||||
NativeList a(device.Get()), b(device.Get()), c(device.Get());
|
NativeList a(device.Get()), b(device.Get()), c(device.Get());
|
||||||
@@ -140,24 +212,71 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t temporary = seed;
|
uint32_t temporary = seed;
|
||||||
b.list->SetComputeRoot32BitConstants(2, 1, &temporary, 0);
|
b.list->SetComputeRoot32BitConstants(2, 1, &temporary, 0);
|
||||||
temporary = 0xdeadbeef; // Deep-copy check: replay must retain Seed.
|
temporary = 0xdeadbeef; // Deep-copy check: replay must retain Seed.
|
||||||
|
const uint32_t expected_seed = root_indirect && iteration % 2 ? 0 : seed;
|
||||||
|
if (indirect_enabled && iteration % 2) {
|
||||||
|
b.list->ExecuteIndirect(indirect.Get(), 1, indirect_buffer.Get(), root_indirect ? 0 : sizeof(uint32_t), nullptr, 0);
|
||||||
|
D3D12_RESOURCE_BARRIER uav{}; uav.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; uav.UAV.pResource = output.Get();
|
||||||
|
b.list->ResourceBarrier(1, &uav);
|
||||||
|
// Only the root-mutating signature resets Seed to zero.
|
||||||
|
// A dispatch-only signature must preserve every binding.
|
||||||
|
}
|
||||||
b.list->Dispatch(count / 64, 1, 1);
|
b.list->Dispatch(count / 64, 1, 1);
|
||||||
Callback callback{device.Get(), input.Get(), output.Get(), second.Get(), prefix.Get()};
|
Callback callback{device.Get(), input.Get(), output.Get(), second.Get(), prefix.Get(), expected_seed};
|
||||||
if (record(b.list.Get(), boundary, &callback) != 1) throw std::runtime_error("Record split failed");
|
if (no_hooks) {
|
||||||
|
dx(b.list->Close(), "Close native prefix");
|
||||||
|
messages(device.Get());
|
||||||
|
ID3D12CommandList* prefix_batch[] = {a.list.Get(), b.list.Get()};
|
||||||
|
queue->ExecuteCommandLists(2, prefix_batch); drain(device.Get(), queue.Get());
|
||||||
|
boundary(queue.Get(), &callback);
|
||||||
|
dx(b.allocator->Reset(), "Reset native prefix allocator");
|
||||||
|
dx(b.list->Reset(b.allocator.Get(), pso.Get()), "Reset native suffix");
|
||||||
|
b.list->SetComputeRootSignature(root.Get());
|
||||||
|
b.list->SetComputeRootUnorderedAccessView(0, output->GetGPUVirtualAddress());
|
||||||
|
b.list->SetComputeRootShaderResourceView(1, input->GetGPUVirtualAddress());
|
||||||
|
b.list->SetComputeRoot32BitConstant(2, expected_seed, 0);
|
||||||
|
} else 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.
|
b.list->Dispatch(count / 64, 1, 1); // No rebinding after the split.
|
||||||
dx(b.list->Close(), "Close logical list B");
|
dx(b.list->Close(), "Close logical list B");
|
||||||
transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||||
c.list->CopyResource(readback.Get(), output.Get());
|
c.list->CopyResource(readback.Get(), output.Get());
|
||||||
transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
transition(c.list.Get(), output.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
dx(c.list->Close(), "Close consumer C");
|
dx(c.list->Close(), "Close consumer C");
|
||||||
|
messages(device.Get());
|
||||||
ID3D12CommandList* batch[] = {a.list.Get(), b.list.Get(), c.list.Get()};
|
ID3D12CommandList* batch[] = {a.list.Get(), b.list.Get(), c.list.Get()};
|
||||||
queue->ExecuteCommandLists(3, batch); drain(device.Get(), queue.Get());
|
queue->ExecuteCommandLists(no_hooks ? 2 : 3, batch + (no_hooks ? 1 : 0)); 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 (!callback.prefix_verified || !verify(readback.Get(), 25, 100, expected_seed)) 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");
|
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);
|
const bool warp = std::getenv("D4R_COMMAND_PROBE_WARP") != nullptr;
|
||||||
|
std::printf("PASS D3D12_COMMAND_BACKEND adapter=%s architecture=%s backend=%u iterations=%u batch_order=1 root_state=1 deep_copy=1 indirect=%u indirect_reset=%u live_recordings=0 frame_age=0\n",
|
||||||
|
warp ? "WARP" : "AMD", warp ? "software" : no_hip ? "not_queried" : "gfx1201", !no_hooks,
|
||||||
|
args.iterations, indirect_enabled, root_indirect);
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& failure) {
|
} catch (const std::exception& failure) {
|
||||||
std::fprintf(stderr, "FAIL D3D12_COMMAND_BACKEND %s\n", failure.what()); loaded_modules(); return 4;
|
std::fprintf(stderr, "FAIL D3D12_COMMAND_BACKEND %s\n", failure.what());
|
||||||
|
if (device) {
|
||||||
|
messages(device.Get());
|
||||||
|
std::fprintf(stderr, "DEVICE_REMOVED result=0x%08x\n", unsigned(device->GetDeviceRemovedReason()));
|
||||||
|
ComPtr<ID3D12DeviceRemovedExtendedData> dred;
|
||||||
|
if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(dred.GetAddressOf())))) {
|
||||||
|
D3D12_DRED_AUTO_BREADCRUMBS_OUTPUT breadcrumbs{};
|
||||||
|
if (SUCCEEDED(dred->GetAutoBreadcrumbsOutput(&breadcrumbs)))
|
||||||
|
for (auto* node = breadcrumbs.pHeadAutoBreadcrumbNode; node; node = node->pNext) {
|
||||||
|
UINT completed = node->pLastBreadcrumbValue ? *node->pLastBreadcrumbValue : 0;
|
||||||
|
std::fprintf(stderr, "DRED list=%p completed=%u total=%u\n", node->pCommandList, completed, node->BreadcrumbCount);
|
||||||
|
for (UINT i = completed > 2 ? completed - 2 : 0; i < std::min(node->BreadcrumbCount, completed + 3); ++i)
|
||||||
|
std::fprintf(stderr, "DRED command=%u operation=%u\n", i, unsigned(node->pCommandHistory[i]));
|
||||||
|
}
|
||||||
|
D3D12_DRED_PAGE_FAULT_OUTPUT fault{};
|
||||||
|
if (SUCCEEDED(dred->GetPageFaultAllocationOutput(&fault)))
|
||||||
|
std::fprintf(stderr, "DRED fault_address=0x%llx\n", fault.PageFaultVA);
|
||||||
|
for (auto* allocation = fault.pHeadExistingAllocationNode; allocation; allocation = allocation->pNext)
|
||||||
|
std::fprintf(stderr, "DRED existing=%ls type=%u\n", allocation->ObjectNameW ? allocation->ObjectNameW : L"(unnamed)", unsigned(allocation->AllocationType));
|
||||||
|
for (auto* allocation = fault.pHeadRecentFreedAllocationNode; allocation; allocation = allocation->pNext)
|
||||||
|
std::fprintf(stderr, "DRED freed=%ls type=%u\n", allocation->ObjectNameW ? allocation->ObjectNameW : L"(unnamed)", unsigned(allocation->AllocationType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loaded_modules(); return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
// ABI subset of Microsoft's MIT-licensed DirectX-Headers, commit adbd6f3b.
|
||||||
|
// No pointers to CPU-owned nested structures exist in SetProgram's descriptor.
|
||||||
|
// The generic/raytracing variants use only the ProgramIdentifier field.
|
||||||
|
namespace d4r::win::commands {
|
||||||
|
struct NewWorkGraphDescription {
|
||||||
|
UINT64 program_identifier[4];
|
||||||
|
UINT flags;
|
||||||
|
D3D12_GPU_VIRTUAL_ADDRESS_RANGE backing_memory;
|
||||||
|
D3D12_GPU_VIRTUAL_ADDRESS_RANGE_AND_STRIDE root_arguments;
|
||||||
|
};
|
||||||
|
struct NewProgramDescription {
|
||||||
|
UINT type;
|
||||||
|
union {
|
||||||
|
UINT64 generic_program_identifier[4];
|
||||||
|
UINT64 raytracing_program_identifier[4];
|
||||||
|
NewWorkGraphDescription work_graph;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(NewWorkGraphDescription) == 80 && sizeof(NewProgramDescription) == 88,
|
||||||
|
"Public Windows x64 SetProgram ABI");
|
||||||
|
inline constexpr GUID command_list8_iid = {0xee936ef9,0x599d,0x4d28,{0x93,0x8e,0x23,0xc4,0xad,0x05,0xce,0x51}};
|
||||||
|
inline constexpr GUID command_list9_iid = {0x34ed2808,0xffe6,0x4c2b,{0xb1,0x1a,0xca,0xbd,0x2b,0x0c,0x59,0xe1}};
|
||||||
|
inline constexpr GUID command_list10_iid = {0x7013c015,0xd161,0x4b63,{0xa0,0x8c,0x23,0x85,0x52,0xdd,0x8a,0xcc}};
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Generate typed command forwarding from public MinGW D3D12 SDK declarations.
|
"""Generate typed command forwarding from public MinGW D3D12 SDK declarations.
|
||||||
|
|
||||||
Run with --header <SDK/include/d3d12.h> --output d3d12_command_hooks.generated.h.
|
Run with --header <SDK/include/d3d12.h> --output d3d12_command_hooks.generated.h.
|
||||||
Every recording method through CommandList7 is forwarded. State setters copy
|
Every recording method through CommandList10 is forwarded. State setters copy
|
||||||
their host arguments; COM object arguments retain a reference. SDK slot numbers
|
their host arguments; COM object arguments retain a reference. SDK slot numbers
|
||||||
are derived from the public interface inheritance, never a driver binary.
|
are derived from the public interface inheritance, never a driver binary.
|
||||||
"""
|
"""
|
||||||
@@ -9,6 +9,17 @@ import argparse
|
|||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
# Public additions verified against Microsoft's DirectX-Headers adbd6f3b.
|
||||||
|
# The old MinGW SDK has the needed scalar types but lacks the work-graph
|
||||||
|
# descriptor names; NewProgramDescription is a typed public-ABI mirror.
|
||||||
|
NEWER = [
|
||||||
|
(8, 'OMSetFrontAndBackStencilRef', 'UINT front_stencil, UINT back_stencil'),
|
||||||
|
(9, 'RSSetDepthBias', 'FLOAT depth_bias, FLOAT clamp, FLOAT slope'),
|
||||||
|
(9, 'IASetIndexBufferStripCutValue', 'D3D12_INDEX_BUFFER_STRIP_CUT_VALUE value'),
|
||||||
|
(10, 'SetProgram', 'const NewProgramDescription* desc'),
|
||||||
|
(10, 'DispatchGraph', 'const void* desc'),
|
||||||
|
]
|
||||||
|
|
||||||
POINTER_COUNTS = {
|
POINTER_COUNTS = {
|
||||||
'RSSetViewports': {'viewports': 'viewport_count'},
|
'RSSetViewports': {'viewports': 'viewport_count'},
|
||||||
'RSSetScissorRects': {'rects': 'rect_count'},
|
'RSSetScissorRects': {'rects': 'rect_count'},
|
||||||
@@ -89,7 +100,26 @@ def generate(source):
|
|||||||
registrations[-1] = f' if (version >= {version})' + registrations[-1][3:]
|
registrations[-1] = f' if (version >= {version})' + registrations[-1][3:]
|
||||||
slot += 1
|
slot += 1
|
||||||
lines.append('')
|
lines.append('')
|
||||||
|
for version, name, arguments in NEWER:
|
||||||
|
declarations = arguments.split(', ')
|
||||||
|
names = [re.search(r'(\w+)$', argument)[1] for argument in declarations]
|
||||||
|
signature = ', '.join(['ID3D12GraphicsCommandList* self'] + declarations)
|
||||||
|
lines += [f'using Fn_{name} = void(STDMETHODCALLTYPE*)({signature});', f'static Fn_{name} original_{name} = nullptr;']
|
||||||
|
if name == 'SetProgram':
|
||||||
|
lines.append(f'static void STDMETHODCALLTYPE hook_{name}({signature});')
|
||||||
|
else:
|
||||||
|
lines += [f'static void STDMETHODCALLTYPE hook_{name}({signature}) {{', ' Access access(self);']
|
||||||
|
if name != 'DispatchGraph':
|
||||||
|
lines += [' if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) {',
|
||||||
|
f' original_{name}(' + ', '.join(['target'] + names) + ');', ' });']
|
||||||
|
lines += [f' original_{name}(' + ', '.join(['access.target()'] + names) + ');', '}']
|
||||||
|
registrations.append(f' if (version >= {version}) attach_method(table[{slot}], reinterpret_cast<void*>(hook_{name}), reinterpret_cast<void**>(&original_{name}), "{name}");')
|
||||||
|
slot += 1
|
||||||
|
lines.append('')
|
||||||
lines += ['static void attach_recording_methods(void** table, unsigned version) {'] + registrations + ['}', '']
|
lines += ['static void attach_recording_methods(void** table, unsigned version) {'] + registrations + ['}', '']
|
||||||
|
device = re.search(r'typedef struct ID3D12DeviceVtbl \{(.*?)\n\} ID3D12DeviceVtbl;', source, re.S)[1]
|
||||||
|
fields = list(dict.fromkeys(re.findall(r'\(STDMETHODCALLTYPE \*(\w+)\)', device)))
|
||||||
|
lines.append(f'static constexpr unsigned device_signature_slot = {fields.index("CreateCommandSignature")};')
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user