From 0e667a9b0fa010ce28705da7fb46dc60a7fcaa2d Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Wed, 4 Mar 2026 22:45:35 +0000 Subject: [PATCH] GS/DX12: Copy constant buffers to default heap --- pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.cpp | 68 ++++++++++++++++--- pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.h | 23 +++++-- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 20 ++++-- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.cpp b/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.cpp index 3f28ce4b88..ec5dadb975 100644 --- a/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.cpp +++ b/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.cpp @@ -20,7 +20,7 @@ D3D12StreamBuffer::~D3D12StreamBuffer() Destroy(); } -bool D3D12StreamBuffer::Create(u32 size) +bool D3D12StreamBuffer::Create(u32 size, bool gpu_backed_buffer) { const GSDevice12::D3D12_RESOURCE_DESCU resource_desc = {{D3D12_RESOURCE_DIMENSION_BUFFER, 0, size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE}}; @@ -51,11 +51,32 @@ bool D3D12StreamBuffer::Create(u32 size) Destroy(true); - m_buffer = std::move(buffer); - m_allocation = std::move(allocation); + m_buffer_upload = std::move(buffer); + m_allocation_upload = std::move(allocation); m_host_pointer = host_pointer; m_size = size; - m_gpu_pointer = m_buffer->GetGPUVirtualAddress(); + + if (gpu_backed_buffer) + { + allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + + if (GSDevice12::GetInstance()->UseEnhancedBarriers()) + hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource3(&allocationDesc, &resource_desc.desc1, + D3D12_BARRIER_LAYOUT_UNDEFINED, nullptr, 0, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put())); + else + hr = GSDevice12::GetInstance()->GetAllocator()->CreateResource(&allocationDesc, &resource_desc.desc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(buffer.put())); + pxAssertMsg(SUCCEEDED(hr), "Allocate buffer"); + if (FAILED(hr)) + return false; + + m_buffer_default = std::move(buffer); + m_allocation_default = std::move(allocation); + m_gpu_pointer = m_buffer_default->GetGPUVirtualAddress(); + } + else + m_gpu_pointer = m_buffer_upload->GetGPUVirtualAddress(); + return true; } @@ -136,21 +157,50 @@ void D3D12StreamBuffer::CommitMemory(u32 final_num_bytes) m_current_space -= final_num_bytes; } +void D3D12StreamBuffer::FlushMemory() +{ + if (m_buffer_default && + m_current_space != m_size) + { + if (m_current_copy_offset < m_current_offset) + { + const u32 size = m_current_offset - m_current_copy_offset; + GSDevice12::GetInstance()->GetInitCommandList().list4->CopyBufferRegion(m_buffer_default.get(), m_current_copy_offset, + m_buffer_upload.get(), m_current_copy_offset, size); + } + else + { + const u32 size = m_size - m_current_copy_offset; + GSDevice12::GetInstance()->GetInitCommandList().list4->CopyBufferRegion(m_buffer_default.get(), m_current_copy_offset, + m_buffer_upload.get(), m_current_copy_offset, size); + GSDevice12::GetInstance()->GetInitCommandList().list4->CopyBufferRegion(m_buffer_default.get(), 0, + m_buffer_upload.get(), 0, m_current_offset); + } + m_current_copy_offset = m_current_offset; + } +} + void D3D12StreamBuffer::Destroy(bool defer) { if (m_host_pointer) { const D3D12_RANGE written_range = {0, m_size}; - m_buffer->Unmap(0, &written_range); + m_buffer_upload->Unmap(0, &written_range); m_host_pointer = nullptr; } - if (m_buffer && defer) - GSDevice12::GetInstance()->DeferResourceDestruction(m_allocation.get(), m_buffer.get()); - m_buffer.reset(); - m_allocation.reset(); + if (m_buffer_upload && defer) + GSDevice12::GetInstance()->DeferResourceDestruction(m_allocation_upload.get(), m_buffer_upload.get()); + m_buffer_upload.reset(); + m_allocation_upload.reset(); + + if (m_buffer_default && defer) + GSDevice12::GetInstance()->DeferResourceDestruction(m_allocation_default.get(), m_buffer_default.get()); + m_buffer_default.reset(); + m_allocation_default.reset(); m_current_offset = 0; + m_current_copy_offset = 0; m_current_space = 0; m_current_gpu_position = 0; m_tracked_fences.clear(); diff --git a/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.h b/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.h index 58a48ae350..8ec2752012 100644 --- a/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.h +++ b/pcsx2/GS/Renderers/DX12/D3D12StreamBuffer.h @@ -6,6 +6,7 @@ #include "common/Pcsx2Defs.h" #include "common/RedtapeWindows.h" #include "common/RedtapeWilCom.h" +#include "common/Assertions.h" #include #include @@ -22,10 +23,17 @@ public: D3D12StreamBuffer(); ~D3D12StreamBuffer(); - bool Create(u32 size); + // gpu_backed_buffer specifies if a second buffer should be created in the default heap. + bool Create(u32 size, bool gpu_backed_buffer = false); - __fi bool IsValid() const { return static_cast(m_buffer); } - __fi ID3D12Resource* GetBuffer() const { return m_buffer.get(); } + __fi bool IsValid() const { return static_cast(m_buffer_upload); } + __fi ID3D12Resource* GetBuffer() const + { + // This function is currently only used for textures, which already need to bt copied to a texture resource. + // For now, assert that gpu_backed_buffer was false. + pxAssert(m_buffer_default == nullptr); + return m_buffer_upload.get(); + } __fi D3D12_GPU_VIRTUAL_ADDRESS GetGPUPointer() const { return m_gpu_pointer; } __fi void* GetHostPointer() const { return m_host_pointer; } __fi void* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; } @@ -36,6 +44,8 @@ public: bool ReserveMemory(u32 num_bytes, u32 alignment); void CommitMemory(u32 final_num_bytes); + // Queues copy to default heap if gpu_backed_buffer was true. + void FlushMemory(); void Destroy(bool defer = true); @@ -48,11 +58,14 @@ private: u32 m_size = 0; u32 m_current_offset = 0; + u32 m_current_copy_offset = 0; u32 m_current_space = 0; u32 m_current_gpu_position = 0; - wil::com_ptr_nothrow m_buffer; - wil::com_ptr_nothrow m_allocation; + wil::com_ptr_nothrow m_buffer_upload; + wil::com_ptr_nothrow m_buffer_default; + wil::com_ptr_nothrow m_allocation_upload; + wil::com_ptr_nothrow m_allocation_default; D3D12_GPU_VIRTUAL_ADDRESS m_gpu_pointer = {}; u8* m_host_pointer = nullptr; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index ee0798380b..6acb3b7f30 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -546,6 +546,12 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion) CommandListResources& res = m_command_lists[m_current_command_list]; HRESULT hr; + // Flush stream buffers to GPU memory + m_vertex_stream_buffer.FlushMemory(); + m_index_stream_buffer.FlushMemory(); + m_vertex_constant_buffer.FlushMemory(); + m_pixel_constant_buffer.FlushMemory(); + if (res.has_timestamp_query) { // write the timestamp back at the end of the cmdlist @@ -576,7 +582,11 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion) if (res.init_command_list_used) { - const std::array execute_lists{res.command_lists[0].list4.get(), res.command_lists[1].list4.get()}; + // Call as seperate ExecuteCommandLists to ensure constant buffer copies are completed. + // This ends up being faster then using barriers on each buffer. + std::array execute_lists{res.command_lists[0].list4.get()}; + m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data()); + execute_lists[0] = res.command_lists[1].list4.get(); m_command_queue->ExecuteCommandLists(static_cast(execute_lists.size()), execute_lists.data()); } else @@ -2569,25 +2579,25 @@ bool GSDevice12::CreateNullTexture() bool GSDevice12::CreateBuffers() { - if (!m_vertex_stream_buffer.Create(VERTEX_BUFFER_SIZE)) + if (!m_vertex_stream_buffer.Create(VERTEX_BUFFER_SIZE, false)) { Host::ReportErrorAsync("GS", "Failed to allocate vertex buffer"); return false; } - if (!m_index_stream_buffer.Create(INDEX_BUFFER_SIZE)) + if (!m_index_stream_buffer.Create(INDEX_BUFFER_SIZE, false)) { Host::ReportErrorAsync("GS", "Failed to allocate index buffer"); return false; } - if (!m_vertex_constant_buffer.Create(VERTEX_UNIFORM_BUFFER_SIZE)) + if (!m_vertex_constant_buffer.Create(VERTEX_UNIFORM_BUFFER_SIZE, !m_uma)) { Host::ReportErrorAsync("GS", "Failed to allocate vertex uniform buffer"); return false; } - if (!m_pixel_constant_buffer.Create(FRAGMENT_UNIFORM_BUFFER_SIZE)) + if (!m_pixel_constant_buffer.Create(FRAGMENT_UNIFORM_BUFFER_SIZE, !m_uma)) { Host::ReportErrorAsync("GS", "Failed to allocate fragment uniform buffer"); return false;