Add validations to the texture copies with BC formats

This patch adds the validation on the texture copies with BC formats.
1. BufferCopyView.offset in B2T and T2B copies must be a multiple of the
   compressed texel block size in bytes.
2. BufferCopyView.rowPitch in B2T and T2B copies refers to the number of
   bytes from the start of one row of blocks to the start of the next
   row of blocks.
3. BufferCopyView.imageHeight must be a multiple of the compressed texel
   block height (4 for BC formats).
4. All members in TextureCopyView.origin must be a multiple of the
   corresponding dimensions of the compressed texel block (4x4x1 for BC
   formats).
5. All the mumbers in 'copySize' must be a multiple of the corresponding
   dimensions of the compressed texel block (4x4x1 for BC formats)
   because D3D12 requires the width and height of a texture in BC
   formats must be multiples of 4.
6. Compute the texture size in non-zero mipmap levels with paddings for
   textures in BC formats when necessary.

BUG=dawn:42
TEST=dawn_unittests

Change-Id: Iac8d6c93ab8b37bb46becffd4175339722ab6016
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7860
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Jiawei Shao 2019-06-18 01:06:09 +00:00 committed by Commit Bot service account
parent 8f4046b0b6
commit 872c1d7fe9
9 changed files with 467 additions and 30 deletions

View File

