diff --git a/docs/windows-rdna4-port.md b/docs/windows-rdna4-port.md index 0c381de..d12e23c 100644 --- a/docs/windows-rdna4-port.md +++ b/docs/windows-rdna4-port.md @@ -137,10 +137,32 @@ 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. +Tracking now also publishes explicit ordinary texture state/layout metadata +in the actual queue submission order. Each split segment retains its own +resource-state snapshot and COM references. An Evaluate input with no barrier +in its own list resolves the most recently submitted state at the callback, +after preceding lists in the batch. The metadata belongs to the resource, with +no global pointer cache or ownership cycle. Legacy buffer/simultaneous-access +decay is not treated as persistent texture state. Unknown states retain the +explicit caller metadata or NGX legacy default and are logged as tracked=0. + +ExecuteCommandLists, Signal and Wait on one queue use a queue-owned submission +mutex: another thread cannot insert a submission/fence into prefix -> DLSS -> +suffix. Internal callback operations bypass that lock while the outer batch +owns it. The lock metadata has no reference back to its owning queue. +The backend now installs 83 public-method hooks. + +`optiscaler-m-inherited-final.zip` passes four complete enhanced-layout M frames +with the transitions recorded only in predecessor A, none in the evaluated B. +`optiscaler-k-inherited.zip` covers legacy state inheritance. Both have all +five planes tracked=1 on all frames (M layouts 21/20; K states 0x40/0x8), full +native network hits, zero NaN/Inf and exact RGB baseline agreement, max +absolute/relative error 0. Full build/CTest still pass 16/16 +(`inherited-queue-final-ctest.log`). Use `-BarrierMode inherited-enhanced` or +`inherited-legacy` to reproduce. Tracking still requires observing creation +or a transition after hook installation; initial enhanced states outside +that observation remain a game 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. diff --git a/scripts/windows/test-windows-rdna4.ps1 b/scripts/windows/test-windows-rdna4.ps1 index d949758..b9ae9a1 100644 --- a/scripts/windows/test-windows-rdna4.ps1 +++ b/scripts/windows/test-windows-rdna4.ps1 @@ -17,7 +17,7 @@ param( [switch]$CommandListBackend, [switch]$CaptureExceptions, [ValidateSet('baseline','packed','unorm')][string]$PixelProfile = 'baseline', - [ValidateSet('legacy','enhanced')][string]$BarrierMode = 'legacy', + [ValidateSet('legacy','enhanced','inherited-legacy','inherited-enhanced')][string]$BarrierMode = 'legacy', [int]$Iterations = 32, [int]$TimeoutSeconds = 180 ) diff --git a/tools/windows/d3d12_command_hooks.cpp b/tools/windows/d3d12_command_hooks.cpp index fcaae2c..920f492 100644 --- a/tools/windows/d3d12_command_hooks.cpp +++ b/tools/windows/d3d12_command_hooks.cpp @@ -15,6 +15,22 @@ std::string last_error; bool installed = false; struct Event { UINT metadata; std::vector data; }; using State = std::function; +struct TrackedResource { + ComPtr owner; + ResourceAccess state; +}; +using ResourceStates = std::unordered_map; +static constexpr GUID submitted_access_tag = {0x741b92df,0x9b13,0x4fc9,{0xb0,0x02,0x2c,0xe3,0x5a,0x68,0x92,0x41}}; +void publish_states(const ResourceStates& states) { + for (const auto& entry : states) { + // Buffers/simultaneous-access textures have automatic legacy state + // decay at ExecuteCommandLists completion. Only ordinary textures + // retain these explicit transitions across submission boundaries. + const auto desc = resource_desc(entry.second.owner.Get()); + if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER || (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)) continue; + dx(entry.second.owner->SetPrivateData(submitted_access_tag, sizeof(ResourceAccess), &entry.second.state), "Publish submitted texture access"); + } +} struct Segment { // The application's original list is a weak pointer; holding it in its // own routing map would prevent final Release and leak the entire feature. @@ -22,6 +38,7 @@ struct Segment { ComPtr owner; ComPtr allocator; Boundary boundary; + ResourceStates resource_states; }; struct Recording { std::recursive_mutex mutex; @@ -31,7 +48,7 @@ struct Recording { std::vector segments; std::vector state; std::vector events; - std::unordered_map resource_states; + ResourceStates resource_states; unsigned active_queries = 0; bool render_pass = false, predication = false, indirect_state_unknown = false, closed = false; explicit Recording(ID3D12GraphicsCommandList* list) : original(list) {} @@ -187,7 +204,9 @@ void STDMETHODCALLTYPE hook_ResourceBarrier(ID3D12GraphicsCommandList* self, UIN if (access.recording) for (UINT i = 0; i < barrier_count; ++i) 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]; + auto& tracked = access.recording->resource_states[barriers[i].Transition.pResource]; + tracked.owner = retain(barriers[i].Transition.pResource); + auto& state = tracked.state; state = ResourceAccess(barriers[i].Transition.StateAfter); state.pending_split = barriers[i].Flags == D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY; } @@ -201,7 +220,8 @@ void STDMETHODCALLTYPE hook_Barrier(ID3D12GraphicsCommandList* self, UINT32 barr 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; + auto& tracked = access.recording->resource_states[barrier.pResource]; tracked.owner = retain(barrier.pResource); + auto& state = tracked.state; state.enhanced = true; state.inherited = false; state.layout = barrier.LayoutAfter; state.access = barrier.AccessAfter; state.sync = barrier.SyncAfter; state.pending_split = barrier.SyncAfter == D3D12_BARRIER_SYNC_SPLIT; } @@ -308,6 +328,48 @@ void STDMETHODCALLTYPE hook_ExecuteIndirect(ID3D12GraphicsCommandList* self, ID3 using ExecuteFn = void(STDMETHODCALLTYPE*)(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*); ExecuteFn original_execute = nullptr; +// Queue-owned synchronization metadata has no reference back to the queue. +// It serializes a split submission against other threads using the same queue. +static constexpr GUID submission_lock_tag = {0x6c6158ba,0x209f,0x4a09,{0x97,0x10,0xa7,0x03,0x52,0x32,0xa0,0x69}}; +std::mutex queue_metadata_mutex; +struct SubmissionLock final : IUnknown { + std::atomic references{1}; + std::recursive_mutex mutex; + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** out) override { + if (!out) return E_POINTER; *out = nullptr; + if (iid != IID_IUnknown) return E_NOINTERFACE; + *out = static_cast(this); AddRef(); return S_OK; + } + ULONG STDMETHODCALLTYPE AddRef() override { return ++references; } + ULONG STDMETHODCALLTYPE Release() override { const auto n = --references; if (!n) delete this; return n; } +}; +ComPtr submission_lock(ID3D12CommandQueue* queue) { + std::lock_guard lock(queue_metadata_mutex); + ComPtr result; UINT bytes = sizeof(IUnknown*); + if (SUCCEEDED(queue->GetPrivateData(submission_lock_tag, &bytes, result.GetAddressOf()))) return result; + result.Attach(new SubmissionLock); + dx(queue->SetPrivateDataInterface(submission_lock_tag, result.Get()), "Queue submission metadata"); + return result; +} +using QueueFenceFn = HRESULT(STDMETHODCALLTYPE*)(ID3D12CommandQueue*, ID3D12Fence*, UINT64); +QueueFenceFn original_queue_signal = nullptr, original_queue_wait = nullptr; +HRESULT queue_fence(QueueFenceFn function, ID3D12CommandQueue* queue, ID3D12Fence* fence, UINT64 value) noexcept { + if (internal) return function(queue, fence, value); + try { + auto queueLock = submission_lock(queue); + std::lock_guard ordered(queueLock->mutex); + return function(queue, fence, value); + } catch (const std::exception& failure) { + { std::lock_guard lock(error_mutex); last_error = failure.what(); } + std::fprintf(stderr, "D4R_QUEUE_FENCE_FAILURE %s\n", failure.what()); return E_FAIL; + } +} +HRESULT STDMETHODCALLTYPE hook_queue_signal(ID3D12CommandQueue* queue, ID3D12Fence* fence, UINT64 value) { + return queue_fence(original_queue_signal, queue, fence, value); +} +HRESULT STDMETHODCALLTYPE hook_queue_wait(ID3D12CommandQueue* queue, ID3D12Fence* fence, UINT64 value) { + return queue_fence(original_queue_wait, queue, fence, value); +} void complete(ID3D12CommandQueue* queue) { ComPtr device; dx(queue->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Queue device(submit drain)"); ComPtr fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())), "Queue fence(submit drain)"); @@ -323,24 +385,39 @@ void STDMETHODCALLTYPE hook_execute(ID3D12CommandQueue* queue, UINT count, ID3D1 if (internal) { original_execute(queue, count, lists); return; } std::vector> owners; std::vector pending; + std::vector pending_states; InternalScope scope; - auto flush = [&] { if (!pending.empty()) { original_execute(queue, UINT(pending.size()), pending.data()); pending.clear(); } }; + auto flush = [&] { + if (!pending.empty()) { + original_execute(queue, UINT(pending.size()), pending.data()); + // Publish in the actual submitted order, never while recording. + // The callback's external fence waits for these producers on GPU. + for (const auto& states : pending_states) publish_states(states); + pending.clear(); pending_states.clear(); + } + }; bool boundaries = false; try { + auto queueLock = submission_lock(queue); + std::lock_guard ordered(queueLock->mutex); for (UINT i = 0; i < count; ++i) { std::shared_ptr recording; { std::lock_guard lock(map_mutex); auto found = recordings.find(static_cast(lists[i])); if (found != recordings.end()) recording = found->second; } - if (!recording || recording->segments.empty()) { pending.push_back(lists[i]); continue; } + if (!recording) { pending.push_back(lists[i]); pending_states.emplace_back(); continue; } std::lock_guard lock(recording->mutex); + if (recording->segments.empty()) { + pending.push_back(lists[i]); pending_states.push_back(recording->resource_states); continue; + } if (!recording->closed) throw std::runtime_error("Logical command list submitted before Close"); owners.push_back(recording); boundaries = true; for (const auto& segment : recording->segments) { - pending.push_back(segment.list); flush(); + pending.push_back(segment.list); pending_states.push_back(segment.resource_states); flush(); segment.boundary(queue); } pending.push_back(recording->current.Get()); + pending_states.push_back(recording->resource_states); } flush(); if (boundaries) complete(queue); @@ -364,10 +441,17 @@ D3D12_RESOURCE_STATES resource_state(ID3D12GraphicsCommandList* list, ID3D12Reso } ResourceAccess resource_access(ID3D12GraphicsCommandList* list, ID3D12Resource* resource, ResourceAccess fallback) { auto recording = get_recording(list, false); + fallback.inherited = true; if (!recording) return fallback; std::lock_guard lock(recording->mutex); auto found = recording->resource_states.find(resource); - return found == recording->resource_states.end() ? fallback : found->second; + return found == recording->resource_states.end() ? fallback : found->second.state; +} +ResourceAccess submitted_resource_access(ID3D12Resource* resource, ResourceAccess fallback) { + if (!resource) return fallback; + ResourceAccess submitted; UINT size = sizeof(submitted); + if (SUCCEEDED(resource->GetPrivateData(submitted_access_tag, &size, &submitted)) && size == sizeof(submitted)) return submitted; + return fallback; } void install(ID3D12Device* device) { std::lock_guard lock(installation_mutex); @@ -405,6 +489,8 @@ void install(ID3D12Device* device) { // Queue inherits IUnknown, Object, DeviceChild; UpdateTileMappings, // CopyTileMappings precede ExecuteCommandLists in the public SDK. attach_method(queueTable[10], reinterpret_cast(hook_execute), reinterpret_cast(&original_execute), "ExecuteCommandLists"); + attach_method(queueTable[queue_signal_slot], reinterpret_cast(hook_queue_signal), reinterpret_cast(&original_queue_signal), "QueueSignal"); + attach_method(queueTable[queue_wait_slot], reinterpret_cast(hook_queue_wait), reinterpret_cast(&original_queue_wait), "QueueWait"); 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"); @@ -435,6 +521,7 @@ void record_boundary(ID3D12GraphicsCommandList* list, Boundary callback) { ComPtr device; dx(list->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Boundary device"); Segment segment; segment.list = access.target(); segment.owner = recording.current; segment.allocator = recording.current_allocator; segment.boundary = std::move(callback); + segment.resource_states = recording.resource_states; ComPtr nextAllocator; ComPtr next; dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(nextAllocator.GetAddressOf())), "Suffix allocator"); dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, nextAllocator.Get(), nullptr, IID_PPV_ARGS(next.GetAddressOf())), "Suffix command list"); diff --git a/tools/windows/d3d12_command_hooks.generated.h b/tools/windows/d3d12_command_hooks.generated.h index 3449a98..89ed165 100644 --- a/tools/windows/d3d12_command_hooks.generated.h +++ b/tools/windows/d3d12_command_hooks.generated.h @@ -699,4 +699,6 @@ static void attach_recording_methods(void** table, unsigned version) { } static constexpr unsigned device_signature_slot = 41; -static constexpr unsigned device_command_list_slot = 12; \ No newline at end of file +static constexpr unsigned device_command_list_slot = 12; +static constexpr unsigned queue_signal_slot = 14; +static constexpr unsigned queue_wait_slot = 15; \ No newline at end of file diff --git a/tools/windows/d3d12_command_hooks.h b/tools/windows/d3d12_command_hooks.h index 929e447..cb41174 100644 --- a/tools/windows/d3d12_command_hooks.h +++ b/tools/windows/d3d12_command_hooks.h @@ -14,6 +14,7 @@ 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); +ResourceAccess submitted_resource_access(ID3D12Resource* resource, ResourceAccess fallback); // Internal copies and callback submissions must bypass logical list routing. struct InternalScope { bool previous; diff --git a/tools/windows/d3d12_dlss_probe.cpp b/tools/windows/d3d12_dlss_probe.cpp index add50bd..6d1414c 100644 --- a/tools/windows/d3d12_dlss_probe.cpp +++ b/tools/windows/d3d12_dlss_probe.cpp @@ -19,7 +19,9 @@ 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"; + const bool inherited = args.barrier_mode.rfind("inherited-", 0) == 0; + const bool enhanced = args.barrier_mode == "enhanced" || args.barrier_mode == "inherited-enhanced"; + if (inherited && !commandBackend) throw std::runtime_error("Inherited state diagnostic requires the command-list backend"); 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"); @@ -117,10 +119,10 @@ 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, - 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); + (enhanced || inherited) ? D3D12_RESOURCE_STATE_COMMON : i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + if (enhanced && !inherited) enhancedBarrier(list.Get(), texture.image.Get(), i, true); } - dx(list->Close(), "Close(synthetic producers)"); ID3D12CommandList* producer[] = {list.Get()}; + if (!inherited) dx(list->Close(), "Close(synthetic producers)"); ID3D12CommandList* producer[] = {list.Get()}; if (!commandBackend) { queue->ExecuteCommandLists(1, producer); drain(); } if (args.interop_mode == "images") { CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit(image interop)"); @@ -218,6 +220,17 @@ int main(int argc, char** argv) { ComPtr predecessor; ComPtr predecessorAllocator; if (commandBackend) { + if (inherited) { + // Record state only in predecessor A after the backend has + // initialized. List B's Evaluate must resolve it at submission, + // after A, and retain it across subsequent frames/resets. + for (unsigned i = 0; i < 5; ++i) { + if (enhanced) enhancedBarrier(list.Get(), textures[i].image.Get(), i, true); + else transition(list.Get(), textures[i].image.Get(), D3D12_RESOURCE_STATE_COMMON, + i == 3 ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + } + dx(list->Close(), "Close tracked predecessor A"); + } // Keep initial synthetic producers unsubmitted until the first // batch, before the list which contains the NGX evaluation. predecessor = list; predecessorAllocator = allocator; @@ -230,7 +243,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); + if (enhanced && !inherited) 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 diff --git a/tools/windows/d3d12_external.h b/tools/windows/d3d12_external.h index 7701875..bba5953 100644 --- a/tools/windows/d3d12_external.h +++ b/tools/windows/d3d12_external.h @@ -67,7 +67,7 @@ inline void transition(ID3D12GraphicsCommandList* list, ID3D12Resource* resource } struct ResourceAccess { D3D12_RESOURCE_STATES legacy = D3D12_RESOURCE_STATE_COMMON; - bool enhanced = false, pending_split = false; + bool enhanced = false, pending_split = false, inherited = 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; diff --git a/tools/windows/d4r_nvngx_windows.cpp b/tools/windows/d4r_nvngx_windows.cpp index 1746ee1..7acfb12 100644 --- a/tools/windows/d4r_nvngx_windows.cpp +++ b/tools/windows/d4r_nvngx_windows.cpp @@ -126,6 +126,7 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co struct Snapshot { void* parameters = d4r_ngx_parameters_create(); std::vector> resources; + d4r::win::ResourceAccess access[5]; ~Snapshot() { if (parameters) d4r_ngx_parameters_destroy(parameters); } }; auto snapshot = std::make_shared(); @@ -139,6 +140,7 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co d4r_ngx_set_d3d12_resource(snapshot->parameters, names[i], resource); 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)); + snapshot->access[i] = state; 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]); @@ -148,7 +150,22 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co 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); }); + d4r::win::commands::record_boundary(list, [feature, snapshot](ID3D12CommandQueue* queue) { + const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; + const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"}; + for (unsigned i = 0; i < 5; ++i) if (snapshot->access[i].inherited) { + const auto resolved = d4r::win::commands::submitted_resource_access(snapshot->resources[i].Get(), snapshot->access[i]); + if (resolved.pending_split) throw std::runtime_error("Inherited NGX texture has an unfinished split barrier"); + const std::string prefix(states[i]); + d4r_ngx_set_uint(snapshot->parameters, states[i], unsigned(resolved.legacy)); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Enhanced").c_str(), resolved.enhanced); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Layout").c_str(), unsigned(resolved.layout)); + d4r_ngx_set_uint(snapshot->parameters, (prefix + ".Access").c_str(), unsigned(resolved.access)); + std::printf("D4R_INHERITED_ACCESS plane=%s tracked=%u enhanced=%u layout=%u legacy=0x%x\n", + names[i], !resolved.inherited, resolved.enhanced, unsigned(resolved.layout), unsigned(resolved.legacy)); + } + feature->evaluate_boundary(queue, snapshot->parameters); + }); return 1u; }); } diff --git a/tools/windows/diagnostic.h b/tools/windows/diagnostic.h index 9ab41da..a02a9f1 100644 --- a/tools/windows/diagnostic.h +++ b/tools/windows/diagnostic.h @@ -145,7 +145,7 @@ struct Args { pixel_profile = value; } else if (key == "--barrier-mode") { - if (value != "legacy" && value != "enhanced") throw std::runtime_error("barrier-mode must be legacy or enhanced"); + if (value != "legacy" && value != "enhanced" && value != "inherited-legacy" && value != "inherited-enhanced") throw std::runtime_error("Invalid barrier-mode"); barrier_mode = value; } else if (key == "--ngx-core") ngx_core = value; diff --git a/tools/windows/generate_d3d12_hooks.py b/tools/windows/generate_d3d12_hooks.py index 204cad6..6dadbd2 100644 --- a/tools/windows/generate_d3d12_hooks.py +++ b/tools/windows/generate_d3d12_hooks.py @@ -121,6 +121,10 @@ def generate(source): 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")};') + queue = re.search(r'typedef struct ID3D12CommandQueueVtbl \{(.*?)\n\} ID3D12CommandQueueVtbl;', source, re.S)[1] + queue_fields = list(dict.fromkeys(re.findall(r'\(STDMETHODCALLTYPE \*(\w+)\)', queue))) + for name in ('Signal', 'Wait'): + lines.append(f'static constexpr unsigned queue_{name.lower()}_slot = {queue_fields.index(name)};') return '\n'.join(lines)