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 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 {
public:

View File

@ -544,6 +544,73 @@ namespace dawn_native { namespace metal {
} // 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
Ref<CommandBuffer> 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;
}

View File

@ -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 {};
}

View File

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

View File

@ -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 =

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_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;