From c2750abd0c62608f0ebed44aae3716257e8dec41 Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Sat, 1 Jun 2019 02:30:51 +0000 Subject: [PATCH] Add validations on the creation of textures in BC formats This patch adds all the BC compressed texture formats and the checks on the creation of textures in BC formats. If a texture is in BC format, then: 1. The width and height of the texture must be multiple of 4. 2. The usage of the texture can only be Sampled or TransferSrc or TransferDst. 3. The sample count of the texture can only be 1. BUG=dawn:42 TEST=dawn_unittests Change-Id: I0844fb6a1aadbb96d94a61fd969db07c21b6adf5 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7600 Commit-Queue: Kai Ninomiya Reviewed-by: Kai Ninomiya --- dawn.json | 19 ++- src/dawn_native/Texture.cpp | 78 ++++++++++++- src/dawn_native/metal/TextureMTL.mm | 3 + .../validation/TextureValidationTests.cpp | 110 ++++++++++++++++++ 4 files changed, 204 insertions(+), 6 deletions(-) diff --git a/dawn.json b/dawn.json index 6cd052feaf..afa69afc98 100644 --- a/dawn.json +++ b/dawn.json @@ -1010,7 +1010,24 @@ {"value": 4, "name": "r8 g8 uint"}, {"value": 5, "name": "r8 uint"}, {"value": 6, "name": "b8 g8 r8 a8 unorm"}, - {"value": 7, "name": "d32 float s8 uint"} + {"value": 7, "name": "d32 float s8 uint"}, + {"value": 8, "name": "BC1 RGBA unorm"}, + {"value": 9, "name": "BC1 RGBA unorm srgb"}, + {"value": 10, "name": "BC2 RGBA unorm"}, + {"value": 11, "name": "BC2 RGBA unorm srgb"}, + {"value": 12, "name": "BC3 RGBA unorm"}, + {"value": 13, "name": "BC3 RGBA unorm srgb"}, + {"value": 14, "name": "BC4 R unorm"}, + {"value": 15, "name": "BC4 R snorm"}, + {"value": 16, "name": "BC5 RG unorm"}, + {"value": 17, "name": "BC5 RG snorm"}, + {"value": 18, "name": "BC6H RGB ufloat"}, + {"value": 19, "name": "BC6H RGB sfloat"}, + {"value": 20, "name": "BC7 RGBA unorm"}, + {"value": 21, "name": "BC7 RGBA unorm srgb"} + ], + "TODO": [ + "jiawei.shao@intel.com: support BC formats as extension" ] }, "texture usage bit": { diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp index 7baf5dcde5..c2f12ed166 100644 --- a/src/dawn_native/Texture.cpp +++ b/src/dawn_native/Texture.cpp @@ -85,6 +85,40 @@ namespace dawn_native { } } + bool IsBCFormat(dawn::TextureFormat format) { + switch (format) { + case dawn::TextureFormat::BC1RGBAUnorm: + case dawn::TextureFormat::BC1RGBAUnormSrgb: + case dawn::TextureFormat::BC2RGBAUnorm: + case dawn::TextureFormat::BC2RGBAUnormSrgb: + case dawn::TextureFormat::BC3RGBAUnorm: + case dawn::TextureFormat::BC3RGBAUnormSrgb: + case dawn::TextureFormat::BC4RUnorm: + case dawn::TextureFormat::BC4RSnorm: + case dawn::TextureFormat::BC5RGUnorm: + case dawn::TextureFormat::BC5RGSnorm: + case dawn::TextureFormat::BC6HRGBUfloat: + case dawn::TextureFormat::BC6HRGBSfloat: + case dawn::TextureFormat::BC7RGBAUnorm: + case dawn::TextureFormat::BC7RGBAUnormSrgb: + return true; + default: + return false; + } + } + + bool IsCompressedFormat(dawn::TextureFormat format) { + return IsBCFormat(format); + } + + bool Is4x4CompressedFormat(dawn::TextureFormat format) { + return IsBCFormat(format); + } + + bool IsWritableFormat(dawn::TextureFormat format) { + return !IsBCFormat(format); + } + // TODO(jiawei.shao@intel.com): support more sample count. MaybeError ValidateSampleCount(const TextureDescriptor* descriptor) { if (!IsValidSampleCount(descriptor->sampleCount)) { @@ -102,6 +136,11 @@ namespace dawn_native { if (descriptor->arrayLayerCount > 1) { return DAWN_VALIDATION_ERROR("Multisampled 2D array texture is not supported."); } + + if (IsCompressedFormat(descriptor->format)) { + return DAWN_VALIDATION_ERROR( + "The sample counts of the textures in BC formats must be 1."); + } } return {}; @@ -157,14 +196,46 @@ namespace dawn_native { return descriptor; } + MaybeError ValidateTextureSize(const TextureDescriptor* descriptor) { + ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0); + + if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 < + descriptor->mipLevelCount) { + return DAWN_VALIDATION_ERROR("Texture has too many mip levels"); + } + + if (Is4x4CompressedFormat(descriptor->format)) { + if (descriptor->size.width % 4 != 0 || descriptor->size.height % 4 != 0) { + return DAWN_VALIDATION_ERROR( + "The size of the texture is incompatible with the texture format"); + } + } + + return {}; + } } // anonymous namespace + MaybeError ValidateTextureUsageBit(const TextureDescriptor* descriptor) { + DAWN_TRY(ValidateTextureUsageBit(descriptor->usage)); + if (!IsWritableFormat(descriptor->format)) { + constexpr dawn::TextureUsageBit kValidUsage = dawn::TextureUsageBit::Sampled | + dawn::TextureUsageBit::TransferSrc | + dawn::TextureUsageBit::TransferDst; + if (descriptor->usage & (~kValidUsage)) { + return DAWN_VALIDATION_ERROR( + "Texture format is incompatible with the texture usage"); + } + } + + return {}; + } + MaybeError ValidateTextureDescriptor(DeviceBase*, const TextureDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(ValidateTextureUsageBit(descriptor->usage)); + DAWN_TRY(ValidateTextureUsageBit(descriptor)); DAWN_TRY(ValidateTextureDimension(descriptor->dimension)); DAWN_TRY(ValidateTextureFormat(descriptor->format)); DAWN_TRY(ValidateSampleCount(descriptor)); @@ -176,10 +247,7 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Cannot create an empty texture"); } - if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 < - descriptor->mipLevelCount) { - return DAWN_VALIDATION_ERROR("Texture has too many mip levels"); - } + DAWN_TRY(ValidateTextureSize(descriptor)); return {}; } diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm index 7de816489b..141ba20e17 100644 --- a/src/dawn_native/metal/TextureMTL.mm +++ b/src/dawn_native/metal/TextureMTL.mm @@ -137,6 +137,9 @@ namespace dawn_native { namespace metal { return MTLPixelFormatBGRA8Unorm; case dawn::TextureFormat::D32FloatS8Uint: return MTLPixelFormatDepth32Float_Stencil8; + default: + UNREACHABLE(); + return MTLPixelFormatRGBA8Unorm; } } diff --git a/src/tests/unittests/validation/TextureValidationTests.cpp b/src/tests/unittests/validation/TextureValidationTests.cpp index 20c2c41bfe..206914bf5a 100644 --- a/src/tests/unittests/validation/TextureValidationTests.cpp +++ b/src/tests/unittests/validation/TextureValidationTests.cpp @@ -234,4 +234,114 @@ TEST_F(TextureValidationTest, EncodeDestroySubmit) { // Submit should fail due to destroyed texture ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } + +// TODO(jiawei.shao@intel.com): use compressed texture formats as extensions. +// TODO(jiawei.shao@intel.com): add tests to verify we cannot create 1D or 3D textures with +// compressed texture formats. +class CompressedTextureFormatsValidationTests : public TextureValidationTest { + protected: + dawn::TextureDescriptor CreateDefaultTextureDescriptor() { + dawn::TextureDescriptor descriptor = + TextureValidationTest::CreateDefaultTextureDescriptor(); + descriptor.usage = dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst | + dawn::TextureUsageBit::Sampled; + return descriptor; + } + + const std::array 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}; +}; + +// Test the validation of texture size when creating textures in compressed texture formats. +TEST_F(CompressedTextureFormatsValidationTests, TextureSize) { + // Test that it is invalid to use a number that is not a multiple of 4 (the compressed block + // width and height of all BC formats) as the width or height of textures in BC formats. + for (dawn::TextureFormat format : kBCFormats) { + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + ASSERT_TRUE(descriptor.size.width % 4 == 0 && descriptor.size.height % 4 == 0); + device.CreateTexture(&descriptor); + } + + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.width = 31; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.height = 31; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.width = 12; + descriptor.size.height = 32; + device.CreateTexture(&descriptor); + } + } +} + +// Test the validation of texture usages when creating textures in compressed texture formats. +TEST_F(CompressedTextureFormatsValidationTests, TextureUsage) { + // Test that only TransferSrc, TransferDst and Sampled are accepted as the texture usage of the + // textures in BC formats. + for (dawn::TextureFormat format : kBCFormats) { + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = dawn::TextureUsageBit::OutputAttachment; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = dawn::TextureUsageBit::Storage; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = dawn::TextureUsageBit::Present; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + } +} + +// Test the validation of sample count when creating textures in compressed texture formats. +TEST_F(CompressedTextureFormatsValidationTests, SampleCount) { + // Test that it is invalid to specify SampleCount > 1 when we create a texture in BC formats. + for (dawn::TextureFormat format : kBCFormats) { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.sampleCount = 4; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } +} + +// Test the validation of creating 2D array textures in compressed texture formats. +TEST_F(CompressedTextureFormatsValidationTests, 2DArrayTexture) { + // Test that it is allowed to create a 2D array texture in BC formats. + for (dawn::TextureFormat format : kBCFormats) { + dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.arrayLayerCount = 6; + device.CreateTexture(&descriptor); + } +} + } // namespace