diff --git a/Source/Core/VideoBackends/D3D12/D3DTexture.cpp b/Source/Core/VideoBackends/D3D12/D3DTexture.cpp index f913715e75..a3fe3b666e 100644 --- a/Source/Core/VideoBackends/D3D12/D3DTexture.cpp +++ b/Source/Core/VideoBackends/D3D12/D3DTexture.cpp @@ -21,6 +21,9 @@ namespace DX12 namespace D3D { +constexpr size_t INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE = 4 * 1024 * 1024; +constexpr size_t MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE = 64 * 1024 * 1024; + static std::unique_ptr s_texture_upload_stream_buffer; void CleanupPersistentD3DTextureResources() @@ -32,16 +35,39 @@ void ReplaceRGBATexture2D(ID3D12Resource* texture12, const u8* buffer, unsigned { const unsigned int upload_size = AlignValue(src_pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * height; - if (!s_texture_upload_stream_buffer) - { - s_texture_upload_stream_buffer = std::make_unique(4 * 1024 * 1024, 64 * 1024 * 1024, nullptr); - } + ID3D12Resource* upload_buffer = nullptr; + size_t upload_buffer_offset = 0; + u8* dest_data = nullptr; - bool current_command_list_executed = s_texture_upload_stream_buffer->AllocateSpaceInBuffer(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - if (current_command_list_executed) + if (upload_size > MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) { - g_renderer->SetViewport(); - D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12()); + // If the texture is too large to fit in the upload buffer, create a temporary buffer instead. + // This will only be the case for large (e.g. 8192x8192) textures from custom texture packs. + CheckHR(D3D::device12->CreateCommittedResource( + &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), + D3D12_HEAP_FLAG_NONE, + &CD3DX12_RESOURCE_DESC::Buffer(upload_size), + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&upload_buffer))); + + CheckHR(upload_buffer->Map(0, nullptr, reinterpret_cast(&dest_data))); + } + else + { + if (!s_texture_upload_stream_buffer) + s_texture_upload_stream_buffer = std::make_unique(INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE, MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE, nullptr); + + bool current_command_list_executed = s_texture_upload_stream_buffer->AllocateSpaceInBuffer(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + if (current_command_list_executed) + { + g_renderer->SetViewport(); + D3D::current_command_list->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV12(), FALSE, &FramebufferManager::GetEFBDepthTexture()->GetDSV12()); + } + + upload_buffer = s_texture_upload_stream_buffer->GetBuffer(); + upload_buffer_offset = s_texture_upload_stream_buffer->GetOffsetOfCurrentAllocation(); + dest_data = reinterpret_cast(s_texture_upload_stream_buffer->GetCPUAddressOfCurrentAllocation()); } ResourceBarrier(current_command_list, texture12, current_resource_state, D3D12_RESOURCE_STATE_COPY_DEST, level); @@ -51,9 +77,8 @@ void ReplaceRGBATexture2D(ID3D12Resource* texture12, const u8* buffer, unsigned u64 upload_row_size_in_bytes = 0; u64 upload_total_bytes = 0; - D3D::device12->GetCopyableFootprints(&texture12->GetDesc(), level, 1, s_texture_upload_stream_buffer->GetOffsetOfCurrentAllocation(), &upload_footprint, &upload_rows, &upload_row_size_in_bytes, &upload_total_bytes); + D3D::device12->GetCopyableFootprints(&texture12->GetDesc(), level, 1, upload_buffer_offset, &upload_footprint, &upload_rows, &upload_row_size_in_bytes, &upload_total_bytes); - u8* dest_data = reinterpret_cast(s_texture_upload_stream_buffer->GetCPUAddressOfCurrentAllocation()); const u8* src_data = reinterpret_cast(buffer); for (u32 y = 0; y < upload_rows; ++y) { @@ -64,9 +89,18 @@ void ReplaceRGBATexture2D(ID3D12Resource* texture12, const u8* buffer, unsigned ); } - D3D::current_command_list->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(texture12, level), 0, 0, 0, &CD3DX12_TEXTURE_COPY_LOCATION(s_texture_upload_stream_buffer->GetBuffer(), upload_footprint), nullptr); + D3D::current_command_list->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(texture12, level), 0, 0, 0, &CD3DX12_TEXTURE_COPY_LOCATION(upload_buffer, upload_footprint), nullptr); ResourceBarrier(D3D::current_command_list, texture12, D3D12_RESOURCE_STATE_COPY_DEST, current_resource_state, level); + + // Release temporary buffer after commands complete. + // We block here because otherwise if there was a large number of texture uploads, we may run out of memory. + if (!s_texture_upload_stream_buffer || upload_buffer != s_texture_upload_stream_buffer->GetBuffer()) + { + D3D::command_list_mgr->ExecuteQueuedWork(true); + upload_buffer->Unmap(0, nullptr); + upload_buffer->Release(); + } } } // namespace