From 7dcb7b4a2c5965a90a7fdd5fd1950d5a1036900a Mon Sep 17 00:00:00 2001 From: Egor Yusov Date: Wed, 29 Aug 2018 22:33:33 -0700 Subject: Fixed handling of small compressed texture subresources --- Graphics/GraphicsEngine/interface/GraphicsTypes.h | 21 +++----- Graphics/GraphicsEngine/src/Texture.cpp | 63 ++++++++++++++++------ .../GraphicsEngineD3D11/src/TextureBaseD3D11.cpp | 22 ++++++-- .../GraphicsEngineD3D12/include/TextureD3D12Impl.h | 10 ++-- .../src/DeviceContextD3D12Impl.cpp | 62 ++++++++++++++------- .../GraphicsEngineD3D12/src/TextureD3D12Impl.cpp | 30 ++++++++++- .../src/Texture1DArray_OGL.cpp | 4 +- .../GraphicsEngineOpenGL/src/Texture1D_OGL.cpp | 4 +- .../src/Texture2DArray_OGL.cpp | 20 ++++--- .../GraphicsEngineOpenGL/src/Texture2D_OGL.cpp | 18 ++++--- .../GraphicsEngineOpenGL/src/Texture3D_OGL.cpp | 6 +-- .../src/TextureCubeArray_OGL.cpp | 18 ++++--- .../GraphicsEngineOpenGL/src/TextureCube_OGL.cpp | 18 ++++--- .../src/DeviceContextVkImpl.cpp | 54 ++++++++++++++----- 14 files changed, 245 insertions(+), 105 deletions(-) (limited to 'Graphics') diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypes.h b/Graphics/GraphicsEngine/interface/GraphicsTypes.h index 8201ebd6..4ac6148a 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypes.h +++ b/Graphics/GraphicsEngine/interface/GraphicsTypes.h @@ -1394,21 +1394,12 @@ namespace Diligent /// Box struct Box { - Uint32 MinX; ///< Minimal X coordinate. Default value is 0 - Uint32 MaxX; ///< Maximal X coordinate. Default value is 0 - Uint32 MinY; ///< Minimal Y coordinate. Default value is 0 - Uint32 MaxY; ///< Maximal Y coordinate. Default value is 0 - Uint32 MinZ; ///< Minimal Z coordinate. Default value is 0 - Uint32 MaxZ; ///< Maximal Z coordinate. Default value is 1 - - /// Constructor intializes the structure - Box(Uint32 _MinX = 0, Uint32 _MaxX = 0, - Uint32 _MinY = 0, Uint32 _MaxY = 0, - Uint32 _MinZ = 0, Uint32 _MaxZ = 1) : - MinX(_MinX), MaxX(_MaxX), - MinY(_MinY), MaxY(_MaxY), - MinZ(_MinZ), MaxZ(_MaxZ) - {} + Uint32 MinX = 0; ///< Minimal X coordinate. Default value is 0 + Uint32 MaxX = 0; ///< Maximal X coordinate. Default value is 0 + Uint32 MinY = 0; ///< Minimal Y coordinate. Default value is 0 + Uint32 MaxY = 0; ///< Maximal Y coordinate. Default value is 0 + Uint32 MinZ = 0; ///< Minimal Z coordinate. Default value is 0 + Uint32 MaxZ = 1; ///< Maximal Z coordinate. Default value is 1 }; diff --git a/Graphics/GraphicsEngine/src/Texture.cpp b/Graphics/GraphicsEngine/src/Texture.cpp index 9b78202f..1bb69c72 100644 --- a/Graphics/GraphicsEngine/src/Texture.cpp +++ b/Graphics/GraphicsEngine/src/Texture.cpp @@ -133,14 +133,34 @@ void ValidateTextureRegion(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 S VERIFY_TEX_PARAMS( Slice == 0, "Array slice (", Slice, ") must be 0 for non-array textures" ); } + const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); Uint32 MipWidth = std::max(TexDesc.Width >> MipLevel, 1U); - VERIFY_TEX_PARAMS( Box.MaxX <= MipWidth, "Region max X coordinate (", Box.MaxX, ") is out of allowed range [0, ", MipWidth, "]" ); + if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + VERIFY_EXPR( (FmtAttribs.BlockWidth & (FmtAttribs.BlockWidth-1)) == 0); + Uint32 BlockAlignedMipWidth = (MipWidth + (FmtAttribs.BlockWidth-1)) & ~(FmtAttribs.BlockWidth-1); + VERIFY_TEX_PARAMS( Box.MaxX <= BlockAlignedMipWidth, "Region max X coordinate (", Box.MaxX, ") is out of allowed range [0, ", BlockAlignedMipWidth, "]" ); + VERIFY_TEX_PARAMS( (Box.MinX % FmtAttribs.BlockWidth) == 0, "For compressed formats, update region min x (", Box.MinX, ") must be a multiple of block width (", FmtAttribs.BlockWidth, ")" ); + VERIFY_TEX_PARAMS( (Box.MaxX % FmtAttribs.BlockWidth) == 0 || Box.MaxX == MipWidth, "For compressed formats, update region max x (", Box.MaxX, ") must be a multiple of block width (", FmtAttribs.BlockWidth, ") or equal the mip level width (", MipWidth, ")" ); + } + else + VERIFY_TEX_PARAMS( Box.MaxX <= MipWidth, "Region max X coordinate (", Box.MaxX, ") is out of allowed range [0, ", MipWidth, "]" ); + if (TexDesc.Type != RESOURCE_DIM_TEX_1D && TexDesc.Type != RESOURCE_DIM_TEX_1D_ARRAY) { Uint32 MipHeight = std::max(TexDesc.Height >> MipLevel, 1U); - VERIFY_TEX_PARAMS( Box.MaxY <= MipHeight, "Region max Y coordinate (", Box.MaxY, ") is out of allowed range [0, ", MipHeight, "]" ); + if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + VERIFY_EXPR( (FmtAttribs.BlockHeight & (FmtAttribs.BlockHeight-1)) == 0); + Uint32 BlockAlignedMipHeight = (MipHeight + (FmtAttribs.BlockHeight-1)) & ~(FmtAttribs.BlockHeight-1); + VERIFY_TEX_PARAMS( Box.MaxY <= BlockAlignedMipHeight, "Region max Y coordinate (", Box.MaxY, ") is out of allowed range [0, ", BlockAlignedMipHeight, "]" ); + VERIFY_TEX_PARAMS( (Box.MinY % FmtAttribs.BlockHeight) == 0, "For compressed formats, update region min y (", Box.MinY, ") must be a multiple of block height (", FmtAttribs.BlockHeight, ")" ); + VERIFY_TEX_PARAMS( (Box.MaxY % FmtAttribs.BlockHeight) == 0 || Box.MaxY == MipHeight, "For compressed formats, update region max y (", Box.MaxY, ") must be a multiple of block height (", FmtAttribs.BlockHeight, ") or equal the mip level height (", MipHeight, ")" ); + } + else + VERIFY_TEX_PARAMS( Box.MaxY <= MipHeight, "Region max Y coordinate (", Box.MaxY, ") is out of allowed range [0, ", MipHeight, "]" ); } if (TexDesc.Type == RESOURCE_DIM_TEX_3D) @@ -148,14 +168,10 @@ void ValidateTextureRegion(const TextureDesc& TexDesc, Uint32 MipLevel, Uint32 S Uint32 MipDepth = std::max(TexDesc.Depth >> MipLevel, 1U); VERIFY_TEX_PARAMS( Box.MaxZ <= MipDepth, "Region max Z coordinate (", Box.MaxZ, ") is out of allowed range [0, ", MipDepth, "]" ); } - - const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); - if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + else { - VERIFY_TEX_PARAMS( (Box.MinX % FmtAttribs.BlockWidth) == 0, "For compressed formats update region min x (", Box.MinX, ") must be a multiple of block width (", FmtAttribs.BlockWidth, ")" ); - VERIFY_TEX_PARAMS( (Box.MaxX % FmtAttribs.BlockWidth) == 0, "For compressed formats update region max x (", Box.MaxX, ") must be a multiple of block width (", FmtAttribs.BlockWidth, ")" ); - VERIFY_TEX_PARAMS( (Box.MinY % FmtAttribs.BlockHeight) == 0, "For compressed formats update region min y (", Box.MinY, ") must be a multiple of block height (", FmtAttribs.BlockHeight, ")" ); - VERIFY_TEX_PARAMS( (Box.MaxY % FmtAttribs.BlockHeight) == 0, "For compressed formats update region max y (", Box.MaxY, ") must be a multiple of block height (", FmtAttribs.BlockHeight, ")" ); + VERIFY_TEX_PARAMS( Box.MinZ == 0, "Region min Z (", Box.MinZ, ") must be 0 for all but 3D textures" ); + VERIFY_TEX_PARAMS( Box.MaxZ == 1, "Region max Z (", Box.MaxZ, ") must be 1 for all but 3D textures" ); } #endif } @@ -171,14 +187,31 @@ void ValidateUpdateDataParams( const TextureDesc& TexDesc, Uint32 MipLevel, Uint VERIFY_TEX_PARAMS( (SubresData.Stride & 0x03) == 0, "Texture data stride (", SubresData.Stride, ") must be at least 32-bit aligned" ); VERIFY_TEX_PARAMS( (SubresData.DepthStride & 0x03) == 0, "Texture data depth stride (", SubresData.DepthStride, ") must be at least 32-bit aligned" ); - const auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; - const auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + auto UpdateRegionDepth = DstBox.MaxZ - DstBox.MinZ; const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); - const Uint32 RowSize = FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED ? - UpdateRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize} : - UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; + Uint32 RowSize = 0; + Uint32 RowCount = 0; + if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + // Align update region size by the block size. This is only necessary when updating + // coarse mip levels. Otherwise UpdateRegionWidth/Height should be multiples of block size + VERIFY_EXPR( (FmtAttribs.BlockWidth & (FmtAttribs.BlockWidth-1)) == 0); + VERIFY_EXPR( (FmtAttribs.BlockHeight & (FmtAttribs.BlockHeight-1)) == 0); + UpdateRegionWidth = (UpdateRegionWidth + (FmtAttribs.BlockWidth-1)) & ~(FmtAttribs.BlockWidth -1); + UpdateRegionHeight = (UpdateRegionHeight + (FmtAttribs.BlockHeight-1)) & ~(FmtAttribs.BlockHeight-1); + RowSize = UpdateRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize}; + RowCount = UpdateRegionHeight / FmtAttribs.BlockHeight; + } + else + { + RowSize = UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; + RowCount = UpdateRegionHeight; + } DEV_CHECK_ERR(SubresData.Stride >= RowSize, "Source data stride (", SubresData.Stride, ") is below the image row size (", RowSize, ")"); - DEV_CHECK_ERR(SubresData.DepthStride == 0 || SubresData.DepthStride >= SubresData.Stride * (UpdateRegionHeight / FmtAttribs.BlockHeight), "Source data depth stride (", SubresData.DepthStride, ") is below the image plane size (", SubresData.Stride * UpdateRegionHeight, ")"); + const Uint32 PlaneSize = SubresData.Stride * RowCount; + DEV_CHECK_ERR(UpdateRegionDepth == 1 || SubresData.DepthStride >= PlaneSize, "Source data depth stride (", SubresData.DepthStride, ") is below the image plane size (", PlaneSize, ")"); #endif } diff --git a/Graphics/GraphicsEngineD3D11/src/TextureBaseD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/TextureBaseD3D11.cpp index 44fc2dc6..c9762716 100644 --- a/Graphics/GraphicsEngineD3D11/src/TextureBaseD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/TextureBaseD3D11.cpp @@ -158,12 +158,24 @@ void TextureBaseD3D11::UpdateData( IDeviceContext* pContext, Uint32 MipLevel, Ui auto* pd3d11DeviceContext = static_cast(pContext)->GetD3D11DeviceContext(); D3D11_BOX D3D11Box; - D3D11Box.left = DstBox.MinX; - D3D11Box.right = DstBox.MaxX; - D3D11Box.top = DstBox.MinY; + D3D11Box.left = DstBox.MinX; + D3D11Box.right = DstBox.MaxX; + D3D11Box.top = DstBox.MinY; D3D11Box.bottom = DstBox.MaxY; - D3D11Box.front = DstBox.MinZ; - D3D11Box.back = DstBox.MaxZ; + D3D11Box.front = DstBox.MinZ; + D3D11Box.back = DstBox.MaxZ; + const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); + if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + // Align update region by the compressed block size + VERIFY( (D3D11Box.left % FmtAttribs.BlockWidth) == 0, "Update region min x (", D3D11Box.left, ") must be multiple of a compressed block width (", FmtAttribs.BlockWidth, ")"); + VERIFY( (FmtAttribs.BlockWidth & (FmtAttribs.BlockWidth-1)) == 0, "Compressed block width (", FmtAttribs.BlockWidth, ") is expected to be power of 2"); + D3D11Box.right = (D3D11Box.right + FmtAttribs.BlockWidth-1) & ~(FmtAttribs.BlockWidth-1); + + VERIFY( (D3D11Box.top % FmtAttribs.BlockHeight) == 0, "Update region min y (", D3D11Box.top, ") must be multiple of a compressed block height (", FmtAttribs.BlockHeight, ")"); + VERIFY( (FmtAttribs.BlockHeight & (FmtAttribs.BlockHeight-1)) == 0, "Compressed block height (", FmtAttribs.BlockHeight, ") is expected to be power of 2"); + D3D11Box.bottom = (D3D11Box.bottom + FmtAttribs.BlockHeight-1) & ~(FmtAttribs.BlockHeight-1); + } auto SubresIndex = D3D11CalcSubresource(MipLevel, Slice, m_Desc.MipLevels); pd3d11DeviceContext->UpdateSubresource(m_pd3d11Texture, SubresIndex, &D3D11Box, SubresData.pData, SubresData.Stride, SubresData.DepthStride); } diff --git a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h index 65f0d493..95430ade 100644 --- a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h @@ -58,13 +58,13 @@ public: ID3D12Resource* pTexture); ~TextureD3D12Impl(); - virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override final; - virtual void UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint32 Slice, const Box &DstBox, const TextureSubResData &SubresData )override; + virtual void UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint32 Slice, const Box &DstBox, const TextureSubResData &SubresData )override final; //virtual void CopyData(CTexture *pSrcTexture, Uint32 SrcOffset, Uint32 DstOffset, Uint32 Size); - virtual void Map( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags, MappedTextureSubresource &MappedData )override; - virtual void Unmap( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags )override; + virtual void Map( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags, MappedTextureSubresource &MappedData )override final; + virtual void Unmap( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags )override final; virtual ID3D12Resource* GetD3D12Texture(){ return GetD3D12Resource(); } @@ -94,7 +94,7 @@ public: } protected: - void CreateViewInternal( const struct TextureViewDesc &ViewDesc, ITextureView **ppView, bool bIsDefaultView )override; + void CreateViewInternal( const struct TextureViewDesc &ViewDesc, ITextureView **ppView, bool bIsDefaultView )override final; //void PrepareD3D12InitData(const TextureData &InitData, Uint32 NumSubresources, std::vector &D3D12InitData); void CreateSRV( TextureViewDesc &SRVDesc, D3D12_CPU_DESCRIPTOR_HANDLE SRVHandle ); diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index b30925b0..ab1ae905 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -794,8 +794,15 @@ namespace Diligent Footpring.Footprint.Format = TexFormatToDXGI_Format(TexDesc.Format); Footpring.Footprint.RowPitch = static_cast(SrcStride); - VERIFY(Footpring.Footprint.RowPitch * Footpring.Footprint.Height * Footpring.Footprint.Depth <= BufferSize, "Buffer is not large enough"); - VERIFY(SrcDepthStride == 0 || static_cast(SrcDepthStride) == Footpring.Footprint.RowPitch * Footpring.Footprint.Height, "Depth stride must be equal to the size 2D level"); + +#ifdef _DEBUG + { + const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); + const Uint32 RowCount = std::max((Footpring.Footprint.Height/FmtAttribs.BlockHeight), 1u); + VERIFY(BufferSize >= Footpring.Footprint.RowPitch * RowCount * Footpring.Footprint.Depth, "Buffer is not large enough"); + VERIFY(Footpring.Footprint.Depth == 1 || static_cast(SrcDepthStride) == Footpring.Footprint.RowPitch * RowCount, "Depth stride must be equal to the size of 2D plane"); + } +#endif D3D12_BOX D3D12SrcBox; D3D12SrcBox.left = 0; @@ -847,24 +854,41 @@ namespace Diligent const auto& TexDesc = pTextureD3D12->GetDesc(); const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); VERIFY_EXPR(DstBox.MaxX > DstBox.MinX && DstBox.MaxY > DstBox.MinY && DstBox.MaxZ > DstBox.MinZ); - const auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; - const auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; - const auto UpdateRegionDepth = DstBox.MaxZ - DstBox.MinZ; - const Uint32 RowSize = FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED ? - UpdateRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize} : - UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; - VERIFY(SrcStride >= RowSize, "Source data stride (", SrcStride, ") is below the image row size (", RowSize, ")"); - VERIFY(SrcDepthStride == 0 || SrcDepthStride >= SrcStride * (UpdateRegionHeight / FmtAttribs.BlockHeight), "Source data depth stride (", SrcDepthStride, ") is below the image plane size (", SrcStride * UpdateRegionHeight, ")"); + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + auto UpdateRegionDepth = DstBox.MaxZ - DstBox.MinZ; + Uint32 RowSize = 0; + Uint32 RowCount = 0; + if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + // Box must be aligned by the calling function + VERIFY_EXPR((UpdateRegionWidth % FmtAttribs.BlockWidth) == 0); + VERIFY_EXPR((UpdateRegionHeight % FmtAttribs.BlockHeight) == 0); + RowSize = UpdateRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize}; + RowCount = UpdateRegionHeight / FmtAttribs.BlockHeight; + } + else + { + RowSize = UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; + RowCount = UpdateRegionHeight; + } +#ifdef _DEBUG + { + VERIFY(SrcStride >= RowSize, "Source data stride (", SrcStride, ") is below the image row size (", RowSize, ")"); + const Uint32 PlaneSize = SrcStride * RowCount; + VERIFY(UpdateRegionDepth == 1 || SrcDepthStride >= PlaneSize, "Source data depth stride (", SrcDepthStride, ") is below the image plane size (", PlaneSize, ")"); + } +#endif // RowPitch must be a multiple of 256 (aka. D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) - const auto BufferDataStride = (RowSize + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT-1) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT-1); - const auto BufferDataDepthStride = UpdateRegionHeight * BufferDataStride; - const auto MemorySize = UpdateRegionDepth * BufferDataDepthStride; - const auto UploadSpace = AllocateDynamicSpace(MemorySize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - const auto AlignedOffset = (UploadSpace.Offset + (D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1)) & ~(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1); + const auto AlignedStride = (RowSize + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT-1) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT-1); + const auto AlignedDepthStride = RowCount * AlignedStride; + const auto MemorySize = UpdateRegionDepth * AlignedDepthStride; + const auto UploadSpace = AllocateDynamicSpace(MemorySize, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + const auto AlignedOffset = (UploadSpace.Offset + (D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1)) & ~(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT-1); for(Uint32 slice = 0; slice < UpdateRegionDepth; ++slice) { - for(Uint32 row = 0; row < UpdateRegionHeight / FmtAttribs.BlockHeight; ++row) + for(Uint32 row = 0; row < RowCount; ++row) { const auto* pSrcPtr = reinterpret_cast(pSrcData) @@ -873,13 +897,13 @@ namespace Diligent auto* pDstPtr = reinterpret_cast(UploadSpace.CPUAddress) + (AlignedOffset - UploadSpace.Offset) - + row * BufferDataStride - + slice * BufferDataDepthStride; + + row * AlignedStride + + slice * AlignedDepthStride; memcpy(pDstPtr, pSrcPtr, RowSize); } } - CopyTextureRegion(UploadSpace.pBuffer, static_cast(AlignedOffset), BufferDataStride, BufferDataDepthStride, MemorySize, pTextureD3D12, DstSubResIndex, DstBox); + CopyTextureRegion(UploadSpace.pBuffer, static_cast(AlignedOffset), AlignedStride, AlignedDepthStride, MemorySize, pTextureD3D12, DstSubResIndex, DstBox); } void DeviceContextD3D12Impl::GenerateMips(TextureViewD3D12Impl* pTexView) diff --git a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp index 77d8894f..2ea0938e 100644 --- a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp @@ -409,12 +409,38 @@ void TextureD3D12Impl::UpdateData( IDeviceContext* pContext, { TTextureBase::UpdateData( pContext, MipLevel, Slice, DstBox, SubresData ); + Box BlockAlignedBox; + const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); + const Box* pBox = nullptr; + if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + // Align update region by the compressed block size + + VERIFY( (DstBox.MinX % FmtAttribs.BlockWidth) == 0, "Update region min x (", DstBox.MinX, ") must be multiple of a compressed block width (", FmtAttribs.BlockWidth, ")"); + BlockAlignedBox.MinX = DstBox.MinX; + VERIFY( (FmtAttribs.BlockWidth & (FmtAttribs.BlockWidth-1)) == 0, "Compressed block width (", FmtAttribs.BlockWidth, ") is expected to be power of 2"); + BlockAlignedBox.MaxX = (DstBox.MaxX + FmtAttribs.BlockWidth-1) & ~(FmtAttribs.BlockWidth-1); + + VERIFY( (DstBox.MinY % FmtAttribs.BlockHeight) == 0, "Update region min y (", DstBox.MinY, ") must be multiple of a compressed block height (", FmtAttribs.BlockHeight, ")"); + BlockAlignedBox.MinY = DstBox.MinY; + VERIFY( (FmtAttribs.BlockHeight & (FmtAttribs.BlockHeight-1)) == 0, "Compressed block height (", FmtAttribs.BlockHeight, ") is expected to be power of 2"); + BlockAlignedBox.MaxY = (DstBox.MaxY + FmtAttribs.BlockHeight-1) & ~(FmtAttribs.BlockHeight-1); + + BlockAlignedBox.MinZ = DstBox.MinZ; + BlockAlignedBox.MaxZ = DstBox.MaxZ; + + pBox = &BlockAlignedBox; + } + else + { + pBox = &DstBox; + } auto *pCtxD3D12 = ValidatedCast(pContext); auto DstSubResIndex = D3D12CalcSubresource(MipLevel, Slice, 0, m_Desc.MipLevels, m_Desc.ArraySize); if (SubresData.pSrcBuffer == nullptr) - pCtxD3D12->UpdateTextureRegion(SubresData.pData, SubresData.Stride, SubresData.DepthStride, this, DstSubResIndex, DstBox); + pCtxD3D12->UpdateTextureRegion(SubresData.pData, SubresData.Stride, SubresData.DepthStride, this, DstSubResIndex, *pBox); else - pCtxD3D12->CopyTextureRegion(SubresData.pSrcBuffer, 0, SubresData.Stride, SubresData.DepthStride, this, DstSubResIndex, DstBox); + pCtxD3D12->CopyTextureRegion(SubresData.pSrcBuffer, 0, SubresData.Stride, SubresData.DepthStride, this, DstSubResIndex, *pBox); } void TextureD3D12Impl :: CopyData(IDeviceContext* pContext, diff --git a/Graphics/GraphicsEngineOpenGL/src/Texture1DArray_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/Texture1DArray_OGL.cpp index d636f8b4..57478e47 100644 --- a/Graphics/GraphicsEngineOpenGL/src/Texture1DArray_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/Texture1DArray_OGL.cpp @@ -66,8 +66,8 @@ Texture1DArray_OGL::Texture1DArray_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width>>Mip, 1U), - 0, 1 ); + Box DstBox{0, std::max(m_Desc.Width>>Mip, 1U), + 0, 1 }; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of Texture1DArray_OGL // is not fully constructed yet. diff --git a/Graphics/GraphicsEngineOpenGL/src/Texture1D_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/Texture1D_OGL.cpp index 2a4a1fd7..f5ee0def 100644 --- a/Graphics/GraphicsEngineOpenGL/src/Texture1D_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/Texture1D_OGL.cpp @@ -64,8 +64,8 @@ Texture1D_OGL::Texture1D_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width>>Mip, 1U), - 0, 1); + Box DstBox{0, std::max(m_Desc.Width>>Mip, 1U), + 0, 1}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of Texture1D_OGL // is not fully constructed yet. diff --git a/Graphics/GraphicsEngineOpenGL/src/Texture2DArray_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/Texture2DArray_OGL.cpp index 5368281a..e1cc57c1 100644 --- a/Graphics/GraphicsEngineOpenGL/src/Texture2DArray_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/Texture2DArray_OGL.cpp @@ -84,8 +84,8 @@ Texture2DArray_OGL::Texture2DArray_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width >>Mip, 1U), - 0, std::max(m_Desc.Height>>Mip, 1U) ); + Box DstBox{0, std::max(m_Desc.Width >>Mip, 1U), + 0, std::max(m_Desc.Height>>Mip, 1U)}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of Texture2DArray_OGL // is not fully constructed yet. @@ -147,9 +147,11 @@ void Texture2DArray_OGL::UpdateData(IDeviceContext *pContext, Uint32 MipLevel, U if( TransferAttribs.IsCompressed ) { - VERIFY( (DstBox.MinX % 4) == 0 && (DstBox.MinY % 4) == 0 && - ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == std::max(m_Desc.Width >>MipLevel, 1U)) && - ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == std::max(m_Desc.Height>>MipLevel, 1U)), + auto MipWidth = std::max(m_Desc.Width >> MipLevel, 1U); + auto MipHeight = std::max(m_Desc.Height >> MipLevel, 1U); + VERIFY( (DstBox.MinX % 4) == 0 && (DstBox.MinY % 4) == 0 && + ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == MipWidth) && + ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == MipHeight), "Compressed texture update region must be 4 pixel-aligned" ); const auto &FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); auto BlockBytesInRow = ((DstBox.MaxX - DstBox.MinX + 3)/4) * Uint32{FmtAttribs.ComponentSize}; @@ -158,12 +160,16 @@ void Texture2DArray_OGL::UpdateData(IDeviceContext *pContext, Uint32 MipLevel, U //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); //glPixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, 0); + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + UpdateRegionWidth = std::min(UpdateRegionWidth, MipWidth - DstBox.MinX); + UpdateRegionHeight = std::min(UpdateRegionHeight, MipHeight - DstBox.MinY); glCompressedTexSubImage3D(m_BindTarget, MipLevel, DstBox.MinX, DstBox.MinY, Slice, - DstBox.MaxX - DstBox.MinX, - DstBox.MaxY - DstBox.MinY, + UpdateRegionWidth, + UpdateRegionHeight, 1, // The format must be the same compressed-texture format previously // specified by glTexStorage2D() (thank you OpenGL for another useless diff --git a/Graphics/GraphicsEngineOpenGL/src/Texture2D_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/Texture2D_OGL.cpp index f4171812..631b44ca 100644 --- a/Graphics/GraphicsEngineOpenGL/src/Texture2D_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/Texture2D_OGL.cpp @@ -88,8 +88,8 @@ Texture2D_OGL::Texture2D_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width >>Mip, 1U), - 0, std::max(m_Desc.Height>>Mip, 1U) ); + Box DstBox{0, std::max(m_Desc.Width >>Mip, 1U), + 0, std::max(m_Desc.Height>>Mip, 1U)}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of Texture2D_OGL // is not fully constructed yet. @@ -149,9 +149,11 @@ void Texture2D_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint3 if( TransferAttribs.IsCompressed ) { + auto MipWidth = std::max(m_Desc.Width >> MipLevel, 1U); + auto MipHeight = std::max(m_Desc.Height >> MipLevel, 1U); VERIFY( (DstBox.MinX % 4) == 0 && (DstBox.MinY % 4) == 0 && - ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == std::max(m_Desc.Width >>MipLevel, 1U)) && - ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == std::max(m_Desc.Height>>MipLevel, 1U)), + ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == MipWidth) && + ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == MipHeight), "Compressed texture update region must be 4 pixel-aligned" ); const auto &FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); auto BlockBytesInRow = ((DstBox.MaxX - DstBox.MinX + 3)/4) * Uint32{FmtAttribs.ComponentSize}; @@ -160,11 +162,15 @@ void Texture2D_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint3 //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); //glPixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, 0); + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + UpdateRegionWidth = std::min(UpdateRegionWidth, MipWidth - DstBox.MinX); + UpdateRegionHeight = std::min(UpdateRegionHeight, MipHeight - DstBox.MinY); glCompressedTexSubImage2D(m_BindTarget, MipLevel, DstBox.MinX, DstBox.MinY, - DstBox.MaxX - DstBox.MinX, - DstBox.MaxY - DstBox.MinY, + UpdateRegionWidth, + UpdateRegionHeight, // The format must be the same compressed-texture format previously // specified by glTexStorage2D() (thank you OpenGL for another useless // parameter that is nothing but the source of confusion), otherwise diff --git a/Graphics/GraphicsEngineOpenGL/src/Texture3D_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/Texture3D_OGL.cpp index 51b52c88..6fb7645a 100644 --- a/Graphics/GraphicsEngineOpenGL/src/Texture3D_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/Texture3D_OGL.cpp @@ -66,9 +66,9 @@ Texture3D_OGL::Texture3D_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width >>Mip, 1U), - 0, std::max(m_Desc.Height>>Mip, 1U), - 0, std::max(m_Desc.Depth >>Mip, 1U)); + Box DstBox{0, std::max(m_Desc.Width >>Mip, 1U), + 0, std::max(m_Desc.Height>>Mip, 1U), + 0, std::max(m_Desc.Depth >>Mip, 1U)}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of Texture3D_OGL // is not fully constructed yet. diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureCubeArray_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureCubeArray_OGL.cpp index 3851d31f..eddea87a 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureCubeArray_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureCubeArray_OGL.cpp @@ -73,8 +73,8 @@ TextureCubeArray_OGL::TextureCubeArray_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width >>Mip, 1U), - 0, std::max(m_Desc.Height>>Mip, 1U) ); + Box DstBox{0, std::max(m_Desc.Width >>Mip, 1U), + 0, std::max(m_Desc.Height>>Mip, 1U)}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of TextureCubeArray_OGL // is not fully constructed yet. @@ -133,9 +133,11 @@ void TextureCubeArray_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel if( TransferAttribs.IsCompressed ) { + auto MipWidth = std::max(m_Desc.Width >> MipLevel, 1U); + auto MipHeight = std::max(m_Desc.Height >> MipLevel, 1U); VERIFY( (DstBox.MinX % 4) == 0 && (DstBox.MinY % 4) == 0 && - ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == std::max(m_Desc.Width >>MipLevel, 1U)) && - ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == std::max(m_Desc.Height>>MipLevel, 1U)), + ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == MipWidth) && + ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == MipHeight), "Compressed texture update region must be 4 pixel-aligned" ); const auto &FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); auto BlockBytesInRow = ((DstBox.MaxX - DstBox.MinX + 3)/4) * Uint32{FmtAttribs.ComponentSize}; @@ -146,12 +148,16 @@ void TextureCubeArray_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel //glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); //glPixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, 0); + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + UpdateRegionWidth = std::min(UpdateRegionWidth, MipWidth - DstBox.MinX); + UpdateRegionHeight = std::min(UpdateRegionHeight, MipHeight - DstBox.MinY); glCompressedTexSubImage3D(m_BindTarget, MipLevel, DstBox.MinX, DstBox.MinY, Slice, - DstBox.MaxX - DstBox.MinX, - DstBox.MaxY - DstBox.MinY, + UpdateRegionWidth, + UpdateRegionHeight, 1, // The format must be the same compressed-texture format previously // specified by glTexStorage2D() (thank you OpenGL for another useless diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureCube_OGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureCube_OGL.cpp index 508ee6b2..ce7fe1d5 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureCube_OGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureCube_OGL.cpp @@ -71,8 +71,8 @@ TextureCube_OGL::TextureCube_OGL( IReferenceCounters *pRefCounters, { for(Uint32 Mip = 0; Mip < m_Desc.MipLevels; ++Mip) { - Box DstBox(0, std::max(m_Desc.Width >>Mip, 1U), - 0, std::max(m_Desc.Height>>Mip, 1U) ); + Box DstBox{0, std::max(m_Desc.Width >>Mip, 1U), + 0, std::max(m_Desc.Height>>Mip, 1U)}; // UpdateData() is a virtual function. If we try to call it through vtbl from here, // we will get into TextureBaseGL::UpdateData(), because instance of TextureCube_OGL // is not fully constructed yet. @@ -147,9 +147,11 @@ void TextureCube_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uin if( TransferAttribs.IsCompressed ) { + auto MipWidth = std::max(m_Desc.Width >> MipLevel, 1U); + auto MipHeight = std::max(m_Desc.Height >> MipLevel, 1U); VERIFY( (DstBox.MinX % 4) == 0 && (DstBox.MinY % 4) == 0 && - ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == std::max(m_Desc.Width >>MipLevel, 1U)) && - ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == std::max(m_Desc.Height>>MipLevel, 1U)), + ((DstBox.MaxX % 4) == 0 || DstBox.MaxX == MipWidth) && + ((DstBox.MaxY % 4) == 0 || DstBox.MaxY == MipHeight), "Compressed texture update region must be 4 pixel-aligned" ); const auto &FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); auto BlockBytesInRow = ((DstBox.MaxX - DstBox.MinX + 3)/4) * Uint32{FmtAttribs.ComponentSize}; @@ -161,11 +163,15 @@ void TextureCube_OGL::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uin // Texture must be bound as GL_TEXTURE_CUBE_MAP, but glCompressedTexSubImage2D() // takes one of GL_TEXTURE_CUBE_MAP_POSITIVE_X ... GL_TEXTURE_CUBE_MAP_NEGATIVE_Z + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + UpdateRegionWidth = std::min(UpdateRegionWidth, MipWidth - DstBox.MinX); + UpdateRegionHeight = std::min(UpdateRegionHeight, MipHeight - DstBox.MinY); glCompressedTexSubImage2D(CubeMapFaceBindTarget, MipLevel, DstBox.MinX, DstBox.MinY, - DstBox.MaxX - DstBox.MinX, - DstBox.MaxY - DstBox.MinY, + UpdateRegionWidth, + UpdateRegionHeight, // The format must be the same compressed-texture format previously // specified by glTexStorage2D() (thank you OpenGL for another useless // parameter that is nothing but the source of confusion), otherwise diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 7810cdce..c9ffb87f 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -1135,23 +1135,49 @@ namespace Diligent VERIFY(TexDesc.SampleCount == 1, "Only single-sample textures can be updated with vkCmdCopyBufferToImage()"); const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format); VERIFY_EXPR(DstBox.MaxX > DstBox.MinX && DstBox.MaxY > DstBox.MinY && DstBox.MaxZ > DstBox.MinZ); - const auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; - const auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; - const auto UpdateRegionDepth = DstBox.MaxZ - DstBox.MinZ; - const Uint32 RowSize = FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED ? - UpdateRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize} : - UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; + auto UpdateRegionWidth = DstBox.MaxX - DstBox.MinX; + auto UpdateRegionHeight = DstBox.MaxY - DstBox.MinY; + auto UpdateRegionDepth = DstBox.MaxZ - DstBox.MinZ; + Uint32 RowSize = 0; + Uint32 RowCount = 0; + if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED) + { + // Align update region size by the block size. This is only necessary when updating + // coarse mip levels. Otherwise UpdateRegionWidth/Height should be multiples of block size + const auto BlockAlignedRegionWidth = (UpdateRegionWidth + (FmtAttribs.BlockWidth-1)) & ~(FmtAttribs.BlockWidth -1); + const auto BlockAlignedRegionHeight = (UpdateRegionHeight + (FmtAttribs.BlockHeight-1)) & ~(FmtAttribs.BlockHeight-1); + RowSize = BlockAlignedRegionWidth / Uint32{FmtAttribs.BlockWidth} * Uint32{FmtAttribs.ComponentSize}; + RowCount = BlockAlignedRegionHeight / FmtAttribs.BlockHeight; + + // (imageExtent.width + imageOffset.x) must be less than or equal to the image subresource width, and + // (imageExtent.height + imageOffset.y) must be less than or equal to the image subresource height (18.4), + // so we need to clamp UpdateRegionWidth and Height + const Uint32 MipWidth = std::max(TexDesc.Width >> MipLevel, 1u); + const Uint32 MipHeight = std::max(TexDesc.Height >> MipLevel, 1u); + VERIFY_EXPR(MipWidth > DstBox.MinX); + UpdateRegionWidth = std::min(UpdateRegionWidth, MipWidth - DstBox.MinX); + VERIFY_EXPR(MipHeight > DstBox.MinY); + UpdateRegionHeight = std::min(UpdateRegionHeight, MipHeight - DstBox.MinY); + } + else + { + RowSize = UpdateRegionWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents}; + RowCount = UpdateRegionHeight; + } +#ifdef _DEBUG VERIFY(SrcStride >= RowSize, "Source data stride (", SrcStride, ") is below the image row size (", RowSize, ")"); - VERIFY(SrcDepthStride == 0 || SrcDepthStride >= SrcStride * (UpdateRegionHeight / FmtAttribs.BlockHeight), "Source data depth stride (", SrcDepthStride, ") is below the image plane size (", SrcStride * UpdateRegionHeight, ")"); + const Uint32 PlaneSize = SrcStride * RowCount; + VERIFY(UpdateRegionDepth == 1 || SrcDepthStride >= PlaneSize, "Source data depth stride (", SrcDepthStride, ") is below the image plane size (", PlaneSize, ")"); +#endif const auto BufferDataStride = RowSize; - const auto BufferDataDepthStride = UpdateRegionHeight * BufferDataStride; - auto MemorySize = UpdateRegionDepth * BufferDataDepthStride; - size_t Alignment = 4; - auto UploadSpace = AllocateDynamicSpace(MemorySize + static_cast(Alignment)); + const auto BufferDataDepthStride = (UpdateRegionHeight / FmtAttribs.BlockHeight) * BufferDataStride; + const auto MemorySize = UpdateRegionDepth * BufferDataDepthStride; + size_t Alignment = 4; + auto UploadSpace = AllocateDynamicSpace(MemorySize + static_cast(Alignment)); auto AlignedOffset = (UploadSpace.Offset + (Alignment-1)) & ~(Alignment-1); for(Uint32 slice = 0; slice < UpdateRegionDepth; ++slice) { - for(Uint32 row = 0; row < UpdateRegionHeight / FmtAttribs.BlockHeight; ++row) + for(Uint32 row = 0; row < RowCount; ++row) { const auto* pSrcPtr = reinterpret_cast(pSrcData) @@ -1201,6 +1227,10 @@ namespace Diligent CopyRegion.imageSubresource.baseArrayLayer = Slice; CopyRegion.imageSubresource.layerCount = 1; CopyRegion.imageSubresource.mipLevel = MipLevel; + // - imageOffset.x and (imageExtent.width + imageOffset.x) must both be greater than or equal to 0 and + // less than or equal to the image subresource width (18.4) + // - imageOffset.y and (imageExtent.height + imageOffset.y) must both be greater than or equal to 0 and + // less than or equal to the image subresource height (18.4) CopyRegion.imageOffset = VkOffset3D{static_cast(DstBox.MinX), static_cast(DstBox.MinY), static_cast(DstBox.MinZ)}; CopyRegion.imageExtent = VkExtent3D{UpdateRegionWidth, UpdateRegionHeight, UpdateRegionDepth}; -- cgit v1.2.3