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:
parent
d066d17ef6
commit
5230c6bd93
|
@ -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:
|
||||
|
|
|
@ -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,24 +727,25 @@ 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
|
||||
|
||||
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
|
||||
|
@ -719,8 +756,31 @@ namespace dawn_native { namespace metal {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue