From 5230c6bd93cc09825d9f97232e8be427ddef85bd Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Tue, 11 May 2021 00:04:13 +0000 Subject: [PATCH] Implement 3D texture copies on Metal Bug: dawn:782 Change-Id: I204bfb087b9b7584d7d0f1964bcb3ea8b0a41d38 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/50242 Commit-Queue: Austin Eng Reviewed-by: Yunchao He --- src/dawn_native/metal/CommandBufferMTL.h | 13 ++ src/dawn_native/metal/CommandBufferMTL.mm | 236 +++++++++++++++------- src/dawn_native/metal/DeviceMTL.mm | 42 +--- src/dawn_native/metal/UtilsMetal.h | 8 + src/dawn_native/metal/UtilsMetal.mm | 2 +- src/tests/end2end/CopyTests.cpp | 20 +- 6 files changed, 196 insertions(+), 125 deletions(-) diff --git a/src/dawn_native/metal/CommandBufferMTL.h b/src/dawn_native/metal/CommandBufferMTL.h index 4283a13a2c..328ac8400d 100644 --- a/src/dawn_native/metal/CommandBufferMTL.h +++ b/src/dawn_native/metal/CommandBufferMTL.h @@ -28,6 +28,19 @@ namespace dawn_native { namespace metal { class CommandRecordingContext; class Device; + class Texture; + + void RecordCopyBufferToTexture(CommandRecordingContext* commandContext, + id mtlBuffer, + uint64_t bufferSize, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + Texture* texture, + uint32_t mipLevel, + const Origin3D& origin, + Aspect aspect, + const Extent3D& copySize); class CommandBuffer final : public CommandBufferBase { public: diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm index d7efd83aa4..04cb72d392 100644 --- a/src/dawn_native/metal/CommandBufferMTL.mm +++ b/src/dawn_native/metal/CommandBufferMTL.mm @@ -544,6 +544,73 @@ namespace dawn_native { namespace metal { } // anonymous namespace + void RecordCopyBufferToTexture(CommandRecordingContext* commandContext, + id mtlBuffer, + uint64_t bufferSize, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + Texture* texture, + uint32_t mipLevel, + const Origin3D& origin, + Aspect aspect, + const Extent3D& copySize) { + TextureBufferCopySplit splitCopies = + ComputeTextureBufferCopySplit(texture, mipLevel, origin, copySize, bufferSize, offset, + bytesPerRow, rowsPerImage, aspect); + + MTLBlitOption blitOption = ComputeMTLBlitOption(texture->GetFormat(), aspect); + + for (const auto& copyInfo : splitCopies) { + uint64_t bufferOffset = copyInfo.bufferOffset; + switch (texture->GetDimension()) { + case wgpu::TextureDimension::e2D: { + const MTLOrigin textureOrigin = + MTLOriginMake(copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); + const MTLSize copyExtent = + MTLSizeMake(copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); + + for (uint32_t z = copyInfo.textureOrigin.z; + z < copyInfo.textureOrigin.z + copyInfo.copyExtent.depthOrArrayLayers; + ++z) { + [commandContext->EnsureBlit() copyFromBuffer:mtlBuffer + sourceOffset:bufferOffset + sourceBytesPerRow:copyInfo.bytesPerRow + sourceBytesPerImage:copyInfo.bytesPerImage + sourceSize:copyExtent + toTexture:texture->GetMTLTexture() + destinationSlice:z + destinationLevel:mipLevel + destinationOrigin:textureOrigin + options:blitOption]; + bufferOffset += copyInfo.bytesPerImage; + } + break; + } + case wgpu::TextureDimension::e3D: { + [commandContext->EnsureBlit() + copyFromBuffer:mtlBuffer + sourceOffset:bufferOffset + sourceBytesPerRow:copyInfo.bytesPerRow + sourceBytesPerImage:copyInfo.bytesPerImage + sourceSize:MTLSizeMake(copyInfo.copyExtent.width, + copyInfo.copyExtent.height, + copyInfo.copyExtent.depthOrArrayLayers) + toTexture:texture->GetMTLTexture() + destinationSlice:0 + destinationLevel:mipLevel + destinationOrigin:MTLOriginMake(copyInfo.textureOrigin.x, + copyInfo.textureOrigin.y, + copyInfo.textureOrigin.z) + options:blitOption]; + break; + } + case wgpu::TextureDimension::e1D: + UNREACHABLE(); + } + } + } + // static Ref CommandBuffer::Create(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) { @@ -634,43 +701,12 @@ namespace dawn_native { namespace metal { Texture* texture = ToBackend(dst.texture.Get()); buffer->EnsureDataInitialized(commandContext); - EnsureDestinationTextureInitialized(commandContext, texture, copy->destination, - copy->copySize); - - TextureBufferCopySplit splitCopies = ComputeTextureBufferCopySplit( - texture, dst.mipLevel, dst.origin, copySize, buffer->GetSize(), src.offset, - src.bytesPerRow, src.rowsPerImage, dst.aspect); - - for (uint32_t i = 0; i < splitCopies.count; ++i) { - const TextureBufferCopySplit::CopyInfo& copyInfo = splitCopies.copies[i]; - - const uint32_t copyBaseLayer = copyInfo.textureOrigin.z; - const uint32_t copyLayerCount = copyInfo.copyExtent.depthOrArrayLayers; - const MTLOrigin textureOrigin = - MTLOriginMake(copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); - const MTLSize copyExtent = - MTLSizeMake(copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); - - MTLBlitOption blitOption = - ComputeMTLBlitOption(texture->GetFormat(), dst.aspect); - - uint64_t bufferOffset = copyInfo.bufferOffset; - for (uint32_t copyLayer = copyBaseLayer; - copyLayer < copyBaseLayer + copyLayerCount; ++copyLayer) { - [commandContext->EnsureBlit() copyFromBuffer:buffer->GetMTLBuffer() - sourceOffset:bufferOffset - sourceBytesPerRow:copyInfo.bytesPerRow - sourceBytesPerImage:copyInfo.bytesPerImage - sourceSize:copyExtent - toTexture:texture->GetMTLTexture() - destinationSlice:copyLayer - destinationLevel:dst.mipLevel - destinationOrigin:textureOrigin - options:blitOption]; - bufferOffset += copyInfo.bytesPerImage; - } - } + EnsureDestinationTextureInitialized(commandContext, texture, dst, copySize); + RecordCopyBufferToTexture(commandContext, buffer->GetMTLBuffer(), + buffer->GetSize(), src.offset, src.bytesPerRow, + src.rowsPerImage, texture, dst.mipLevel, dst.origin, + dst.aspect, copySize); break; } @@ -691,36 +727,60 @@ namespace dawn_native { namespace metal { texture, src.mipLevel, src.origin, copySize, buffer->GetSize(), dst.offset, dst.bytesPerRow, dst.rowsPerImage, src.aspect); - for (uint32_t i = 0; i < splitCopies.count; ++i) { - const TextureBufferCopySplit::CopyInfo& copyInfo = splitCopies.copies[i]; - - const uint32_t copyBaseLayer = copyInfo.textureOrigin.z; - const uint32_t copyLayerCount = copyInfo.copyExtent.depthOrArrayLayers; - const MTLOrigin textureOrigin = - MTLOriginMake(copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); - const MTLSize copyExtent = - MTLSizeMake(copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); - + for (const auto& copyInfo : splitCopies) { MTLBlitOption blitOption = ComputeMTLBlitOption(texture->GetFormat(), src.aspect); - uint64_t bufferOffset = copyInfo.bufferOffset; - for (uint32_t copyLayer = copyBaseLayer; - copyLayer < copyBaseLayer + copyLayerCount; ++copyLayer) { - [commandContext->EnsureBlit() copyFromTexture:texture->GetMTLTexture() - sourceSlice:copyLayer - sourceLevel:src.mipLevel - sourceOrigin:textureOrigin - sourceSize:copyExtent - toBuffer:buffer->GetMTLBuffer() - destinationOffset:bufferOffset - destinationBytesPerRow:copyInfo.bytesPerRow - destinationBytesPerImage:copyInfo.bytesPerImage - options:blitOption]; - bufferOffset += copyInfo.bytesPerImage; + + switch (texture->GetDimension()) { + case wgpu::TextureDimension::e2D: { + const MTLOrigin textureOrigin = MTLOriginMake( + copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); + const MTLSize copyExtent = MTLSizeMake( + copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); + + for (uint32_t z = copyInfo.textureOrigin.z; + z < copyInfo.textureOrigin.z + + copyInfo.copyExtent.depthOrArrayLayers; + ++z) { + [commandContext->EnsureBlit() + copyFromTexture:texture->GetMTLTexture() + sourceSlice:z + sourceLevel:src.mipLevel + sourceOrigin:textureOrigin + sourceSize:copyExtent + toBuffer:buffer->GetMTLBuffer() + destinationOffset:bufferOffset + destinationBytesPerRow:copyInfo.bytesPerRow + destinationBytesPerImage:copyInfo.bytesPerImage + options:blitOption]; + bufferOffset += copyInfo.bytesPerImage; + } + break; + } + case wgpu::TextureDimension::e3D: { + [commandContext->EnsureBlit() + copyFromTexture:texture->GetMTLTexture() + sourceSlice:0 + sourceLevel:src.mipLevel + sourceOrigin:MTLOriginMake(copyInfo.textureOrigin.x, + copyInfo.textureOrigin.y, + copyInfo.textureOrigin.z) + sourceSize:MTLSizeMake(copyInfo.copyExtent.width, + copyInfo.copyExtent.height, + copyInfo.copyExtent + .depthOrArrayLayers) + toBuffer:buffer->GetMTLBuffer() + destinationOffset:bufferOffset + destinationBytesPerRow:copyInfo.bytesPerRow + destinationBytesPerImage:copyInfo.bytesPerImage + options:blitOption]; + break; + } + case wgpu::TextureDimension::e1D: + UNREACHABLE(); } } - break; } @@ -736,27 +796,51 @@ namespace dawn_native { namespace metal { EnsureDestinationTextureInitialized(commandContext, dstTexture, copy->destination, copy->copySize); - // TODO(jiawei.shao@intel.com): support copies with 1D and 3D textures. - ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && - dstTexture->GetDimension() == wgpu::TextureDimension::e2D); - const MTLSize sizeOneLayer = - MTLSizeMake(copy->copySize.width, copy->copySize.height, 1); - const MTLOrigin sourceOriginNoLayer = - MTLOriginMake(copy->source.origin.x, copy->source.origin.y, 0); - const MTLOrigin destinationOriginNoLayer = - MTLOriginMake(copy->destination.origin.x, copy->destination.origin.y, 0); + // TODO(jiawei.shao@intel.com): support copies with 1D textures. + ASSERT(srcTexture->GetDimension() != wgpu::TextureDimension::e1D && + dstTexture->GetDimension() != wgpu::TextureDimension::e1D); + + const MTLSize sizeOneSlice = + MTLSizeMake(copy->copySize.width, copy->copySize.height, 1); + + uint32_t sourceLayer = 0; + uint32_t sourceOriginZ = 0; + + uint32_t destinationLayer = 0; + uint32_t destinationOriginZ = 0; + + uint32_t* sourceZPtr; + if (srcTexture->GetDimension() == wgpu::TextureDimension::e2D) { + sourceZPtr = &sourceLayer; + } else { + sourceZPtr = &sourceOriginZ; + } + + uint32_t* destinationZPtr; + if (dstTexture->GetDimension() == wgpu::TextureDimension::e2D) { + destinationZPtr = &destinationLayer; + } else { + destinationZPtr = &destinationOriginZ; + } + + // TODO(crbug.com/dawn/782): Do a single T2T copy if both are 3D. + for (uint32_t z = 0; z < copy->copySize.depthOrArrayLayers; ++z) { + *sourceZPtr = copy->source.origin.z + z; + *destinationZPtr = copy->destination.origin.z + z; - for (uint32_t slice = 0; slice < copy->copySize.depthOrArrayLayers; ++slice) { [commandContext->EnsureBlit() copyFromTexture:srcTexture->GetMTLTexture() - sourceSlice:copy->source.origin.z + slice + sourceSlice:sourceLayer sourceLevel:copy->source.mipLevel - sourceOrigin:sourceOriginNoLayer - sourceSize:sizeOneLayer + sourceOrigin:MTLOriginMake(copy->source.origin.x, + copy->source.origin.y, sourceOriginZ) + sourceSize:sizeOneSlice toTexture:dstTexture->GetMTLTexture() - destinationSlice:copy->destination.origin.z + slice + destinationSlice:destinationLayer destinationLevel:copy->destination.mipLevel - destinationOrigin:destinationOriginNoLayer]; + destinationOrigin:MTLOriginMake(copy->destination.origin.x, + copy->destination.origin.y, + destinationOriginZ)]; } break; } diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm index 514c8049de..dc95fc8c4e 100644 --- a/src/dawn_native/metal/DeviceMTL.mm +++ b/src/dawn_native/metal/DeviceMTL.mm @@ -298,47 +298,13 @@ namespace dawn_native { namespace metal { TextureCopy* dst, const Extent3D& copySizePixels) { Texture* texture = ToBackend(dst->texture.Get()); - - // This function assumes data is perfectly aligned. Otherwise, it might be necessary - // to split copying to several stages: see ComputeTextureBufferCopySplit. - const TexelBlockInfo& blockInfo = texture->GetFormat().GetAspectInfo(dst->aspect).block; - ASSERT(dataLayout.rowsPerImage == copySizePixels.height / blockInfo.height); - ASSERT(dataLayout.bytesPerRow == - copySizePixels.width / blockInfo.width * blockInfo.byteSize); - EnsureDestinationTextureInitialized(GetPendingCommandContext(), texture, *dst, copySizePixels); - // Metal validation layer requires that if the texture's pixel format is a compressed - // format, the sourceSize must be a multiple of the pixel format's block size or be - // clamped to the edge of the texture if the block extends outside the bounds of a - // texture. - const Extent3D clampedSize = - texture->ClampToMipLevelVirtualSize(dst->mipLevel, dst->origin, copySizePixels); - const uint32_t copyBaseLayer = dst->origin.z; - const uint32_t copyLayerCount = copySizePixels.depthOrArrayLayers; - const uint64_t bytesPerImage = dataLayout.rowsPerImage * dataLayout.bytesPerRow; - - MTLBlitOption blitOption = ComputeMTLBlitOption(texture->GetFormat(), dst->aspect); - - uint64_t bufferOffset = dataLayout.offset; - for (uint32_t copyLayer = copyBaseLayer; copyLayer < copyBaseLayer + copyLayerCount; - ++copyLayer) { - [GetPendingCommandContext()->EnsureBlit() - copyFromBuffer:ToBackend(source)->GetBufferHandle() - sourceOffset:bufferOffset - sourceBytesPerRow:dataLayout.bytesPerRow - sourceBytesPerImage:bytesPerImage - sourceSize:MTLSizeMake(clampedSize.width, clampedSize.height, 1) - toTexture:texture->GetMTLTexture() - destinationSlice:copyLayer - destinationLevel:dst->mipLevel - destinationOrigin:MTLOriginMake(dst->origin.x, dst->origin.y, 0) - options:blitOption]; - - bufferOffset += bytesPerImage; - } - + RecordCopyBufferToTexture(GetPendingCommandContext(), ToBackend(source)->GetBufferHandle(), + source->GetSize(), dataLayout.offset, dataLayout.bytesPerRow, + dataLayout.rowsPerImage, texture, dst->mipLevel, dst->origin, + dst->aspect, copySizePixels); return {}; } diff --git a/src/dawn_native/metal/UtilsMetal.h b/src/dawn_native/metal/UtilsMetal.h index c5d2a5854d..6855734f8e 100644 --- a/src/dawn_native/metal/UtilsMetal.h +++ b/src/dawn_native/metal/UtilsMetal.h @@ -38,6 +38,14 @@ namespace dawn_native { namespace metal { uint32_t count = 0; std::array copies; + + auto begin() const { + return copies.begin(); + } + + auto end() const { + return copies.begin() + count; + } }; TextureBufferCopySplit ComputeTextureBufferCopySplit(const Texture* texture, diff --git a/src/dawn_native/metal/UtilsMetal.mm b/src/dawn_native/metal/UtilsMetal.mm index 40b23bcf47..51fa99325c 100644 --- a/src/dawn_native/metal/UtilsMetal.mm +++ b/src/dawn_native/metal/UtilsMetal.mm @@ -82,7 +82,7 @@ namespace dawn_native { namespace metal { const Extent3D clampedCopyExtent = texture->ClampToMipLevelVirtualSize(mipLevel, origin, copyExtent); - ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D); // Check whether buffer size is big enough. bool needWorkaround = diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp index 951b923086..0684597b32 100644 --- a/src/tests/end2end/CopyTests.cpp +++ b/src/tests/end2end/CopyTests.cpp @@ -968,9 +968,9 @@ TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) { // Test that copying whole 3D texture in one texture-to-buffer-copy works. TEST_P(CopyTests_T2B, Texture3DFull) { - // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES + // TODO(yunchao.he@intel.com): implement 3D texture copy on OpenGL and OpenGLES // backend. - DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); + DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; @@ -985,9 +985,9 @@ TEST_P(CopyTests_T2B, Texture3DFull) { // Test that copying a range of texture 3D depths in one texture-to-buffer-copy works. TEST_P(CopyTests_T2B, Texture3DSubRegion) { - // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES + // TODO(yunchao.he@intel.com): implement 3D texture copy on OpenGL and OpenGLES // backend. - DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); + DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; @@ -1445,9 +1445,9 @@ TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetEvenRowsPerImage) { // Test that copying whole texture 3D in one buffer-to-texture-copy works. TEST_P(CopyTests_B2T, Texture3DFull) { - // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES + // TODO(yunchao.he@intel.com): implement 3D texture copy on OpenGL and OpenGLES // backend. - DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); + DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; @@ -1462,9 +1462,9 @@ TEST_P(CopyTests_B2T, Texture3DFull) { // Test that copying a range of texture 3D Depths in one texture-to-buffer-copy works. TEST_P(CopyTests_B2T, Texture3DSubRegion) { - // TODO(yunchao.he@intel.com): implement 3D texture copy on Metal, OpenGL and OpenGLES + // TODO(yunchao.he@intel.com): implement 3D texture copy on OpenGL and OpenGLES // backend. - DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); + DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; @@ -1799,9 +1799,9 @@ TEST_P(CopyTests_T2T, CopyFromNonZeroMipLevelWithTexelBlockSizeLessThan4Bytes) { // Test that copying whole 3D texture in one texture-to-texture-copy works. TEST_P(CopyTests_T2T, Texture3DFull) { - // TODO(yunchao.he@intel.com): implement 3D texture copy, Metal, OpenGL and OpenGLES + // TODO(yunchao.he@intel.com): implement 3D texture copy on OpenGL and OpenGLES // backend. - DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); + DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128;