From 44fae3838e6c8fea179b2dd279c862af4575c985 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Jan 2025 20:39:04 +0100 Subject: [PATCH] Add `GPUResourceUsage.Staging` for both CPU read/write access --- Source/Engine/Graphics/Enums.h | 11 ++++++++++ Source/Engine/Graphics/GPUBuffer.cpp | 21 ++++++++++++++++++- Source/Engine/Graphics/GPUBuffer.h | 16 +++++--------- .../Engine/Graphics/GPUBufferDescription.cs | 17 +++++++++++++-- Source/Engine/Graphics/GPUBufferDescription.h | 1 + .../Engine/Graphics/Textures/GPUTexture.cpp | 13 ++++++++++++ Source/Engine/Graphics/Textures/GPUTexture.h | 5 +---- .../Textures/GPUTextureDescription.cs | 16 ++++++++++++-- .../Graphics/Textures/GPUTextureDescription.h | 1 + .../DirectX/DX12/GPUBufferDX12.cpp | 3 ++- .../GraphicsDevice/DirectX/RenderToolsDX.h | 3 +++ .../GraphicsDevice/Vulkan/GPUBufferVulkan.cpp | 3 +++ 12 files changed, 89 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index 01ac11667..1e2f09f97 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -308,6 +308,17 @@ API_ENUM() enum class GPUResourceUsage /// - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection. /// StagingReadback = 3, + + /// + /// A resource that supports both read and write from the CPU. + /// This is likely to be the common choice for read-write buffers to transfer data between GPU compute buffers and CPU memory. + /// It usually means CPU (system) memory. + /// + /// + /// Usage: + /// - Staging memory to upload to GPU for compute and gather results back after processing. + /// + Staging = 4, }; /// diff --git a/Source/Engine/Graphics/GPUBuffer.cpp b/Source/Engine/Graphics/GPUBuffer.cpp index 6a0221b14..1afdcb8b3 100644 --- a/Source/Engine/Graphics/GPUBuffer.cpp +++ b/Source/Engine/Graphics/GPUBuffer.cpp @@ -71,6 +71,15 @@ GPUBufferDescription GPUBufferDescription::ToStagingReadback() const return desc; } +GPUBufferDescription GPUBufferDescription::ToStaging() const +{ + auto desc = *this; + desc.Usage = GPUResourceUsage::Staging; + desc.Flags = GPUBufferFlags::None; + desc.InitData = nullptr; + return desc; +} + bool GPUBufferDescription::Equals(const GPUBufferDescription& other) const { return Size == other.Size @@ -123,6 +132,16 @@ GPUBuffer::GPUBuffer() _desc.Size = 0; } +bool GPUBuffer::IsStaging() const +{ + return _desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::StagingUpload || _desc.Usage == GPUResourceUsage::Staging; +} + +bool GPUBuffer::IsDynamic() const +{ + return _desc.Usage == GPUResourceUsage::Dynamic; +} + bool GPUBuffer::Init(const GPUBufferDescription& desc) { ASSERT(Math::IsInRange(desc.Size, 1, MAX_int32) @@ -215,7 +234,7 @@ bool GPUBuffer::DownloadData(BytesContainer& result) LOG(Warning, "Cannot download GPU buffer data from an empty buffer."); return true; } - if (_desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::Dynamic) + if (_desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::Dynamic || _desc.Usage == GPUResourceUsage::Staging) { // Use faster path for staging resources return GetData(result); diff --git a/Source/Engine/Graphics/GPUBuffer.h b/Source/Engine/Graphics/GPUBuffer.h index d40b7744d..1d5e7bc93 100644 --- a/Source/Engine/Graphics/GPUBuffer.h +++ b/Source/Engine/Graphics/GPUBuffer.h @@ -86,20 +86,14 @@ public: } /// - /// Checks if buffer is a staging buffer (supports CPU readback). + /// Checks if buffer is a staging buffer (supports CPU access). /// - API_PROPERTY() FORCE_INLINE bool IsStaging() const - { - return _desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::StagingUpload; - } + API_PROPERTY() bool IsStaging() const; /// - /// Checks if buffer is a staging buffer (supports CPU readback). + /// Checks if buffer is a dynamic buffer. /// - API_PROPERTY() FORCE_INLINE bool IsDynamic() const - { - return _desc.Usage == GPUResourceUsage::Dynamic; - } + API_PROPERTY() bool IsDynamic() const; /// /// Gets a value indicating whether this buffer is a shader resource. @@ -173,7 +167,7 @@ public: Task* DownloadDataAsync(BytesContainer& result); /// - /// Gets the buffer data via map/memcpy/unmap sequence. Always supported for dynamic and staging readback buffers (other types support depends on graphics backend implementation). + /// Gets the buffer data via map/memcpy/unmap sequence. Always supported for dynamic and staging buffers (other types support depends on graphics backend implementation). /// /// The output data container. /// True if failed, otherwise false. diff --git a/Source/Engine/Graphics/GPUBufferDescription.cs b/Source/Engine/Graphics/GPUBufferDescription.cs index 6bc0051a8..c044344ec 100644 --- a/Source/Engine/Graphics/GPUBufferDescription.cs +++ b/Source/Engine/Graphics/GPUBufferDescription.cs @@ -271,7 +271,7 @@ namespace FlaxEngine } /// - /// Gets the staging upload description for this instance. + /// Gets the staging upload (CPU write) description for this instance. /// /// A staging buffer description public GPUBufferDescription ToStagingUpload() @@ -284,7 +284,7 @@ namespace FlaxEngine } /// - /// Gets the staging readback description for this instance. + /// Gets the staging readback (CPU read) description for this instance. /// /// A staging buffer description public GPUBufferDescription ToStagingReadback() @@ -296,6 +296,19 @@ namespace FlaxEngine return desc; } + /// + /// Gets the staging (CPU read/write) description for this instance. + /// + /// A staging buffer description + public GPUBufferDescription ToStaging() + { + var desc = this; + desc.Usage = GPUResourceUsage.Staging; + desc.Flags = GPUBufferFlags.None; + desc.InitData = IntPtr.Zero; + return desc; + } + /// public bool Equals(GPUBufferDescription other) { diff --git a/Source/Engine/Graphics/GPUBufferDescription.h b/Source/Engine/Graphics/GPUBufferDescription.h index d4049ea5b..17ddffad7 100644 --- a/Source/Engine/Graphics/GPUBufferDescription.h +++ b/Source/Engine/Graphics/GPUBufferDescription.h @@ -335,6 +335,7 @@ public: void Clear(); GPUBufferDescription ToStagingUpload() const; GPUBufferDescription ToStagingReadback() const; + GPUBufferDescription ToStaging() const; bool Equals(const GPUBufferDescription& other) const; String ToString() const; diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index 23302deb8..a48da4244 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -142,6 +142,14 @@ GPUTextureDescription GPUTextureDescription::ToStagingReadback() const return copy; } +GPUTextureDescription GPUTextureDescription::ToStaging() const +{ + auto copy = *this; + copy.Flags = GPUTextureFlags::None; + copy.Usage = GPUResourceUsage::Staging; + return copy; +} + bool GPUTextureDescription::Equals(const GPUTextureDescription& other) const { return Dimensions == other.Dimensions @@ -208,6 +216,11 @@ GPUTexture::GPUTexture() _desc.Clear(); } +bool GPUTexture::IsStaging() const +{ + return _desc.Usage == GPUResourceUsage::StagingUpload || _desc.Usage == GPUResourceUsage::StagingReadback || _desc.Usage == GPUResourceUsage::Staging; +} + Float2 GPUTexture::Size() const { return Float2(static_cast(_desc.Width), static_cast(_desc.Height)); diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index 685e59985..9aa6571e0 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -294,10 +294,7 @@ public: /// /// Checks if texture is a staging buffer (supports direct CPU access). /// - FORCE_INLINE bool IsStaging() const - { - return _desc.Usage == GPUResourceUsage::StagingUpload || _desc.Usage == GPUResourceUsage::StagingReadback; - } + bool IsStaging() const; /// /// Gets a boolean indicating whether this is a using a block compress format (BC1, BC2, BC3, BC4, BC5, BC6H, BC7, etc.). diff --git a/Source/Engine/Graphics/Textures/GPUTextureDescription.cs b/Source/Engine/Graphics/Textures/GPUTextureDescription.cs index c5fb409f0..c67c389d0 100644 --- a/Source/Engine/Graphics/Textures/GPUTextureDescription.cs +++ b/Source/Engine/Graphics/Textures/GPUTextureDescription.cs @@ -300,7 +300,7 @@ namespace FlaxEngine } /// - /// Gets the staging description for this instance. + /// Gets the staging upload (CPU write) description for this instance. /// /// A staging texture description public GPUTextureDescription ToStagingUpload() @@ -312,7 +312,7 @@ namespace FlaxEngine } /// - /// Gets the staging description for this instance. + /// Gets the staging readback (CPU read) description for this instance. /// /// A staging texture description public GPUTextureDescription ToStagingReadback() @@ -323,6 +323,18 @@ namespace FlaxEngine return desc; } + /// + /// Gets the staging (CPU read/write) description for this instance. + /// + /// A staging texture description + public GPUTextureDescription ToStaging() + { + var desc = this; + desc.Flags = GPUTextureFlags.None; + desc.Usage = GPUResourceUsage.Staging; + return desc; + } + /// public override string ToString() { diff --git a/Source/Engine/Graphics/Textures/GPUTextureDescription.h b/Source/Engine/Graphics/Textures/GPUTextureDescription.h index 281cef8d8..9844e05a8 100644 --- a/Source/Engine/Graphics/Textures/GPUTextureDescription.h +++ b/Source/Engine/Graphics/Textures/GPUTextureDescription.h @@ -397,6 +397,7 @@ public: void Clear(); GPUTextureDescription ToStagingUpload() const; GPUTextureDescription ToStagingReadback() const; + GPUTextureDescription ToStaging() const; bool Equals(const GPUTextureDescription& other) const; String ToString() const; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index c38b3edef..066f019ae 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -120,6 +120,7 @@ bool GPUBufferDX12::OnInit() switch (_desc.Usage) { case GPUResourceUsage::StagingUpload: + case GPUResourceUsage::Staging: heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; break; case GPUResourceUsage::StagingReadback: @@ -152,7 +153,7 @@ bool GPUBufferDX12::OnInit() // But if we are doing it during update or from the other thread we have to register resource data upload job. // In both cases options.InitData data have to exist for a few next frames. - if (_desc.Usage == GPUResourceUsage::StagingUpload) + if (_desc.Usage == GPUResourceUsage::StagingUpload || _desc.Usage == GPUResourceUsage::Staging) { // Modify staging resource data now SetData(_desc.InitData, _desc.Size); diff --git a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h index 5cfb5b4eb..cdcc55c9b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h +++ b/Source/Engine/GraphicsDevice/DirectX/RenderToolsDX.h @@ -28,6 +28,7 @@ namespace RenderToolsDX { case GPUResourceUsage::Dynamic: return D3D11_USAGE_DYNAMIC; + case GPUResourceUsage::Staging: case GPUResourceUsage::StagingUpload: case GPUResourceUsage::StagingReadback: return D3D11_USAGE_STAGING; @@ -47,6 +48,8 @@ namespace RenderToolsDX { case GPUResourceUsage::Dynamic: return D3D11_CPU_ACCESS_WRITE; + case GPUResourceUsage::Staging: + return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; case GPUResourceUsage::StagingReadback: return D3D11_CPU_ACCESS_READ; case GPUResourceUsage::StagingUpload: diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp index 99d993dcd..fbb3392ab 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUBufferVulkan.cpp @@ -128,6 +128,9 @@ bool GPUBufferVulkan::OnInit() case GPUResourceUsage::StagingReadback: allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU; break; + case GPUResourceUsage::Staging: + allocInfo.usage = VMA_MEMORY_USAGE_CPU_COPY; + break; default: allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; }