mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[GPU/D3D12] Implement ZPD queries for ROV path
A new counter buffer path has been added for the ROV ZPD path. The backend allocates and publishes a counter slot for the active query, the translated PS accumulates surviving MSAA samples into that slot, and submission close copies finished counters to readback.
This commit is contained in:
@@ -57,7 +57,8 @@ DEFINE_string(
|
||||
occlusion_query, "fast",
|
||||
"Controls hardware occlusion query behavior for EVENT_WRITE_ZPD.\n"
|
||||
"Used for effects like lens flares, object culling, and auto-exposure.\n"
|
||||
"ROV render path currently supports fake mode only.\n"
|
||||
"Titles that use QueryBatch are not currently supported and fall back to\n"
|
||||
"fake mode, regardless of this setting.\n"
|
||||
" fake: Write a fake result without asking the GPU. Safe for most games,\n"
|
||||
" though some effects may look slightly wrong.\n"
|
||||
" fast: Ask the GPU but don't wait for the answer. Writes a cached\n"
|
||||
|
||||
@@ -310,8 +310,8 @@ ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature(
|
||||
parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
}
|
||||
|
||||
// Shared memory and, if ROVs are used, EDRAM.
|
||||
D3D12_DESCRIPTOR_RANGE shared_memory_and_edram_ranges[3];
|
||||
// Shared memory and, if ROVs are used, EDRAM and the ZPD counter.
|
||||
D3D12_DESCRIPTOR_RANGE shared_memory_and_edram_ranges[4];
|
||||
{
|
||||
auto& parameter = parameters[kRootParameter_Bindful_SharedMemoryAndEdram];
|
||||
parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
@@ -344,6 +344,14 @@ ID3D12RootSignature* D3D12CommandProcessor::GetRootSignature(
|
||||
UINT(DxbcShaderTranslator::UAVRegister::kEdram);
|
||||
shared_memory_and_edram_ranges[2].RegisterSpace = 0;
|
||||
shared_memory_and_edram_ranges[2].OffsetInDescriptorsFromTableStart = 2;
|
||||
++parameter.DescriptorTable.NumDescriptorRanges;
|
||||
shared_memory_and_edram_ranges[3].RangeType =
|
||||
D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
shared_memory_and_edram_ranges[3].NumDescriptors = 1;
|
||||
shared_memory_and_edram_ranges[3].BaseShaderRegister =
|
||||
UINT(DxbcShaderTranslator::UAVRegister::kZpdRovCounter);
|
||||
shared_memory_and_edram_ranges[3].RegisterSpace = 0;
|
||||
shared_memory_and_edram_ranges[3].OffsetInDescriptorsFromTableStart = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,8 +879,8 @@ bool D3D12CommandProcessor::SetupContext() {
|
||||
}
|
||||
// Initially in open state, wait until a deferred command list submission.
|
||||
command_list_->Close();
|
||||
// Optional - added in Creators Update (SDK 10.0.15063.0).
|
||||
command_list_->QueryInterface(IID_PPV_ARGS(&command_list_1_));
|
||||
command_list_->QueryInterface(IID_PPV_ARGS(&command_list_2_));
|
||||
|
||||
bindless_resources_used_ =
|
||||
cvars::d3d12_bindless &&
|
||||
@@ -1098,7 +1106,7 @@ bool D3D12CommandProcessor::SetupContext() {
|
||||
root_bindless_sampler_range.OffsetInDescriptorsFromTableStart = 0;
|
||||
}
|
||||
// View heap.
|
||||
D3D12_DESCRIPTOR_RANGE root_bindless_view_ranges[4];
|
||||
D3D12_DESCRIPTOR_RANGE root_bindless_view_ranges[5];
|
||||
{
|
||||
auto& parameter =
|
||||
root_parameters_bindless[kRootParameter_Bindless_ViewHeap];
|
||||
@@ -1121,6 +1129,18 @@ bool D3D12CommandProcessor::SetupContext() {
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart =
|
||||
UINT(SystemBindlessView::kEdramR32UintUAV);
|
||||
assert_true(parameter.DescriptorTable.NumDescriptorRanges <
|
||||
xe::countof(root_bindless_view_ranges));
|
||||
auto& counter_range =
|
||||
root_bindless_view_ranges[parameter.DescriptorTable
|
||||
.NumDescriptorRanges++];
|
||||
counter_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
counter_range.NumDescriptors = 1;
|
||||
counter_range.BaseShaderRegister =
|
||||
UINT(DxbcShaderTranslator::UAVRegister::kZpdRovCounter);
|
||||
counter_range.RegisterSpace = 0;
|
||||
counter_range.OffsetInDescriptorsFromTableStart =
|
||||
UINT(SystemBindlessView::kZpdROVCounterRawUAV);
|
||||
}
|
||||
// Used UAV and SRV ranges must not overlap on Nvidia Fermi, so textures
|
||||
// have OffsetInDescriptorsFromTableStart after all static descriptors of
|
||||
@@ -1672,6 +1692,13 @@ bool D3D12CommandProcessor::SetupContext() {
|
||||
view_bindless_heap_cpu_start_,
|
||||
uint32_t(SystemBindlessView::kEdramR32G32B32A32UintUAV)),
|
||||
4);
|
||||
// kZpdROVCounterRawUAV.
|
||||
ui::d3d12::util::CreateBufferRawUAV(
|
||||
device,
|
||||
provider.OffsetViewDescriptor(
|
||||
view_bindless_heap_cpu_start_,
|
||||
uint32_t(SystemBindlessView::kZpdROVCounterRawUAV)),
|
||||
nullptr, 0);
|
||||
// kGammaRampTableSRV.
|
||||
WriteGammaRampSRV(false,
|
||||
provider.OffsetViewDescriptor(
|
||||
@@ -1798,6 +1825,7 @@ void D3D12CommandProcessor::ShutdownContext() {
|
||||
|
||||
deferred_command_list_.Reset();
|
||||
ui::d3d12::util::ReleaseAndNull(command_list_1_);
|
||||
ui::d3d12::util::ReleaseAndNull(command_list_2_);
|
||||
ui::d3d12::util::ReleaseAndNull(command_list_);
|
||||
ClearCommandAllocatorCache();
|
||||
|
||||
@@ -4027,7 +4055,8 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) {
|
||||
command_allocator_writable_first_->command_allocator;
|
||||
command_allocator->Reset();
|
||||
command_list_->Reset(command_allocator, nullptr);
|
||||
deferred_command_list_.Execute(command_list_, command_list_1_);
|
||||
deferred_command_list_.Execute(command_list_, command_list_1_,
|
||||
command_list_2_);
|
||||
command_list_->Close();
|
||||
ID3D12CommandList* execute_command_lists[] = {command_list_};
|
||||
direct_queue->ExecuteCommandLists(1, execute_command_lists);
|
||||
@@ -4664,9 +4693,17 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
rb_depth_info.depth_base * edram_tile_dwords_scaled;
|
||||
update_dirty_uint32_cmp(system_constants_.edram_depth_base_dwords_scaled,
|
||||
depth_base_dwords_scaled);
|
||||
|
||||
system_constants_.edram_depth_base_dwords_scaled = depth_base_dwords_scaled;
|
||||
|
||||
uint32_t zpd_rov_counter_index = UINT32_MAX;
|
||||
if (zpd_active_query_is_rov_ && zpd_active_query_index_ != UINT32_MAX &&
|
||||
zpd_host_query_pool_->rov_counter_initialized()) {
|
||||
zpd_rov_counter_index = zpd_active_query_index_;
|
||||
}
|
||||
update_dirty_uint32_cmp(system_constants_.zpd_rov_counter_index,
|
||||
zpd_rov_counter_index);
|
||||
system_constants_.zpd_rov_counter_index = zpd_rov_counter_index;
|
||||
|
||||
// For non-polygons, front polygon offset is used, and it's enabled if
|
||||
// POLY_OFFSET_PARA_ENABLED is set, for polygons, separate front and back
|
||||
// are used.
|
||||
@@ -5543,8 +5580,8 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
||||
size_t view_count_full_update =
|
||||
2 + texture_count_vertex + texture_count_pixel;
|
||||
if (edram_rov_used) {
|
||||
// + EDRAM UAV.
|
||||
++view_count_full_update;
|
||||
// + EDRAM UAV and the ZPD counter UAV.
|
||||
view_count_full_update += 2;
|
||||
}
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE view_cpu_handle;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE view_gpu_handle;
|
||||
@@ -5588,7 +5625,7 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
||||
bindful_textures_written_vertex_ = false;
|
||||
bindful_textures_written_pixel_ = false;
|
||||
// If updating fully, write the shared memory SRV and UAV descriptors and,
|
||||
// if needed, the EDRAM descriptor.
|
||||
// if needed, the EDRAM and ZPD counter descriptors.
|
||||
gpu_handle_shared_memory_srv_and_edram_ = view_gpu_handle;
|
||||
shared_memory_->WriteRawSRVDescriptor(view_cpu_handle);
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
@@ -5600,8 +5637,19 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
||||
render_target_cache_->WriteEdramUintPow2UAVDescriptor(view_cpu_handle, 2);
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
view_gpu_handle.ptr += descriptor_size_view;
|
||||
if (zpd_host_query_pool_->rov_counter_initialized()) {
|
||||
ui::d3d12::util::CreateBufferRawUAV(
|
||||
provider.GetDevice(), view_cpu_handle,
|
||||
zpd_host_query_pool_->rov_counter_buffer(),
|
||||
sizeof(uint32_t) * zpd_host_query_pool_->capacity());
|
||||
} else {
|
||||
ui::d3d12::util::CreateBufferRawUAV(provider.GetDevice(),
|
||||
view_cpu_handle, nullptr, 0);
|
||||
}
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
view_gpu_handle.ptr += descriptor_size_view;
|
||||
}
|
||||
// Null SRV + UAV + EDRAM.
|
||||
// Null SRV + UAV + EDRAM + ZPD counter.
|
||||
gpu_handle_shared_memory_uav_and_edram_ = view_gpu_handle;
|
||||
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle,
|
||||
nullptr, 0);
|
||||
@@ -5614,6 +5662,17 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
||||
render_target_cache_->WriteEdramUintPow2UAVDescriptor(view_cpu_handle, 2);
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
view_gpu_handle.ptr += descriptor_size_view;
|
||||
if (zpd_host_query_pool_->rov_counter_initialized()) {
|
||||
ui::d3d12::util::CreateBufferRawUAV(
|
||||
provider.GetDevice(), view_cpu_handle,
|
||||
zpd_host_query_pool_->rov_counter_buffer(),
|
||||
sizeof(uint32_t) * zpd_host_query_pool_->capacity());
|
||||
} else {
|
||||
ui::d3d12::util::CreateBufferRawUAV(provider.GetDevice(),
|
||||
view_cpu_handle, nullptr, 0);
|
||||
}
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
view_gpu_handle.ptr += descriptor_size_view;
|
||||
}
|
||||
current_graphics_root_up_to_date_ &=
|
||||
~(1u << kRootParameter_Bindful_SharedMemoryAndEdram);
|
||||
@@ -5746,16 +5805,57 @@ void D3D12CommandProcessor::EnsureZPDQueryResources() {
|
||||
|
||||
bool can_recreate = !zpd_active_segment_.logical_active &&
|
||||
!zpd_active_segment_.segment_active &&
|
||||
zpd_active_query_index_ == UINT32_MAX &&
|
||||
!zpd_active_query_is_rov_ &&
|
||||
!zpd_host_query_pool_->has_pending_resolve_batch() &&
|
||||
zpd_resolves_in_flight_.empty();
|
||||
// The ROV counter clear uses WriteBufferImmediate, so only initialize when
|
||||
// CommandList2 is available.
|
||||
bool initialize_rov_counter =
|
||||
render_target_cache_->GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock &&
|
||||
command_list_2_ != nullptr;
|
||||
|
||||
zpd_host_query_pool_->EnsureInitialized(GetD3D12Provider(),
|
||||
kZPDQueryPoolCapacity, can_recreate);
|
||||
kZPDQueryPoolCapacity, can_recreate,
|
||||
initialize_rov_counter);
|
||||
if (bindless_resources_used_) {
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE handle =
|
||||
GetD3D12Provider().OffsetViewDescriptor(
|
||||
view_bindless_heap_cpu_start_,
|
||||
uint32_t(SystemBindlessView::kZpdROVCounterRawUAV));
|
||||
if (zpd_host_query_pool_->rov_counter_initialized()) {
|
||||
ui::d3d12::util::CreateBufferRawUAV(
|
||||
GetD3D12Provider().GetDevice(), handle,
|
||||
zpd_host_query_pool_->rov_counter_buffer(),
|
||||
sizeof(uint32_t) * zpd_host_query_pool_->capacity());
|
||||
} else {
|
||||
ui::d3d12::util::CreateBufferRawUAV(GetD3D12Provider().GetDevice(),
|
||||
handle, nullptr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::IsZPDQueryPoolReady() const {
|
||||
if (!zpd_host_query_pool_ || !zpd_host_query_pool_->is_initialized()) {
|
||||
return false;
|
||||
}
|
||||
if (!render_target_cache_ ||
|
||||
render_target_cache_->GetPath() !=
|
||||
RenderTargetCache::Path::kPixelShaderInterlock) {
|
||||
return true;
|
||||
}
|
||||
return zpd_host_query_pool_->rov_counter_initialized();
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::CanOpenZPDQuery() const { return submission_open_; }
|
||||
|
||||
CommandProcessor::QueryOpenResult D3D12CommandProcessor::OpenZPDQuery(
|
||||
ReportHandle report_handle, bool can_close_submission) {
|
||||
bool use_rov_counter_path =
|
||||
zpd_host_query_pool_->rov_counter_initialized() &&
|
||||
render_target_cache_->GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
bool is_pool_exhausted = !zpd_host_query_pool_->has_free_indices();
|
||||
|
||||
if (is_pool_exhausted) {
|
||||
@@ -5808,29 +5908,58 @@ CommandProcessor::QueryOpenResult D3D12CommandProcessor::OpenZPDQuery(
|
||||
return QueryOpenResult::kFailed;
|
||||
}
|
||||
|
||||
zpd_active_query_is_rov_ = use_rov_counter_path;
|
||||
|
||||
// ROV queries don't use D3D12 occlusion queries at all.
|
||||
// While the segment is open, the translated pixel shader accumulates passed
|
||||
// MSAA samples into one counter slot selected via zpd_rov_counter_index.
|
||||
// Clear the slot here so a recycled index never inherits old counts.
|
||||
if (zpd_active_query_is_rov_) {
|
||||
zpd_host_query_pool_->ClearROVCounter(deferred_command_list_,
|
||||
zpd_active_query_index_);
|
||||
return QueryOpenResult::kOpened;
|
||||
}
|
||||
|
||||
zpd_host_query_pool_->BeginQuery(deferred_command_list_,
|
||||
zpd_active_query_index_);
|
||||
return QueryOpenResult::kOpened;
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::CloseZPDQuery(ReportHandle report_handle) {
|
||||
zpd_host_query_pool_->EndQuery(deferred_command_list_,
|
||||
zpd_active_query_index_);
|
||||
zpd_host_query_pool_->QueueQueryResolve(zpd_active_query_index_);
|
||||
if (zpd_active_query_is_rov_) {
|
||||
zpd_host_query_pool_->QueueQueryResolve(zpd_active_query_index_, true);
|
||||
} else {
|
||||
zpd_host_query_pool_->EndQuery(deferred_command_list_,
|
||||
zpd_active_query_index_);
|
||||
zpd_host_query_pool_->QueueQueryResolve(zpd_active_query_index_, false);
|
||||
}
|
||||
|
||||
PendingQueryResolve resolve;
|
||||
resolve.submission = GetCurrentSubmission();
|
||||
resolve.query_index = zpd_active_query_index_;
|
||||
resolve.query_generation = zpd_active_query_generation_;
|
||||
resolve.uses_rov_counter = zpd_active_query_is_rov_;
|
||||
resolve.report_handle = report_handle;
|
||||
zpd_resolves_in_flight_.push_back(resolve);
|
||||
|
||||
zpd_active_query_index_ = UINT32_MAX;
|
||||
zpd_active_query_generation_ = 0;
|
||||
zpd_active_query_is_rov_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::DiscardZPDQuery() {
|
||||
if (zpd_active_query_is_rov_) {
|
||||
// The slot counter may be dirty if draws ran between OpenZPDQuery and here,
|
||||
// but the next OpenZPDQuery will zero it before any new shader accumulates.
|
||||
zpd_host_query_pool_->ReleaseQueryIndex(zpd_active_query_index_,
|
||||
zpd_active_query_generation_);
|
||||
zpd_active_query_index_ = UINT32_MAX;
|
||||
zpd_active_query_generation_ = 0;
|
||||
zpd_active_query_is_rov_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// D3D12 requires a paired EndQuery before the slot can be released.
|
||||
// EndSubmission flushes it so the slot can be freed without a resolve.
|
||||
zpd_host_query_pool_->EndQuery(deferred_command_list_,
|
||||
@@ -5842,6 +5971,7 @@ bool D3D12CommandProcessor::DiscardZPDQuery() {
|
||||
zpd_active_query_generation_);
|
||||
zpd_active_query_index_ = UINT32_MAX;
|
||||
zpd_active_query_generation_ = 0;
|
||||
zpd_active_query_is_rov_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5864,8 +5994,8 @@ void D3D12CommandProcessor::PumpQueryResolves() {
|
||||
|
||||
if (zpd_host_query_pool_->GenerationMatches(resolve.query_index,
|
||||
resolve.query_generation)) {
|
||||
uint64_t raw_samples =
|
||||
zpd_host_query_pool_->GetQueryReadbackValue(resolve.query_index);
|
||||
uint64_t raw_samples = zpd_host_query_pool_->GetQueryReadbackValue(
|
||||
resolve.query_index, resolve.uses_rov_counter);
|
||||
zpd_host_query_pool_->ReleaseQueryIndex(resolve.query_index,
|
||||
resolve.query_generation);
|
||||
OnZPDQueryResolved(resolve.report_handle, raw_samples);
|
||||
|
||||
@@ -182,6 +182,7 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
kEdramR32UintUAV,
|
||||
kEdramR32G32UintUAV,
|
||||
kEdramR32G32B32A32UintUAV,
|
||||
kZpdROVCounterRawUAV,
|
||||
|
||||
kGammaRampTableSRV,
|
||||
kGammaRampPWLSRV,
|
||||
@@ -511,18 +512,19 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
// BeginQuery/EndQuery must be in the same command list, segments split at
|
||||
// EndSubmission, resume at BeginSubmission. Discarded queries still need
|
||||
// EndQuery or the heap slot breaks on some drivers. RecordZPDResolveBatch
|
||||
// emits coalesced ResolveQueryData at submit.
|
||||
// emits coalesced ResolveQueryData and ROV counter copies at submit.
|
||||
void EnsureZPDQueryResources() override;
|
||||
void ShutdownZPDQueryResources() override {
|
||||
zpd_resolves_in_flight_.clear();
|
||||
zpd_active_query_index_ = UINT32_MAX;
|
||||
zpd_active_query_generation_ = 0;
|
||||
zpd_active_query_is_rov_ = false;
|
||||
if (zpd_host_query_pool_) {
|
||||
zpd_host_query_pool_->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsZPDQueryPoolReady() const override {
|
||||
return zpd_host_query_pool_->is_initialized();
|
||||
}
|
||||
bool IsZPDQueryPoolReady() const override;
|
||||
bool CanOpenZPDQuery() const override;
|
||||
|
||||
QueryOpenResult OpenZPDQuery(ReportHandle report_handle,
|
||||
@@ -542,10 +544,12 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
uint64_t submission = 0;
|
||||
uint32_t query_index = UINT32_MAX;
|
||||
uint32_t query_generation = 0;
|
||||
bool uses_rov_counter = false;
|
||||
ReportHandle report_handle = kInvalidReportHandle;
|
||||
};
|
||||
uint32_t zpd_active_query_index_ = UINT32_MAX;
|
||||
uint32_t zpd_active_query_generation_ = 0;
|
||||
bool zpd_active_query_is_rov_ = false;
|
||||
std::deque<PendingQueryResolve> zpd_resolves_in_flight_;
|
||||
|
||||
std::unique_ptr<ui::d3d12::D3D12GPUCompletionTimeline> completion_timeline_;
|
||||
@@ -578,6 +582,7 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
CommandAllocator* command_allocator_submitted_last_ = nullptr;
|
||||
ID3D12GraphicsCommandList* command_list_ = nullptr;
|
||||
ID3D12GraphicsCommandList1* command_list_1_ = nullptr;
|
||||
ID3D12GraphicsCommandList2* command_list_2_ = nullptr;
|
||||
DeferredCommandList deferred_command_list_;
|
||||
|
||||
// Should bindless textures and samplers be used - many times faster
|
||||
|
||||
@@ -22,81 +22,156 @@ namespace d3d12 {
|
||||
|
||||
bool D3D12ZPDQueryPool::EnsureInitialized(
|
||||
const ui::d3d12::D3D12Provider& provider, uint32_t requested_capacity,
|
||||
bool can_recreate) {
|
||||
if (is_initialized() && (capacity_ == requested_capacity || !can_recreate)) {
|
||||
bool can_recreate, bool initialize_rov_counter) {
|
||||
bool rtv_initialized = is_initialized();
|
||||
bool rov_initialized = rov_counter_initialized();
|
||||
if (rtv_initialized && (!initialize_rov_counter || rov_initialized) &&
|
||||
(capacity_ == requested_capacity || !can_recreate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can't recreate while resolves are in-flight, that would destroy the heap
|
||||
// under a live ResolveQueryData call.
|
||||
assert_true(!is_initialized() || !has_pending_resolve_batch());
|
||||
Shutdown();
|
||||
if (rtv_initialized && capacity_ != requested_capacity) {
|
||||
if (!can_recreate) {
|
||||
requested_capacity = capacity_;
|
||||
} else {
|
||||
// Can't recreate while resolves are in-flight, that would destroy the
|
||||
// backing resources under pending resolve or copy work.
|
||||
assert_true(!has_pending_resolve_batch());
|
||||
Shutdown();
|
||||
rtv_initialized = false;
|
||||
rov_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
ID3D12Device* device = provider.GetDevice();
|
||||
|
||||
D3D12_QUERY_HEAP_DESC heap_desc = {};
|
||||
heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
|
||||
heap_desc.Count = requested_capacity;
|
||||
heap_desc.NodeMask = 0;
|
||||
if (!rtv_initialized) {
|
||||
D3D12_QUERY_HEAP_DESC heap_desc = {};
|
||||
heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
|
||||
heap_desc.Count = requested_capacity;
|
||||
heap_desc.NodeMask = 0;
|
||||
|
||||
if (FAILED(device->CreateQueryHeap(&heap_desc, IID_PPV_ARGS(&query_heap_)))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to create the ZPD query "
|
||||
"heap, falling back to fake sample counts.");
|
||||
if (FAILED(
|
||||
device->CreateQueryHeap(&heap_desc, IID_PPV_ARGS(&query_heap_)))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to create the ZPD query "
|
||||
"heap, falling back to fake sample counts.");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_DESC buffer_desc;
|
||||
ui::d3d12::util::FillBufferResourceDesc(
|
||||
buffer_desc, sizeof(uint64_t) * requested_capacity,
|
||||
D3D12_RESOURCE_FLAG_NONE);
|
||||
|
||||
if (FAILED(device->CreateCommittedResource(
|
||||
&ui::d3d12::util::kHeapPropertiesReadback,
|
||||
provider.GetHeapFlagCreateNotZeroed(), &buffer_desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||
IID_PPV_ARGS(&readback_buffer_)))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to allocate the ZPD query "
|
||||
"readback buffer, falling back to fake sample counts.");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RANGE read_range = {};
|
||||
read_range.Begin = 0;
|
||||
read_range.End = sizeof(uint64_t) * requested_capacity;
|
||||
|
||||
void* mapping = nullptr;
|
||||
if (FAILED(readback_buffer_->Map(0, &read_range, &mapping))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to map the ZPD query "
|
||||
"readback buffer, falling back to fake sample counts.");
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
readback_mapping_ = reinterpret_cast<uint64_t*>(mapping);
|
||||
capacity_ = requested_capacity;
|
||||
|
||||
resolve_batch_pending_.assign(requested_capacity, 0);
|
||||
resolve_batch_indices_.clear();
|
||||
rov_counter_resolve_batch_pending_.assign(requested_capacity, 0);
|
||||
rov_counter_resolve_batch_indices_.clear();
|
||||
resolve_batch_ranges_.clear();
|
||||
|
||||
free_indices_.clear();
|
||||
free_indices_.reserve(requested_capacity);
|
||||
for (uint32_t i = requested_capacity; i > 0; --i) {
|
||||
free_indices_.push_back(i - 1);
|
||||
}
|
||||
index_generations_.assign(requested_capacity, 0);
|
||||
}
|
||||
|
||||
if (!initialize_rov_counter || rov_initialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rov_counter_readback_mapping_ && rov_counter_readback_buffer_) {
|
||||
D3D12_RANGE written_range = {0, 0};
|
||||
rov_counter_readback_buffer_->Unmap(0, &written_range);
|
||||
}
|
||||
rov_counter_readback_mapping_ = nullptr;
|
||||
rov_counter_readback_buffer_.Reset();
|
||||
rov_counter_buffer_.Reset();
|
||||
rov_counter_resolve_batch_pending_.assign(requested_capacity, 0);
|
||||
rov_counter_resolve_batch_indices_.clear();
|
||||
|
||||
D3D12_RESOURCE_DESC counter_buffer_desc;
|
||||
ui::d3d12::util::FillBufferResourceDesc(
|
||||
counter_buffer_desc, sizeof(uint32_t) * requested_capacity,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
|
||||
if (FAILED(device->CreateCommittedResource(
|
||||
&ui::d3d12::util::kHeapPropertiesDefault,
|
||||
provider.GetHeapFlagCreateNotZeroed(), &counter_buffer_desc,
|
||||
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr,
|
||||
IID_PPV_ARGS(&rov_counter_buffer_)))) {
|
||||
XELOGW("D3D12ZPDQueryPool: Failed to allocate the ZPD ROV counter buffer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_DESC buffer_desc;
|
||||
ui::d3d12::util::FillBufferResourceDesc(buffer_desc,
|
||||
sizeof(uint64_t) * requested_capacity,
|
||||
D3D12_RESOURCE_DESC readback_buffer_desc;
|
||||
ui::d3d12::util::FillBufferResourceDesc(readback_buffer_desc,
|
||||
sizeof(uint32_t) * requested_capacity,
|
||||
D3D12_RESOURCE_FLAG_NONE);
|
||||
|
||||
if (FAILED(device->CreateCommittedResource(
|
||||
&ui::d3d12::util::kHeapPropertiesReadback,
|
||||
provider.GetHeapFlagCreateNotZeroed(), &buffer_desc,
|
||||
provider.GetHeapFlagCreateNotZeroed(), &readback_buffer_desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||
IID_PPV_ARGS(&readback_buffer_)))) {
|
||||
IID_PPV_ARGS(&rov_counter_readback_buffer_)))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to allocate the ZPD query "
|
||||
"readback buffer, falling back to fake sample counts.");
|
||||
Shutdown();
|
||||
"D3D12ZPDQueryPool: Failed to allocate the ZPD ROV counter readback "
|
||||
"buffer.");
|
||||
rov_counter_buffer_.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RANGE read_range = {};
|
||||
read_range.Begin = 0;
|
||||
read_range.End = sizeof(uint64_t) * requested_capacity;
|
||||
read_range.End = sizeof(uint32_t) * requested_capacity;
|
||||
|
||||
void* mapping = nullptr;
|
||||
if (FAILED(readback_buffer_->Map(0, &read_range, &mapping))) {
|
||||
if (FAILED(rov_counter_readback_buffer_->Map(0, &read_range, &mapping))) {
|
||||
XELOGW(
|
||||
"D3D12ZPDQueryPool: Failed to map the ZPD query "
|
||||
"readback buffer, falling back to fake sample counts.");
|
||||
Shutdown();
|
||||
"D3D12ZPDQueryPool: Failed to map the ZPD ROV counter readback "
|
||||
"buffer.");
|
||||
rov_counter_readback_buffer_.Reset();
|
||||
rov_counter_buffer_.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
readback_mapping_ = reinterpret_cast<uint64_t*>(mapping);
|
||||
capacity_ = requested_capacity;
|
||||
|
||||
resolve_batch_pending_.assign(requested_capacity, 0);
|
||||
resolve_batch_indices_.clear();
|
||||
resolve_batch_index_count_ = 0;
|
||||
|
||||
free_indices_.clear();
|
||||
free_indices_.reserve(requested_capacity);
|
||||
for (uint32_t i = requested_capacity; i > 0; --i) {
|
||||
free_indices_.push_back(i - 1);
|
||||
}
|
||||
index_generations_.assign(requested_capacity, 0);
|
||||
|
||||
rov_counter_readback_mapping_ = reinterpret_cast<uint32_t*>(mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12ZPDQueryPool::Shutdown() {
|
||||
resolve_batch_pending_.clear();
|
||||
resolve_batch_indices_.clear();
|
||||
resolve_batch_index_count_ = 0;
|
||||
rov_counter_resolve_batch_pending_.clear();
|
||||
rov_counter_resolve_batch_indices_.clear();
|
||||
free_indices_.clear();
|
||||
index_generations_.clear();
|
||||
|
||||
@@ -107,10 +182,14 @@ void D3D12ZPDQueryPool::Shutdown() {
|
||||
D3D12_RANGE written_range = {0, 0};
|
||||
readback_buffer_->Unmap(0, &written_range);
|
||||
}
|
||||
readback_mapping_ = nullptr;
|
||||
if (rov_counter_readback_mapping_ && rov_counter_readback_buffer_) {
|
||||
D3D12_RANGE written_range = {0, 0};
|
||||
rov_counter_readback_buffer_->Unmap(0, &written_range);
|
||||
}
|
||||
|
||||
readback_buffer_.Reset();
|
||||
query_heap_.Reset();
|
||||
rov_counter_readback_mapping_ = nullptr;
|
||||
rov_counter_readback_buffer_.Reset();
|
||||
rov_counter_buffer_.Reset();
|
||||
}
|
||||
|
||||
bool D3D12ZPDQueryPool::AcquireQueryIndex(uint32_t& query_index,
|
||||
@@ -174,87 +253,165 @@ void D3D12ZPDQueryPool::EndQuery(DeferredCommandList& deferred_command_list,
|
||||
D3D12_QUERY_TYPE_OCCLUSION, query_index);
|
||||
}
|
||||
|
||||
void D3D12ZPDQueryPool::QueueQueryResolve(uint32_t query_index) {
|
||||
void D3D12ZPDQueryPool::QueueQueryResolve(uint32_t query_index,
|
||||
bool uses_rov_counter) {
|
||||
if (query_index >= capacity_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against duplicates. Split paths can touch the same index twice before
|
||||
// the batch drains at EndSubmission.
|
||||
if (uses_rov_counter) {
|
||||
if (!rov_counter_resolve_batch_pending_[query_index]) {
|
||||
rov_counter_resolve_batch_pending_[query_index] = 1;
|
||||
rov_counter_resolve_batch_indices_.push_back(query_index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resolve_batch_pending_[query_index]) {
|
||||
resolve_batch_pending_[query_index] = 1;
|
||||
resolve_batch_indices_.push_back(query_index);
|
||||
++resolve_batch_index_count_;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12ZPDQueryPool::ClearROVCounter(
|
||||
DeferredCommandList& deferred_command_list, uint32_t query_index) const {
|
||||
if (!rov_counter_initialized() || query_index >= capacity_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This buffer stays in UNORDERED_ACCESS for the duration of its use. Before
|
||||
// reusing a slot, order this write after any atomic adds issued by the
|
||||
// previous query that owned the same index.
|
||||
D3D12_RESOURCE_BARRIER uav_barrier = {};
|
||||
uav_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
||||
uav_barrier.UAV.pResource = rov_counter_buffer_.Get();
|
||||
deferred_command_list.D3DResourceBarrier(1, &uav_barrier);
|
||||
|
||||
// Only the selected 32 bit slot needs to be reset, so use
|
||||
// WriteBufferImmediate instead of transitioning the whole buffer through a
|
||||
// copy path.
|
||||
deferred_command_list.D3DWriteBufferImmediate(
|
||||
rov_counter_buffer_->GetGPUVirtualAddress() +
|
||||
static_cast<uint64_t>(query_index) * sizeof(uint32_t),
|
||||
0u);
|
||||
|
||||
// Order the zero write before any upcoming PS atomic adds so the next query
|
||||
// using this slot sees the cleared counter value.
|
||||
deferred_command_list.D3DResourceBarrier(1, &uav_barrier);
|
||||
}
|
||||
|
||||
void D3D12ZPDQueryPool::FlushResolveBatch(
|
||||
DeferredCommandList& deferred_command_list, bool submission_open) {
|
||||
if (!submission_open) {
|
||||
if (!submission_open || (resolve_batch_indices_.empty() &&
|
||||
rov_counter_resolve_batch_indices_.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resolve_batch_index_count_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_initialized()) {
|
||||
for (uint32_t index : resolve_batch_indices_) {
|
||||
resolve_batch_pending_[index] = 0;
|
||||
}
|
||||
resolve_batch_indices_.clear();
|
||||
resolve_batch_index_count_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort so we can coalesce contiguous indices into ranges, cutting down on
|
||||
// ResolveQueryData calls which have considerable overhead.
|
||||
std::sort(resolve_batch_indices_.begin(), resolve_batch_indices_.end());
|
||||
|
||||
resolve_batch_ranges_.clear();
|
||||
uint32_t range_start = 0;
|
||||
uint32_t range_count = 0;
|
||||
for (uint32_t index : resolve_batch_indices_) {
|
||||
if (range_count == 0) {
|
||||
// Sorts indices, coalesces contiguous runs into resolve_batch_ranges_, resets
|
||||
// pending flags, and clears the index list.
|
||||
auto build_ranges = [this](std::vector<uint32_t>& indices,
|
||||
std::vector<uint8_t>& pending) {
|
||||
std::sort(indices.begin(), indices.end());
|
||||
resolve_batch_ranges_.clear();
|
||||
uint32_t range_start = 0;
|
||||
uint32_t range_count = 0;
|
||||
for (uint32_t index : indices) {
|
||||
if (range_count == 0) {
|
||||
range_start = index;
|
||||
range_count = 1;
|
||||
continue;
|
||||
}
|
||||
if (index == range_start + range_count) {
|
||||
++range_count;
|
||||
continue;
|
||||
}
|
||||
resolve_batch_ranges_.push_back({range_start, range_count});
|
||||
range_start = index;
|
||||
range_count = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index == range_start + range_count) {
|
||||
++range_count;
|
||||
continue;
|
||||
if (range_count != 0) {
|
||||
resolve_batch_ranges_.push_back({range_start, range_count});
|
||||
}
|
||||
for (uint32_t index : indices) {
|
||||
pending[index] = 0;
|
||||
}
|
||||
indices.clear();
|
||||
};
|
||||
|
||||
resolve_batch_ranges_.push_back({range_start, range_count});
|
||||
range_start = index;
|
||||
range_count = 1;
|
||||
if (!resolve_batch_indices_.empty()) {
|
||||
if (!is_initialized()) {
|
||||
for (uint32_t index : resolve_batch_indices_) {
|
||||
resolve_batch_pending_[index] = 0;
|
||||
}
|
||||
resolve_batch_indices_.clear();
|
||||
} else {
|
||||
build_ranges(resolve_batch_indices_, resolve_batch_pending_);
|
||||
for (const ResolveRange& range : resolve_batch_ranges_) {
|
||||
deferred_command_list.D3DResolveQueryData(
|
||||
query_heap_.Get(), D3D12_QUERY_TYPE_OCCLUSION, range.start,
|
||||
range.count, readback_buffer_.Get(),
|
||||
range.start * sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (range_count != 0) {
|
||||
resolve_batch_ranges_.push_back({range_start, range_count});
|
||||
if (rov_counter_resolve_batch_indices_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the batch. ENDs from later in this submission belong to the next.
|
||||
for (uint32_t index : resolve_batch_indices_) {
|
||||
resolve_batch_pending_[index] = 0;
|
||||
if (!rov_counter_initialized()) {
|
||||
for (uint32_t index : rov_counter_resolve_batch_indices_) {
|
||||
rov_counter_resolve_batch_pending_[index] = 0;
|
||||
}
|
||||
rov_counter_resolve_batch_indices_.clear();
|
||||
return;
|
||||
}
|
||||
resolve_batch_indices_.clear();
|
||||
resolve_batch_index_count_ = 0;
|
||||
|
||||
// The shader path writes counters through UAV atomics, so resolve on
|
||||
// this path means copying the finished 32 bit slots out of the UAV buffer.
|
||||
// The whole buffer is transitioned for the copy and then returned to
|
||||
// UNORDERED_ACCESS since D3D12 state is tracked per resource, not per range.
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition.pResource = rov_counter_buffer_.Get();
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
deferred_command_list.D3DResourceBarrier(1, &barrier);
|
||||
|
||||
build_ranges(rov_counter_resolve_batch_indices_,
|
||||
rov_counter_resolve_batch_pending_);
|
||||
for (const ResolveRange& range : resolve_batch_ranges_) {
|
||||
deferred_command_list.D3DResolveQueryData(
|
||||
query_heap_.Get(), D3D12_QUERY_TYPE_OCCLUSION, range.start, range.count,
|
||||
readback_buffer_.Get(), range.start * sizeof(uint64_t));
|
||||
uint64_t offset = static_cast<uint64_t>(range.start) * sizeof(uint32_t);
|
||||
uint64_t size = static_cast<uint64_t>(range.count) * sizeof(uint32_t);
|
||||
deferred_command_list.D3DCopyBufferRegion(
|
||||
rov_counter_readback_buffer_.Get(), offset, rov_counter_buffer_.Get(),
|
||||
offset, size);
|
||||
}
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
deferred_command_list.D3DResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
uint64_t D3D12ZPDQueryPool::GetQueryReadbackValue(uint32_t query_index) const {
|
||||
if (!readback_mapping_ || query_index >= capacity_) {
|
||||
uint64_t D3D12ZPDQueryPool::GetQueryReadbackValue(uint32_t query_index,
|
||||
bool uses_rov_counter) const {
|
||||
if (query_index >= capacity_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return readback_mapping_[query_index];
|
||||
if (uses_rov_counter) {
|
||||
// ROV queries read back a translated 32 bit sample count. Widen here so
|
||||
// paths feed the uint64_t resolve.
|
||||
return rov_counter_readback_mapping_
|
||||
? static_cast<uint64_t>(
|
||||
rov_counter_readback_mapping_[query_index])
|
||||
: 0;
|
||||
}
|
||||
|
||||
return readback_mapping_ ? readback_mapping_[query_index] : 0;
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
|
||||
@@ -37,6 +37,11 @@ class DeferredCommandList;
|
||||
//
|
||||
// FlushResolveBatch coalesces pending indices into contiguous ranges to cut
|
||||
// down on ResolveQueryData call count.
|
||||
//
|
||||
// ROV queries use a separate path instead of normal D3D12 results. They write
|
||||
// surviving MSAA coverage into a dedicated buffer, one slot per active query.
|
||||
// QueueQueryResolve + ClearROVCounter are used instead of BeginQuery and
|
||||
// EndQuery.
|
||||
class D3D12ZPDQueryPool {
|
||||
public:
|
||||
D3D12ZPDQueryPool() = default;
|
||||
@@ -45,7 +50,8 @@ class D3D12ZPDQueryPool {
|
||||
~D3D12ZPDQueryPool() { Shutdown(); }
|
||||
|
||||
bool EnsureInitialized(const ui::d3d12::D3D12Provider& provider,
|
||||
uint32_t requested_capacity, bool can_recreate);
|
||||
uint32_t requested_capacity, bool can_recreate,
|
||||
bool initialize_rov_counter);
|
||||
void Shutdown();
|
||||
|
||||
bool is_initialized() const {
|
||||
@@ -56,7 +62,17 @@ class D3D12ZPDQueryPool {
|
||||
uint32_t capacity() const { return capacity_; }
|
||||
|
||||
bool has_pending_resolve_batch() const {
|
||||
return resolve_batch_index_count_ != 0;
|
||||
return !resolve_batch_indices_.empty() ||
|
||||
!rov_counter_resolve_batch_indices_.empty();
|
||||
}
|
||||
|
||||
bool rov_counter_initialized() const {
|
||||
return rov_counter_buffer_ && rov_counter_readback_buffer_ &&
|
||||
rov_counter_readback_mapping_ != nullptr && capacity_ != 0;
|
||||
}
|
||||
|
||||
ID3D12Resource* rov_counter_buffer() const {
|
||||
return rov_counter_buffer_.Get();
|
||||
}
|
||||
|
||||
bool has_free_indices() const { return !free_indices_.empty(); }
|
||||
@@ -69,11 +85,15 @@ class D3D12ZPDQueryPool {
|
||||
uint32_t query_index) const;
|
||||
void EndQuery(DeferredCommandList& deferred_command_list,
|
||||
uint32_t query_index) const;
|
||||
void QueueQueryResolve(uint32_t query_index);
|
||||
void QueueQueryResolve(uint32_t query_index, bool uses_rov_counter);
|
||||
void ClearROVCounter(DeferredCommandList& deferred_command_list,
|
||||
uint32_t query_index) const;
|
||||
|
||||
void FlushResolveBatch(DeferredCommandList& deferred_command_list,
|
||||
bool submission_open);
|
||||
|
||||
uint64_t GetQueryReadbackValue(uint32_t query_index) const;
|
||||
uint64_t GetQueryReadbackValue(uint32_t query_index,
|
||||
bool uses_rov_counter) const;
|
||||
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ID3D12QueryHeap> query_heap_;
|
||||
@@ -82,6 +102,10 @@ class D3D12ZPDQueryPool {
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> readback_buffer_;
|
||||
uint64_t* readback_mapping_ = nullptr;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> rov_counter_buffer_;
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> rov_counter_readback_buffer_;
|
||||
uint32_t* rov_counter_readback_mapping_ = nullptr;
|
||||
|
||||
uint32_t capacity_ = 0;
|
||||
std::vector<uint32_t> free_indices_;
|
||||
|
||||
@@ -92,9 +116,10 @@ class D3D12ZPDQueryPool {
|
||||
// Active indices with resolve_batch_pending_[i] == 1, so flush iterates
|
||||
// only the active entries instead of scanning the full capacity.
|
||||
std::vector<uint32_t> resolve_batch_indices_;
|
||||
std::vector<uint8_t> rov_counter_resolve_batch_pending_;
|
||||
std::vector<uint32_t> rov_counter_resolve_batch_indices_;
|
||||
// Reusable scratch for coalesced contiguous ranges during flush.
|
||||
std::vector<ResolveRange> resolve_batch_ranges_;
|
||||
uint32_t resolve_batch_index_count_ = 0;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
|
||||
@@ -28,7 +28,8 @@ DeferredCommandList::DeferredCommandList(
|
||||
void DeferredCommandList::Reset() { command_stream_.clear(); }
|
||||
|
||||
void DeferredCommandList::Execute(ID3D12GraphicsCommandList* command_list,
|
||||
ID3D12GraphicsCommandList1* command_list_1) {
|
||||
ID3D12GraphicsCommandList1* command_list_1,
|
||||
ID3D12GraphicsCommandList2* command_list_2) {
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
@@ -295,6 +296,19 @@ void DeferredCommandList::Execute(ID3D12GraphicsCommandList* command_list,
|
||||
: nullptr);
|
||||
}
|
||||
} break;
|
||||
case Command::kD3DWriteBufferImmediate: {
|
||||
if (command_list_2 != nullptr) {
|
||||
auto& args =
|
||||
*reinterpret_cast<const D3DWriteBufferImmediateArguments*>(
|
||||
stream);
|
||||
D3D12_WRITEBUFFERIMMEDIATE_PARAMETER param;
|
||||
param.Dest = args.dest;
|
||||
param.Value = args.value;
|
||||
D3D12_WRITEBUFFERIMMEDIATE_MODE mode =
|
||||
D3D12_WRITEBUFFERIMMEDIATE_MODE_DEFAULT;
|
||||
command_list_2->WriteBufferImmediate(1, ¶m, &mode);
|
||||
}
|
||||
} break;
|
||||
case Command::kBeginDebugMarker: {
|
||||
auto& args = *reinterpret_cast<const DebugMarkerHeader*>(stream);
|
||||
const char* label_name = reinterpret_cast<const char*>(
|
||||
|
||||
@@ -40,7 +40,8 @@ class DeferredCommandList {
|
||||
|
||||
void Reset();
|
||||
void Execute(ID3D12GraphicsCommandList* command_list,
|
||||
ID3D12GraphicsCommandList1* command_list_1);
|
||||
ID3D12GraphicsCommandList1* command_list_1,
|
||||
ID3D12GraphicsCommandList2* command_list_2);
|
||||
|
||||
D3D12_RECT* ClearDepthStencilViewAllocatedRects(
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE depth_stencil_view,
|
||||
@@ -471,6 +472,14 @@ class DeferredCommandList {
|
||||
sizeof(D3D12_SAMPLE_POSITION));
|
||||
}
|
||||
|
||||
void D3DWriteBufferImmediate(D3D12_GPU_VIRTUAL_ADDRESS dest, UINT value) {
|
||||
auto& args = *reinterpret_cast<D3DWriteBufferImmediateArguments*>(
|
||||
WriteCommand(Command::kD3DWriteBufferImmediate,
|
||||
sizeof(D3DWriteBufferImmediateArguments)));
|
||||
args.dest = dest;
|
||||
args.value = value;
|
||||
}
|
||||
|
||||
// Debug marker support for PIX/RenderDoc annotation.
|
||||
void BeginDebugMarker(const char* label_name) {
|
||||
size_t label_len = std::strlen(label_name);
|
||||
@@ -535,6 +544,7 @@ class DeferredCommandList {
|
||||
kD3DSetPipelineState,
|
||||
kSetPipelineStateHandle,
|
||||
kD3DSetSamplePositions,
|
||||
kD3DWriteBufferImmediate,
|
||||
kBeginDebugMarker,
|
||||
kEndDebugMarker,
|
||||
kInsertDebugMarker,
|
||||
@@ -677,6 +687,11 @@ class DeferredCommandList {
|
||||
D3D12_SAMPLE_POSITION sample_positions[16];
|
||||
};
|
||||
|
||||
struct D3DWriteBufferImmediateArguments {
|
||||
D3D12_GPU_VIRTUAL_ADDRESS dest;
|
||||
UINT value;
|
||||
};
|
||||
|
||||
struct DebugMarkerHeader {
|
||||
uint32_t label_length;
|
||||
// Followed by null-terminated label string.
|
||||
|
||||
@@ -1516,6 +1516,7 @@ enum class Opcode : uint32_t {
|
||||
kStoreRaw = 166,
|
||||
kAtomicAnd = 169,
|
||||
kAtomicOr = 170,
|
||||
kAtomicIAdd = 173,
|
||||
kEvalSampleIndex = 204,
|
||||
kEvalCentroid = 205,
|
||||
};
|
||||
@@ -2416,6 +2417,10 @@ class Assembler {
|
||||
uint32_t address_components, const Src& value) {
|
||||
EmitAtomicOp(Opcode::kAtomicOr, dest, address, address_components, value);
|
||||
}
|
||||
void OpAtomicIAdd(const Dest& dest, const Src& address,
|
||||
uint32_t address_components, const Src& value) {
|
||||
EmitAtomicOp(Opcode::kAtomicIAdd, dest, address, address_components, value);
|
||||
}
|
||||
void OpEvalSampleIndex(const Dest& dest, const Src& value,
|
||||
const Src& sample_index) {
|
||||
uint32_t dest_write_mask = dest.GetMask();
|
||||
|
||||
@@ -173,6 +173,7 @@ void DxbcShaderTranslator::Reset() {
|
||||
uav_count_ = 0;
|
||||
uav_index_shared_memory_ = kBindingIndexUnallocated;
|
||||
uav_index_edram_ = kBindingIndexUnallocated;
|
||||
uav_index_zpd_rov_counter_ = kBindingIndexUnallocated;
|
||||
|
||||
sampler_bindings_.clear();
|
||||
|
||||
@@ -2146,7 +2147,9 @@ constexpr DxbcShaderTranslator::SystemConstantRdef
|
||||
{"xe_edram_32bpp_tile_pitch_dwords_scaled", ShaderRdefTypeIndex::kUint,
|
||||
sizeof(uint32_t)},
|
||||
{"xe_edram_depth_base_dwords_scaled", ShaderRdefTypeIndex::kUint,
|
||||
sizeof(uint32_t), sizeof(uint32_t)},
|
||||
sizeof(uint32_t)},
|
||||
{"xe_zpd_rov_counter_index", ShaderRdefTypeIndex::kUint,
|
||||
sizeof(uint32_t)},
|
||||
|
||||
{"xe_color_exp_bias", ShaderRdefTypeIndex::kFloat4, sizeof(float) * 4},
|
||||
|
||||
@@ -2548,6 +2551,11 @@ void DxbcShaderTranslator::WriteResourceDefinition() {
|
||||
if (uav_index_edram_ != kBindingIndexUnallocated) {
|
||||
name_ptr += dxbc::AppendAlignedString(shader_object_, "xe_edram");
|
||||
}
|
||||
uint32_t zpd_rov_counter_name_ptr = name_ptr;
|
||||
if (uav_index_zpd_rov_counter_ != kBindingIndexUnallocated) {
|
||||
name_ptr +=
|
||||
dxbc::AppendAlignedString(shader_object_, "xe_zpd_rov_counter_uav");
|
||||
}
|
||||
|
||||
uint32_t bindings_position_dwords = uint32_t(shader_object_.size());
|
||||
|
||||
@@ -2678,6 +2686,13 @@ void DxbcShaderTranslator::WriteResourceDefinition() {
|
||||
uav.dimension = dxbc::RdefDimension::kUAVBuffer;
|
||||
uav.sample_count = UINT32_MAX;
|
||||
uav.bind_point = uint32_t(UAVRegister::kEdram);
|
||||
} else if (i == uav_index_zpd_rov_counter_) {
|
||||
// ROV ZPD counter buffer.
|
||||
uav.name_ptr = zpd_rov_counter_name_ptr;
|
||||
uav.type = dxbc::RdefInputType::kUAVRWByteAddress;
|
||||
uav.return_type = dxbc::ResourceReturnType::kMixed;
|
||||
uav.dimension = dxbc::RdefDimension::kUAVBuffer;
|
||||
uav.bind_point = uint32_t(UAVRegister::kZpdRovCounter);
|
||||
} else {
|
||||
assert_unhandled_case(i);
|
||||
}
|
||||
@@ -3572,6 +3587,12 @@ void DxbcShaderTranslator::WriteShaderCode() {
|
||||
dxbc::Src::U(dxbc::Src::Dcl, uav_index_edram_,
|
||||
uint32_t(UAVRegister::kEdram),
|
||||
uint32_t(UAVRegister::kEdram)));
|
||||
} else if (i == uav_index_zpd_rov_counter_) {
|
||||
// ROV ZPD counter buffer.
|
||||
ao_.OpDclUnorderedAccessViewRaw(
|
||||
0, dxbc::Src::U(dxbc::Src::Dcl, uav_index_zpd_rov_counter_,
|
||||
uint32_t(UAVRegister::kZpdRovCounter),
|
||||
uint32_t(UAVRegister::kZpdRovCounter)));
|
||||
} else {
|
||||
assert_unhandled_case(i);
|
||||
}
|
||||
|
||||
@@ -329,7 +329,9 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t alpha_to_mask;
|
||||
uint32_t edram_32bpp_tile_pitch_dwords_scaled;
|
||||
uint32_t edram_depth_base_dwords_scaled;
|
||||
uint32_t padding_edram_depth_base_dwords_scaled;
|
||||
// UINT32_MAX when this draw is outside an active ZPD segment. The shader
|
||||
// helper should treat that as a skip sentinel.
|
||||
uint32_t zpd_rov_counter_index;
|
||||
|
||||
float color_exp_bias[4];
|
||||
|
||||
@@ -431,6 +433,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
kAlphaToMask,
|
||||
kEdram32bppTilePitchDwordsScaled,
|
||||
kEdramDepthBaseDwordsScaled,
|
||||
kZpdRovCounterIndex,
|
||||
|
||||
kColorExpBias,
|
||||
|
||||
@@ -516,6 +519,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
enum class UAVRegister {
|
||||
kSharedMemory,
|
||||
kEdram,
|
||||
kZpdRovCounter,
|
||||
};
|
||||
|
||||
uint64_t GetDefaultVertexShaderModification(
|
||||
@@ -762,6 +766,9 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// unchanged or known that it's safe not to await kills/alphatest/AtoC),
|
||||
// returns from the shader.
|
||||
void ROV_DepthStencilTest();
|
||||
// Adds the surviving coverage MSAA counts from ROV params to the active ZPD
|
||||
// counter slot after the final PS depth/stencil decision.
|
||||
void ROV_AddPassedMSAASamplesToZPD();
|
||||
// Unpacks a 32bpp or a 64bpp color in packed_temp.packed_temp_components to
|
||||
// color_temp, using 2 temporary VGPRs.
|
||||
void ROV_UnpackColor(uint32_t rt_index, uint32_t packed_temp,
|
||||
@@ -1207,6 +1214,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t uav_count_;
|
||||
uint32_t uav_index_shared_memory_;
|
||||
uint32_t uav_index_edram_;
|
||||
uint32_t uav_index_zpd_rov_counter_;
|
||||
|
||||
std::vector<SamplerBinding> sampler_bindings_;
|
||||
};
|
||||
|
||||
@@ -2120,6 +2120,59 @@ void DxbcShaderTranslator::CompletePixelShader_AlphaToMask() {
|
||||
a_.OpEndIf();
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::ROV_AddPassedMSAASamplesToZPD() {
|
||||
if (uav_index_zpd_rov_counter_ == kBindingIndexUnallocated) {
|
||||
uav_index_zpd_rov_counter_ = uav_count_++;
|
||||
}
|
||||
|
||||
uint32_t temp = PushSystemTemp();
|
||||
dxbc::Dest temp_x_dest(dxbc::Dest::R(temp, 0b0001));
|
||||
dxbc::Src temp_x_src(dxbc::Src::R(temp, dxbc::Src::kXXXX));
|
||||
dxbc::Dest temp_y_dest(dxbc::Dest::R(temp, 0b0010));
|
||||
dxbc::Src temp_y_src(dxbc::Src::R(temp, dxbc::Src::kYYYY));
|
||||
|
||||
dxbc::Src counter_index_src(LoadSystemConstant(
|
||||
SystemConstants::Index::kZpdRovCounterIndex,
|
||||
offsetof(SystemConstants, zpd_rov_counter_index), dxbc::Src::kXXXX));
|
||||
|
||||
// UINT32_MAX means no ZPD segment is currently open for this draw.
|
||||
a_.OpINE(temp_x_dest, counter_index_src, dxbc::Src::LU(UINT32_MAX));
|
||||
a_.OpIf(true, temp_x_src);
|
||||
|
||||
{
|
||||
dxbc::Src covered_samples_src(
|
||||
dxbc::Src::R(system_temp_rov_params_, dxbc::Src::kXXXX));
|
||||
a_.OpUBFE(temp_x_dest, dxbc::Src::LU(1), dxbc::Src::LU(0),
|
||||
covered_samples_src);
|
||||
a_.OpUBFE(temp_y_dest, dxbc::Src::LU(1), dxbc::Src::LU(1),
|
||||
covered_samples_src);
|
||||
a_.OpIAdd(temp_x_dest, temp_x_src, temp_y_src);
|
||||
a_.OpUBFE(temp_y_dest, dxbc::Src::LU(1), dxbc::Src::LU(2),
|
||||
covered_samples_src);
|
||||
a_.OpIAdd(temp_x_dest, temp_x_src, temp_y_src);
|
||||
a_.OpUBFE(temp_y_dest, dxbc::Src::LU(1), dxbc::Src::LU(3),
|
||||
covered_samples_src);
|
||||
a_.OpIAdd(temp_x_dest, temp_x_src, temp_y_src);
|
||||
a_.OpIf(true, temp_x_src);
|
||||
{
|
||||
// The counter UAV is raw, so address it in bytes.
|
||||
// One counter slot is one uint32_t.
|
||||
a_.OpUMul(dxbc::Dest::Null(), temp_y_dest, counter_index_src,
|
||||
dxbc::Src::LU(sizeof(uint32_t)));
|
||||
// Add the number of samples that survived depth/stencil for this pixel to
|
||||
// the active query slot. This slot is copied to the readback buffer when
|
||||
// the ZPD segment is closed.
|
||||
a_.OpAtomicIAdd(dxbc::Dest::U(uav_index_zpd_rov_counter_,
|
||||
uint32_t(UAVRegister::kZpdRovCounter), 0),
|
||||
temp_y_src, 0b0001, temp_x_src);
|
||||
}
|
||||
a_.OpEndIf();
|
||||
}
|
||||
a_.OpEndIf();
|
||||
|
||||
PopSystemTemp();
|
||||
}
|
||||
|
||||
void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
||||
uint32_t temp = PushSystemTemp();
|
||||
dxbc::Dest temp_x_dest(dxbc::Dest::R(temp, 0b0001));
|
||||
@@ -2176,6 +2229,8 @@ void DxbcShaderTranslator::CompletePixelShader_WriteToROV() {
|
||||
// system_temp_rov_params_.y (the depth / stencil sample address) is not
|
||||
// needed anymore, can be used for color writing.
|
||||
|
||||
ROV_AddPassedMSAASamplesToZPD();
|
||||
|
||||
if (!is_depth_only_pixel_shader_) {
|
||||
// Check if any sample is still covered after depth testing and writing,
|
||||
// skip color writing completely in this case.
|
||||
|
||||
Reference in New Issue
Block a user