Support BC5 formats on Metal
This patch adds the support of BC5 formats on Metal and the related dawn_end2end_tests to verify Dawn works correctly when (bufferSize - bufferOffset < bytesPerImage * copyExtent.depth), which is the special case of buffer-to-texture and texture-to-buffer copies on Metal. BUG=dawn:42 TEST=dawn_end2end_tests Change-Id: I27c384d0d8d2bb908f1ad15c2451fd23c1313598 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8720 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
parent
4b90c47ce0
commit
ea2d558479
|
@ -314,6 +314,7 @@ namespace dawn_native { namespace metal {
|
|||
TextureBufferCopySplit ComputeTextureBufferCopySplit(Origin3D origin,
|
||||
Extent3D copyExtent,
|
||||
Format textureFormat,
|
||||
Extent3D virtualSizeAtLevel,
|
||||
uint64_t bufferSize,
|
||||
uint64_t bufferOffset,
|
||||
uint32_t rowPitch,
|
||||
|
@ -323,9 +324,10 @@ namespace dawn_native { namespace metal {
|
|||
// 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):
|
||||
// buffer, it computes the volume of the 3D box with rowPitch * (imageHeight /
|
||||
// format.blockHeight) * 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|
|
||||
|
@ -336,7 +338,21 @@ namespace dawn_native { namespace metal {
|
|||
|
||||
// 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;
|
||||
uint32_t rowPitchCountPerImage = imageHeight / textureFormat.blockHeight;
|
||||
uint32_t bytesPerImage = rowPitch * rowPitchCountPerImage;
|
||||
|
||||
// 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.
|
||||
uint32_t clampedCopyExtentWidth =
|
||||
(origin.x + copyExtent.width > virtualSizeAtLevel.width)
|
||||
? (virtualSizeAtLevel.width - origin.x)
|
||||
: copyExtent.width;
|
||||
uint32_t clampedCopyExtentHeight =
|
||||
(origin.y + copyExtent.height > virtualSizeAtLevel.height)
|
||||
? (virtualSizeAtLevel.height - origin.y)
|
||||
: copyExtent.height;
|
||||
|
||||
// Check whether buffer size is big enough.
|
||||
bool needWorkaround = bufferSize - bufferOffset < bytesPerImage * copyExtent.depth;
|
||||
|
@ -347,7 +363,7 @@ namespace dawn_native { namespace metal {
|
|||
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);
|
||||
MTLSizeMake(clampedCopyExtentWidth, clampedCopyExtentHeight, copyExtent.depth);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@ -359,8 +375,8 @@ namespace dawn_native { namespace metal {
|
|||
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.copies[copy.count].copyExtent = MTLSizeMake(
|
||||
clampedCopyExtentWidth, clampedCopyExtentHeight, copyExtent.depth - 1);
|
||||
|
||||
++copy.count;
|
||||
|
||||
|
@ -369,30 +385,40 @@ namespace dawn_native { namespace metal {
|
|||
}
|
||||
|
||||
// Doing all the copy in last image except the last row.
|
||||
if (copyExtent.height > 1) {
|
||||
uint32_t copyBlockRowCount = copyExtent.height / textureFormat.blockHeight;
|
||||
if (copyBlockRowCount > 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].bytesPerImage = rowPitch * (copyBlockRowCount - 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);
|
||||
|
||||
ASSERT(copyExtent.height - textureFormat.blockHeight < virtualSizeAtLevel.height);
|
||||
copy.copies[copy.count].copyExtent = MTLSizeMake(
|
||||
clampedCopyExtentWidth, copyExtent.height - textureFormat.blockHeight, 1);
|
||||
|
||||
++copy.count;
|
||||
|
||||
// Update offset to copy to the last row.
|
||||
currentOffset += (copyExtent.height - 1) * rowPitch;
|
||||
currentOffset += (copyBlockRowCount - 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;
|
||||
uint32_t lastRowDataSize =
|
||||
(copyExtent.width / textureFormat.blockWidth) * textureFormat.blockByteSize;
|
||||
uint32_t lastRowCopyExtentHeight =
|
||||
textureFormat.blockHeight + clampedCopyExtentHeight - copyExtent.height;
|
||||
ASSERT(lastRowCopyExtentHeight <= textureFormat.blockHeight);
|
||||
|
||||
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.copies[copy.count].textureOrigin =
|
||||
MTLOriginMake(origin.x, origin.y + copyExtent.height - textureFormat.blockHeight,
|
||||
origin.z + copyExtent.depth - 1);
|
||||
copy.copies[copy.count].copyExtent =
|
||||
MTLSizeMake(clampedCopyExtentWidth, lastRowCopyExtentHeight, 1);
|
||||
++copy.count;
|
||||
|
||||
return copy;
|
||||
|
@ -446,9 +472,10 @@ namespace dawn_native { namespace metal {
|
|||
Buffer* buffer = ToBackend(src.buffer.Get());
|
||||
Texture* texture = ToBackend(dst.texture.Get());
|
||||
|
||||
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(dst.mipLevel);
|
||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||
dst.origin, copySize, texture->GetFormat(), buffer->GetSize(), src.offset,
|
||||
src.rowPitch, src.imageHeight);
|
||||
dst.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
||||
buffer->GetSize(), src.offset, src.rowPitch, src.imageHeight);
|
||||
|
||||
encoders.EnsureBlit(commandBuffer);
|
||||
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||
|
@ -473,9 +500,10 @@ namespace dawn_native { namespace metal {
|
|||
Texture* texture = ToBackend(src.texture.Get());
|
||||
Buffer* buffer = ToBackend(dst.buffer.Get());
|
||||
|
||||
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(src.mipLevel);
|
||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||
src.origin, copySize, texture->GetFormat(), buffer->GetSize(), dst.offset,
|
||||
dst.rowPitch, dst.imageHeight);
|
||||
src.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
||||
buffer->GetSize(), dst.offset, dst.rowPitch, dst.imageHeight);
|
||||
|
||||
encoders.EnsureBlit(commandBuffer);
|
||||
for (uint32_t i = 0; i < splittedCopies.count; ++i) {
|
||||
|
|
|
@ -215,6 +215,12 @@ namespace dawn_native { namespace metal {
|
|||
case dawn::TextureFormat::Depth24PlusStencil8:
|
||||
return MTLPixelFormatDepth32Float_Stencil8;
|
||||
|
||||
// TODO(jiawei.shao@intel.com): support all BC formats
|
||||
case dawn::TextureFormat::BC5RGSnorm:
|
||||
return MTLPixelFormatBC5_RGSnorm;
|
||||
case dawn::TextureFormat::BC5RGUnorm:
|
||||
return MTLPixelFormatBC5_RGUnorm;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ struct CopyConfig {
|
|||
uint32_t baseArrayLayer = 0;
|
||||
uint32_t bufferOffset = 0;
|
||||
uint32_t rowPitchAlignment = kTextureRowPitchAlignment;
|
||||
uint32_t imageHeight = 0;
|
||||
};
|
||||
|
||||
class CompressedTextureBCFormatTest : public DawnTest {
|
||||
|
@ -102,8 +103,9 @@ class CompressedTextureBCFormatTest : public DawnTest {
|
|||
// Copy texture data from a staging buffer to the destination texture.
|
||||
dawn::Buffer stagingBuffer = utils::CreateBufferFromData(
|
||||
device, uploadData.data(), uploadBufferSize, dawn::BufferUsageBit::CopySrc);
|
||||
dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
|
||||
stagingBuffer, copyConfig.bufferOffset, copyConfig.rowPitchAlignment, 0);
|
||||
dawn::BufferCopyView bufferCopyView =
|
||||
utils::CreateBufferCopyView(stagingBuffer, copyConfig.bufferOffset,
|
||||
copyConfig.rowPitchAlignment, copyConfig.imageHeight);
|
||||
dawn::TextureCopyView textureCopyView =
|
||||
utils::CreateTextureCopyView(bcCompressedTexture, copyConfig.baseMipmapLevel,
|
||||
copyConfig.baseArrayLayer, copyConfig.copyOrigin3D);
|
||||
|
@ -654,5 +656,56 @@ TEST_P(CompressedTextureBCFormatTest, RowPitchEqualToSlicePitch) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(jiawei.shao@intel.com): support BC formats on Metal and OpenGL backend
|
||||
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, D3D12Backend, VulkanBackend);
|
||||
// Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage *
|
||||
// copyExtent.depth) on Metal backends. As copyExtent.depth can only be 1 for BC formats, on Metal
|
||||
// backend we will use two copies to implement such copy.
|
||||
TEST_P(CompressedTextureBCFormatTest, LargeImageHeight) {
|
||||
CopyConfig config;
|
||||
config.textureWidthLevel0 = 8;
|
||||
config.textureHeightLevel0 = 8;
|
||||
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
|
||||
|
||||
config.imageHeight = config.textureHeightLevel0 * 2;
|
||||
|
||||
for (dawn::TextureFormat format : kBCFormats) {
|
||||
config.format = format;
|
||||
TestCopyRegionIntoBCFormatTextures(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage *
|
||||
// copyExtent.depth) and copyExtent needs to be clamped.
|
||||
TEST_P(CompressedTextureBCFormatTest, LargeImageHeightAndClampedCopyExtent) {
|
||||
CopyConfig config;
|
||||
config.textureHeightLevel0 = 56;
|
||||
config.textureWidthLevel0 = 56;
|
||||
config.rowPitchAlignment = kTextureRowPitchAlignment;
|
||||
|
||||
constexpr uint32_t kMipmapLevelCount = 3;
|
||||
config.mipmapLevelCount = kMipmapLevelCount;
|
||||
config.baseMipmapLevel = kMipmapLevelCount - 1;
|
||||
|
||||
// The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
|
||||
// required in the copies.
|
||||
const uint32_t kActualWidthAtLevel = config.textureWidthLevel0 >> config.baseMipmapLevel;
|
||||
const uint32_t kActualHeightAtLevel = config.textureHeightLevel0 >> config.baseMipmapLevel;
|
||||
ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0);
|
||||
ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0);
|
||||
|
||||
const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) /
|
||||
kBCBlockWidthInTexels * kBCBlockWidthInTexels;
|
||||
const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) /
|
||||
kBCBlockHeightInTexels * kBCBlockHeightInTexels;
|
||||
|
||||
config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1};
|
||||
|
||||
config.imageHeight = kCopyHeightAtLevel * 2;
|
||||
|
||||
for (dawn::TextureFormat format : kBCFormats) {
|
||||
config.format = format;
|
||||
TestCopyRegionIntoBCFormatTextures(config);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend
|
||||
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, D3D12Backend, MetalBackend, VulkanBackend);
|
||||
|
|
Loading…
Reference in New Issue