Resolve texture states in queue order across DLSS command lists
This commit is contained in:
1 parent
e14b4cb54c
commit
e4c1c87b66
10 files changed
+167
-21
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -15,6 +15,22 @@ std::string last_error;
|
||||
bool installed = false;
|
||||
struct Event { UINT metadata; std::vector<uint8_t> data; };
|
||||
using State = std::function<void(ID3D12GraphicsCommandList*)>;
|
||||
struct TrackedResource {
|
||||
ComPtr<ID3D12Resource> owner;
|
||||
ResourceAccess state;
|
||||
};
|
||||
using ResourceStates = std::unordered_map<ID3D12Resource*, TrackedResource>;
|
||||
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<ID3D12GraphicsCommandList> owner;
|
||||
ComPtr<ID3D12CommandAllocator> allocator;
|
||||
Boundary boundary;
|
||||
ResourceStates resource_states;
|
||||
};
|
||||
struct Recording {
|
||||
std::recursive_mutex mutex;
|
||||
@@ -31,7 +48,7 @@ struct Recording {
|
||||
std::vector<Segment> segments;
|
||||
std::vector<State> state;
|
||||
std::vector<Event> events;
|
||||
std::unordered_map<ID3D12Resource*, ResourceAccess> 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<ULONG> 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<IUnknown*>(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<SubmissionLock> submission_lock(ID3D12CommandQueue* queue) {
|
||||
std::lock_guard<std::mutex> lock(queue_metadata_mutex);
|
||||
ComPtr<SubmissionLock> 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<std::recursive_mutex> ordered(queueLock->mutex);
|
||||
return function(queue, fence, value);
|
||||
} catch (const std::exception& failure) {
|
||||
{ std::lock_guard<std::mutex> 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<ID3D12Device> device; dx(queue->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Queue device(submit drain)");
|
||||
ComPtr<ID3D12Fence> fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.GetAddressOf())), "Queue fence(submit drain)");
|
||||
@@ -323,24 +385,39 @@ void STDMETHODCALLTYPE hook_execute(ID3D12CommandQueue* queue, UINT count, ID3D1
|
||||
if (internal) { original_execute(queue, count, lists); return; }
|
||||
std::vector<std::shared_ptr<Recording>> owners;
|
||||
std::vector<ID3D12CommandList*> pending;
|
||||
std::vector<ResourceStates> 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<std::recursive_mutex> ordered(queueLock->mutex);
|
||||
for (UINT i = 0; i < count; ++i) {
|
||||
std::shared_ptr<Recording> recording;
|
||||
{ std::lock_guard<std::mutex> lock(map_mutex);
|
||||
auto found = recordings.find(static_cast<ID3D12GraphicsCommandList*>(lists[i]));
|
||||
if (found != recordings.end()) recording = found->second; }
|
||||
if (!recording || recording->segments.empty()) { pending.push_back(lists[i]); continue; }
|
||||
if (!recording) { pending.push_back(lists[i]); pending_states.emplace_back(); continue; }
|
||||
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::mutex> 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<void*>(hook_execute), reinterpret_cast<void**>(&original_execute), "ExecuteCommandLists");
|
||||
attach_method(queueTable[queue_signal_slot], reinterpret_cast<void*>(hook_queue_signal), reinterpret_cast<void**>(&original_queue_signal), "QueueSignal");
|
||||
attach_method(queueTable[queue_wait_slot], reinterpret_cast<void*>(hook_queue_wait), reinterpret_cast<void**>(&original_queue_wait), "QueueWait");
|
||||
auto deviceTable = *reinterpret_cast<void***>(device);
|
||||
attach_method(deviceTable[device_command_list_slot], reinterpret_cast<void*>(hook_create_list), reinterpret_cast<void**>(&original_create_list), "CreateCommandList");
|
||||
attach_method(deviceTable[device_signature_slot], reinterpret_cast<void*>(hook_signature), reinterpret_cast<void**>(&original_signature), "CreateCommandSignature");
|
||||
@@ -435,6 +521,7 @@ void record_boundary(ID3D12GraphicsCommandList* list, Boundary callback) {
|
||||
ComPtr<ID3D12Device> device; dx(list->GetDevice(IID_PPV_ARGS(device.GetAddressOf())), "Boundary device");
|
||||
Segment segment; segment.list = access.target(); segment.owner = recording.current;
|
||||
segment.allocator = recording.current_allocator; segment.boundary = std::move(callback);
|
||||
segment.resource_states = recording.resource_states;
|
||||
ComPtr<ID3D12CommandAllocator> nextAllocator; ComPtr<ID3D12GraphicsCommandList> next;
|
||||
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(nextAllocator.GetAddressOf())), "Suffix allocator");
|
||||
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, nextAllocator.Get(), nullptr, IID_PPV_ARGS(next.GetAddressOf())), "Suffix command list");
|
||||
|
||||
@@ -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;
|
||||
static constexpr unsigned device_command_list_slot = 12;
|
||||
static constexpr unsigned queue_signal_slot = 14;
|
||||
static constexpr unsigned queue_wait_slot = 15;
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ID3D12GraphicsCommandList> predecessor;
|
||||
ComPtr<ID3D12CommandAllocator> 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<RecordedEvaluate>("NVSDK_NGX_D3D12_EvaluateFeature")(list.Get(), handle, parameters, nullptr), "D3D12 recorded same-frame Evaluate");
|
||||
// The caller may reuse/mutate its parameter object as soon as
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -126,6 +126,7 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co
|
||||
struct Snapshot {
|
||||
void* parameters = d4r_ngx_parameters_create();
|
||||
std::vector<d4r::win::ComPtr<ID3D12Resource>> resources;
|
||||
d4r::win::ResourceAccess access[5];
|
||||
~Snapshot() { if (parameters) d4r_ngx_parameters_destroy(parameters); }
|
||||
};
|
||||
auto snapshot = std::make_shared<Snapshot>();
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user