Add texture creation validation rules for 3D texture

In order to support 3D texture, new validation rules are added:
- to say that multisample 3D texture is not supported.
- to distinguish 3D texture from 2D array texture via texture type,
  and its impact on max values of size.depth, mipmap levels,
  array layer count, etc.
- to say that 3D compressed texture is not supported.

This change also adds validation tests for zero-sized textures,
in addition to validation tests for the validation rules above.

Bug: dawn:558
Change-Id: Ib7d398fdab49a702eaa798f6353639d3721747e6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/34922
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Yunchao He 2021-01-28 20:37:01 +00:00 committed by Commit Bot service account
parent 7bfb3ed2c6
commit 54d0d43e58
2 changed files with 193 additions and 43 deletions

View File

@ -48,8 +48,10 @@ namespace dawn_native {
case wgpu::TextureViewDimension::CubeArray:
return textureDimension == wgpu::TextureDimension::e2D;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::e3D:
return textureDimension == wgpu::TextureDimension::e3D;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::Undefined:
UNREACHABLE();
}
@ -106,11 +108,12 @@ namespace dawn_native {
"The mipmap level count of a multisampled texture must be 1.");
}
// Multisampled 1D and 3D textures are not supported in D3D12/Metal/Vulkan.
// Multisampled 2D array texture is not supported because on Metal it requires the
// version of macOS be greater than 10.14.
if (descriptor->size.depth > 1) {
return DAWN_VALIDATION_ERROR(
"Multisampled textures with depth > 1 are not supported.");
if (descriptor->dimension != wgpu::TextureDimension::e2D ||
descriptor->size.depth > 1) {
return DAWN_VALIDATION_ERROR("Multisampled texture must be 2D with depth=1");
}
if (format->isCompressed) {
@ -154,16 +157,40 @@ namespace dawn_native {
}
MaybeError ValidateTextureSize(const TextureDescriptor* descriptor, const Format* format) {
ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0);
if (descriptor->size.width > kMaxTextureDimension2D ||
descriptor->size.height > kMaxTextureDimension2D) {
return DAWN_VALIDATION_ERROR("Texture max size exceeded");
ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0 &&
descriptor->size.depth != 0);
Extent3D maxExtent;
switch (descriptor->dimension) {
case wgpu::TextureDimension::e2D:
maxExtent = {kMaxTextureDimension2D, kMaxTextureDimension2D,
kMaxTextureArrayLayers};
break;
case wgpu::TextureDimension::e3D:
maxExtent = {kMaxTextureDimension3D, kMaxTextureDimension3D,
kMaxTextureDimension3D};
break;
case wgpu::TextureDimension::e1D:
default:
UNREACHABLE();
}
if (descriptor->size.width > maxExtent.width ||
descriptor->size.height > maxExtent.height ||
descriptor->size.depth > maxExtent.depth) {
return DAWN_VALIDATION_ERROR("Texture dimension (width, height or depth) exceeded");
}
if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 <
descriptor->mipLevelCount) {
uint32_t maxMippedDimension = descriptor->size.width;
if (descriptor->dimension != wgpu::TextureDimension::e1D) {
maxMippedDimension = std::max(maxMippedDimension, descriptor->size.height);
}
if (descriptor->dimension == wgpu::TextureDimension::e3D) {
maxMippedDimension = std::max(maxMippedDimension, descriptor->size.depth);
}
if (Log2(maxMippedDimension) + 1 < descriptor->mipLevelCount) {
return DAWN_VALIDATION_ERROR("Texture has too many mip levels");
}
ASSERT(descriptor->mipLevelCount <= kMaxTexture2DMipLevels);
if (format->isCompressed) {
const TexelBlockInfo& blockInfo =
@ -175,14 +202,6 @@ namespace dawn_native {
}
}
if (descriptor->dimension == wgpu::TextureDimension::e2D &&
descriptor->size.depth > kMaxTextureArrayLayers) {
return DAWN_VALIDATION_ERROR("Texture 2D array layer count exceeded");
}
if (descriptor->mipLevelCount > kMaxTexture2DMipLevels) {
return DAWN_VALIDATION_ERROR("Max texture 2D mip level exceeded");
}
return {};
}
@ -221,6 +240,9 @@ namespace dawn_native {
if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
if (descriptor->dimension == wgpu::TextureDimension::e1D) {
return DAWN_VALIDATION_ERROR("1D textures aren't supported (yet).");
}
const Format* format;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format));
@ -235,8 +257,15 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Cannot create an empty texture");
}
if (descriptor->dimension != wgpu::TextureDimension::e2D) {
return DAWN_VALIDATION_ERROR("Texture dimension must be 2D (for now)");
// Disallow 1D and 3D textures as unsafe until they are fully implemented.
if (descriptor->dimension != wgpu::TextureDimension::e2D &&
device->IsToggleEnabled(Toggle::DisallowUnsafeAPIs)) {
return DAWN_VALIDATION_ERROR(
"1D and 3D textures are disallowed because they are not fully implemented ");
}
if (descriptor->dimension != wgpu::TextureDimension::e2D && format->isCompressed) {
return DAWN_VALIDATION_ERROR("Compressed texture must be 2D");
}
DAWN_TRY(ValidateTextureSize(descriptor, format));

View File

@ -92,7 +92,20 @@ namespace {
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Currently we do not support multisampled 2D array textures.
// It is an error to create a multisampled 1D or 3D texture.
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.sampleCount = 4;
descriptor.size.height = 1;
descriptor.dimension = wgpu::TextureDimension::e1D;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.dimension = wgpu::TextureDimension::e3D;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Currently we do not support multisampled 2D textures with depth>1.
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.sampleCount = 4;
@ -188,7 +201,7 @@ namespace {
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Non square mip map halves the resolution until a 1x1 dimension.
// Non square mip map halves the resolution until a 1x1 dimension
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = 32;
@ -199,6 +212,36 @@ namespace {
device.CreateTexture(&descriptor);
}
// Non square mip map for a 3D textures
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = 32;
descriptor.size.height = 8;
descriptor.size.depth = 64;
descriptor.dimension = wgpu::TextureDimension::e3D;
// Non square mip map halves width, height and depth until a 1x1x1 dimension for a 3D
// texture. So there are 7 mipmaps at most: 32 * 8 * 64, 16 * 4 * 32, 8 * 2 * 16,
// 4 * 1 * 8, 2 * 1 * 4, 1 * 1 * 2, 1 * 1 * 1.
descriptor.mipLevelCount = 7;
device.CreateTexture(&descriptor);
}
// Non square mip map for 2D textures with depth > 1
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = 32;
descriptor.size.height = 8;
descriptor.size.depth = 64;
// Non square mip map halves width and height until a 1x1 dimension for a 2D texture,
// even its depth > 1. So there are 6 mipmaps at most: 32 * 8, 16 * 4, 8 * 2, 4 * 1, 2 *
// 1, 1 * 1.
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.mipLevelCount = 7;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.mipLevelCount = 6;
device.CreateTexture(&descriptor);
}
// Mip level exceeding kMaxTexture2DMipLevels not allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
@ -209,63 +252,132 @@ namespace {
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
}
// Test the validation of array layer count
TEST_F(TextureValidationTest, ArrayLayerCount) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
// Array layer count exceeding kMaxTextureArrayLayers is not allowed
// Array layer count exceeding kMaxTextureArrayLayers is not allowed for 2D texture
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depth = kMaxTextureArrayLayers + 1u;
descriptor.size.depth = kMaxTextureArrayLayers + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Array layer count less than kMaxTextureArrayLayers is allowed;
// Array layer count less than kMaxTextureArrayLayers is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depth = kMaxTextureArrayLayers >> 1;
device.CreateTexture(&descriptor);
}
// Array layer count equal to kMaxTextureArrayLayers is allowed;
// Array layer count equal to kMaxTextureArrayLayers is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depth = kMaxTextureArrayLayers;
device.CreateTexture(&descriptor);
}
}
// Test the validation of texture size
TEST_F(TextureValidationTest, TextureSize) {
// Test the validation of 2D texture size
TEST_F(TextureValidationTest, 2DTextureSize) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
// Texture size exceeding kMaxTextureDimension2D is not allowed
// Out-of-bound texture dimension is not allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D + 1u;
descriptor.size.height = kMaxTextureDimension2D + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size.width = 1;
descriptor.size.height = kMaxTextureDimension2D + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Texture size less than kMaxTextureDimension2D is allowed
// Zero-sized texture is not allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size = {0, 1, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 0, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 1, 0};
// 2D texture with depth=0 is not allowed
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Texture size less than max dimension is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D >> 1;
descriptor.size.height = kMaxTextureDimension2D >> 1;
device.CreateTexture(&descriptor);
}
// Texture equal to kMaxTextureDimension2D is allowed
// Texture size equal to max dimension is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D;
descriptor.size.height = kMaxTextureDimension2D;
descriptor.dimension = wgpu::TextureDimension::e2D;
device.CreateTexture(&descriptor);
}
}
// Test the validation of 3D texture size
TEST_F(TextureValidationTest, 3DTextureSize) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
// Out-of-bound texture dimension is not allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D + 1u, 1, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, kMaxTextureDimension3D + 1u, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 1, kMaxTextureDimension3D + 1u};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Zero-sized texture is not allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {0, 1, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 0, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 1, 0};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
// Texture size less than max dimension is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D >> 1, kMaxTextureDimension3D >> 1,
kMaxTextureDimension3D >> 1};
device.CreateTexture(&descriptor);
}
// Texture size equal to max dimension is allowed
{
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D, kMaxTextureDimension3D,
kMaxTextureDimension3D};
device.CreateTexture(&descriptor);
}
}
@ -409,9 +521,9 @@ namespace {
};
// Test the validation of texture size when creating textures in compressed texture formats.
// 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.
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 (wgpu::TextureFormat format : utils::kBCFormats) {
{
wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
@ -445,9 +557,9 @@ namespace {
}
// Test the validation of texture usages when creating textures in compressed texture formats.
// Only CopySrc, CopyDst and Sampled are accepted as the texture usage of the textures in BC
// formats.
TEST_F(CompressedTextureFormatsValidationTests, TextureUsage) {
// Test that only CopySrc, CopyDst and Sampled are accepted as the texture usage of the
// textures in BC formats.
wgpu::TextureUsage invalidUsages[] = {
wgpu::TextureUsage::RenderAttachment,
wgpu::TextureUsage::Storage,
@ -464,9 +576,8 @@ namespace {
}
// Test the validation of sample count when creating textures in compressed texture formats.
// It is invalid to specify SampleCount > 1 when we create a texture in BC formats.
TEST_F(CompressedTextureFormatsValidationTests, SampleCount) {
// Test that it is invalid to specify SampleCount > 1 when we create a texture in BC
// formats.
for (wgpu::TextureFormat format : utils::kBCFormats) {
wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
descriptor.format = format;
@ -475,9 +586,8 @@ namespace {
}
}
// Test the validation of creating 2D array textures in compressed texture formats.
// Test that it is allowed to create a 2D texture with depth>1 in BC formats.
TEST_F(CompressedTextureFormatsValidationTests, 2DArrayTexture) {
// Test that it is allowed to create a 2D array texture in BC formats.
for (wgpu::TextureFormat format : utils::kBCFormats) {
wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
descriptor.format = format;
@ -486,4 +596,15 @@ namespace {
}
}
// Test that it is not allowed to create a 3D texture in BC formats.
TEST_F(CompressedTextureFormatsValidationTests, 3DTexture) {
for (wgpu::TextureFormat format : utils::kBCFormats) {
wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
descriptor.format = format;
descriptor.size.depth = 4;
descriptor.dimension = wgpu::TextureDimension::e3D;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
}
} // namespace