From fc55b1f81dcddc6ee2ddce6be4bf6a8e1ce1df9f Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 20 Sep 2020 20:34:22 +0300 Subject: [PATCH] [UI] Remove pass-unfriendly UpdateTexture of ImmediateDrawer --- src/xenia/ui/d3d12/d3d12_immediate_drawer.cc | 506 +++++++++--------- src/xenia/ui/d3d12/d3d12_immediate_drawer.h | 14 +- .../ui/d3d12/d3d12_upload_buffer_pool.cc | 2 +- src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h | 4 +- src/xenia/ui/d3d12/d3d12_util.cc | 2 +- src/xenia/ui/d3d12/d3d12_util.h | 2 +- src/xenia/ui/immediate_drawer.h | 8 +- .../ui/vulkan/vulkan_immediate_drawer.cc | 7 +- src/xenia/ui/vulkan/vulkan_immediate_drawer.h | 1 - 9 files changed, 275 insertions(+), 271 deletions(-) diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc index c1bcac5e6..f5cd384ed 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -10,6 +10,9 @@ #include "xenia/ui/d3d12/d3d12_immediate_drawer.h" #include +#include +#include +#include #include "xenia/base/assert.h" #include "xenia/base/logging.h" @@ -28,95 +31,48 @@ namespace d3d12 { class D3D12ImmediateTexture : public ImmediateTexture { public: static constexpr DXGI_FORMAT kFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - D3D12ImmediateTexture(uint32_t width, uint32_t height, - ImmediateTextureFilter filter, bool repeat); + ID3D12Resource* resource_if_exists, + ImmediateTextureFilter filter, bool is_repeated); ~D3D12ImmediateTexture() override; - - bool Initialize(D3D12Provider& provider); - void Shutdown(); - - ID3D12Resource* GetResource() const { return resource_; } - void Transition(D3D12_RESOURCE_STATES new_state, - ID3D12GraphicsCommandList* command_list); - - ImmediateTextureFilter GetFilter() const { return filter_; } - bool IsRepeated() const { return repeat_; } + ID3D12Resource* resource() const { return resource_; } + ImmediateTextureFilter filter() const { return filter_; } + bool is_repeated() const { return is_repeated_; } private: - ID3D12Resource* resource_ = nullptr; - D3D12_RESOURCE_STATES state_; + ID3D12Resource* resource_; ImmediateTextureFilter filter_; - bool repeat_; + bool is_repeated_; }; D3D12ImmediateTexture::D3D12ImmediateTexture(uint32_t width, uint32_t height, + ID3D12Resource* resource_if_exists, ImmediateTextureFilter filter, - bool repeat) - : ImmediateTexture(width, height), filter_(filter), repeat_(repeat) { + bool is_repeated) + : ImmediateTexture(width, height), + resource_(resource_if_exists), + filter_(filter), + is_repeated_(is_repeated) { + if (resource_) { + resource_->AddRef(); + } handle = reinterpret_cast(this); } -D3D12ImmediateTexture::~D3D12ImmediateTexture() { Shutdown(); } - -bool D3D12ImmediateTexture::Initialize(D3D12Provider& provider) { - // The first operation will likely be copying the contents. - state_ = D3D12_RESOURCE_STATE_COPY_DEST; - - D3D12_RESOURCE_DESC resource_desc; - resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - resource_desc.Alignment = 0; - resource_desc.Width = width; - resource_desc.Height = height; - resource_desc.DepthOrArraySize = 1; - resource_desc.MipLevels = 1; - resource_desc.Format = kFormat; - resource_desc.SampleDesc.Count = 1; - resource_desc.SampleDesc.Quality = 0; - resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE; - if (FAILED(provider.GetDevice()->CreateCommittedResource( - &util::kHeapPropertiesDefault, provider.GetHeapFlagCreateNotZeroed(), - &resource_desc, state_, nullptr, IID_PPV_ARGS(&resource_)))) { - XELOGE("Failed to create a {}x{} texture for immediate drawing", width, - height); - return false; - } - - return true; -} - -void D3D12ImmediateTexture::Shutdown() { - if (resource_ != nullptr) { +D3D12ImmediateTexture::~D3D12ImmediateTexture() { + if (resource_) { resource_->Release(); - resource_ = nullptr; } } -void D3D12ImmediateTexture::Transition( - D3D12_RESOURCE_STATES new_state, ID3D12GraphicsCommandList* command_list) { - if (resource_ == nullptr || state_ == new_state) { - return; - } - D3D12_RESOURCE_BARRIER barrier; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = resource_; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = state_; - barrier.Transition.StateAfter = new_state; - command_list->ResourceBarrier(1, &barrier); - state_ = new_state; -} - D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context& graphics_context) : ImmediateDrawer(&graphics_context), context_(graphics_context) {} D3D12ImmediateDrawer::~D3D12ImmediateDrawer() { Shutdown(); } bool D3D12ImmediateDrawer::Initialize() { - auto& provider = context_.GetD3D12Provider(); - auto device = provider.GetDevice(); + const D3D12Provider& provider = context_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); // Create the root signature. D3D12_ROOT_PARAMETER root_parameters[size_t(RootParameter::kCount)]; @@ -174,20 +130,20 @@ bool D3D12ImmediateDrawer::Initialize() { D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; root_signature_ = util::CreateRootSignature(provider, root_signature_desc); if (root_signature_ == nullptr) { - XELOGE("Failed to create the immediate drawer root signature"); + XELOGE("Failed to create the Direct3D 12 immediate drawer root signature"); Shutdown(); return false; } // Create the pipeline states. - D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {}; - pipeline_desc.pRootSignature = root_signature_; - pipeline_desc.VS.pShaderBytecode = immediate_vs; - pipeline_desc.VS.BytecodeLength = sizeof(immediate_vs); - pipeline_desc.PS.pShaderBytecode = immediate_ps; - pipeline_desc.PS.BytecodeLength = sizeof(immediate_ps); + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_state_desc = {}; + pipeline_state_desc.pRootSignature = root_signature_; + pipeline_state_desc.VS.pShaderBytecode = immediate_vs; + pipeline_state_desc.VS.BytecodeLength = sizeof(immediate_vs); + pipeline_state_desc.PS.pShaderBytecode = immediate_ps; + pipeline_state_desc.PS.BytecodeLength = sizeof(immediate_ps); D3D12_RENDER_TARGET_BLEND_DESC& pipeline_blend_desc = - pipeline_desc.BlendState.RenderTarget[0]; + pipeline_state_desc.BlendState.RenderTarget[0]; pipeline_blend_desc.BlendEnable = TRUE; pipeline_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA; pipeline_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA; @@ -199,11 +155,11 @@ bool D3D12ImmediateDrawer::Initialize() { pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_RED | D3D12_COLOR_WRITE_ENABLE_GREEN | D3D12_COLOR_WRITE_ENABLE_BLUE; - pipeline_desc.SampleMask = UINT_MAX; - pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; - pipeline_desc.RasterizerState.FrontCounterClockwise = FALSE; - pipeline_desc.RasterizerState.DepthClipEnable = TRUE; + pipeline_state_desc.SampleMask = UINT_MAX; + pipeline_state_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + pipeline_state_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; + pipeline_state_desc.RasterizerState.FrontCounterClockwise = FALSE; + pipeline_state_desc.RasterizerState.DepthClipEnable = TRUE; D3D12_INPUT_ELEMENT_DESC pipeline_input_elements[3] = {}; pipeline_input_elements[0].SemanticName = "POSITION"; pipeline_input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT; @@ -215,23 +171,29 @@ bool D3D12ImmediateDrawer::Initialize() { pipeline_input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM; pipeline_input_elements[2].AlignedByteOffset = offsetof(ImmediateVertex, color); - pipeline_desc.InputLayout.pInputElementDescs = pipeline_input_elements; - pipeline_desc.InputLayout.NumElements = + pipeline_state_desc.InputLayout.pInputElementDescs = pipeline_input_elements; + pipeline_state_desc.InputLayout.NumElements = UINT(xe::countof(pipeline_input_elements)); - pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - pipeline_desc.NumRenderTargets = 1; - pipeline_desc.RTVFormats[0] = D3D12Context::kSwapChainFormat; - pipeline_desc.SampleDesc.Count = 1; + pipeline_state_desc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + pipeline_state_desc.NumRenderTargets = 1; + pipeline_state_desc.RTVFormats[0] = D3D12Context::kSwapChainFormat; + pipeline_state_desc.SampleDesc.Count = 1; if (FAILED(device->CreateGraphicsPipelineState( - &pipeline_desc, IID_PPV_ARGS(&pipeline_triangle_)))) { - XELOGE("Failed to create immediate drawer triangle pipeline state"); + &pipeline_state_desc, IID_PPV_ARGS(&pipeline_state_triangle_)))) { + XELOGE( + "Failed to create the Direct3D 12 immediate drawer triangle pipeline " + "state"); Shutdown(); return false; } - pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; + pipeline_state_desc.PrimitiveTopologyType = + D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; if (FAILED(device->CreateGraphicsPipelineState( - &pipeline_desc, IID_PPV_ARGS(&pipeline_line_)))) { - XELOGE("Failed to create immediate drawer line pipeline state"); + &pipeline_state_desc, IID_PPV_ARGS(&pipeline_state_line_)))) { + XELOGE( + "Failed to create the Direct3D 12 immediate drawer line pipeline " + "state"); Shutdown(); return false; } @@ -244,7 +206,9 @@ bool D3D12ImmediateDrawer::Initialize() { sampler_heap_desc.NodeMask = 0; if (FAILED(device->CreateDescriptorHeap(&sampler_heap_desc, IID_PPV_ARGS(&sampler_heap_)))) { - XELOGE("Failed to create immediate drawer sampler descriptor heap"); + XELOGE( + "Failed to create the Direct3D 12 immediate drawer sampler descriptor " + "heap"); Shutdown(); return false; } @@ -293,8 +257,6 @@ bool D3D12ImmediateDrawer::Initialize() { vertex_buffer_pool_ = std::make_unique(provider); texture_descriptor_pool_ = std::make_unique( device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048); - texture_descriptor_pool_heap_index_ = - D3D12DescriptorHeapPool::kHeapIndexInvalid; // Reset the current state. current_command_list_ = nullptr; @@ -306,11 +268,13 @@ bool D3D12ImmediateDrawer::Initialize() { void D3D12ImmediateDrawer::Shutdown() { for (auto& texture_upload : texture_uploads_submitted_) { texture_upload.buffer->Release(); + texture_upload.texture->Release(); } texture_uploads_submitted_.clear(); for (auto& texture_upload : texture_uploads_pending_) { texture_upload.buffer->Release(); + texture_upload.texture->Release(); } texture_uploads_pending_.clear(); @@ -319,8 +283,8 @@ void D3D12ImmediateDrawer::Shutdown() { util::ReleaseAndNull(sampler_heap_); - util::ReleaseAndNull(pipeline_line_); - util::ReleaseAndNull(pipeline_triangle_); + util::ReleaseAndNull(pipeline_state_line_); + util::ReleaseAndNull(pipeline_state_triangle_); util::ReleaseAndNull(root_signature_); } @@ -328,93 +292,97 @@ void D3D12ImmediateDrawer::Shutdown() { std::unique_ptr D3D12ImmediateDrawer::CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, const uint8_t* data) { - auto texture = - std::make_unique(width, height, filter, repeat); - texture->Initialize(context_.GetD3D12Provider()); - if (data != nullptr) { - UpdateTexture(texture.get(), data); - } - return std::unique_ptr(texture.release()); -} + const D3D12Provider& provider = context_.GetD3D12Provider(); + ID3D12Device* device = provider.GetDevice(); + D3D12_HEAP_FLAGS heap_flag_create_not_zeroed = + provider.GetHeapFlagCreateNotZeroed(); -void D3D12ImmediateDrawer::UpdateTexture(ImmediateTexture* texture, - const uint8_t* data) { - D3D12ImmediateTexture* d3d_texture = - static_cast(texture); - ID3D12Resource* texture_resource = d3d_texture->GetResource(); - if (texture_resource == nullptr) { - return; - } - uint32_t width = d3d_texture->width, height = d3d_texture->height; - - auto& provider = context_.GetD3D12Provider(); - auto device = provider.GetDevice(); - - // Create and fill the upload buffer. - D3D12_RESOURCE_DESC texture_desc = texture_resource->GetDesc(); - D3D12_PLACED_SUBRESOURCE_FOOTPRINT upload_footprint; - UINT64 upload_size; - device->GetCopyableFootprints(&texture_desc, 0, 1, 0, &upload_footprint, - nullptr, nullptr, &upload_size); - D3D12_RESOURCE_DESC buffer_desc; - util::FillBufferResourceDesc(buffer_desc, upload_size, - D3D12_RESOURCE_FLAG_NONE); - ID3D12Resource* buffer; - if (FAILED(device->CreateCommittedResource( - &util::kHeapPropertiesUpload, provider.GetHeapFlagCreateNotZeroed(), - &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, - IID_PPV_ARGS(&buffer)))) { - XELOGE( - "Failed to create an upload buffer for a {}x{} texture for " - "immediate drawing", - width, height); - return; - } - D3D12_RANGE buffer_read_range; - buffer_read_range.Begin = 0; - buffer_read_range.End = 0; - void* buffer_mapping; - if (FAILED(buffer->Map(0, &buffer_read_range, &buffer_mapping))) { - XELOGE( - "Failed to map an upload buffer for a {}x{} texture for immediate " - "drawing", - width, height); - buffer->Release(); - return; - } - uint8_t* buffer_row = - reinterpret_cast(buffer_mapping) + upload_footprint.Offset; - for (uint32_t i = 0; i < height; ++i) { - std::memcpy(buffer_row, data, width * 4); - data += width * 4; - buffer_row += upload_footprint.Footprint.RowPitch; - } - buffer->Unmap(0, nullptr); - - if (current_command_list_ != nullptr) { - // Upload the texture right now if we can. - d3d_texture->Transition(D3D12_RESOURCE_STATE_COPY_DEST, - current_command_list_); - D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = buffer; - location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - location_source.PlacedFootprint = upload_footprint; - location_dest.pResource = texture_resource; - location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - location_dest.SubresourceIndex = 0; - current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, - &location_source, nullptr); - SubmittedTextureUpload submitted_upload; - submitted_upload.buffer = buffer; - submitted_upload.fence_value = context_.GetSwapCurrentFenceValue(); - texture_uploads_submitted_.push_back(submitted_upload); + D3D12_RESOURCE_DESC resource_desc; + resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resource_desc.Alignment = 0; + resource_desc.Width = width; + resource_desc.Height = height; + resource_desc.DepthOrArraySize = 1; + resource_desc.MipLevels = 1; + resource_desc.Format = D3D12ImmediateTexture::kFormat; + resource_desc.SampleDesc.Count = 1; + resource_desc.SampleDesc.Quality = 0; + resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + ID3D12Resource* resource; + if (SUCCEEDED(provider.GetDevice()->CreateCommittedResource( + &util::kHeapPropertiesDefault, heap_flag_create_not_zeroed, + &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, + IID_PPV_ARGS(&resource)))) { + // Create and fill the upload buffer. + D3D12_PLACED_SUBRESOURCE_FOOTPRINT upload_footprint; + UINT64 upload_size; + device->GetCopyableFootprints(&resource_desc, 0, 1, 0, &upload_footprint, + nullptr, nullptr, &upload_size); + D3D12_RESOURCE_DESC upload_buffer_desc; + util::FillBufferResourceDesc(upload_buffer_desc, upload_size, + D3D12_RESOURCE_FLAG_NONE); + ID3D12Resource* upload_buffer; + if (SUCCEEDED(device->CreateCommittedResource( + &util::kHeapPropertiesUpload, heap_flag_create_not_zeroed, + &upload_buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, + IID_PPV_ARGS(&upload_buffer)))) { + D3D12_RANGE upload_buffer_read_range; + upload_buffer_read_range.Begin = 0; + upload_buffer_read_range.End = 0; + void* upload_buffer_mapping; + if (SUCCEEDED(upload_buffer->Map(0, &upload_buffer_read_range, + &upload_buffer_mapping))) { + uint8_t* upload_buffer_row = + reinterpret_cast(upload_buffer_mapping) + + upload_footprint.Offset; + const uint8_t* data_row = data; + for (uint32_t i = 0; i < height; ++i) { + std::memcpy(upload_buffer_row, data_row, width * 4); + data_row += width * 4; + upload_buffer_row += upload_footprint.Footprint.RowPitch; + } + upload_buffer->Unmap(0, nullptr); + // Defer uploading and transition to the next draw. + PendingTextureUpload pending_upload; + // While the upload has not been yet completed, keep a reference to the + // resource because its lifetime is not tied to that of the + // ImmediateTexture (and thus to context's submissions) now. + resource->AddRef(); + pending_upload.texture = resource; + pending_upload.buffer = upload_buffer; + texture_uploads_pending_.push_back(pending_upload); + } else { + XELOGE( + "Failed to map a Direct3D 12 upload buffer for a {}x{} texture for " + "immediate drawing", + width, height); + upload_buffer->Release(); + resource->Release(); + resource = nullptr; + } + } else { + XELOGE( + "Failed to create a Direct3D 12 upload buffer for a {}x{} texture " + "for immediate drawing", + width, height); + resource->Release(); + resource = nullptr; + } } else { - // Defer uploading to the next frame when there's a command list. - PendingTextureUpload pending_upload; - pending_upload.texture = texture; - pending_upload.buffer = buffer; - texture_uploads_pending_.push_back(pending_upload); + XELOGE("Failed to create a {}x{} Direct3D 12 texture for immediate drawing", + width, height); + resource = nullptr; } + + std::unique_ptr texture = + std::make_unique(width, height, resource, filter, + repeat); + if (resource) { + // D3D12ImmediateTexture now holds a reference. + resource->Release(); + } + return std::move(texture); } void D3D12ImmediateDrawer::Begin(int render_target_width, @@ -422,7 +390,7 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, assert_null(current_command_list_); assert_false(batch_open_); - auto device = context_.GetD3D12Provider().GetDevice(); + ID3D12Device* device = context_.GetD3D12Provider().GetDevice(); // Use the compositing command list. current_command_list_ = context_.GetSwapCommandList(); @@ -430,7 +398,7 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, uint64_t completed_fence_value = context_.GetSwapCompletedFenceValue(); uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); - // Remove temporary buffers for completed texture uploads. + // Release upload buffers for completed texture uploads. auto erase_uploads_end = texture_uploads_submitted_.begin(); while (erase_uploads_end != texture_uploads_submitted_.end()) { uint64_t upload_fence_value = erase_uploads_end->fence_value; @@ -439,42 +407,15 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, break; } erase_uploads_end->buffer->Release(); + // Release the texture reference held for uploading. + erase_uploads_end->texture->Release(); ++erase_uploads_end; } texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(), erase_uploads_end); - // Submit texture updates that happened between frames. - while (!texture_uploads_pending_.empty()) { - const PendingTextureUpload& pending_upload = - texture_uploads_pending_.back(); - D3D12ImmediateTexture* texture = - static_cast(pending_upload.texture); - texture->Transition(D3D12_RESOURCE_STATE_COPY_DEST, current_command_list_); - ID3D12Resource* texture_resource = texture->GetResource(); - D3D12_RESOURCE_DESC texture_desc = texture_resource->GetDesc(); - D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; - location_source.pResource = pending_upload.buffer; - location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - device->GetCopyableFootprints(&texture_desc, 0, 1, 0, - &location_source.PlacedFootprint, nullptr, - nullptr, nullptr); - location_dest.pResource = texture_resource; - location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - location_dest.SubresourceIndex = 0; - current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, - &location_source, nullptr); - SubmittedTextureUpload submitted_upload; - submitted_upload.buffer = pending_upload.buffer; - submitted_upload.fence_value = current_fence_value; - texture_uploads_submitted_.push_back(submitted_upload); - texture_uploads_pending_.pop_back(); - } - vertex_buffer_pool_->Reclaim(completed_fence_value); texture_descriptor_pool_->Reclaim(completed_fence_value); - texture_descriptor_pool_heap_index_ = - D3D12DescriptorHeapPool::kHeapIndexInvalid; current_render_target_width_ = render_target_width; current_render_target_height_ = render_target_height; @@ -486,10 +427,6 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; current_command_list_->RSSetViewports(1, &viewport); - current_scissor_.left = 0; - current_scissor_.top = 0; - current_scissor_.right = 0; - current_scissor_.bottom = 0; current_command_list_->SetGraphicsRootSignature(root_signature_); float viewport_inv_size[2]; @@ -498,8 +435,15 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, current_command_list_->SetGraphicsRoot32BitConstants( UINT(RootParameter::kViewportSizeInv), 2, viewport_inv_size, 0); + current_scissor_.left = 0; + current_scissor_.top = 0; + current_scissor_.right = 0; + current_scissor_.bottom = 0; + current_primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; current_texture_ = nullptr; + current_texture_descriptor_heap_index_ = + D3D12DescriptorHeapPool::kHeapIndexInvalid; current_sampler_index_ = SamplerIndex::kInvalid; } @@ -554,9 +498,6 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { return; } - auto& provider = context_.GetD3D12Provider(); - auto device = provider.GetDevice(); - // Set the scissor rectangle if enabled. D3D12_RECT scissor; if (draw.scissor) { @@ -583,43 +524,56 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { current_command_list_->RSSetScissorRects(1, &scissor); } - // Bind the texture. + // Ensure texture data is available if any texture is loaded, upload all in a + // batch, then transition all at once. + UploadTextures(); + + // Bind the texture. If this is the first draw in a frame, the descriptor heap + // index will be invalid initially, and the texture will be bound regardless + // of what's in current_texture_. auto texture = reinterpret_cast(draw.texture_handle); - ID3D12Resource* texture_resource; - if (texture != nullptr) { - texture->Transition(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - current_command_list_); - texture_resource = texture->GetResource(); - } else { - texture_resource = nullptr; - } - bool bind_texture = current_texture_ != texture; + ID3D12Resource* texture_resource = texture ? texture->resource() : nullptr; + bool bind_texture = current_texture_ != texture_resource; uint32_t texture_descriptor_index; uint64_t texture_heap_index = texture_descriptor_pool_->Request( - context_.GetSwapCurrentFenceValue(), texture_descriptor_pool_heap_index_, - bind_texture ? 1 : 0, 1, texture_descriptor_index); + context_.GetSwapCurrentFenceValue(), + current_texture_descriptor_heap_index_, bind_texture ? 1 : 0, 1, + texture_descriptor_index); if (texture_heap_index == D3D12DescriptorHeapPool::kHeapIndexInvalid) { return; } - if (texture_descriptor_pool_heap_index_ != texture_heap_index) { + if (current_texture_descriptor_heap_index_ != texture_heap_index) { + current_texture_descriptor_heap_index_ = texture_heap_index; bind_texture = true; - texture_descriptor_pool_heap_index_ = texture_heap_index; ID3D12DescriptorHeap* descriptor_heaps[] = { texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_}; current_command_list_->SetDescriptorHeaps(2, descriptor_heaps); } + + const D3D12Provider& provider = context_.GetD3D12Provider(); + if (bind_texture) { - current_texture_ = texture; + current_texture_ = texture_resource; D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc; texture_view_desc.Format = D3D12ImmediateTexture::kFormat; texture_view_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - texture_view_desc.Shader4ComponentMapping = - D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + if (texture_resource) { + texture_view_desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + } else { + // No texture, solid color. + texture_view_desc.Shader4ComponentMapping = + D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING( + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1, + D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1); + } texture_view_desc.Texture2D.MostDetailedMip = 0; texture_view_desc.Texture2D.MipLevels = 1; texture_view_desc.Texture2D.PlaneSlice = 0; texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f; - device->CreateShaderResourceView( + provider.GetDevice()->CreateShaderResourceView( texture_resource, &texture_view_desc, provider.OffsetViewDescriptor( texture_descriptor_pool_->GetLastRequestHeapCPUStart(), @@ -634,12 +588,12 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { // Bind the sampler. SamplerIndex sampler_index; if (texture != nullptr) { - if (texture->GetFilter() == ImmediateTextureFilter::kLinear) { - sampler_index = texture->IsRepeated() ? SamplerIndex::kLinearRepeat - : SamplerIndex::kLinearClamp; + if (texture->filter() == ImmediateTextureFilter::kLinear) { + sampler_index = texture->is_repeated() ? SamplerIndex::kLinearRepeat + : SamplerIndex::kLinearClamp; } else { - sampler_index = texture->IsRepeated() ? SamplerIndex::kNearestRepeat - : SamplerIndex::kNearestClamp; + sampler_index = texture->is_repeated() ? SamplerIndex::kNearestRepeat + : SamplerIndex::kNearestClamp; } } else { sampler_index = SamplerIndex::kNearestClamp; @@ -658,17 +612,17 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { UINT(RootParameter::kRestrictTextureSamples), 1, &restrict_texture_samples, 0); - // Set the primitive type and the pipeline for it. + // Set the primitive type and the pipeline state for it. D3D_PRIMITIVE_TOPOLOGY primitive_topology; - ID3D12PipelineState* pipeline; + ID3D12PipelineState* pipeline_state; switch (draw.primitive_type) { case ImmediatePrimitiveType::kLines: primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; - pipeline = pipeline_line_; + pipeline_state = pipeline_state_line_; break; case ImmediatePrimitiveType::kTriangles: primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - pipeline = pipeline_triangle_; + pipeline_state = pipeline_state_triangle_; break; default: assert_unhandled_case(draw.primitive_type); @@ -677,7 +631,7 @@ void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { if (current_primitive_topology_ != primitive_topology) { current_primitive_topology_ = primitive_topology; current_command_list_->IASetPrimitiveTopology(primitive_topology); - current_command_list_->SetPipelineState(pipeline); + current_command_list_->SetPipelineState(pipeline_state); } // Draw. @@ -693,7 +647,65 @@ void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; } void D3D12ImmediateDrawer::End() { assert_false(batch_open_); - current_command_list_ = nullptr; + if (current_command_list_) { + // Don't keep upload buffers forever if nothing was drawn in this frame. + UploadTextures(); + current_command_list_ = nullptr; + } +} + +void D3D12ImmediateDrawer::UploadTextures() { + assert_not_null(current_command_list_); + if (texture_uploads_pending_.empty()) { + // Called often - don't initialize anything. + return; + } + + ID3D12Device* device = context_.GetD3D12Provider().GetDevice(); + uint64_t current_fence_value = context_.GetSwapCurrentFenceValue(); + + // Copy all at once, then transition all at once (not interleaving copying and + // pipeline barriers). + std::vector barriers; + barriers.reserve(texture_uploads_pending_.size()); + while (!texture_uploads_pending_.empty()) { + const PendingTextureUpload& pending_upload = + texture_uploads_pending_.back(); + ID3D12Resource* texture = pending_upload.texture; + + D3D12_RESOURCE_DESC texture_desc = texture->GetDesc(); + D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; + location_source.pResource = pending_upload.buffer; + location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + device->GetCopyableFootprints(&texture_desc, 0, 1, 0, + &location_source.PlacedFootprint, nullptr, + nullptr, nullptr); + location_dest.pResource = texture; + location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + location_dest.SubresourceIndex = 0; + current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, + &location_source, nullptr); + + D3D12_RESOURCE_BARRIER& barrier = barriers.emplace_back(); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = texture; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + SubmittedTextureUpload submitted_upload; + // Transfer the reference to the texture - need to keep it until the upload + // is completed. + submitted_upload.texture = texture; + submitted_upload.buffer = pending_upload.buffer; + submitted_upload.fence_value = current_fence_value; + texture_uploads_submitted_.push_back(submitted_upload); + texture_uploads_pending_.pop_back(); + } + assert_false(barriers.empty()); + current_command_list_->ResourceBarrier(UINT(barriers.size()), + barriers.data()); } } // namespace d3d12 diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h index 53ae81054..bf11f0cff 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -38,7 +38,6 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { ImmediateTextureFilter filter, bool repeat, const uint8_t* data) override; - void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override; void Begin(int render_target_width, int render_target_height) override; void BeginDrawBatch(const ImmediateDrawBatch& batch) override; @@ -47,6 +46,8 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { void End() override; private: + void UploadTextures(); + D3D12Context& context_; ID3D12RootSignature* root_signature_ = nullptr; @@ -59,8 +60,8 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { kCount }; - ID3D12PipelineState* pipeline_triangle_ = nullptr; - ID3D12PipelineState* pipeline_line_ = nullptr; + ID3D12PipelineState* pipeline_state_triangle_ = nullptr; + ID3D12PipelineState* pipeline_state_line_ = nullptr; enum class SamplerIndex { kNearestClamp, @@ -77,15 +78,15 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { std::unique_ptr vertex_buffer_pool_; std::unique_ptr texture_descriptor_pool_; - uint64_t texture_descriptor_pool_heap_index_; struct PendingTextureUpload { - ImmediateTexture* texture; + ID3D12Resource* texture; ID3D12Resource* buffer; }; std::vector texture_uploads_pending_; struct SubmittedTextureUpload { + ID3D12Resource* texture; ID3D12Resource* buffer; uint64_t fence_value; }; @@ -97,7 +98,8 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { bool batch_has_index_buffer_; D3D12_RECT current_scissor_; D3D_PRIMITIVE_TOPOLOGY current_primitive_topology_; - ImmediateTexture* current_texture_; + ID3D12Resource* current_texture_; + uint64_t current_texture_descriptor_heap_index_; SamplerIndex current_sampler_index_; }; diff --git a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc index 4675280dc..0d6cf8b72 100644 --- a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc +++ b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.cc @@ -20,7 +20,7 @@ namespace d3d12 { // Align to D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT not to waste any space if // it's smaller (the size of the heap backing the buffer will be aligned to // D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT anyway). -D3D12UploadBufferPool::D3D12UploadBufferPool(D3D12Provider& provider, +D3D12UploadBufferPool::D3D12UploadBufferPool(const D3D12Provider& provider, size_t page_size) : GraphicsUploadBufferPool(xe::align( page_size, size_t(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT))), diff --git a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h index 9fc2ce45e..2a8d2ac0a 100644 --- a/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h +++ b/src/xenia/ui/d3d12/d3d12_upload_buffer_pool.h @@ -19,7 +19,7 @@ namespace d3d12 { class D3D12UploadBufferPool : public GraphicsUploadBufferPool { public: - D3D12UploadBufferPool(D3D12Provider& provider, + D3D12UploadBufferPool(const D3D12Provider& provider, size_t page_size = kDefaultPageSize); uint8_t* Request(uint64_t submission_index, size_t size, size_t alignment, @@ -44,7 +44,7 @@ class D3D12UploadBufferPool : public GraphicsUploadBufferPool { D3D12_GPU_VIRTUAL_ADDRESS gpu_address_; }; - D3D12Provider& provider_; + const D3D12Provider& provider_; }; } // namespace d3d12 diff --git a/src/xenia/ui/d3d12/d3d12_util.cc b/src/xenia/ui/d3d12/d3d12_util.cc index b139d9adb..710d3b6db 100644 --- a/src/xenia/ui/d3d12/d3d12_util.cc +++ b/src/xenia/ui/d3d12/d3d12_util.cc @@ -23,7 +23,7 @@ const D3D12_HEAP_PROPERTIES kHeapPropertiesReadback = { D3D12_HEAP_TYPE_READBACK}; ID3D12RootSignature* CreateRootSignature( - D3D12Provider& provider, const D3D12_ROOT_SIGNATURE_DESC& desc) { + const D3D12Provider& provider, const D3D12_ROOT_SIGNATURE_DESC& desc) { ID3DBlob* blob; ID3DBlob* error_blob = nullptr; if (FAILED(provider.SerializeRootSignature( diff --git a/src/xenia/ui/d3d12/d3d12_util.h b/src/xenia/ui/d3d12/d3d12_util.h index 92dfb1535..5bce23568 100644 --- a/src/xenia/ui/d3d12/d3d12_util.h +++ b/src/xenia/ui/d3d12/d3d12_util.h @@ -36,7 +36,7 @@ inline bool ReleaseAndNull(T& object) { return false; }; -ID3D12RootSignature* CreateRootSignature(D3D12Provider& provider, +ID3D12RootSignature* CreateRootSignature(const D3D12Provider& provider, const D3D12_ROOT_SIGNATURE_DESC& desc); ID3D12PipelineState* CreateComputePipelineState( diff --git a/src/xenia/ui/immediate_drawer.h b/src/xenia/ui/immediate_drawer.h index a2e80cb22..38f91363f 100644 --- a/src/xenia/ui/immediate_drawer.h +++ b/src/xenia/ui/immediate_drawer.h @@ -96,14 +96,10 @@ class ImmediateDrawer { public: virtual ~ImmediateDrawer() = default; - // Creates a new texture with the given attributes and optionally updates - // initial data. + // Creates a new texture with the given attributes and R8G8B8A8 data. virtual std::unique_ptr CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, - bool repeat, const uint8_t* data = nullptr) = 0; - // Uploads data to the given texture, replacing the current contents. - virtual void UpdateTexture(ImmediateTexture* texture, - const uint8_t* data) = 0; + bool repeat, const uint8_t* data) = 0; // Begins drawing in immediate mode using the given projection matrix. virtual void Begin(int render_target_width, int render_target_height) = 0; diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc index a911ac332..fafb03b02 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.cc @@ -730,7 +730,7 @@ std::unique_ptr VulkanImmediateDrawer::CreateTexture( } if (data) { - UpdateTexture(texture.get(), data); + texture->Upload(data); } return std::unique_ptr(texture.release()); } @@ -751,11 +751,6 @@ std::unique_ptr VulkanImmediateDrawer::WrapTexture( return texture; } -void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture, - const uint8_t* data) { - static_cast(texture)->Upload(data); -} - void VulkanImmediateDrawer::Begin(int render_target_width, int render_target_height) { auto device = context_->device(); diff --git a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h index 6e4f5ce1a..5cc66fd26 100644 --- a/src/xenia/ui/vulkan/vulkan_immediate_drawer.h +++ b/src/xenia/ui/vulkan/vulkan_immediate_drawer.h @@ -39,7 +39,6 @@ class VulkanImmediateDrawer : public ImmediateDrawer { VkSampler sampler, uint32_t width, uint32_t height); - void UpdateTexture(ImmediateTexture* texture, const uint8_t* data) override; void Begin(int render_target_width, int render_target_height) override; void BeginDrawBatch(const ImmediateDrawBatch& batch) override;