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 <enga@chromium.org>
Reviewed-by: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Austin Eng 2021-05-11 00:04:13 +00:00 committed by Commit Bot service account
parent d066d17ef6
commit 5230c6bd93
6 changed files with 196 additions and 125 deletions

View File

@ -28,6 +28,19 @@ namespace dawn_native { namespace metal {
class CommandRecordingContext; class CommandRecordingContext;
class Device; class Device;
class Texture;
void RecordCopyBufferToTexture(CommandRecordingContext* commandContext,
id<MTLBuffer> 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 { class CommandBuffer final : public CommandBufferBase {
public: public:

View File

@ -544,6 +544,73 @@ namespace dawn_native { namespace metal {
} // anonymous namespace } // anonymous namespace
void RecordCopyBufferToTexture(CommandRecordingContext* commandContext,
id<MTLBuffer> 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 // static
Ref<CommandBuffer> CommandBuffer::Create(CommandEncoder* encoder, Ref<CommandBuffer> CommandBuffer::Create(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) { const CommandBufferDescriptor* descriptor) {
@ -634,43 +701,12 @@ namespace dawn_native { namespace metal {
Texture* texture = ToBackend(dst.texture.Get()); Texture* texture = ToBackend(dst.texture.Get());
buffer->EnsureDataInitialized(commandContext); buffer->EnsureDataInitialized(commandContext);
EnsureDestinationTextureInitialized(commandContext, texture, copy->destination, EnsureDestinationTextureInitialized(commandContext, texture, dst, copySize);
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;
}
}
RecordCopyBufferToTexture(commandContext, buffer->GetMTLBuffer(),
buffer->GetSize(), src.offset, src.bytesPerRow,
src.rowsPerImage, texture, dst.mipLevel, dst.origin,
dst.aspect, copySize);
break; break;
} }
@ -691,36 +727,60 @@ namespace dawn_native { namespace metal {
texture, src.mipLevel, src.origin, copySize, buffer->GetSize(), dst.offset, texture, src.mipLevel, src.origin, copySize, buffer->GetSize(), dst.offset,
dst.bytesPerRow, dst.rowsPerImage, src.aspect); dst.bytesPerRow, dst.rowsPerImage, src.aspect);
for (uint32_t i = 0; i < splitCopies.count; ++i) { for (const auto& copyInfo : splitCopies) {
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 = MTLBlitOption blitOption =
ComputeMTLBlitOption(texture->GetFormat(), src.aspect); ComputeMTLBlitOption(texture->GetFormat(), src.aspect);
uint64_t bufferOffset = copyInfo.bufferOffset; uint64_t bufferOffset = copyInfo.bufferOffset;
for (uint32_t copyLayer = copyBaseLayer;
copyLayer < copyBaseLayer + copyLayerCount; ++copyLayer) { switch (texture->GetDimension()) {
[commandContext->EnsureBlit() copyFromTexture:texture->GetMTLTexture() case wgpu::TextureDimension::e2D: {
sourceSlice:copyLayer const MTLOrigin textureOrigin = MTLOriginMake(
sourceLevel:src.mipLevel copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0);
sourceOrigin:textureOrigin const MTLSize copyExtent = MTLSizeMake(
sourceSize:copyExtent copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1);
toBuffer:buffer->GetMTLBuffer()
destinationOffset:bufferOffset for (uint32_t z = copyInfo.textureOrigin.z;
destinationBytesPerRow:copyInfo.bytesPerRow z < copyInfo.textureOrigin.z +
destinationBytesPerImage:copyInfo.bytesPerImage copyInfo.copyExtent.depthOrArrayLayers;
options:blitOption]; ++z) {
bufferOffset += copyInfo.bytesPerImage; [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; break;
} }
@ -736,27 +796,51 @@ namespace dawn_native { namespace metal {
EnsureDestinationTextureInitialized(commandContext, dstTexture, EnsureDestinationTextureInitialized(commandContext, dstTexture,
copy->destination, copy->copySize); copy->destination, copy->copySize);
// TODO(jiawei.shao@intel.com): support copies with 1D and 3D textures. // TODO(jiawei.shao@intel.com): support copies with 1D textures.
ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && ASSERT(srcTexture->GetDimension() != wgpu::TextureDimension::e1D &&
dstTexture->GetDimension() == wgpu::TextureDimension::e2D); dstTexture->GetDimension() != wgpu::TextureDimension::e1D);
const MTLSize sizeOneLayer =
MTLSizeMake(copy->copySize.width, copy->copySize.height, 1); const MTLSize sizeOneSlice =
const MTLOrigin sourceOriginNoLayer = MTLSizeMake(copy->copySize.width, copy->copySize.height, 1);
MTLOriginMake(copy->source.origin.x, copy->source.origin.y, 0);
const MTLOrigin destinationOriginNoLayer = uint32_t sourceLayer = 0;
MTLOriginMake(copy->destination.origin.x, copy->destination.origin.y, 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() [commandContext->EnsureBlit()
copyFromTexture:srcTexture->GetMTLTexture() copyFromTexture:srcTexture->GetMTLTexture()
sourceSlice:copy->source.origin.z + slice sourceSlice:sourceLayer
sourceLevel:copy->source.mipLevel sourceLevel:copy->source.mipLevel
sourceOrigin:sourceOriginNoLayer sourceOrigin:MTLOriginMake(copy->source.origin.x,
sourceSize:sizeOneLayer copy->source.origin.y, sourceOriginZ)
sourceSize:sizeOneSlice
toTexture:dstTexture->GetMTLTexture() toTexture:dstTexture->GetMTLTexture()
destinationSlice:copy->destination.origin.z + slice destinationSlice:destinationLayer
destinationLevel:copy->destination.mipLevel destinationLevel:copy->destination.mipLevel
destinationOrigin:destinationOriginNoLayer]; destinationOrigin:MTLOriginMake(copy->destination.origin.x,
copy->destination.origin.y,
destinationOriginZ)];
} }
break; break;
} }

View File

@ -298,47 +298,13 @@ namespace dawn_native { namespace metal {
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
Texture* texture = ToBackend(dst->texture.Get()); 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, EnsureDestinationTextureInitialized(GetPendingCommandContext(), texture, *dst,
copySizePixels); copySizePixels);
// Metal validation layer requires that if the texture's pixel format is a compressed RecordCopyBufferToTexture(GetPendingCommandContext(), ToBackend(source)->GetBufferHandle(),
// format, the sourceSize must be a multiple of the pixel format's block size or be source->GetSize(), dataLayout.offset, dataLayout.bytesPerRow,
// clamped to the edge of the texture if the block extends outside the bounds of a dataLayout.rowsPerImage, texture, dst->mipLevel, dst->origin,
// texture. dst->aspect, copySizePixels);
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;
}
return {}; return {};
} }

View File

@ -38,6 +38,14 @@ namespace dawn_native { namespace metal {
uint32_t count = 0; uint32_t count = 0;
std::array<CopyInfo, kMaxTextureBufferCopyRegions> copies; std::array<CopyInfo, kMaxTextureBufferCopyRegions> copies;
auto begin() const {
return copies.begin();
}
auto end() const {
return copies.begin() + count;
}
}; };
TextureBufferCopySplit ComputeTextureBufferCopySplit(const Texture* texture, TextureBufferCopySplit ComputeTextureBufferCopySplit(const Texture* texture,

View File

@ -82,7 +82,7 @@ namespace dawn_native { namespace metal {
const Extent3D clampedCopyExtent = const Extent3D clampedCopyExtent =
texture->ClampToMipLevelVirtualSize(mipLevel, origin, copyExtent); texture->ClampToMipLevelVirtualSize(mipLevel, origin, copyExtent);
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); ASSERT(texture->GetDimension() != wgpu::TextureDimension::e1D);
// Check whether buffer size is big enough. // Check whether buffer size is big enough.
bool needWorkaround = bool needWorkaround =

View File

@ -968,9 +968,9 @@ TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
// Test that copying whole 3D texture in one texture-to-buffer-copy works. // Test that copying whole 3D texture in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture3DFull) { 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. // backend.
DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; 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 that copying a range of texture 3D depths in one texture-to-buffer-copy works.
TEST_P(CopyTests_T2B, Texture3DSubRegion) { 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. // backend.
DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; 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 that copying whole texture 3D in one buffer-to-texture-copy works.
TEST_P(CopyTests_B2T, Texture3DFull) { 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. // backend.
DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; 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 that copying a range of texture 3D Depths in one texture-to-buffer-copy works.
TEST_P(CopyTests_B2T, Texture3DSubRegion) { 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. // backend.
DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; 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 that copying whole 3D texture in one texture-to-texture-copy works.
TEST_P(CopyTests_T2T, Texture3DFull) { 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. // backend.
DAWN_SKIP_TEST_IF(IsMetal() || IsOpenGL() || IsOpenGLES()); DAWN_SKIP_TEST_IF(IsOpenGL() || IsOpenGLES());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; constexpr uint32_t kHeight = 128;