diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.cc b/src/xenia/gpu/d3d12/d3d12_command_processor.cc index 8b9a60024..1275fdd0f 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.cc @@ -799,10 +799,9 @@ std::unique_ptr D3D12CommandProcessor::Capture() { deferred_command_list_->CopyTexture(location_dest, location_source); PushTransitionBarrier(swap_texture_, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - if (!EndSubmission(false)) { + if (!AwaitAllQueueOperationsCompletion()) { return nullptr; } - AwaitAllQueueOperationsCompletion(); D3D12_RANGE readback_range; readback_range.Begin = swap_texture_copy_footprint_.Offset; readback_range.End = swap_texture_copy_size_; @@ -862,7 +861,7 @@ bool D3D12CommandProcessor::SetupContext() { } if (FAILED(device->CreateFence( 0, D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(&queue_operations_since_submission_signal_fence_)))) { + IID_PPV_ARGS(&queue_operations_since_submission_fence_)))) { XELOGE( "Failed to create the fence for awaiting queue operations done since " "the latest submission"); @@ -1557,8 +1556,8 @@ void D3D12CommandProcessor::ShutdownContext() { // First release the fences since they may reference fence_completion_event_. queue_operations_done_since_submission_signal_ = false; - ui::d3d12::util::ReleaseAndNull( - queue_operations_since_submission_signal_fence_); + queue_operations_since_submission_fence_last_ = 0; + ui::d3d12::util::ReleaseAndNull(queue_operations_since_submission_fence_); ui::d3d12::util::ReleaseAndNull(submission_fence_); submission_open_ = false; @@ -2286,24 +2285,25 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, memexport_range.base_address_dwords << 2, memexport_range_size); readback_buffer_offset += memexport_range_size; } - AwaitAllQueueOperationsCompletion(); - D3D12_RANGE readback_range; - readback_range.Begin = 0; - readback_range.End = memexport_total_size; - void* readback_mapping; - if (SUCCEEDED(readback_buffer->Map(0, &readback_range, - &readback_mapping))) { - const uint32_t* readback_dwords = - reinterpret_cast(readback_mapping); - for (uint32_t i = 0; i < memexport_range_count; ++i) { - const MemExportRange& memexport_range = memexport_ranges[i]; - std::memcpy(memory_->TranslatePhysical( - memexport_range.base_address_dwords << 2), - readback_dwords, memexport_range.size_dwords << 2); - readback_dwords += memexport_range.size_dwords; + if (AwaitAllQueueOperationsCompletion()) { + D3D12_RANGE readback_range; + readback_range.Begin = 0; + readback_range.End = memexport_total_size; + void* readback_mapping; + if (SUCCEEDED(readback_buffer->Map(0, &readback_range, + &readback_mapping))) { + const uint32_t* readback_dwords = + reinterpret_cast(readback_mapping); + for (uint32_t i = 0; i < memexport_range_count; ++i) { + const MemExportRange& memexport_range = memexport_ranges[i]; + std::memcpy(memory_->TranslatePhysical( + memexport_range.base_address_dwords << 2), + readback_dwords, memexport_range.size_dwords << 2); + readback_dwords += memexport_range.size_dwords; + } + D3D12_RANGE readback_write_range = {}; + readback_buffer->Unmap(0, &readback_write_range); } - D3D12_RANGE readback_write_range = {}; - readback_buffer->Unmap(0, &readback_write_range); } } } @@ -2322,10 +2322,9 @@ void D3D12CommandProcessor::InitializeTrace() { if (!render_target_cache_submitted && !shared_memory_submitted) { return; } - if (!EndSubmission(false)) { + if (!AwaitAllQueueOperationsCompletion()) { return; } - AwaitAllQueueOperationsCompletion(); if (render_target_cache_submitted) { render_target_cache_->InitializeTraceCompleteDownloads(); } @@ -2357,8 +2356,7 @@ bool D3D12CommandProcessor::IssueCopy() { deferred_command_list_->D3DCopyBufferRegion( readback_buffer, 0, shared_memory_buffer, written_address, written_length); - if (EndSubmission(false)) { - AwaitAllQueueOperationsCompletion(); + if (AwaitAllQueueOperationsCompletion()) { D3D12_RANGE readback_range; readback_range.Begin = 0; readback_range.End = written_length; @@ -2377,19 +2375,48 @@ bool D3D12CommandProcessor::IssueCopy() { } void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) { - assert_true(await_submission <= submission_current_); - if (await_submission == submission_current_) { - assert_true(submission_open_); - EndSubmission(false); + if (await_submission >= submission_current_) { + if (submission_open_) { + EndSubmission(false); + } + // Ending an open submission should result in queue operations done directly + // (like UpdateTileMappings) to be tracked within the scope of that + // submission, but just in case of a failure, or queue operations being done + // outside of a submission, await explicitly. + if (queue_operations_done_since_submission_signal_) { + UINT64 fence_value = ++queue_operations_since_submission_fence_last_; + ID3D12CommandQueue* direct_queue = + GetD3D12Context().GetD3D12Provider().GetDirectQueue(); + if (SUCCEEDED( + direct_queue->Signal(queue_operations_since_submission_fence_, + fence_value) && + SUCCEEDED(queue_operations_since_submission_fence_ + ->SetEventOnCompletion(fence_value, + fence_completion_event_)))) { + WaitForSingleObject(fence_completion_event_, INFINITE); + queue_operations_done_since_submission_signal_ = false; + } else { + XELOGE( + "Failed to await an out-of-submission queue operation completion " + "Direct3D 12 fence"); + } + } + // A submission won't be ended if it hasn't been started, or if ending + // has failed - clamp the index. + await_submission = submission_current_ - 1; } uint64_t submission_completed_before = submission_completed_; submission_completed_ = submission_fence_->GetCompletedValue(); if (submission_completed_ < await_submission) { - submission_fence_->SetEventOnCompletion(await_submission, - fence_completion_event_); - WaitForSingleObject(fence_completion_event_, INFINITE); - submission_completed_ = submission_fence_->GetCompletedValue(); + if (SUCCEEDED(submission_fence_->SetEventOnCompletion( + await_submission, fence_completion_event_))) { + WaitForSingleObject(fence_completion_event_, INFINITE); + submission_completed_ = submission_fence_->GetCompletedValue(); + } + } + if (submission_completed_ < await_submission) { + XELOGE("Failed to await a submission completion Direct3D 12 fence"); } if (submission_completed_ <= submission_completed_before) { // Not updated - no need to reclaim or download things. @@ -2461,6 +2488,9 @@ void D3D12CommandProcessor::BeginSubmission(bool is_guest_command) { is_opening_frame ? closed_frame_submissions_[frame_current_ % kQueueFrames] : 0); + // TODO(Triang3l): If failed to await (completed submission < awaited frame + // submission), do something like dropping the draw command that wanted to + // open the frame. if (is_opening_frame) { // Update the completed frame index, also obtaining the actual completed // frame number (since the CPU may be actually less than 3 frames behind) @@ -2645,9 +2675,8 @@ bool D3D12CommandProcessor::EndSubmission(bool is_swap) { closed_frame_submissions_[(frame_current_++) % kQueueFrames] = submission_current_ - 1; - if (cache_clear_requested_) { + if (cache_clear_requested_ && AwaitAllQueueOperationsCompletion()) { cache_clear_requested_ = false; - AwaitAllQueueOperationsCompletion(); ClearCommandAllocatorCache(); @@ -2692,34 +2721,6 @@ bool D3D12CommandProcessor::CanEndSubmissionImmediately() const { return !submission_open_ || !pipeline_cache_->IsCreatingPipelineStates(); } -void D3D12CommandProcessor::AwaitAllQueueOperationsCompletion() { - ui::d3d12::D3D12Context& context = GetD3D12Context(); - if (context.WasLost()) { - return; - } - if (queue_operations_done_since_submission_signal_) { - assert_not_null(queue_operations_since_submission_signal_fence_); - UINT64 fence_value = - queue_operations_since_submission_signal_fence_->GetCompletedValue() + - 1; - context.GetD3D12Provider().GetDirectQueue()->Signal( - queue_operations_since_submission_signal_fence_, fence_value); - queue_operations_since_submission_signal_fence_->SetEventOnCompletion( - fence_value, fence_completion_event_); - queue_operations_done_since_submission_signal_ = false; - // Since this indicates operations made after the last submission was done, - // it will await all the preceding submissions too. - } else if ((submission_completed_ + 1) < submission_current_) { - assert_not_null(submission_fence_); - submission_fence_->SetEventOnCompletion(submission_current_ - 1, - fence_completion_event_); - } else { - return; - } - WaitForSingleObject(fence_completion_event_, INFINITE); - submission_completed_ = submission_current_ - 1; -} - void D3D12CommandProcessor::ClearCommandAllocatorCache() { while (command_allocator_submitted_first_) { auto next = command_allocator_submitted_first_->next; diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.h b/src/xenia/gpu/d3d12/d3d12_command_processor.h index c809399a3..58015cdb4 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.h +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.h @@ -17,6 +17,7 @@ #include #include +#include "xenia/base/assert.h" #include "xenia/gpu/command_processor.h" #include "xenia/gpu/d3d12/d3d12_graphics_system.h" #include "xenia/gpu/d3d12/deferred_command_list.h" @@ -54,7 +55,6 @@ class D3D12CommandProcessor : public CommandProcessor { void RestoreEdramSnapshot(const void* snapshot) override; - // Needed by everything that owns transient objects. ui::d3d12::D3D12Context& GetD3D12Context() const { return static_cast(*context_); } @@ -62,6 +62,7 @@ class D3D12CommandProcessor : public CommandProcessor { // Returns the deferred drawing command list for the currently open // submission. DeferredCommandList& GetDeferredCommandList() { + assert_true(submission_open_); return *deferred_command_list_; } @@ -69,8 +70,8 @@ class D3D12CommandProcessor : public CommandProcessor { uint64_t GetCompletedSubmission() const { return submission_completed_; } // Must be called when a subsystem does something like UpdateTileMappings so - // it can be awaited in AwaitAllQueueOperationsCompletion if it was done after - // the latest ExecuteCommandLists + Signal. + // it can be awaited in CheckSubmissionFence(submission_current_) if it was + // done after the latest ExecuteCommandLists + Signal. void NotifyQueueOperationsDoneDirectly() { queue_operations_done_since_submission_signal_ = true; } @@ -299,7 +300,8 @@ class D3D12CommandProcessor : public CommandProcessor { // submission has already been closed. // Rechecks submission number and reclaims per-submission resources. Pass 0 as - // the submission to await to simply check status. + // the submission to await to simply check status, or pass submission_current_ + // to wait for all queue operations to be completed. void CheckSubmissionFence(uint64_t await_submission); // If is_guest_command is true, a new full frame - with full cleanup of // resources and, if needed, starting capturing - is opened if pending (as @@ -314,7 +316,10 @@ class D3D12CommandProcessor : public CommandProcessor { // as when there are unfinished graphics pipeline state creation requests that // would need to be fulfilled before actually submitting the command list. bool CanEndSubmissionImmediately() const; - void AwaitAllQueueOperationsCompletion(); + bool AwaitAllQueueOperationsCompletion() { + CheckSubmissionFence(submission_current_); + return submission_completed_ + 1 >= submission_current_; + } // Need to await submission completion before calling. void ClearCommandAllocatorCache(); @@ -369,7 +374,8 @@ class D3D12CommandProcessor : public CommandProcessor { // AwaitAllQueueOperationsCompletion when they're queued after the latest // ExecuteCommandLists + Signal, thus won't be awaited by just awaiting the // submission. - ID3D12Fence* queue_operations_since_submission_signal_fence_ = nullptr; + ID3D12Fence* queue_operations_since_submission_fence_ = nullptr; + uint64_t queue_operations_since_submission_fence_last_ = 0; bool queue_operations_done_since_submission_signal_ = false; bool frame_open_ = false;