diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ef40c8..35649ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,9 +192,15 @@ include(CTest) add_test(NAME d3d12_gpu_pixel_formats COMMAND d4r_d3d12_pixel_probe --hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --iterations 2) set_tests_properties(d3d12_gpu_pixel_formats PROPERTIES TIMEOUT 120 LABELS "hardware;gfx1201;formats") +add_test(NAME d3d12_gpu_enhanced_formats COMMAND d4r_d3d12_pixel_probe + --hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --interop-mode enhanced --iterations 2) +set_tests_properties(d3d12_gpu_enhanced_formats PROPERTIES TIMEOUT 120 LABELS "hardware;gfx1201;formats;enhanced_barriers") add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe --hip-root "${D4R_HIP_ROOT}" --module "$" --interop-mode indirect --iterations 32) set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime") +add_test(NAME d3d12_command_initial_pso COMMAND d4r_d3d12_command_probe + --hip-root "${D4R_HIP_ROOT}" --module "$" --interop-mode initial-pso --iterations 32) +set_tests_properties(d3d12_command_initial_pso 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 "$" --interop-mode indirect-root-reset --iterations 32) set_tests_properties(d3d12_command_root_reset_warp PROPERTIES TIMEOUT 120 LABELS "software;d3d12;command_lists;lifetime" diff --git a/docs/windows-rdna4-port.md b/docs/windows-rdna4-port.md index e20ec38..12149af 100644 --- a/docs/windows-rdna4-port.md +++ b/docs/windows-rdna4-port.md @@ -38,6 +38,44 @@ Do not integrate NGX until the integer PTX workload is stable on the real GPU. | 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 | +## Initial PSO and enhanced texture layouts (2026-10-01) + +The backend now captures the initial PSO supplied to public CreateCommandList, +in addition to Reset and SetPipelineState. Its suffix can dispatch without +rebinding that PSO. The public foundation probe's `initial-pso` mode passes +32 native RX 9070 XT iterations; the WARP diagnostic passes too. The runtime +now installs 81 documented method hooks, including CreateCommandList. + +Resource tracking distinguishes legacy states from enhanced texture layouts +for mip/array/plane zero. Barriers on unrelated subresources do not overwrite +that state. An unfinished split barrier on an NGX texture explicitly rejects +Evaluate. The queued snapshot owns the layout/access metadata. Native interop +uses CommandList7 texture barriers to enter COPY_SOURCE/COPY_DEST and restore +the exact enhanced layout and access class; it does not reinterpret enhanced +layouts as legacy states. Legacy texture copies also affect only subresource +zero. Shared linear buffers continue to use their own legacy COMMON/copy states. +This follows [Microsoft's enhanced barrier interoperability rules](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html). + +`d3d12_gpu_enhanced_formats` independently tests all nineteen storage/plane +cases with a D3D12 enhanced producer and consumer. Full build and CTest pass +16/16 gates (`enhanced-final-ctest.log`, fifteen hardware and one WARP). +`d3d12-m-enhanced-packed.zip` and `d3d12-k-enhanced-packed.zip` each pass four +recorded NGX frames with their complete native transformer. Enhanced snapshot +logs report shader-resource layout 21 and unordered-access layout 20. All +four RGB images in both runs exactly match their independently quantised +FP16 baselines, max absolute/relative error 0. Frame age and shim CPU image +copy counts remain zero. Add `-BarrierMode enhanced` to the packed-run command +below to reproduce. The explicit harness can also supply layout metadata. + +Tracking currently observes the evaluated list's barriers. A texture whose +state was established only in a different list uses the caller-supplied state +or the legacy NGX default; queue-wide state inheritance remains an integration +gate. Unknown pre-initialization indirect signatures are still rejected. +OptiScaler's default restore hotfixes are false; integration will keep +RestoreComputeSignature, RestoreGraphicSignature and ExtendedStateRestore +false because their original-method trampolines can bypass logical-list routing. +Actual OptiScaler execution remains untested; no game files have been changed. + ## Native GPU resource-format conversion (2026-10-01) `pixel_convert_gfx1201.hsaco` converts imported D3D12 copy footprints to NGX's diff --git a/scripts/windows/test-d3d12-command.ps1 b/scripts/windows/test-d3d12-command.ps1 index 061945f..e2df09f 100644 --- a/scripts/windows/test-d3d12-command.ps1 +++ b/scripts/windows/test-d3d12-command.ps1 @@ -5,7 +5,7 @@ param( [switch]$DebugLayer, [switch]$Warp, [switch]$NoHip, - [ValidateSet('basic','indirect','indirect-root-reset')][string]$Mode = 'indirect', + [ValidateSet('basic','indirect','indirect-root-reset','initial-pso')][string]$Mode = 'indirect', [string]$OutputDirectory = 'test-results/d3d12-command-development' ) $ErrorActionPreference = 'Stop' diff --git a/scripts/windows/test-windows-rdna4.ps1 b/scripts/windows/test-windows-rdna4.ps1 index d462405..fdc0569 100644 --- a/scripts/windows/test-windows-rdna4.ps1 +++ b/scripts/windows/test-windows-rdna4.ps1 @@ -14,6 +14,7 @@ param( [switch]$RequireNativeNetwork, [switch]$CommandListBackend, [ValidateSet('baseline','packed','unorm')][string]$PixelProfile = 'baseline', + [ValidateSet('legacy','enhanced')][string]$BarrierMode = 'legacy', [int]$Iterations = 32, [int]$TimeoutSeconds = 180 ) @@ -258,7 +259,7 @@ try { $localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll' Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim Copy-Item -LiteralPath (Join-Path $bin 'pixel_convert_gfx1201.hsaco') -Destination $ngxRuntimeDirectory - $ngxArguments += @('--module', $localShim, '--pixel-profile', $PixelProfile) + $ngxArguments += @('--module', $localShim, '--pixel-profile', $PixelProfile, '--barrier-mode', $BarrierMode) 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' } diff --git a/tools/windows/d3d12_command_hooks.cpp b/tools/windows/d3d12_command_hooks.cpp index 55581df..fcaae2c 100644 --- a/tools/windows/d3d12_command_hooks.cpp +++ b/tools/windows/d3d12_command_hooks.cpp @@ -31,7 +31,7 @@ struct Recording { std::vector segments; std::vector state; std::vector events; - std::unordered_map resource_states; + std::unordered_map resource_states; unsigned active_queries = 0; bool render_pass = false, predication = false, indirect_state_unknown = false, closed = false; explicit Recording(ID3D12GraphicsCommandList* list) : original(list) {} @@ -91,6 +91,31 @@ void attach_method(void* target, void* replacement, void** original, const char* 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; +using CreateListFn = HRESULT(STDMETHODCALLTYPE*)(ID3D12Device*, UINT, D3D12_COMMAND_LIST_TYPE, + ID3D12CommandAllocator*, ID3D12PipelineState*, REFIID, void**); +CreateListFn original_create_list = nullptr; +HRESULT STDMETHODCALLTYPE hook_create_list(ID3D12Device* device, UINT node, D3D12_COMMAND_LIST_TYPE type, + ID3D12CommandAllocator* allocator, ID3D12PipelineState* initial, REFIID iid, void** out) { + const auto result = original_create_list(device, node, type, allocator, initial, iid, out); + if (!internal && SUCCEEDED(result) && out && *out) try { + ComPtr list; + if (SUCCEEDED(static_cast(*out)->QueryInterface(IID_PPV_ARGS(list.GetAddressOf())))) { + auto recording = get_recording(list.Get()); + std::lock_guard lock(recording->mutex); + auto pipeline = retain(initial); + if (pipeline) recording->state.emplace_back([pipeline](ID3D12GraphicsCommandList* target) { + original_SetPipelineState(target, pipeline.Get()); + }); + } + } catch (const std::exception& failure) { + // A public Create must retain its result even if tracking runs out of + // memory. A later split must fail explicitly instead of replaying an + // incomplete initial state. + { std::lock_guard lock(error_mutex); last_error = failure.what(); } + std::fprintf(stderr, "D4R_CREATE_LIST_TRACK_FAILURE %s\n", failure.what()); + } + return result; +} 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 { @@ -160,14 +185,28 @@ void STDMETHODCALLTYPE hook_ClearState(ID3D12GraphicsCommandList* self, ID3D12Pi 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; + if (barriers[i].Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION && + (barriers[i].Transition.Subresource == 0 || barriers[i].Transition.Subresource == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)) { + auto& state = access.recording->resource_states[barriers[i].Transition.pResource]; + state = ResourceAccess(barriers[i].Transition.StateAfter); + state.pending_split = barriers[i].Flags == D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY; + } 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); + if (access.recording) for (UINT32 g = 0; g < barrier_groups_count; ++g) if (barrier_groups[g].Type == D3D12_BARRIER_TYPE_TEXTURE) + for (UINT32 b = 0; b < barrier_groups[g].NumBarriers; ++b) { + const auto& barrier = barrier_groups[g].pTextureBarriers[b]; const auto& range = barrier.Subresources; + const bool containsZero = range.NumMipLevels == 0 ? (range.IndexOrFirstMipLevel == 0 || range.IndexOrFirstMipLevel == UINT32_MAX) : + range.IndexOrFirstMipLevel == 0 && range.FirstArraySlice == 0 && range.FirstPlane == 0; + if (containsZero) { + auto& state = access.recording->resource_states[barrier.pResource]; state.enhanced = true; + state.layout = barrier.LayoutAfter; state.access = barrier.AccessAfter; state.sync = barrier.SyncAfter; + state.pending_split = barrier.SyncAfter == D3D12_BARRIER_SYNC_SPLIT; + } + } 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); @@ -319,6 +358,11 @@ InternalScope::~InternalScope() { internal = previous; } std::string error() { std::lock_guard lock(error_mutex); return last_error; } size_t live_recordings() { std::lock_guard lock(map_mutex); return recordings.size(); } D3D12_RESOURCE_STATES resource_state(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, D3D12_RESOURCE_STATES fallback) { + const auto state = resource_access(list, resource, ResourceAccess(fallback)); + if (state.enhanced) throw std::runtime_error("Enhanced texture layout cannot be treated as a legacy state"); + return state.legacy; +} +ResourceAccess resource_access(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, ResourceAccess fallback) { auto recording = get_recording(list, false); if (!recording) return fallback; std::lock_guard lock(recording->mutex); @@ -362,6 +406,7 @@ void install(ID3D12Device* device) { // CopyTileMappings precede ExecuteCommandLists in the public SDK. attach_method(queueTable[10], reinterpret_cast(hook_execute), reinterpret_cast(&original_execute), "ExecuteCommandLists"); auto deviceTable = *reinterpret_cast(device); + attach_method(deviceTable[device_command_list_slot], reinterpret_cast(hook_create_list), reinterpret_cast(&original_create_list), "CreateCommandList"); attach_method(deviceTable[device_signature_slot], reinterpret_cast(hook_signature), reinterpret_cast(&original_signature), "CreateCommandSignature"); for (const auto& hook : attached) { const auto result = MH_QueueEnableHook(hook.target); diff --git a/tools/windows/d3d12_command_hooks.generated.h b/tools/windows/d3d12_command_hooks.generated.h index a7f8dd1..3449a98 100644 --- a/tools/windows/d3d12_command_hooks.generated.h +++ b/tools/windows/d3d12_command_hooks.generated.h @@ -698,4 +698,5 @@ static void attach_recording_methods(void** table, unsigned version) { if (version >= 10) attach_method(table[85], reinterpret_cast(hook_DispatchGraph), reinterpret_cast(&original_DispatchGraph), "DispatchGraph"); } -static constexpr unsigned device_signature_slot = 41; \ No newline at end of file +static constexpr unsigned device_signature_slot = 41; +static constexpr unsigned device_command_list_slot = 12; \ No newline at end of file diff --git a/tools/windows/d3d12_command_hooks.h b/tools/windows/d3d12_command_hooks.h index 2d10d0b..929e447 100644 --- a/tools/windows/d3d12_command_hooks.h +++ b/tools/windows/d3d12_command_hooks.h @@ -13,6 +13,7 @@ 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); +ResourceAccess resource_access(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, ResourceAccess fallback); // Internal copies and callback submissions must bypass logical list routing. struct InternalScope { bool previous; diff --git a/tools/windows/d3d12_command_probe.cpp b/tools/windows/d3d12_command_probe.cpp index 50dd8b3..ba0a468 100644 --- a/tools/windows/d3d12_command_probe.cpp +++ b/tools/windows/d3d12_command_probe.cpp @@ -39,9 +39,9 @@ void drain(ID3D12Device* device, ID3D12CommandQueue* queue) { struct NativeList { ComPtr allocator; ComPtr list; - explicit NativeList(ID3D12Device* device) { + explicit NativeList(ID3D12Device* device, ID3D12PipelineState* initial = nullptr) { 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"); + dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), initial, IID_PPV_ARGS(list.GetAddressOf())), "Probe command list"); } void submit(ID3D12Device* device, ID3D12CommandQueue* queue) { dx(list->Close(), "Close probe list"); ID3D12CommandList* lists[] = {list.Get()}; @@ -164,9 +164,10 @@ int main(int argc, char** argv) { auto record = shim.symbol("d4r_D3D12_RecordDiagnosticBoundary"); auto live = shim.symbol("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"); + const bool initial_pso = args.interop_mode == "initial-pso"; + const bool indirect_enabled = args.interop_mode == "indirect" || root_indirect; + if (args.interop_mode != "direct" && args.interop_mode != "basic" && args.interop_mode != "indirect" && !root_indirect && !initial_pso) + throw std::runtime_error("Command probe mode must be basic, indirect, indirect-root-reset or initial-pso"); D3D12_INDIRECT_ARGUMENT_DESC arguments[2]{}; arguments[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT; arguments[0].Constant.RootParameterIndex = 2; @@ -202,11 +203,11 @@ int main(int argc, char** argv) { input->GetGPUVirtualAddress(), output->GetGPUVirtualAddress(), indirect_buffer->GetGPUVirtualAddress()); 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(), initial_pso ? pso.Get() : nullptr), 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->SetComputeRootSignature(root.Get()); if (!initial_pso) b.list->SetPipelineState(pso.Get()); b.list->SetComputeRootUnorderedAccessView(0, output->GetGPUVirtualAddress()); b.list->SetComputeRootShaderResourceView(1, input->GetGPUVirtualAddress()); uint32_t temporary = seed; diff --git a/tools/windows/d3d12_dlss_probe.cpp b/tools/windows/d3d12_dlss_probe.cpp index c7c60e0..ef3bb9b 100644 --- a/tools/windows/d3d12_dlss_probe.cpp +++ b/tools/windows/d3d12_dlss_probe.cpp @@ -18,6 +18,7 @@ int main(int argc, char** argv) { const std::filesystem::path directory(diagDir ? wide(diagDir) : std::filesystem::current_path().wstring()); std::filesystem::create_directories(directory); const bool commandBackend = args.interop_mode == "command-list"; + const bool enhanced = args.barrier_mode == "enhanced"; if (args.interop_mode == "images" && args.pixel_profile != "baseline") throw std::runtime_error("Raw CUDA image diagnostic requires baseline formats"); std::printf("D3D12_PIXEL_PROFILE %s\n", args.pixel_profile.c_str()); if (commandBackend && _putenv_s("D4R_D3D12_COMMAND_BACKEND", "1")) throw std::runtime_error("Cannot enable command backend"); @@ -38,6 +39,17 @@ int main(int argc, char** argv) { if (!found) throw std::runtime_error("HIP adapter missing from DXGI"); ComPtr device; dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "D3D12CreateDevice"); + if (enhanced) { D3D12_FEATURE_DATA_D3D12_OPTIONS12 support{}; dx(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &support, sizeof(support)), "Enhanced barrier support"); if (!support.EnhancedBarriersSupported) throw std::runtime_error("Enhanced barriers unavailable"); } + auto enhancedBarrier = [&](ID3D12GraphicsCommandList* command, ID3D12Resource* texture, unsigned plane, bool initial) { + ComPtr newer; dx(command->QueryInterface(IID_PPV_ARGS(newer.GetAddressOf())), "Harness enhanced list"); + D3D12_TEXTURE_BARRIER barrier{}; barrier.pResource = texture; + barrier.LayoutAfter = plane == 3 ? D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE; + barrier.LayoutBefore = initial ? D3D12_BARRIER_LAYOUT_COMMON : barrier.LayoutAfter; + barrier.AccessAfter = plane == 3 ? D3D12_BARRIER_ACCESS_UNORDERED_ACCESS : D3D12_BARRIER_ACCESS_SHADER_RESOURCE; + barrier.AccessBefore = initial ? D3D12_BARRIER_ACCESS_COMMON : barrier.AccessAfter; + barrier.SyncBefore = barrier.SyncAfter = D3D12_BARRIER_SYNC_ALL; + D3D12_BARRIER_GROUP group{}; group.Type = D3D12_BARRIER_TYPE_TEXTURE; group.NumBarriers = 1; group.pTextureBarriers = &barrier; newer->Barrier(1, &group); + }; ComPtr queue; D3D12_COMMAND_QUEUE_DESC qd{}; qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; dx(device->CreateCommandQueue(&qd, IID_PPV_ARGS(queue.GetAddressOf())), "CreateCommandQueue"); @@ -104,7 +116,8 @@ int main(int argc, char** argv) { dst.pResource = texture.image.Get(); dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); transition(list.Get(), texture.image.Get(), D3D12_RESOURCE_STATE_COPY_DEST, - i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + enhanced ? D3D12_RESOURCE_STATE_COMMON : i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + if (enhanced) enhancedBarrier(list.Get(), texture.image.Get(), i, true); } dx(list->Close(), "Close(synthetic producers)"); ID3D12CommandList* producer[] = {list.Get()}; if (!commandBackend) { queue->ExecuteCommandLists(1, producer); drain(); } @@ -176,6 +189,12 @@ int main(int argc, char** argv) { struct FeatureCleanup { Release fn; void* h; ~FeatureCleanup() { (void)fn(h); } } featureCleanup{release, handle}; static const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; for (unsigned i = 0; i < 5; ++i) d4r_ngx_set_d3d12_resource(parameters, names[i], textures[i].image.Get()); + if (enhanced && !commandBackend) { + 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) { const std::string prefix(states[i]); d4r_ngx_set_uint(parameters,(prefix+".Enhanced").c_str(),1); + d4r_ngx_set_uint(parameters,(prefix+".Layout").c_str(), i == 3 ? D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE); + d4r_ngx_set_uint(parameters,(prefix+".Access").c_str(), i == 3 ? D3D12_BARRIER_ACCESS_UNORDERED_ACCESS : D3D12_BARRIER_ACCESS_SHADER_RESOURCE); } + } d4r_ngx_set_int(parameters, "Disable.Watermark", 1); auto readback = make_buffer(device.Get(), textures[3].bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST); ComPtr predecessor; @@ -193,6 +212,7 @@ int main(int argc, char** argv) { d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0); if (commandBackend) { dx(allocator->Reset(), "Reset(game-style allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(game-style list)"); + if (enhanced) for (unsigned i = 0; i < 5; ++i) enhancedBarrier(list.Get(), textures[i].image.Get(), i, false); using RecordedEvaluate = unsigned(*)(ID3D12GraphicsCommandList*, void*, void*, void*); check(shim.symbol("NVSDK_NGX_D3D12_EvaluateFeature")(list.Get(), handle, parameters, nullptr), "D3D12 recorded same-frame Evaluate"); // The caller may reuse/mutate its parameter object as soon as @@ -203,12 +223,14 @@ int main(int argc, char** argv) { check(shim.symbol("d4r_D3D12_EvaluateAtBoundary")(queue.Get(), handle, parameters), "D3D12 same-frame Evaluate"); dx(allocator->Reset(), "Reset(harness allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(harness list)"); } - transition(list.Get(), textures[3].image.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE); + ResourceAccess outputAccess(D3D12_RESOURCE_STATE_UNORDERED_ACCESS); outputAccess.enhanced = enhanced; + outputAccess.layout = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS; outputAccess.access = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS; + texture_copy_barrier(list.Get(), textures[3].image.Get(), outputAccess, false, false); D3D12_TEXTURE_COPY_LOCATION src{}, dst{}; src.pResource = textures[3].image.Get(); src.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dst.pResource = readback.Get(); dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst.PlacedFootprint = textures[3].footprint; list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); - transition(list.Get(), textures[3].image.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + texture_copy_barrier(list.Get(), textures[3].image.Get(), outputAccess, false, true); dx(list->Close(), "Close(verification)"); if (commandBackend && frame == 0) { ID3D12CommandList* batch[] = {predecessor.Get(), list.Get()}; queue->ExecuteCommandLists(2, batch); @@ -255,8 +277,8 @@ int main(int argc, char** argv) { if (variance < 1e-6) throw std::runtime_error("D3D12 output lost the synthetic pattern"); } loaded_modules(); - std::printf("PASS D3D12_DLSS architecture=gfx1201 preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 queue_integration=%s\n", - args.preset, args.iterations, commandBackend ? "recorded_command_list" : "explicit_harness"); + std::printf("PASS D3D12_DLSS architecture=gfx1201 preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 enhanced=%u queue_integration=%s\n", + args.preset, args.iterations, enhanced, commandBackend ? "recorded_command_list" : "explicit_harness"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; } } diff --git a/tools/windows/d3d12_external.h b/tools/windows/d3d12_external.h index c83db3b..7701875 100644 --- a/tools/windows/d3d12_external.h +++ b/tools/windows/d3d12_external.h @@ -65,6 +65,40 @@ inline void transition(ID3D12GraphicsCommandList* list, ID3D12Resource* resource barrier.Transition = {resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, before, after}; list->ResourceBarrier(1, &barrier); } +struct ResourceAccess { + D3D12_RESOURCE_STATES legacy = D3D12_RESOURCE_STATE_COMMON; + bool enhanced = false, pending_split = false; + D3D12_BARRIER_LAYOUT layout = D3D12_BARRIER_LAYOUT_COMMON; + D3D12_BARRIER_ACCESS access = D3D12_BARRIER_ACCESS_COMMON; + D3D12_BARRIER_SYNC sync = D3D12_BARRIER_SYNC_ALL; + ResourceAccess() = default; + ResourceAccess(D3D12_RESOURCE_STATES state) : legacy(state) {} +}; +inline void texture_copy_barrier(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, + const ResourceAccess& original, bool output, bool restore) { + if (original.pending_split) throw std::runtime_error("Cannot use a texture inside an unfinished split barrier"); + if (!original.enhanced) { + const auto copy = output ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_COPY_SOURCE; + if (original.legacy == copy) return; + D3D12_RESOURCE_BARRIER barrier{}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition = {texture, 0, restore ? copy : original.legacy, restore ? original.legacy : copy}; + list->ResourceBarrier(1, &barrier); return; + } + ComPtr newer; + dx(list->QueryInterface(IID_PPV_ARGS(newer.GetAddressOf())), "Enhanced interop command list"); + D3D12_TEXTURE_BARRIER barrier{}; barrier.pResource = texture; + barrier.LayoutBefore = restore ? (output ? D3D12_BARRIER_LAYOUT_COPY_DEST : D3D12_BARRIER_LAYOUT_COPY_SOURCE) : original.layout; + barrier.LayoutAfter = restore ? original.layout : (output ? D3D12_BARRIER_LAYOUT_COPY_DEST : D3D12_BARRIER_LAYOUT_COPY_SOURCE); + barrier.AccessBefore = restore ? (output ? D3D12_BARRIER_ACCESS_COPY_DEST : D3D12_BARRIER_ACCESS_COPY_SOURCE) : original.access; + barrier.AccessAfter = restore ? original.access : (output ? D3D12_BARRIER_ACCESS_COPY_DEST : D3D12_BARRIER_ACCESS_COPY_SOURCE); + // Interop runs in a separate submitted direct list. ALL is a conservative + // synchronization scope for the app's original access class/layout. + barrier.SyncBefore = restore ? D3D12_BARRIER_SYNC_COPY : D3D12_BARRIER_SYNC_ALL; + barrier.SyncAfter = restore ? D3D12_BARRIER_SYNC_ALL : D3D12_BARRIER_SYNC_COPY; + barrier.Subresources.IndexOrFirstMipLevel = 0; // Single subresource 0. + D3D12_BARRIER_GROUP group{}; group.Type = D3D12_BARRIER_TYPE_TEXTURE; group.NumBarriers = 1; group.pTextureBarriers = &barrier; + newer->Barrier(1, &group); +} class SharedPlane { ExternalApi& api_; @@ -108,25 +142,25 @@ public: } SharedPlane(const SharedPlane&) = delete; ~SharedPlane() { release(); } - void copy_input(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, D3D12_RESOURCE_STATES state) { - transition(list, texture, state, D3D12_RESOURCE_STATE_COPY_SOURCE); + void copy_input(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, ResourceAccess state) { + texture_copy_barrier(list, texture, state, false, false); transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST); D3D12_TEXTURE_COPY_LOCATION source{}, dest{}; source.pResource = texture; source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; dest.pResource = buffer.Get(); dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dest.PlacedFootprint = footprint; list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr); transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON); - transition(list, texture, D3D12_RESOURCE_STATE_COPY_SOURCE, state); + texture_copy_barrier(list, texture, state, false, true); } - void copy_output(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, D3D12_RESOURCE_STATES state) { - transition(list, texture, state, D3D12_RESOURCE_STATE_COPY_DEST); + void copy_output(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, ResourceAccess state) { + texture_copy_barrier(list, texture, state, true, false); transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE); D3D12_TEXTURE_COPY_LOCATION source{}, dest{}; source.pResource = buffer.Get(); source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; source.PlacedFootprint = footprint; dest.pResource = texture; dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr); transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON); - transition(list, texture, D3D12_RESOURCE_STATE_COPY_DEST, state); + texture_copy_barrier(list, texture, state, true, true); } }; diff --git a/tools/windows/d3d12_pixel_probe.cpp b/tools/windows/d3d12_pixel_probe.cpp index 30f9c26..bd57644 100644 --- a/tools/windows/d3d12_pixel_probe.cpp +++ b/tools/windows/d3d12_pixel_probe.cpp @@ -60,6 +60,17 @@ ComPtr uav_buffer(ID3D12Device* device) { dx(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(result.GetAddressOf())), "Pixel reference buffer"); return result; } +ResourceAccess shader_access() { + ResourceAccess result; result.enhanced = true; result.layout = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE; + result.access = D3D12_BARRIER_ACCESS_SHADER_RESOURCE; return result; +} +void native_layout(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, D3D12_BARRIER_LAYOUT before, D3D12_BARRIER_LAYOUT after, + D3D12_BARRIER_ACCESS accessBefore, D3D12_BARRIER_ACCESS accessAfter) { + ComPtr newer; dx(list->QueryInterface(IID_PPV_ARGS(newer.GetAddressOf())), "Probe enhanced list"); + D3D12_TEXTURE_BARRIER barrier{}; barrier.pResource = texture; barrier.LayoutBefore = before; barrier.LayoutAfter = after; + barrier.AccessBefore = accessBefore; barrier.AccessAfter = accessAfter; barrier.SyncBefore = barrier.SyncAfter = D3D12_BARRIER_SYNC_ALL; + D3D12_BARRIER_GROUP group{}; group.Type = D3D12_BARRIER_TYPE_TEXTURE; group.NumBarriers = 1; group.pTextureBarriers = &barrier; newer->Barrier(1,&group); +} void fixture(uint8_t* row, unsigned y, PixelSpec spec) { for (unsigned x = 0; x < width; ++x) { unsigned i = x + y * width; auto* p = row + x * spec.bytes; @@ -163,6 +174,8 @@ int main(int argc, char** argv) { ComPtr adapter; for (UINT i = 0; ; ++i) { dx(factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()), "Pixel adapter"); DXGI_ADAPTER_DESC1 d{}; dx(adapter->GetDesc1(&d), "Pixel adapter desc"); if (!std::memcmp(&d.AdapterLuid, props.luid, sizeof(LUID))) break; } ComPtr device; dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(device.GetAddressOf())), "Pixel D3D12"); + const bool enhanced = args.interop_mode == "enhanced"; + if (enhanced) { D3D12_FEATURE_DATA_D3D12_OPTIONS12 support{}; dx(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &support, sizeof(support)), "Enhanced barrier support"); if (!support.EnhancedBarriersSupported) throw std::runtime_error("Enhanced barriers unsupported"); } ComPtr queue; D3D12_COMMAND_QUEUE_DESC qd{}; qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; dx(device->CreateCommandQueue(&qd, IID_PPV_ARGS(queue.GetAddressOf())), "Pixel queue"); SharedTimeline timeline(external, device.Get()); Shaders shader(device.Get()); auto reference = uav_buffer(device.Get()); unsigned verified = 0; @@ -173,7 +186,10 @@ int main(int argc, char** argv) { void* data = nullptr; D3D12_RANGE noRead{}; dx(upload->Map(0, &noRead, &data), "Pixel fixture upload"); std::memset(data, 0x5a, size_t(shared.bytes)); for (unsigned y = 0; y < height; ++y) fixture(static_cast(data) + y * shared.footprint.Footprint.RowPitch, y, spec); upload->Unmap(0, nullptr); { List list(device.Get()); D3D12_TEXTURE_COPY_LOCATION src{}, dst{}; src.pResource = upload.Get(); src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; src.PlacedFootprint = shared.footprint; dst.pResource = input.Get(); dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - transition(list.list.Get(), input.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST); list.list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); transition(list.list.Get(), input.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); shared.copy_input(list.list.Get(), input.Get(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); list.submit(queue.Get(), timeline); } + transition(list.list.Get(), input.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST); list.list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + transition(list.list.Get(), input.Get(), D3D12_RESOURCE_STATE_COPY_DEST, enhanced ? D3D12_RESOURCE_STATE_COMMON : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + if (enhanced) native_layout(list.list.Get(),input.Get(),D3D12_BARRIER_LAYOUT_COMMON,D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE,D3D12_BARRIER_ACCESS_COMMON,D3D12_BARRIER_ACCESS_SHADER_RESOURCE); + shared.copy_input(list.list.Get(), input.Get(), enhanced ? shader_access() : ResourceAccess(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)); list.submit(queue.Get(), timeline); } D3D12_DESCRIPTOR_HEAP_DESC hd{}; hd.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; hd.NumDescriptors = 4; hd.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; ComPtr heap; dx(device->CreateDescriptorHeap(&hd, IID_PPV_ARGS(heap.GetAddressOf())), "Pixel descriptors"); D3D12_CPU_DESCRIPTOR_HANDLE cpu{}, initial{}; D3D12_GPU_DESCRIPTOR_HANDLE gpu{}; @@ -218,7 +234,11 @@ int main(int argc, char** argv) { program.convert(true, canonical.data, canonical.pitch, shared.mapped, shared.footprint.Footprint.RowPitch, width, height, spec.storage, 3); cpu.ptr = initial.ptr + stride; srv = {}; srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srv.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; srv.Buffer.NumElements = count; srv.Buffer.StructureByteStride = 16; device->CreateShaderResourceView(canonicalUpload.Get(), &srv, cpu); cpu.ptr += stride; D3D12_UNORDERED_ACCESS_VIEW_DESC uv{}; uv.Format = encodedFormat; uv.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; device->CreateUnorderedAccessView(encoded.Get(), nullptr, &uv, cpu); - { List list(device.Get()); transition(list.list.Get(), encoded.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); list.list->SetPipelineState(shader.encode.Get()); list.list->SetComputeRootSignature(shader.root.Get()); ID3D12DescriptorHeap* heaps[] = {heap.Get()}; list.list->SetDescriptorHeaps(1, heaps); auto handle = gpu; handle.ptr += stride; list.list->SetComputeRootDescriptorTable(1, handle); handle.ptr += stride; list.list->SetComputeRootDescriptorTable(2, handle); unsigned constants[] = {width,height,0}; list.list->SetComputeRoot32BitConstants(4,3,constants,0); list.list->Dispatch((count+63)/64,1,1); transition(list.list.Get(), encoded.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); shared.copy_output(list.list.Get(), actualTexture.Get(), D3D12_RESOURCE_STATE_COMMON); transition(list.list.Get(), actualTexture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); list.submit(queue.Get(),timeline); } + { List list(device.Get()); transition(list.list.Get(), encoded.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); list.list->SetPipelineState(shader.encode.Get()); list.list->SetComputeRootSignature(shader.root.Get()); ID3D12DescriptorHeap* heaps[] = {heap.Get()}; list.list->SetDescriptorHeaps(1, heaps); auto handle = gpu; handle.ptr += stride; list.list->SetComputeRootDescriptorTable(1, handle); handle.ptr += stride; list.list->SetComputeRootDescriptorTable(2, handle); unsigned constants[] = {width,height,0}; list.list->SetComputeRoot32BitConstants(4,3,constants,0); list.list->Dispatch((count+63)/64,1,1); transition(list.list.Get(), encoded.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + ResourceAccess state; state.enhanced = enhanced; shared.copy_output(list.list.Get(), actualTexture.Get(), state); + if (enhanced) native_layout(list.list.Get(),actualTexture.Get(),D3D12_BARRIER_LAYOUT_COMMON,D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE,D3D12_BARRIER_ACCESS_COMMON,D3D12_BARRIER_ACCESS_SHADER_RESOURCE); + else transition(list.list.Get(), actualTexture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + list.submit(queue.Get(),timeline); } srv = {}; srv.Format = item.view; srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srv.Texture2D.MipLevels = 1; device->CreateShaderResourceView(actualTexture.Get(), &srv, initial); auto encodedActual = read_reference(device.Get(), queue.Get(), timeline, shader, heap.Get(), gpu, reference.Get(), 4); srv.Format = encodedFormat; device->CreateShaderResourceView(encoded.Get(), &srv, initial); @@ -228,6 +248,6 @@ int main(int argc, char** argv) { } ++verified; std::printf("PASS PIXEL_FORMAT name=%s plane=%u pixels=%u decode_max_abs=%.9g encode=%u\n", item.name,item.plane,count,maxAbs,item.plane==0); } - std::printf("PASS D3D12_GPU_FORMATS architecture=gfx1201 cases=%u iterations=%u width=%u height=%u typed_srv_reference=1 typed_uav_reference=1\n", verified,args.iterations,width,height); return 0; + std::printf("PASS D3D12_GPU_FORMATS architecture=gfx1201 cases=%u iterations=%u width=%u height=%u enhanced=%u typed_srv_reference=1 typed_uav_reference=1\n", verified,args.iterations,width,height,enhanced); return 0; } catch (const std::exception& error) { std::fprintf(stderr,"FAIL D3D12_GPU_FORMATS %s\n",error.what()); loaded_modules(); return 1; } } diff --git a/tools/windows/d4r_nvngx_windows.cpp b/tools/windows/d4r_nvngx_windows.cpp index a3372f1..bcd0dad 100644 --- a/tools/windows/d4r_nvngx_windows.cpp +++ b/tools/windows/d4r_nvngx_windows.cpp @@ -106,9 +106,16 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co 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)); + const auto state = d4r::win::commands::resource_access(list, resource, d4r::win::ngx_resource_access(p, states[i], + i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)); + if (state.pending_split) throw std::runtime_error("NGX resource has an unfinished split barrier"); + d4r_ngx_set_uint(snapshot->parameters, states[i], unsigned(state.legacy)); + const std::string prefix(states[i]); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Enhanced").c_str(), state.enhanced); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Layout").c_str(), unsigned(state.layout)); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Access").c_str(), unsigned(state.access)); + std::printf("D4R_RESOURCE_ACCESS plane=%s enhanced=%u legacy=0x%x layout=%u access=0x%x\n", + names[i], state.enhanced, unsigned(state.legacy), unsigned(state.layout), unsigned(state.access)); } d4r::win::commands::record_boundary(list, [feature, snapshot](ID3D12CommandQueue* queue) { feature->evaluate_boundary(queue, snapshot->parameters); }); return 1u; diff --git a/tools/windows/diagnostic.h b/tools/windows/diagnostic.h index ce92398..1c7bbe5 100644 --- a/tools/windows/diagnostic.h +++ b/tools/windows/diagnostic.h @@ -111,6 +111,7 @@ struct Args { std::string stream_lifetime = "persistent"; std::string interop_mode = "roundtrip"; std::string pixel_profile = "baseline"; + std::string barrier_mode = "legacy"; std::string ngx_core, dlss_dll; std::string nvapi_dll; std::string ngx_abi = "driver"; @@ -136,6 +137,10 @@ struct Args { if (value != "baseline" && value != "packed" && value != "unorm") throw std::runtime_error("pixel-profile must be baseline, packed or unorm"); pixel_profile = value; } + else if (key == "--barrier-mode") { + if (value != "legacy" && value != "enhanced") throw std::runtime_error("barrier-mode must be legacy or enhanced"); + barrier_mode = value; + } else if (key == "--ngx-core") ngx_core = value; else if (key == "--dlss-dll") dlss_dll = value; else if (key == "--nvapi-dll") nvapi_dll = value; diff --git a/tools/windows/generate_d3d12_hooks.py b/tools/windows/generate_d3d12_hooks.py index f85669b..204cad6 100644 --- a/tools/windows/generate_d3d12_hooks.py +++ b/tools/windows/generate_d3d12_hooks.py @@ -120,6 +120,7 @@ def generate(source): 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")};') + lines.append(f'static constexpr unsigned device_command_list_slot = {fields.index("CreateCommandList")};') return '\n'.join(lines) diff --git a/tools/windows/ngx_windows_runtime.h b/tools/windows/ngx_windows_runtime.h index 55a4a31..392299b 100644 --- a/tools/windows/ngx_windows_runtime.h +++ b/tools/windows/ngx_windows_runtime.h @@ -19,6 +19,16 @@ inline void ngx_check(unsigned result, const char* operation) { std::printf("D4R_NGX %s result=0x%08x\n", operation, result); if (result != 1) throw std::runtime_error(std::string(operation) + " NGX=" + std::to_string(result)); } +inline ResourceAccess ngx_resource_access(void* parameters, const char* name, D3D12_RESOURCE_STATES fallback) { + ResourceAccess result(D3D12_RESOURCE_STATES(ngx::uint_value(parameters, name, unsigned(fallback)))); + const std::string prefix(name); + result.enhanced = ngx::uint_value(parameters, (prefix + ".Enhanced").c_str()) != 0; + if (result.enhanced) { + result.layout = D3D12_BARRIER_LAYOUT(ngx::uint_value(parameters, (prefix + ".Layout").c_str())); + result.access = D3D12_BARRIER_ACCESS(ngx::uint_value(parameters, (prefix + ".Access").c_str())); + } + return result; +} inline std::filesystem::path pixel_module_path() { if (const char* path = std::getenv("D4R_FORMAT_MODULE")) return diag::wide(env_path("D4R_FORMAT_MODULE")); HMODULE module = nullptr; @@ -251,7 +261,7 @@ private: void record_inputs(ID3D12GraphicsCommandList* list, void* parameters) { for (unsigned i : {0u, 1u, 2u, 4u}) if (planes_[i].texture) { static const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"}; - planes_[i].shared->copy_input(list, planes_[i].texture.Get(), D3D12_RESOURCE_STATES(ngx::uint_value(parameters, states[i], D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE))); + planes_[i].shared->copy_input(list, planes_[i].texture.Get(), ngx_resource_access(parameters, states[i], D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)); } } void execute_cuda(ID3D12CommandQueue* queue) { @@ -296,7 +306,7 @@ private: std::chrono::duration(std::chrono::steady_clock::now() - begin).count()); } void record_output(ID3D12GraphicsCommandList* list, void* parameters) { - planes_[3].shared->copy_output(list, planes_[3].texture.Get(), D3D12_RESOURCE_STATES(ngx::uint_value(parameters, "D4R.Output.State", D3D12_RESOURCE_STATE_UNORDERED_ACCESS))); + planes_[3].shared->copy_output(list, planes_[3].texture.Get(), ngx_resource_access(parameters, "D4R.Output.State", D3D12_RESOURCE_STATE_UNORDERED_ACCESS)); } }; }