Fix GPUTexture::GetData to properly handle volume textures

This commit is contained in:
Wojtek Figat
2024-05-28 14:56:04 +02:00
parent 054e77be42
commit 8a2acd360d
10 changed files with 49 additions and 118 deletions

View File

@@ -545,12 +545,12 @@ public:
/// <summary> /// <summary>
/// Gets texture mipmap data (raw bytes). Can be used only with textures created with Staging flag. /// Gets texture mipmap data (raw bytes). Can be used only with textures created with Staging flag.
/// </summary> /// </summary>
/// <param name="arrayOrDepthSliceIndex">Array or depth slice index.</param> /// <param name="arrayIndex">Array slice index.</param>
/// <param name="mipMapIndex">Mip map index.</param> /// <param name="mipMapIndex">Mip map index.</param>
/// <param name="data">Output mip data.</param> /// <param name="data">Output mip data.</param>
/// <param name="mipRowPitch">Output mip data row pitch to use. Use 0 to use the pitch from the internal GPU storage.</param> /// <param name="mipRowPitch">Output mip data row pitch to use. Use 0 to use the pitch from the internal GPU storage.</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
virtual bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch = 0) = 0; virtual bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch = 0) = 0;
/// <summary> /// <summary>
/// Sets the number of resident mipmap levels in the texture (already uploaded to the GPU). /// Sets the number of resident mipmap levels in the texture (already uploaded to the GPU).

View File

@@ -169,6 +169,38 @@ bool TextureMipData::GetPixels(Array<Color>& pixels, int32 width, int32 height,
return false; return false;
} }
void TextureMipData::Copy(void* data, uint32 dataRowPitch, uint32 dataDepthPitch, uint32 dataDepthSlices, uint32 targetRowPitch)
{
// Check if target row pitch is the same
if (targetRowPitch == dataRowPitch || targetRowPitch == 0)
{
Lines = dataDepthPitch / dataRowPitch;
DepthPitch = dataDepthPitch;
RowPitch = dataRowPitch;
// Single memory copy
Data.Copy((byte*)data, dataDepthPitch * dataDepthSlices);
}
else
{
Lines = dataDepthPitch / dataRowPitch;
DepthPitch = targetRowPitch * Lines;
RowPitch = targetRowPitch;
// Convert row by row
Data.Allocate(DepthPitch * dataDepthSlices);
for (uint32 depth = 0; depth < dataDepthSlices; depth++)
{
byte* src = (byte*)data + depth * dataDepthPitch;
byte* dst = Data.Get() + depth * DepthPitch;
for (uint32 row = 0; row < Lines; row++)
{
Platform::MemoryCopy(dst + row * RowPitch, src + row * dataRowPitch, RowPitch);
}
}
}
}
REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase"); REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase");
TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info) TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info)

View File

@@ -26,6 +26,7 @@ public:
bool GetPixels(Array<Color32>& pixels, int32 width, int32 height, PixelFormat format) const; bool GetPixels(Array<Color32>& pixels, int32 width, int32 height, PixelFormat format) const;
bool GetPixels(Array<Color>& pixels, int32 width, int32 height, PixelFormat format) const; bool GetPixels(Array<Color>& pixels, int32 width, int32 height, PixelFormat format) const;
void Copy(void* data, uint32 dataRowPitch, uint32 dataDepthPitch, uint32 dataDepthSlices, uint32 targetRowPitch);
template<typename T> template<typename T>
T& Get(int32 x, int32 y) T& Get(int32 x, int32 y)

View File

