[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:
goldislead
2026-04-13 04:27:33 -07:00
parent aa877ad157
commit 5c2eeb9162
11 changed files with 558 additions and 122 deletions
+2 -1
View File
@@ -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"
+146 -16
View File
@@ -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
+249 -92
View File
@@ -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
+30 -5
View File
@@ -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
+15 -1
View File
@@ -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, &param, &mode);
}
} break;
case Command::kBeginDebugMarker: {
auto& args = *reinterpret_cast<const DebugMarkerHeader*>(stream);
const char* label_name = reinterpret_cast<const char*>(
+16 -1
View File
@@ -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.
+5
View File
@@ -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();
+22 -1
View File
@@ -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);
}
+9 -1
View File
@@ -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.