Metal: Move all the computation in buffer-texture copies to one function
This patch refactors the code of buffer-texture copies on Metal backend by moving all the computations on the split of one buffer-texture copy into multiple copies into one function ComputeTextureBufferCopySplit so that a lot of redundant code can be removed. BUG=dawn:42 Change-Id: Ie82e34e55aad3981d7b19da786da383e0a9a985c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8700 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
f697fe3b7d
commit
3e1bca702c
|
@ -296,6 +296,107 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TextureBufferCopySplit {
|
||||||
|
static constexpr uint32_t kMaxTextureBufferCopyRegions = 3;
|
||||||
|
|
||||||
|
struct CopyInfo {
|
||||||
|
NSUInteger bufferOffset;
|
||||||
|
NSUInteger bytesPerRow;
|
||||||
|
NSUInteger bytesPerImage;
|
||||||
|
MTLOrigin textureOrigin;
|
||||||
|
MTLSize copyExtent;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
std::array<CopyInfo, kMaxTextureBufferCopyRegions> copies;
|
||||||
|
};
|
||||||
|
|
||||||
|
TextureBufferCopySplit ComputeTextureBufferCopySplit(Origin3D origin,
|
||||||
|
Extent3D copyExtent,
|
||||||
|
Format textureFormat,
|
||||||
|
uint64_t bufferSize,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
uint32_t rowPitch,
|
||||||
|
uint32_t imageHeight) {
|
||||||
|
TextureBufferCopySplit copy;
|
||||||
|
|
||||||
|
// When copying textures from/to an unpacked buffer, the Metal validation layer doesn't
|
||||||
|
// compute the correct range when checking if the buffer is big enough to contain the
|
||||||
|
// data for the whole copy. Instead of looking at the position of the last texel in the
|
||||||
|
// buffer, it computes the volume of the 3D box with rowPitch * imageHeight *
|
||||||
|
// copySize.depth. For example considering the pixel buffer below where in memory, each
|
||||||
|
// row data (D) of the texture is followed by some padding data (P):
|
||||||
|
// |DDDDDDD|PP|
|
||||||
|
// |DDDDDDD|PP|
|
||||||
|
// |DDDDDDD|PP|
|
||||||
|
// |DDDDDDD|PP|
|
||||||
|
// |DDDDDDA|PP|
|
||||||
|
// The last pixel read will be A, but the driver will think it is the whole last padding
|
||||||
|
// row, causing it to generate an error when the pixel buffer is just big enough.
|
||||||
|
|
||||||
|
// We work around this limitation by detecting when Metal would complain and copy the
|
||||||
|
// last image and row separately using tight sourceBytesPerRow or sourceBytesPerImage.
|
||||||
|
uint32_t bytesPerImage = rowPitch * imageHeight;
|
||||||
|
|
||||||
|
// Check whether buffer size is big enough.
|
||||||
|
bool needWorkaround = bufferSize - bufferOffset < bytesPerImage * copyExtent.depth;
|
||||||
|
if (!needWorkaround) {
|
||||||
|
copy.count = 1;
|
||||||
|
copy.copies[0].bufferOffset = bufferOffset;
|
||||||
|
copy.copies[0].bytesPerRow = rowPitch;
|
||||||
|
copy.copies[0].bytesPerImage = bytesPerImage;
|
||||||
|
copy.copies[0].textureOrigin = MTLOriginMake(origin.x, origin.y, origin.z);
|
||||||
|
copy.copies[0].copyExtent =
|
||||||
|
MTLSizeMake(copyExtent.width, copyExtent.height, copyExtent.depth);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t currentOffset = bufferOffset;
|
||||||
|
|
||||||
|
// Doing all the copy except the last image.
|
||||||
|
if (copyExtent.depth > 1) {
|
||||||
|
copy.copies[copy.count].bufferOffset = currentOffset;
|
||||||
|
copy.copies[copy.count].bytesPerRow = rowPitch;
|
||||||
|
copy.copies[copy.count].bytesPerImage = bytesPerImage;
|
||||||
|
copy.copies[copy.count].textureOrigin = MTLOriginMake(origin.x, origin.y, origin.z);
|
||||||
|
copy.copies[copy.count].copyExtent =
|
||||||
|
MTLSizeMake(copyExtent.width, copyExtent.height, copyExtent.depth - 1);
|
||||||
|
|
||||||
|
++copy.count;
|
||||||
|
|
||||||
|
// Update offset to copy to the last image.
|
||||||
|
currentOffset += (copyExtent.depth - 1) * bytesPerImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing all the copy in last image except the last row.
|
||||||
|
if (copyExtent.height > 1) {
|
||||||
|
copy.copies[copy.count].bufferOffset = currentOffset;
|
||||||
|
copy.copies[copy.count].bytesPerRow = rowPitch;
|
||||||
|
copy.copies[copy.count].bytesPerImage = rowPitch * (imageHeight - 1);
|
||||||
|
copy.copies[copy.count].textureOrigin =
|
||||||
|
MTLOriginMake(origin.x, origin.y, origin.z + copyExtent.depth - 1);
|
||||||
|
copy.copies[copy.count].copyExtent =
|
||||||
|
MTLSizeMake(copyExtent.width, copyExtent.height - 1, 1);
|
||||||
|
|
||||||
|
++copy.count;
|
||||||
|
|
||||||
|
// Update offset to copy to the last row.
|
||||||
|
currentOffset += (copyExtent.height - 1) * rowPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing the last row copy with the exact number of bytes in last row.
|
||||||
|
// Workaround this issue in a way just like the copy to a 1D texture.
|
||||||
|
uint32_t lastRowDataSize = copyExtent.width * textureFormat.blockByteSize;
|
||||||
|
copy.copies[copy.count].bufferOffset = currentOffset;
|
||||||
|
copy.copies[copy.count].bytesPerRow = lastRowDataSize;
|
||||||
|
copy.copies[copy.count].bytesPerImage = lastRowDataSize;
|
||||||
|
copy.copies[copy.count].textureOrigin = MTLOriginMake(
|
||||||
|
origin.x, origin.y + copyExtent.height - 1, origin.z + copyExtent.depth - 1);
|
||||||
|
copy.copies[copy.count].copyExtent = MTLSizeMake(copyExtent.width, 1, 1);
|
||||||
|
++copy.count;
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
|
CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
|
||||||
|
@ -344,107 +445,23 @@ namespace dawn_native { namespace metal {
|
||||||
Buffer* buffer = ToBackend(src.buffer.Get());
|
Buffer* buffer = ToBackend(src.buffer.Get());
|
||||||
Texture* texture = ToBackend(dst.texture.Get());
|
Texture* texture = ToBackend(dst.texture.Get());
|
||||||
|
|
||||||
MTLOrigin origin;
|
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||||
origin.x = dst.origin.x;
|
dst.origin, copySize, texture->GetFormat(), buffer->GetSize(), src.offset,
|
||||||
origin.y = dst.origin.y;
|
src.rowPitch, src.imageHeight);
|
||||||
origin.z = dst.origin.z;
|
|
||||||
|
|
||||||
MTLSize size;
|
|
||||||
size.width = copySize.width;
|
|
||||||
size.height = copySize.height;
|
|
||||||
size.depth = copySize.depth;
|
|
||||||
|
|
||||||
// When uploading textures from an unpacked buffer, Metal validation layer
|
|
||||||
// doesn't compute the correct range when checking if the buffer is big enough
|
|
||||||
// to contain the data for the whole copy. Instead of looking at the position
|
|
||||||
// of the last texel in the buffer, it computes the volume of the 3D box with
|
|
||||||
// rowPitch * imageHeight * copySize.depth. For example considering the pixel
|
|
||||||
// buffer below where in memory, each row data (D) of the texture is followed
|
|
||||||
// by some padding data (P):
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDA|PP|
|
|
||||||
// The last pixel read will be A, but the driver will think it is the whole
|
|
||||||
// last padding row, causing it to generate an error when the pixel buffer is
|
|
||||||
// just big enough.
|
|
||||||
|
|
||||||
// We work around this limitation by detecting when Metal would complain and
|
|
||||||
// copy the last image and row separately using tight sourceBytesPerRow or
|
|
||||||
// sourceBytesPerImage.
|
|
||||||
uint32_t bytesPerImage = src.rowPitch * src.imageHeight;
|
|
||||||
|
|
||||||
// Check whether buffer size is big enough.
|
|
||||||
bool needWorkaround =
|
|
||||||
(buffer->GetSize() - src.offset < bytesPerImage * size.depth);
|
|
||||||
|
|
||||||
encoders.EnsureBlit(commandBuffer);
|
encoders.EnsureBlit(commandBuffer);
|
||||||
|
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||||
if (!needWorkaround) {
|
const TextureBufferCopySplit::CopyInfo& copyInfo = splittedCopies.copies[i];
|
||||||
[encoders.blit copyFromBuffer:buffer->GetMTLBuffer()
|
[encoders.blit copyFromBuffer:buffer->GetMTLBuffer()
|
||||||
sourceOffset:src.offset
|
sourceOffset:copyInfo.bufferOffset
|
||||||
sourceBytesPerRow:src.rowPitch
|
sourceBytesPerRow:copyInfo.bytesPerRow
|
||||||
sourceBytesPerImage:(src.rowPitch * src.imageHeight)
|
sourceBytesPerImage:copyInfo.bytesPerImage
|
||||||
sourceSize:size
|
sourceSize:copyInfo.copyExtent
|
||||||
toTexture:texture->GetMTLTexture()
|
toTexture:texture->GetMTLTexture()
|
||||||
destinationSlice:dst.arrayLayer
|
destinationSlice:dst.arrayLayer
|
||||||
destinationLevel:dst.mipLevel
|
destinationLevel:dst.mipLevel
|
||||||
destinationOrigin:origin];
|
destinationOrigin:copyInfo.textureOrigin];
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t offset = src.offset;
|
|
||||||
|
|
||||||
// Doing all the copy except the last image.
|
|
||||||
if (size.depth > 1) {
|
|
||||||
[encoders.blit
|
|
||||||
copyFromBuffer:buffer->GetMTLBuffer()
|
|
||||||
sourceOffset:offset
|
|
||||||
sourceBytesPerRow:src.rowPitch
|
|
||||||
sourceBytesPerImage:(src.rowPitch * src.imageHeight)
|
|
||||||
sourceSize:MTLSizeMake(size.width, size.height, size.depth - 1)
|
|
||||||
toTexture:texture->GetMTLTexture()
|
|
||||||
destinationSlice:dst.arrayLayer
|
|
||||||
destinationLevel:dst.mipLevel
|
|
||||||
destinationOrigin:origin];
|
|
||||||
|
|
||||||
// Update offset to copy to the last image.
|
|
||||||
offset += (copySize.depth - 1) * bytesPerImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing all the copy in last image except the last row.
|
|
||||||
if (size.height > 1) {
|
|
||||||
[encoders.blit copyFromBuffer:buffer->GetMTLBuffer()
|
|
||||||
sourceOffset:offset
|
|
||||||
sourceBytesPerRow:src.rowPitch
|
|
||||||
sourceBytesPerImage:(src.rowPitch * (src.imageHeight - 1))
|
|
||||||
sourceSize:MTLSizeMake(size.width, size.height - 1, 1)
|
|
||||||
toTexture:texture->GetMTLTexture()
|
|
||||||
destinationSlice:dst.arrayLayer
|
|
||||||
destinationLevel:dst.mipLevel
|
|
||||||
destinationOrigin:MTLOriginMake(origin.x, origin.y,
|
|
||||||
origin.z + size.depth - 1)];
|
|
||||||
|
|
||||||
// Update offset to copy to the last row.
|
|
||||||
offset += (copySize.height - 1) * src.rowPitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing the last row copy with the exact number of bytes in last row.
|
|
||||||
// Like copy to a 1D texture to workaround the issue.
|
|
||||||
uint32_t lastRowDataSize = copySize.width * texture->GetFormat().blockByteSize;
|
|
||||||
|
|
||||||
[encoders.blit
|
|
||||||
copyFromBuffer:buffer->GetMTLBuffer()
|
|
||||||
sourceOffset:offset
|
|
||||||
sourceBytesPerRow:lastRowDataSize
|
|
||||||
sourceBytesPerImage:lastRowDataSize
|
|
||||||
sourceSize:MTLSizeMake(size.width, 1, 1)
|
|
||||||
toTexture:texture->GetMTLTexture()
|
|
||||||
destinationSlice:dst.arrayLayer
|
|
||||||
destinationLevel:dst.mipLevel
|
|
||||||
destinationOrigin:MTLOriginMake(origin.x, origin.y + size.height - 1,
|
|
||||||
origin.z + size.depth - 1)];
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Command::CopyTextureToBuffer: {
|
case Command::CopyTextureToBuffer: {
|
||||||
|
@ -455,109 +472,23 @@ namespace dawn_native { namespace metal {
|
||||||
Texture* texture = ToBackend(src.texture.Get());
|
Texture* texture = ToBackend(src.texture.Get());
|
||||||
Buffer* buffer = ToBackend(dst.buffer.Get());
|
Buffer* buffer = ToBackend(dst.buffer.Get());
|
||||||
|
|
||||||
MTLOrigin origin;
|
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||||
origin.x = src.origin.x;
|
src.origin, copySize, texture->GetFormat(), buffer->GetSize(), dst.offset,
|
||||||
origin.y = src.origin.y;
|
dst.rowPitch, dst.imageHeight);
|
||||||
origin.z = src.origin.z;
|
|
||||||
|
|
||||||
MTLSize size;
|
|
||||||
size.width = copySize.width;
|
|
||||||
size.height = copySize.height;
|
|
||||||
size.depth = copySize.depth;
|
|
||||||
|
|
||||||
// When Copy textures to an unpacked buffer, Metal validation layer doesn't
|
|
||||||
// compute the correct range when checking if the buffer is big enough to
|
|
||||||
// contain the data for the whole copy. Instead of looking at the position
|
|
||||||
// of the last texel in the buffer, it computes the volume of the 3D box with
|
|
||||||
// rowPitch * imageHeight * copySize.depth.
|
|
||||||
// For example considering the texture below where in memory, each row
|
|
||||||
// data (D) of the texture is followed by some padding data (P):
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDD|PP|
|
|
||||||
// |DDDDDDA|PP|
|
|
||||||
// The last valid pixel read will be A, but the driver will think it needs the
|
|
||||||
// whole last padding row, causing it to generate an error when the buffer is
|
|
||||||
// just big enough.
|
|
||||||
|
|
||||||
// We work around this limitation by detecting when Metal would complain and
|
|
||||||
// copy the last image and row separately using tight destinationBytesPerRow or
|
|
||||||
// destinationBytesPerImage.
|
|
||||||
uint32_t bytesPerImage = dst.rowPitch * dst.imageHeight;
|
|
||||||
|
|
||||||
// Check whether buffer size is big enough.
|
|
||||||
bool needWorkaround =
|
|
||||||
(buffer->GetSize() - dst.offset < bytesPerImage * size.depth);
|
|
||||||
|
|
||||||
encoders.EnsureBlit(commandBuffer);
|
encoders.EnsureBlit(commandBuffer);
|
||||||
|
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||||
if (!needWorkaround) {
|
const TextureBufferCopySplit::CopyInfo& copyInfo = splittedCopies.copies[i];
|
||||||
[encoders.blit copyFromTexture:texture->GetMTLTexture()
|
[encoders.blit copyFromTexture:texture->GetMTLTexture()
|
||||||
sourceSlice:src.arrayLayer
|
sourceSlice:src.arrayLayer
|
||||||
sourceLevel:src.mipLevel
|
sourceLevel:src.mipLevel
|
||||||
sourceOrigin:origin
|
sourceOrigin:copyInfo.textureOrigin
|
||||||
sourceSize:size
|
sourceSize:copyInfo.copyExtent
|
||||||
toBuffer:buffer->GetMTLBuffer()
|
toBuffer:buffer->GetMTLBuffer()
|
||||||
destinationOffset:dst.offset
|
destinationOffset:copyInfo.bufferOffset
|
||||||
destinationBytesPerRow:dst.rowPitch
|
destinationBytesPerRow:copyInfo.bytesPerRow
|
||||||
destinationBytesPerImage:(dst.rowPitch * dst.imageHeight)];
|
destinationBytesPerImage:copyInfo.bytesPerImage];
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t offset = dst.offset;
|
|
||||||
|
|
||||||
// Doing all the copy except the last image.
|
|
||||||
if (size.depth > 1) {
|
|
||||||
size.depth = copySize.depth - 1;
|
|
||||||
|
|
||||||
[encoders.blit copyFromTexture:texture->GetMTLTexture()
|
|
||||||
sourceSlice:src.arrayLayer
|
|
||||||
sourceLevel:src.mipLevel
|
|
||||||
sourceOrigin:origin
|
|
||||||
sourceSize:MTLSizeMake(size.width, size.height,
|
|
||||||
size.depth - 1)
|
|
||||||
toBuffer:buffer->GetMTLBuffer()
|
|
||||||
destinationOffset:offset
|
|
||||||
destinationBytesPerRow:dst.rowPitch
|
|
||||||
destinationBytesPerImage:dst.rowPitch * dst.imageHeight];
|
|
||||||
|
|
||||||
// Update offset to copy from the last image.
|
|
||||||
offset += (copySize.depth - 1) * bytesPerImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing all the copy in last image except the last row.
|
|
||||||
if (size.height > 1) {
|
|
||||||
[encoders.blit copyFromTexture:texture->GetMTLTexture()
|
|
||||||
sourceSlice:src.arrayLayer
|
|
||||||
sourceLevel:src.mipLevel
|
|
||||||
sourceOrigin:MTLOriginMake(origin.x, origin.y,
|
|
||||||
origin.z + size.depth - 1)
|
|
||||||
sourceSize:MTLSizeMake(size.width, size.height - 1, 1)
|
|
||||||
toBuffer:buffer->GetMTLBuffer()
|
|
||||||
destinationOffset:offset
|
|
||||||
destinationBytesPerRow:dst.rowPitch
|
|
||||||
destinationBytesPerImage:dst.rowPitch * (dst.imageHeight - 1)];
|
|
||||||
|
|
||||||
// Update offset to copy from the last row.
|
|
||||||
offset += (copySize.height - 1) * dst.rowPitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing the last row copy with the exact number of bytes in last row.
|
|
||||||
// Like copy from a 1D texture to workaround the issue.
|
|
||||||
uint32_t lastRowDataSize = copySize.width * texture->GetFormat().blockByteSize;
|
|
||||||
|
|
||||||
[encoders.blit
|
|
||||||
copyFromTexture:texture->GetMTLTexture()
|
|
||||||
sourceSlice:src.arrayLayer
|
|
||||||
sourceLevel:src.mipLevel
|
|
||||||
sourceOrigin:MTLOriginMake(origin.x, origin.y + size.height - 1,
|
|
||||||
origin.z + size.depth - 1)
|
|
||||||
sourceSize:MTLSizeMake(size.width, 1, 1)
|
|
||||||
toBuffer:buffer->GetMTLBuffer()
|
|
||||||
destinationOffset:offset
|
|
||||||
destinationBytesPerRow:lastRowDataSize
|
|
||||||
destinationBytesPerImage:lastRowDataSize];
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Command::CopyTextureToTexture: {
|
case Command::CopyTextureToTexture: {
|
||||||
|
|
Loading…
Reference in New Issue