@@ -549,18 +549,17 @@ void GPUTextureDX11::initHandles()
} }
} }
bool GPUTextureDX11::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) bool GPUTextureDX11::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
{ {
if (!IsStaging()) if (!IsStaging())
{ {
LOG(Warning, "Texture::GetData is valid only for staging resources."); LOG(Warning, "Texture::GetData is valid only for staging resources.");
return true; return true;
} }
GPUDeviceLock lock(_device); GPUDeviceLock lock(_device);
// Map the staging resource mip map for reading // Map the staging resource mip map for reading
const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayOrDepthSliceIndex, MipLevels()); const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayIndex, MipLevels());
D3D11_MAPPED_SUBRESOURCE mapped; D3D11_MAPPED_SUBRESOURCE mapped;
const HRESULT mapResult = _device->GetIM()->Map(_resource, subresource, D3D11_MAP_READ, 0, &mapped); const HRESULT mapResult = _device->GetIM()->Map(_resource, subresource, D3D11_MAP_READ, 0, &mapped);
if (FAILED(mapResult)) if (FAILED(mapResult))
@@ -569,31 +568,7 @@ bool GPUTextureDX11::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, Te
return true; return true;
} }
// Check if target row pitch is the same data.Copy(mapped.pData, mapped.RowPitch, mapped.DepthPitch, Depth(), mipRowPitch);
if (mipRowPitch == mapped.RowPitch || mipRowPitch == 0)
{
// Init mip info
data.Lines = mapped.DepthPitch / mapped.RowPitch;
data.DepthPitch = mapped.DepthPitch;
data.RowPitch = mapped.RowPitch;
// Copy data
data.Data.Copy((byte*)mapped.pData, mapped.DepthPitch);
}
else
{
// Init mip info
data.Lines = mapped.DepthPitch / mapped.RowPitch;
data.DepthPitch = mipRowPitch * data.Lines;
data.RowPitch = mipRowPitch;
// Copy data
data.Data.Allocate(data.DepthPitch);
for (uint32 i = 0; i < data.Lines; i++)
{
Platform::MemoryCopy(data.Data.Get() + data.RowPitch * i, ((byte*)mapped.pData) + mapped.RowPitch * i, data.RowPitch);
}
}
// Unmap texture // Unmap texture
_device->GetIM()->Unmap(_resource, subresource); _device->GetIM()->Unmap(_resource, subresource);

View File

@@ -176,7 +176,6 @@ public:
class GPUTextureDX11 : public GPUResourceDX11<GPUTexture> class GPUTextureDX11 : public GPUResourceDX11<GPUTexture>
{ {
private: private:
ID3D11Resource* _resource = nullptr; ID3D11Resource* _resource = nullptr;
GPUTextureViewDX11 _handleArray; GPUTextureViewDX11 _handleArray;
@@ -191,7 +190,6 @@ private:
DXGI_FORMAT _dxgiFormatUAV; DXGI_FORMAT _dxgiFormatUAV;
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GPUTextureDX11"/> class. /// Initializes a new instance of the <see cref="GPUTextureDX11"/> class.
/// </summary> /// </summary>
@@ -203,18 +201,15 @@ public:
} }
public: public:
/// <summary> /// <summary>
/// Gets DX11 texture resource. /// Gets DX11 texture resource.
/// </summary> /// </summary>
/// <returns>DX11 texture resource.</returns>
FORCE_INLINE ID3D11Resource* GetResource() const FORCE_INLINE ID3D11Resource* GetResource() const
{ {
return _resource; return _resource;
} }
private: private:
void initHandles(); void initHandles();
ID3D11Texture2D* GetTexture2D() const ID3D11Texture2D* GetTexture2D() const
@@ -222,7 +217,6 @@ private:
ASSERT(_desc.Dimensions == TextureDimensions::Texture || _desc.Dimensions == TextureDimensions::CubeTexture); ASSERT(_desc.Dimensions == TextureDimensions::Texture || _desc.Dimensions == TextureDimensions::CubeTexture);
return (ID3D11Texture2D*)_resource; return (ID3D11Texture2D*)_resource;
} }
ID3D11Texture3D* GetTexture3D() const ID3D11Texture3D* GetTexture3D() const
{ {
ASSERT(_desc.Dimensions == TextureDimensions::VolumeTexture); ASSERT(_desc.Dimensions == TextureDimensions::VolumeTexture);
@@ -230,42 +224,35 @@ private:
} }
public: public:
// [GPUTexture] // [GPUTexture]
GPUTextureView* View(int32 arrayOrDepthIndex) const override GPUTextureView* View(int32 arrayOrDepthIndex) const override
{ {
return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex]; return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex];
} }
GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override
{ {
return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex]; return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex];
} }
GPUTextureView* ViewArray() const override GPUTextureView* ViewArray() const override
{ {
ASSERT(ArraySize() > 1); ASSERT(ArraySize() > 1);
return (GPUTextureView*)&_handleArray; return (GPUTextureView*)&_handleArray;
} }
GPUTextureView* ViewVolume() const override GPUTextureView* ViewVolume() const override
{ {
ASSERT(IsVolume()); ASSERT(IsVolume());
return (GPUTextureView*)&_handleVolume; return (GPUTextureView*)&_handleVolume;
} }
GPUTextureView* ViewReadOnlyDepth() const override GPUTextureView* ViewReadOnlyDepth() const override
{ {
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
return (GPUTextureView*)&_handleReadOnlyDepth; return (GPUTextureView*)&_handleReadOnlyDepth;
} }
void* GetNativePtr() const override void* GetNativePtr() const override
{ {
return static_cast<void*>(_resource); return static_cast<void*>(_resource);
} }
bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
// [GPUResourceDX11] // [GPUResourceDX11]
ID3D11Resource* GetResource() override ID3D11Resource* GetResource() override
@@ -274,7 +261,6 @@ public:
} }
protected: protected:
// [GPUTexture] // [GPUTexture]
bool OnInit() override; bool OnInit() override;
void OnResidentMipsChanged() override; void OnResidentMipsChanged() override;

View File

@@ -7,7 +7,7 @@
#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/Textures/TextureData.h"
bool GPUTextureDX12::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) bool GPUTextureDX12::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
{ {
if (!IsStaging()) if (!IsStaging())
{ {
@@ -18,7 +18,7 @@ bool GPUTextureDX12::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, Te
GPUDeviceLock lock(_device); GPUDeviceLock lock(_device);
// Internally it's a buffer, so adapt resource index and offset // Internally it's a buffer, so adapt resource index and offset
const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayOrDepthSliceIndex, MipLevels()); const uint32 subresource = RenderToolsDX::CalcSubresourceIndex(mipMapIndex, arrayIndex, MipLevels());
const int32 offsetInBytes = ComputeBufferOffset(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); const int32 offsetInBytes = ComputeBufferOffset(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
const int32 lengthInBytes = ComputeSubresourceSize(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); const int32 lengthInBytes = ComputeSubresourceSize(subresource, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
const int32 rowPitch = ComputeRowPitch(mipMapIndex, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); const int32 rowPitch = ComputeRowPitch(mipMapIndex, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
@@ -37,31 +37,7 @@ bool GPUTextureDX12::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, Te
} }
mapped = (byte*)mapped + offsetInBytes; mapped = (byte*)mapped + offsetInBytes;
// Check if target row pitch is the same data.Copy(mapped, rowPitch, depthPitch, Depth(), mipRowPitch);
if (mipRowPitch == rowPitch || mipRowPitch == 0)
{
// Init mip info
data.Lines = depthPitch / rowPitch;
data.DepthPitch = depthPitch;
data.RowPitch = rowPitch;
// Copy data
data.Data.Copy((byte*)mapped, depthPitch);
}
else
{
// Init mip info
data.Lines = depthPitch / rowPitch;
data.DepthPitch = mipRowPitch * data.Lines;
data.RowPitch = mipRowPitch;
// Copy data
data.Data.Allocate(data.DepthPitch);
for (uint32 i = 0; i < data.Lines; i++)
{
Platform::MemoryCopy(data.Data.Get() + data.RowPitch * i, ((byte*)mapped) + rowPitch * i, data.RowPitch);
}
}
// Unmap buffer // Unmap buffer
_resource->Unmap(0, nullptr); _resource->Unmap(0, nullptr);

View File

@@ -186,7 +186,7 @@ public:
{ {
return (void*)_resource; return (void*)_resource;
} }
bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override; bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
// [ResourceOwnerDX12] // [ResourceOwnerDX12]
GPUResource* AsGPUResource() const override GPUResource* AsGPUResource() const override

View File

@@ -12,51 +12,42 @@
class GPUTextureNull : public GPUTexture class GPUTextureNull : public GPUTexture
{ {
public: public:
// [GPUTexture] // [GPUTexture]
GPUTextureView* View(int32 arrayOrDepthIndex) const override GPUTextureView* View(int32 arrayOrDepthIndex) const override
{ {
return nullptr; return nullptr;
} }
GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override
{ {
return nullptr; return nullptr;
} }
GPUTextureView* ViewArray() const override GPUTextureView* ViewArray() const override
{ {
return nullptr; return nullptr;
} }
GPUTextureView* ViewVolume() const override GPUTextureView* ViewVolume() const override
{ {
return nullptr; return nullptr;
} }
GPUTextureView* ViewReadOnlyDepth() const override GPUTextureView* ViewReadOnlyDepth() const override
{ {
return nullptr; return nullptr;
} }
void* GetNativePtr() const override void* GetNativePtr() const override
{ {
return nullptr; return nullptr;
} }
bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override
bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override
{ {
return true; return true;
} }
protected: protected:
// [GPUTexture] // [GPUTexture]
bool OnInit() override bool OnInit() override
{ {
return false; return false;
} }
void OnResidentMipsChanged() override void OnResidentMipsChanged() override
{ {
} }

View File

@@ -179,7 +179,7 @@ void GPUTextureViewVulkan::DescriptorAsStorageImage(GPUContextVulkan* context, V
context->AddImageBarrier(this, VK_IMAGE_LAYOUT_GENERAL); context->AddImageBarrier(this, VK_IMAGE_LAYOUT_GENERAL);
} }
bool GPUTextureVulkan::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) bool GPUTextureVulkan::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
{ {
if (!IsStaging()) if (!IsStaging())
{ {
@@ -189,12 +189,12 @@ bool GPUTextureVulkan::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex,
GPUDeviceLock lock(_device); GPUDeviceLock lock(_device);
// Internally it's a buffer, so adapt resource index and offset // Internally it's a buffer, so adapt resource index and offset
const uint32 subresource = mipMapIndex + arrayOrDepthSliceIndex * MipLevels(); const uint32 subresource = mipMapIndex + arrayIndex * MipLevels();
// TODO: rowAlign/sliceAlign on Vulkan texture ??? // TODO: rowAlign/sliceAlign on Vulkan texture ???
int32 offsetInBytes = ComputeBufferOffset(subresource, 1, 1); int32 offsetInBytes = ComputeBufferOffset(subresource, 1, 1);
int32 lengthInBytes = ComputeSubresourceSize(subresource, 1, 1); int32 lengthInBytes = ComputeSubresourceSize(subresource, 1, 1);
int32 rowPitch = ComputeRowPitch(mipMapIndex, 1); int32 rowPitch = ComputeRowPitch(mipMapIndex, 1);
int32 depthPicth = ComputeSlicePitch(mipMapIndex, 1); int32 depthPitch = ComputeSlicePitch(mipMapIndex, 1);
// Map the staging resource mip map for reading // Map the staging resource mip map for reading
auto allocation = StagingBuffer->GetAllocation(); auto allocation = StagingBuffer->GetAllocation();
@@ -205,31 +205,7 @@ bool GPUTextureVulkan::GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex,
// Shift mapped buffer to the beginning of the mip data start // Shift mapped buffer to the beginning of the mip data start
mapped = (void*)((byte*)mapped + offsetInBytes); mapped = (void*)((byte*)mapped + offsetInBytes);
// Check if target row pitch is the same data.Copy(mapped, rowPitch, depthPitch, Depth(), mipRowPitch);
if (mipRowPitch == rowPitch || mipRowPitch == 0)
{
// Init mip info
data.Lines = depthPicth / rowPitch;
data.DepthPitch = depthPicth;
data.RowPitch = rowPitch;
// Copy data
data.Data.Copy((byte*)mapped, depthPicth);
}
else
{
// Init mip info
data.Lines = depthPicth / rowPitch;
data.DepthPitch = mipRowPitch * data.Lines;
data.RowPitch = mipRowPitch;
// Copy data
data.Data.Allocate(data.DepthPitch);
for (uint32 i = 0; i < data.Lines; i++)
{
Platform::MemoryCopy(data.Data.Get() + data.RowPitch * i, ((byte*)mapped) + rowPitch * i, data.RowPitch);
}
}
// Unmap resource // Unmap resource
vmaUnmapMemory(_device->Allocator, allocation); vmaUnmapMemory(_device->Allocator, allocation);

View File

@@ -133,36 +133,30 @@ public:
{ {
return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex]; return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex];
} }
GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override
{ {
return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex]; return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex];
} }
GPUTextureView* ViewArray() const override GPUTextureView* ViewArray() const override
{ {
ASSERT(ArraySize() > 1); ASSERT(ArraySize() > 1);
return (GPUTextureView*)&_handleArray; return (GPUTextureView*)&_handleArray;
} }
GPUTextureView* ViewVolume() const override GPUTextureView* ViewVolume() const override
{ {
ASSERT(IsVolume()); ASSERT(IsVolume());
return (GPUTextureView*)&_handleVolume; return (GPUTextureView*)&_handleVolume;
} }
GPUTextureView* ViewReadOnlyDepth() const override GPUTextureView* ViewReadOnlyDepth() const override
{ {
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
return (GPUTextureView*)&_handleReadOnlyDepth; return (GPUTextureView*)&_handleReadOnlyDepth;
} }
void* GetNativePtr() const override void* GetNativePtr() const override
{ {
return (void*)_image; return (void*)_image;
} }
bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
// [ResourceOwnerVulkan] // [ResourceOwnerVulkan]
GPUResource* AsGPUResource() const override GPUResource* AsGPUResource() const override