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