@ -46,10 +46,22 @@ namespace dawn_native {
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows. // overflows.
uint64_t level = textureCopy.level; uint64_t level = textureCopy.level;
uint32_t widthAtLevel = texture->GetSize().width >> level;
uint32_t heightAtLevel = texture->GetSize().height >> level;
// Compressed Textures will have paddings if their width or height is not a multiple of
// 4 at non-zero mipmap levels.
if (Is4x4CompressedFormat(texture->GetFormat())) {
// TODO(jiawei.shao@intel.com): check if there are any overflows.
widthAtLevel = (widthAtLevel + 3) / 4 * 4;
heightAtLevel = (heightAtLevel + 3) / 4 * 4;
}
if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) > if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) >
(static_cast<uint64_t>(texture->GetSize().width) >> level) || static_cast<uint64_t>(widthAtLevel) ||
uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) > uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) >
(static_cast<uint64_t>(texture->GetSize().height) >> level)) { static_cast<uint64_t>(heightAtLevel)) {
return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture"); return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture");
} }
@ -96,10 +108,10 @@ namespace dawn_native {
} }
MaybeError ValidateTexelBufferOffset(TextureBase* texture, const BufferCopy& bufferCopy) { MaybeError ValidateTexelBufferOffset(TextureBase* texture, const BufferCopy& bufferCopy) {
uint32_t texelSize = uint32_t blockSize = TextureFormatTexelBlockSizeInBytes(texture->GetFormat());
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())); if (bufferCopy.offset % blockSize != 0) {
if (bufferCopy.offset % texelSize != 0) { return DAWN_VALIDATION_ERROR(
return DAWN_VALIDATION_ERROR("Buffer offset must be a multiple of the texel size"); "Buffer offset must be a multiple of the texel or block size");
} }
return {}; return {};
@ -194,18 +206,24 @@ namespace dawn_native {
uint32_t* bufferSize) { uint32_t* bufferSize) {
DAWN_TRY(ValidateImageHeight(imageHeight, copySize.height)); DAWN_TRY(ValidateImageHeight(imageHeight, copySize.height));
uint32_t texelOrBlockSizeInBytes = TextureFormatTexelBlockSizeInBytes(textureFormat);
uint32_t blockWidthInTexels = TextureFormatBlockWidthInTexels(textureFormat);
uint32_t blockHeightInTexels = TextureFormatBlockWidthInTexels(textureFormat);
// TODO(cwallez@chromium.org): check for overflows // TODO(cwallez@chromium.org): check for overflows
uint32_t slicePitch = rowPitch * imageHeight; uint32_t slicePitch = rowPitch * imageHeight / blockWidthInTexels;
uint32_t sliceSize = rowPitch * (copySize.height - 1) + uint32_t sliceSize = rowPitch * (copySize.height / blockHeightInTexels - 1) +
copySize.width * TextureFormatPixelSize(textureFormat); (copySize.width / blockWidthInTexels) * texelOrBlockSizeInBytes;
*bufferSize = (slicePitch * (copySize.depth - 1)) + sliceSize; *bufferSize = (slicePitch * (copySize.depth - 1)) + sliceSize;
return {}; return {};
} }
uint32_t ComputeDefaultRowPitch(TextureBase* texture, uint32_t width) { uint32_t ComputeDefaultRowPitch(TextureBase* texture, uint32_t width) {
uint32_t texelSize = TextureFormatPixelSize(texture->GetFormat()); const dawn::TextureFormat format = texture->GetFormat();
return texelSize * width; uint32_t texelOrBlockSizeInBytes = TextureFormatTexelBlockSizeInBytes(format);
uint32_t blockWidthInTexels = TextureFormatBlockWidthInTexels(format);
return width / blockWidthInTexels * texelOrBlockSizeInBytes;
} }
MaybeError ValidateRowPitch(dawn::TextureFormat format, MaybeError ValidateRowPitch(dawn::TextureFormat format,
@ -215,8 +233,9 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Row pitch must be a multiple of 256"); return DAWN_VALIDATION_ERROR("Row pitch must be a multiple of 256");
} }
uint32_t texelSize = TextureFormatPixelSize(format); uint32_t texelOrBlockSizeInBytes = TextureFormatTexelBlockSizeInBytes(format);
if (rowPitch < copySize.width * texelSize) { uint32_t blockWidthInTexels = TextureFormatBlockWidthInTexels(format);
if (rowPitch < copySize.width / blockWidthInTexels * texelOrBlockSizeInBytes) {
return DAWN_VALIDATION_ERROR( return DAWN_VALIDATION_ERROR(
"Row pitch must not be less than the number of bytes per row"); "Row pitch must not be less than the number of bytes per row");
} }
@ -224,6 +243,43 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateImageHeight(dawn::TextureFormat format, uint32_t imageHeight) {
if (imageHeight % TextureFormatBlockHeightInTexels(format) != 0) {
return DAWN_VALIDATION_ERROR(
"Image height must be a multiple of compressed texture format block width");
}
return {};
}
MaybeError ValidateImageOrigin(dawn::TextureFormat format, const Origin3D& offset) {
if (offset.x % TextureFormatBlockWidthInTexels(format) != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.x must be a multiple of compressed texture format block width");
}
if (offset.y % TextureFormatBlockHeightInTexels(format) != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.y must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateImageCopySize(dawn::TextureFormat format, const Extent3D& extent) {
if (extent.width % TextureFormatBlockWidthInTexels(format) != 0) {
return DAWN_VALIDATION_ERROR(
"Extent.width must be a multiple of compressed texture format block width");
}
if (extent.height % TextureFormatBlockHeightInTexels(format) != 0) {
return DAWN_VALIDATION_ERROR(
"Extent.height must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateCanUseAs(BufferBase* buffer, dawn::BufferUsageBit usage) { MaybeError ValidateCanUseAs(BufferBase* buffer, dawn::BufferUsageBit usage) {
ASSERT(HasZeroOrOneBits(usage)); ASSERT(HasZeroOrOneBits(usage));
if (!(buffer->GetUsage() & usage)) { if (!(buffer->GetUsage() & usage)) {
@ -904,6 +960,13 @@ namespace dawn_native {
DAWN_TRY( DAWN_TRY(
ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get())); ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get()));
DAWN_TRY(ValidateImageHeight(copy->destination.texture->GetFormat(),
copy->source.imageHeight));
DAWN_TRY(ValidateImageOrigin(copy->destination.texture->GetFormat(),
copy->destination.origin));
DAWN_TRY(ValidateImageCopySize(copy->destination.texture->GetFormat(),
copy->copySize));
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateRowPitch(copy->destination.texture->GetFormat(), DAWN_TRY(ValidateRowPitch(copy->destination.texture->GetFormat(),
copy->copySize, copy->source.rowPitch)); copy->copySize, copy->source.rowPitch));
@ -931,6 +994,13 @@ namespace dawn_native {
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get())); DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get()));
DAWN_TRY(ValidateImageHeight(copy->source.texture->GetFormat(),
copy->destination.imageHeight));
DAWN_TRY(ValidateImageOrigin(copy->source.texture->GetFormat(),
copy->source.origin));
DAWN_TRY(
ValidateImageCopySize(copy->source.texture->GetFormat(), copy->copySize));
uint32_t bufferCopySize = 0; uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateRowPitch(copy->source.texture->GetFormat(), copy->copySize, DAWN_TRY(ValidateRowPitch(copy->source.texture->GetFormat(), copy->copySize,
copy->destination.rowPitch)); copy->destination.rowPitch));
@ -960,6 +1030,15 @@ namespace dawn_native {
DAWN_TRY(ValidateTextureToTextureCopyRestrictions( DAWN_TRY(ValidateTextureToTextureCopyRestrictions(
copy->source, copy->destination, copy->copySize)); copy->source, copy->destination, copy->copySize));
DAWN_TRY(ValidateImageOrigin(copy->source.texture->GetFormat(),
copy->source.origin));
DAWN_TRY(
ValidateImageCopySize(copy->source.texture->GetFormat(), copy->copySize));
DAWN_TRY(ValidateImageOrigin(copy->destination.texture->GetFormat(),
copy->destination.origin));
DAWN_TRY(ValidateImageCopySize(copy->destination.texture->GetFormat(),
copy->copySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(copy->source, copy->copySize)); DAWN_TRY(ValidateCopySizeFitsInTexture(copy->source, copy->copySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(copy->destination, copy->copySize)); DAWN_TRY(ValidateCopySizeFitsInTexture(copy->destination, copy->copySize));

View File

@ -112,10 +112,6 @@ namespace dawn_native {
return IsBCFormat(format); return IsBCFormat(format);
} }
bool Is4x4CompressedFormat(dawn::TextureFormat format) {
return IsBCFormat(format);
}
bool IsWritableFormat(dawn::TextureFormat format) { bool IsWritableFormat(dawn::TextureFormat format) {
return !IsBCFormat(format); return !IsBCFormat(format);
} }
@ -216,6 +212,28 @@ namespace dawn_native {
} }
} // anonymous namespace } // anonymous namespace
bool Is4x4CompressedFormat(dawn::TextureFormat format) {
return IsBCFormat(format);
}
// We treat non-compressed texture formats as the block texture formats in 1x1 blocks.
uint32_t TextureFormatBlockWidthInTexels(dawn::TextureFormat format) {
if (Is4x4CompressedFormat(format)) {
return 4;
}
return 1;
}
// We treat non-compressed texture formats as the block texture formats in 1x1 blocks.
uint32_t TextureFormatBlockHeightInTexels(dawn::TextureFormat format) {
if (Is4x4CompressedFormat(format)) {
return 4;
}
return 1;
}
MaybeError ValidateTextureUsageBit(const TextureDescriptor* descriptor) { MaybeError ValidateTextureUsageBit(const TextureDescriptor* descriptor) {
DAWN_TRY(ValidateTextureUsageBit(descriptor->usage)); DAWN_TRY(ValidateTextureUsageBit(descriptor->usage));
if (!IsWritableFormat(descriptor->format)) { if (!IsWritableFormat(descriptor->format)) {
@ -289,8 +307,10 @@ namespace dawn_native {
return {}; return {};
} }
uint32_t TextureFormatPixelSize(dawn::TextureFormat format) { // We treat non-compressed texture formats as the block texture formats in 1x1 blocks.
uint32_t TextureFormatTexelBlockSizeInBytes(dawn::TextureFormat format) {
switch (format) { switch (format) {
// Non-compressed texture formats
case dawn::TextureFormat::R8Unorm: case dawn::TextureFormat::R8Unorm:
case dawn::TextureFormat::R8Uint: case dawn::TextureFormat::R8Uint:
return 1; return 1;
@ -303,6 +323,25 @@ namespace dawn_native {
return 4; return 4;
case dawn::TextureFormat::D32FloatS8Uint: case dawn::TextureFormat::D32FloatS8Uint:
return 8; return 8;
// BC formats
case dawn::TextureFormat::BC1RGBAUnorm:
case dawn::TextureFormat::BC1RGBAUnormSrgb:
case dawn::TextureFormat::BC4RSnorm:
case dawn::TextureFormat::BC4RUnorm:
return 8;
case dawn::TextureFormat::BC2RGBAUnorm:
case dawn::TextureFormat::BC2RGBAUnormSrgb:
case dawn::TextureFormat::BC3RGBAUnorm:
case dawn::TextureFormat::BC3RGBAUnormSrgb:
case dawn::TextureFormat::BC5RGSnorm:
case dawn::TextureFormat::BC5RGUnorm:
case dawn::TextureFormat::BC6HRGBSfloat:
case dawn::TextureFormat::BC6HRGBUfloat:
case dawn::TextureFormat::BC7RGBAUnorm:
case dawn::TextureFormat::BC7RGBAUnormSrgb:
return 16;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@ -418,6 +457,8 @@ namespace dawn_native {
ASSERT(!IsError()); ASSERT(!IsError());
return mDimension; return mDimension;
} }
// TODO(jiawei.shao@intel.com): return more information about texture format
dawn::TextureFormat TextureBase::GetFormat() const { dawn::TextureFormat TextureBase::GetFormat() const {
ASSERT(!IsError()); ASSERT(!IsError());
return mFormat; return mFormat;

View File

@ -29,7 +29,7 @@ namespace dawn_native {
const TextureBase* texture, const TextureBase* texture,
const TextureViewDescriptor* descriptor); const TextureViewDescriptor* descriptor);
uint32_t TextureFormatPixelSize(dawn::TextureFormat format); uint32_t TextureFormatTexelBlockSizeInBytes(dawn::TextureFormat format);
bool TextureFormatHasDepth(dawn::TextureFormat format); bool TextureFormatHasDepth(dawn::TextureFormat format);
bool TextureFormatHasStencil(dawn::TextureFormat format); bool TextureFormatHasStencil(dawn::TextureFormat format);
bool TextureFormatHasDepthOrStencil(dawn::TextureFormat format); bool TextureFormatHasDepthOrStencil(dawn::TextureFormat format);
@ -37,6 +37,10 @@ namespace dawn_native {
bool IsDepthStencilRenderableTextureFormat(dawn::TextureFormat format); bool IsDepthStencilRenderableTextureFormat(dawn::TextureFormat format);
bool IsValidSampleCount(uint32_t sampleCount); bool IsValidSampleCount(uint32_t sampleCount);
bool Is4x4CompressedFormat(dawn::TextureFormat format);
uint32_t TextureFormatBlockWidthInTexels(dawn::TextureFormat format);
uint32_t TextureFormatBlockHeightInTexels(dawn::TextureFormat format);
static constexpr dawn::TextureUsageBit kReadOnlyTextureUsages = static constexpr dawn::TextureUsageBit kReadOnlyTextureUsages =
dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::Sampled |
dawn::TextureUsageBit::Present; dawn::TextureUsageBit::Present;

View File

@ -505,7 +505,8 @@ namespace dawn_native { namespace d3d12 {
auto copySplit = ComputeTextureCopySplit( auto copySplit = ComputeTextureCopySplit(
copy->destination.origin, copy->copySize, copy->destination.origin, copy->copySize,
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())), static_cast<uint32_t>(
TextureFormatTexelBlockSizeInBytes(texture->GetFormat())),
copy->source.offset, copy->source.rowPitch, copy->source.imageHeight); copy->source.offset, copy->source.rowPitch, copy->source.imageHeight);
D3D12_TEXTURE_COPY_LOCATION textureLocation = D3D12_TEXTURE_COPY_LOCATION textureLocation =
@ -549,7 +550,8 @@ namespace dawn_native { namespace d3d12 {
auto copySplit = ComputeTextureCopySplit( auto copySplit = ComputeTextureCopySplit(
copy->source.origin, copy->copySize, copy->source.origin, copy->copySize,
static_cast<uint32_t>(TextureFormatPixelSize(texture->GetFormat())), static_cast<uint32_t>(
TextureFormatTexelBlockSizeInBytes(texture->GetFormat())),
copy->destination.offset, copy->destination.rowPitch, copy->destination.offset, copy->destination.rowPitch,
copy->destination.imageHeight); copy->destination.imageHeight);

View File

@ -454,7 +454,7 @@ namespace dawn_native { namespace metal {
// Doing the last row copy with the exact number of bytes in last row. // Doing the last row copy with the exact number of bytes in last row.
// Like copy to a 1D texture to workaround the issue. // Like copy to a 1D texture to workaround the issue.
uint32_t lastRowDataSize = uint32_t lastRowDataSize =
copySize.width * TextureFormatPixelSize(texture->GetFormat()); copySize.width * TextureFormatTexelBlockSizeInBytes(texture->GetFormat());
[encoders.blit [encoders.blit
copyFromBuffer:buffer->GetMTLBuffer() copyFromBuffer:buffer->GetMTLBuffer()
@ -568,7 +568,7 @@ namespace dawn_native { namespace metal {
// Doing the last row copy with the exact number of bytes in last row. // Doing the last row copy with the exact number of bytes in last row.
// Like copy from a 1D texture to workaround the issue. // Like copy from a 1D texture to workaround the issue.
uint32_t lastRowDataSize = uint32_t lastRowDataSize =
copySize.width * TextureFormatPixelSize(texture->GetFormat()); copySize.width * TextureFormatTexelBlockSizeInBytes(texture->GetFormat());
[encoders.blit [encoders.blit
copyFromTexture:texture->GetMTLTexture() copyFromTexture:texture->GetMTLTexture()

View File

@ -389,8 +389,9 @@ namespace dawn_native { namespace opengl {
gl.ActiveTexture(GL_TEXTURE0); gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(target, texture->GetHandle()); gl.BindTexture(target, texture->GetHandle());
gl.PixelStorei(GL_UNPACK_ROW_LENGTH, gl.PixelStorei(
src.rowPitch / TextureFormatPixelSize(texture->GetFormat())); GL_UNPACK_ROW_LENGTH,
src.rowPitch / TextureFormatTexelBlockSizeInBytes(texture->GetFormat()));
gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, src.imageHeight); gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, src.imageHeight);
switch (texture->GetDimension()) { switch (texture->GetDimension()) {
case dawn::TextureDimension::e2D: case dawn::TextureDimension::e2D:
@ -451,8 +452,9 @@ namespace dawn_native { namespace opengl {
} }
gl.BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle()); gl.BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle());
gl.PixelStorei(GL_PACK_ROW_LENGTH, gl.PixelStorei(
dst.rowPitch / TextureFormatPixelSize(texture->GetFormat())); GL_PACK_ROW_LENGTH,
dst.rowPitch / TextureFormatTexelBlockSizeInBytes(texture->GetFormat()));
gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, dst.imageHeight); gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, dst.imageHeight);
ASSERT(copySize.depth == 1 && src.origin.z == 0); ASSERT(copySize.depth == 1 && src.origin.z == 0);
void* offset = reinterpret_cast<void*>(static_cast<uintptr_t>(dst.offset)); void* offset = reinterpret_cast<void*>(static_cast<uintptr_t>(dst.offset));

View File

@ -171,7 +171,7 @@ namespace dawn_native { namespace opengl {
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
static constexpr uint32_t MAX_TEXEL_SIZE = 16; static constexpr uint32_t MAX_TEXEL_SIZE = 16;
ASSERT(TextureFormatPixelSize(GetFormat()) <= MAX_TEXEL_SIZE); ASSERT(TextureFormatTexelBlockSizeInBytes(GetFormat()) <= MAX_TEXEL_SIZE);
GLubyte clearColor[MAX_TEXEL_SIZE]; GLubyte clearColor[MAX_TEXEL_SIZE];
std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255); std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255);

View File

@ -51,7 +51,7 @@ namespace dawn_native { namespace vulkan {
region.bufferOffset = bufferCopy.offset; region.bufferOffset = bufferCopy.offset;
// In Vulkan the row length is in texels while it is in bytes for Dawn // In Vulkan the row length is in texels while it is in bytes for Dawn
region.bufferRowLength = region.bufferRowLength =
bufferCopy.rowPitch / TextureFormatPixelSize(texture->GetFormat()); bufferCopy.rowPitch / TextureFormatTexelBlockSizeInBytes(texture->GetFormat());
region.bufferImageHeight = bufferCopy.imageHeight; region.bufferImageHeight = bufferCopy.imageHeight;
region.imageSubresource.aspectMask = texture->GetVkAspectMask(); region.imageSubresource.aspectMask = texture->GetVkAspectMask();

View File

@ -1080,4 +1080,313 @@ TEST_F(CopyCommandTest_T2T, MultisampledCopies) {
TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, 0, {0, 0, 0}, TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, 0, {0, 0, 0},
destinationMultiSampled4x, 0, 0, {0, 0, 0}, {15, 15, 1}); destinationMultiSampled4x, 0, 0, {0, 0, 0}, {15, 15, 1});
} }
} }
class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
protected:
dawn::Texture Create2DTexture(dawn::TextureFormat format,
uint32_t mipmapLevels = 1,
uint32_t width = kWidth,
uint32_t height = kHeight) {
constexpr dawn::TextureUsageBit kUsage = dawn::TextureUsageBit::TransferDst |
dawn::TextureUsageBit::TransferSrc |
dawn::TextureUsageBit::Sampled;
constexpr uint32_t kArrayLayers = 1;
return CopyCommandTest::Create2DTexture(width, height, mipmapLevels, kArrayLayers, format,
kUsage, 1);
}
static uint32_t CompressedFormatBlockSizeInBytes(dawn::TextureFormat format) {
switch (format) {
case dawn::TextureFormat::BC1RGBAUnorm:
case dawn::TextureFormat::BC1RGBAUnormSrgb:
case dawn::TextureFormat::BC4RSnorm:
case dawn::TextureFormat::BC4RUnorm:
return 8;
case dawn::TextureFormat::BC2RGBAUnorm:
case dawn::TextureFormat::BC2RGBAUnormSrgb:
case dawn::TextureFormat::BC3RGBAUnorm:
case dawn::TextureFormat::BC3RGBAUnormSrgb:
case dawn::TextureFormat::BC5RGSnorm:
case dawn::TextureFormat::BC5RGUnorm:
case dawn::TextureFormat::BC6HRGBSfloat:
case dawn::TextureFormat::BC6HRGBUfloat:
case dawn::TextureFormat::BC7RGBAUnorm:
case dawn::TextureFormat::BC7RGBAUnormSrgb:
return 16;
default:
UNREACHABLE();
return 0;
}
}
void TestBothTBCopies(utils::Expectation expectation,
dawn::Buffer buffer,
uint64_t bufferOffset,
uint32_t bufferRowPitch,
uint32_t imageHeight,
dawn::Texture texture,
uint32_t level,
uint32_t arraySlice,
dawn::Origin3D origin,
dawn::Extent3D extent3D) {
TestB2TCopy(expectation, buffer, bufferOffset, bufferRowPitch, imageHeight, texture, level,
arraySlice, origin, extent3D);
TestT2BCopy(expectation, texture, level, arraySlice, origin, buffer, bufferOffset,
bufferRowPitch, imageHeight, extent3D);
}
void TestBothT2TCopies(utils::Expectation expectation,
dawn::Texture texture1,
uint32_t level1,
uint32_t slice1,
dawn::Origin3D origin1,
dawn::Texture texture2,
uint32_t level2,
uint32_t slice2,
dawn::Origin3D origin2,
dawn::Extent3D extent3D) {
TestT2TCopy(expectation, texture1, level1, slice1, origin1, texture2, level2, slice2,
origin2, extent3D);
TestT2TCopy(expectation, texture2, level2, slice2, origin2, texture1, level1, slice1,
origin1, extent3D);
}
static constexpr uint32_t kWidth = 16;
static constexpr uint32_t kHeight = 16;
const std::array<dawn::TextureFormat, 14> kBCFormats = {
dawn::TextureFormat::BC1RGBAUnorm, dawn::TextureFormat::BC1RGBAUnormSrgb,
dawn::TextureFormat::BC2RGBAUnorm, dawn::TextureFormat::BC2RGBAUnormSrgb,
dawn::TextureFormat::BC3RGBAUnorm, dawn::TextureFormat::BC3RGBAUnormSrgb,
dawn::TextureFormat::BC4RUnorm, dawn::TextureFormat::BC4RSnorm,
dawn::TextureFormat::BC5RGUnorm, dawn::TextureFormat::BC5RGSnorm,
dawn::TextureFormat::BC6HRGBUfloat, dawn::TextureFormat::BC6HRGBSfloat,
dawn::TextureFormat::BC7RGBAUnorm, dawn::TextureFormat::BC7RGBAUnormSrgb};
};
// Tests to verify that bufferOffset must be a multiple of the compressed texture blocks in bytes
// in buffer-to-texture or texture-to-buffer copies with compressed texture formats.
TEST_F(CopyCommandTest_CompressedTextureFormats, BufferOffset) {
dawn::Buffer buffer =
CreateBuffer(512, dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst);
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat);
// Valid usages of BufferOffset in B2T and T2B copies with compressed texture formats.
{
uint32_t validBufferOffset = CompressedFormatBlockSizeInBytes(bcFormat);
TestBothTBCopies(utils::Expectation::Success, buffer, validBufferOffset, 256, 4,
texture, 0, 0, {0, 0, 0}, {4, 4, 1});
}
// Failures on invalid bufferOffset.
{
uint32_t kInvalidBufferOffset = CompressedFormatBlockSizeInBytes(bcFormat) / 2;
TestBothTBCopies(utils::Expectation::Failure, buffer, kInvalidBufferOffset, 256, 4,
texture, 0, 0, {0, 0, 0}, {4, 4, 1});
}
}
}
// Tests to verify that RowPitch must not be smaller than (width / blockWidth) * blockSizeInBytes
// and it is valid to use 0 as RowPitch in buffer-to-texture or texture-to-buffer copies with
// compressed texture formats.
// Note that in Dawn we require RowPitch be a multiple of 256, which ensures RowPitch will always be
// the multiple of compressed texture block width in bytes.
TEST_F(CopyCommandTest_CompressedTextureFormats, RowPitch) {
dawn::Buffer buffer =
CreateBuffer(1024, dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst);
{
constexpr uint32_t kTestWidth = 160;
constexpr uint32_t kTestHeight = 160;
// Failures on the RowPitch that is not large enough.
{
constexpr uint32_t kSmallRowPitch = 256;
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kSmallRowPitch, 4, texture,
0, 0, {0, 0, 0}, {kTestWidth, 4, 1});
}
}
// Test it is not valid to use a RowPitch that is not a multiple of 256.
{
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
uint32_t inValidRowPitch =
kTestWidth / 4 * CompressedFormatBlockSizeInBytes(bcFormat);
ASSERT_NE(0u, inValidRowPitch % 256);
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, inValidRowPitch, 4,
texture, 0, 0, {0, 0, 0}, {kTestWidth, 4, 1});
}
}
// Test the smallest valid RowPitch should work.
{
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
uint32_t smallestValidRowPitch =
Align(kTestWidth / 4 * CompressedFormatBlockSizeInBytes(bcFormat), 256);
TestBothTBCopies(utils::Expectation::Success, buffer, 0, smallestValidRowPitch, 4,
texture, 0, 0, {0, 0, 0}, {kTestWidth, 4, 1});
}
}
}
// Test RowPitch == 0.
{
constexpr uint32_t kZeroRowPitch = 0;
constexpr uint32_t kTestHeight = 128;
{
constexpr uint32_t kValidWidth = 128;
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, 1, kValidWidth, kTestHeight);
TestBothTBCopies(utils::Expectation::Success, buffer, 0, kZeroRowPitch, 4, texture,
0, 0, {0, 0, 0}, {kValidWidth, 4, 1});
}
}
{
constexpr uint32_t kInValidWidth = 16;
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, 1, kInValidWidth, kTestHeight);
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kZeroRowPitch, 4, texture,
0, 0, {0, 0, 0}, {kInValidWidth, 4, 1});
}
}
}
}
// Tests to verify that imageHeight must be a multiple of the compressed texture block height in
// buffer-to-texture or texture-to-buffer copies with compressed texture formats.
TEST_F(CopyCommandTest_CompressedTextureFormats, ImageHeight) {
dawn::Buffer buffer =
CreateBuffer(512, dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst);
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat);
// Valid usages of imageHeight in B2T and T2B copies with compressed texture formats.
{
constexpr uint32_t kValidImageHeight = 8;
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, kValidImageHeight,
texture, 0, 0, {0, 0, 0}, {4, 4, 1});
}
// Failures on invalid imageHeight.
{
constexpr uint32_t kInvalidImageHeight = 3;
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, kInvalidImageHeight,
texture, 0, 0, {0, 0, 0}, {4, 4, 1});
}
}
}
// Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width and
// ImageOffset.y must be a multiple of the compressed texture block height in buffer-to-texture,
// texture-to-buffer or texture-to-texture copies with compressed texture formats.
TEST_F(CopyCommandTest_CompressedTextureFormats, ImageOffset) {
dawn::Buffer buffer =
CreateBuffer(512, dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst);
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat);
dawn::Texture texture2 = Create2DTexture(bcFormat);
constexpr dawn::Origin3D kSmallestValidOrigin3D = {4, 4, 0};
// Valid usages of ImageOffset in B2T, T2B and T2T copies with compressed texture formats.
{
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture, 0, 0,
kSmallestValidOrigin3D, {4, 4, 1});
TestBothT2TCopies(utils::Expectation::Success, texture, 0, 0, {0, 0, 0}, texture2, 0, 0,
kSmallestValidOrigin3D, {4, 4, 1});
}
// Failures on invalid ImageOffset.x.
{
constexpr dawn::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x - 1,
kSmallestValidOrigin3D.y, 0};
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, 0,
kInvalidOrigin3D, {4, 4, 1});
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, 0, kInvalidOrigin3D,
texture2, 0, 0, {0, 0, 0}, {4, 4, 1});
}
// Failures on invalid ImageOffset.y.
{
constexpr dawn::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x,
kSmallestValidOrigin3D.y - 1, 0};
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, 0,
kInvalidOrigin3D, {4, 4, 1});
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, 0, kInvalidOrigin3D,
texture2, 0, 0, {0, 0, 0}, {4, 4, 1});
}
}
}
// Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width and
// ImageExtent.y must be a multiple of the compressed texture block height in buffer-to-texture,
// texture-to-buffer or texture-to-texture copies with compressed texture formats.
TEST_F(CopyCommandTest_CompressedTextureFormats, ImageExtent) {
dawn::Buffer buffer =
CreateBuffer(512, dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst);
constexpr uint32_t kMipmapLevels = 3;
constexpr uint32_t kTestWidth = 60;
constexpr uint32_t kTestHeight = 60;
for (dawn::TextureFormat bcFormat : kBCFormats) {
dawn::Texture texture = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
dawn::Texture texture2 = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
constexpr dawn::Extent3D kSmallestValidExtent3D = {4, 4, 1};
// Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats.
{
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 8, texture, 0, 0,
{0, 0, 0}, kSmallestValidExtent3D);
TestBothT2TCopies(utils::Expectation::Success, texture, 0, 0, {0, 0, 0}, texture2, 0, 0,
{0, 0, 0}, kSmallestValidExtent3D);
}
// Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats
// and non-zero mipmap levels.
{
constexpr uint32_t kTestMipmapLevel = 2;
constexpr dawn::Origin3D kTestOrigin = {
(kTestWidth >> kTestMipmapLevel) - kSmallestValidExtent3D.width + 1,
(kTestHeight >> kTestMipmapLevel) - kSmallestValidExtent3D.height + 1, 0};
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture,
kTestMipmapLevel, 0, kTestOrigin, kSmallestValidExtent3D);
TestBothT2TCopies(utils::Expectation::Success, texture, kTestMipmapLevel, 0,
kTestOrigin, texture2, 0, 0, {0, 0, 0}, kSmallestValidExtent3D);
}
// Failures on invalid ImageExtent.x.
{
constexpr dawn::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width - 1,
kSmallestValidExtent3D.height, 1};
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, 0,
{0, 0, 0}, kInValidExtent3D);
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, 0, {0, 0, 0}, texture2, 0, 0,
{0, 0, 0}, kInValidExtent3D);
}
// Failures on invalid ImageExtent.y.
{
constexpr dawn::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width,
kSmallestValidExtent3D.height - 1, 1};
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, 0,
{0, 0, 0}, kInValidExtent3D);
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, 0, {0, 0, 0}, texture2, 0, 0,
{0, 0, 0}, kInValidExtent3D);
}
}
}