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
|
||||
|
||||
CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder)
|
||||
|
@ -344,107 +445,23 @@ namespace dawn_native { namespace metal {
|
|||
Buffer* buffer = ToBackend(src.buffer.Get());
|
||||
Texture* texture = ToBackend(dst.texture.Get());
|
||||
|
||||
MTLOrigin origin;
|
||||
origin.x = dst.origin.x;
|
||||
origin.y = dst.origin.y;
|
||||
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);
|
||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||
dst.origin, copySize, texture->GetFormat(), buffer->GetSize(), src.offset,
|
||||
src.rowPitch, src.imageHeight);
|
||||
|
||||
encoders.EnsureBlit(commandBuffer);
|
||||
|
||||
if (!needWorkaround) {
|
||||
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||
const TextureBufferCopySplit::CopyInfo& copyInfo = splittedCopies.copies[i];
|
||||
[encoders.blit copyFromBuffer:buffer->GetMTLBuffer()
|
||||
sourceOffset:src.offset
|
||||
sourceBytesPerRow:src.rowPitch
|
||||
sourceBytesPerImage:(src.rowPitch * src.imageHeight)
|
||||
sourceSize:size
|
||||
sourceOffset:copyInfo.bufferOffset
|
||||
sourceBytesPerRow:copyInfo.bytesPerRow
|
||||
sourceBytesPerImage:copyInfo.bytesPerImage
|
||||
sourceSize:copyInfo.copyExtent
|
||||
toTexture:texture->GetMTLTexture()
|
||||
destinationSlice:dst.arrayLayer
|
||||
destinationLevel:dst.mipLevel
|
||||
destinationOrigin:origin];
|
||||
break;
|
||||
destinationOrigin:copyInfo.textureOrigin];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case Command::CopyTextureToBuffer: {
|
||||
|
@ -455,109 +472,23 @@ namespace dawn_native { namespace metal {
|
|||
Texture* texture = ToBackend(src.texture.Get());
|
||||
Buffer* buffer = ToBackend(dst.buffer.Get());
|
||||
|
||||
MTLOrigin origin;
|
||||
origin.x = src.origin.x;
|
||||
origin.y = src.origin.y;
|
||||
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);
|
||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||
src.origin, copySize, texture->GetFormat(), buffer->GetSize(), dst.offset,
|
||||
dst.rowPitch, dst.imageHeight);
|
||||
|
||||
encoders.EnsureBlit(commandBuffer);
|
||||
|
||||
if (!needWorkaround) {
|
||||
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||
const TextureBufferCopySplit::CopyInfo& copyInfo = splittedCopies.copies[i];
|
||||
[encoders.blit copyFromTexture:texture->GetMTLTexture()
|
||||
sourceSlice:src.arrayLayer
|
||||
sourceLevel:src.mipLevel
|
||||
sourceOrigin:origin
|
||||
sourceSize:size
|
||||
sourceOrigin:copyInfo.textureOrigin
|
||||
sourceSize:copyInfo.copyExtent
|
||||
toBuffer:buffer->GetMTLBuffer()
|
||||
destinationOffset:dst.offset
|
||||
destinationBytesPerRow:dst.rowPitch
|
||||
destinationBytesPerImage:(dst.rowPitch * dst.imageHeight)];
|
||||
break;
|
||||
destinationOffset:copyInfo.bufferOffset
|
||||
destinationBytesPerRow:copyInfo.bytesPerRow
|
||||
destinationBytesPerImage:copyInfo.bytesPerImage];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case Command::CopyTextureToTexture: {
|
||||
|
|
Loading…
Reference in New Issue