Convert StorageTextureValidationTests to WGSL

Bug: tint:140
Bug: tint:368
Change-Id: I6593e40db5db4058bf6fdf3303ec7e99c0a3df12
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37900
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Ben Clayton 2021-01-21 14:16:09 +00:00 committed by Commit Bot service account
parent 2231fcda56
commit 88f7f580bc
2 changed files with 114 additions and 108 deletions

View File

@ -35,20 +35,21 @@ class StorageTextureValidationTests : public ValidationTest {
})"); })");
} }
static const char* GetGLSLFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) { static const char* GetFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) {
// TODO(bclayton): Support and test texture_storage_1d_array
switch (dimension) { switch (dimension) {
case wgpu::TextureViewDimension::e1D: case wgpu::TextureViewDimension::e1D:
return "image1D"; return "texture_storage_1d";
case wgpu::TextureViewDimension::e2D: case wgpu::TextureViewDimension::e2D:
return "image2D"; return "texture_storage_2d";
case wgpu::TextureViewDimension::e2DArray: case wgpu::TextureViewDimension::e2DArray:
return "image2DArray"; return "texture_storage_2d_array";
case wgpu::TextureViewDimension::Cube:
return "imageCube";
case wgpu::TextureViewDimension::CubeArray:
return "imageCubeArray";
case wgpu::TextureViewDimension::e3D: case wgpu::TextureViewDimension::e3D:
return "image3D"; return "texture_storage_3d";
case wgpu::TextureViewDimension::Cube:
return "texture_storage_cube"; // Note: Doesn't exist in WGSL (yet)
case wgpu::TextureViewDimension::CubeArray:
return "texture_storage_cube_array"; // Note: Doesn't exist in WGSL (yet)
case wgpu::TextureViewDimension::Undefined: case wgpu::TextureViewDimension::Undefined:
default: default:
UNREACHABLE(); UNREACHABLE();
@ -60,26 +61,22 @@ class StorageTextureValidationTests : public ValidationTest {
wgpu::StorageTextureAccess storageTextureBindingType, wgpu::StorageTextureAccess storageTextureBindingType,
wgpu::TextureFormat textureFormat, wgpu::TextureFormat textureFormat,
wgpu::TextureViewDimension textureViewDimension = wgpu::TextureViewDimension::e2D) { wgpu::TextureViewDimension textureViewDimension = wgpu::TextureViewDimension::e2D) {
const char* glslImageFormatQualifier = utils::GetGLSLImageFormatQualifier(textureFormat);
const char* textureComponentTypePrefix =
utils::GetColorTextureComponentGLSLTypePrefix(textureFormat);
return CreateComputeShaderWithStorageTexture( return CreateComputeShaderWithStorageTexture(
storageTextureBindingType, glslImageFormatQualifier, textureComponentTypePrefix, storageTextureBindingType, utils::GetWGSLImageFormatQualifier(textureFormat),
GetGLSLFloatImageTypeDeclaration(textureViewDimension)); GetFloatImageTypeDeclaration(textureViewDimension));
} }
static std::string CreateComputeShaderWithStorageTexture( static std::string CreateComputeShaderWithStorageTexture(
wgpu::StorageTextureAccess storageTextureBindingType, wgpu::StorageTextureAccess storageTextureBindingType,
const char* glslImageFormatQualifier, const char* imageFormatQualifier,
const char* textureComponentTypePrefix, const char* imageTypeDeclaration = "texture_storage_2d") {
const char* glslImageTypeDeclaration = "image2D") { const char* access = "";
const char* memoryQualifier = "";
switch (storageTextureBindingType) { switch (storageTextureBindingType) {
case wgpu::StorageTextureAccess::ReadOnly: case wgpu::StorageTextureAccess::ReadOnly:
memoryQualifier = "readonly"; access = "read";
break; break;
case wgpu::StorageTextureAccess::WriteOnly: case wgpu::StorageTextureAccess::WriteOnly:
memoryQualifier = "writeonly"; access = "write";
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
@ -87,12 +84,11 @@ class StorageTextureValidationTests : public ValidationTest {
} }
std::ostringstream ostream; std::ostringstream ostream;
ostream << "#version 450\n" ostream << "[[group(0), binding(0)]] var<uniform_constant> image0 : "
"layout (set = 0, binding = 0, " << "[[access(" << access << ")]] " << imageTypeDeclaration << "<"
<< glslImageFormatQualifier << ") uniform " << memoryQualifier << " " << imageFormatQualifier
<< textureComponentTypePrefix << glslImageTypeDeclaration << ">;\n"
<< " image0;\n" "[[stage(compute)]] fn main() -> void {\n"
"void main() {\n"
"}\n"; "}\n";
return ostream.str(); return ostream.str();
@ -126,7 +122,7 @@ TEST_F(StorageTextureValidationTests, RenderPipeline) {
{ {
wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"( wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>; [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>;
[[builtin(vertex_idx)]] var<in> VertexIndex : u32; [[builtin(vertex_index)]] var<in> VertexIndex : u32;
[[builtin(position)]] var<out> Position : vec4<f32>; [[builtin(position)]] var<out> Position : vec4<f32>;
[[stage(vertex)]] fn main() -> void { [[stage(vertex)]] fn main() -> void {
Position = textureLoad(image0, vec2<i32>(i32(VertexIndex), 0)); Position = textureLoad(image0, vec2<i32>(i32(VertexIndex), 0));
@ -157,13 +153,12 @@ TEST_F(StorageTextureValidationTests, RenderPipeline) {
} }
// Write-only storage textures cannot be declared in a vertex shader. // Write-only storage textures cannot be declared in a vertex shader.
{ if ((false) /* TODO(https://crbug.com/tint/449) */) {
wgpu::ShaderModule vsModule = wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( [[builtin(vertex_index)]] var<in> vertex_index : u32;
#version 450 [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0; [[stage(vertex)]] fn main() -> void {
void main() { textureStore(image0, vec2<i32>(i32(vertex_index), 0), vec4<f32>(1.0, 0.0, 0.0, 1.0));
imageStore(image0, ivec2(gl_VertexIndex, 0), vec4(1.f, 0.f, 0.f, 1.f));
})"); })");
utils::ComboRenderPipelineDescriptor descriptor(device); utils::ComboRenderPipelineDescriptor descriptor(device);
@ -175,12 +170,11 @@ TEST_F(StorageTextureValidationTests, RenderPipeline) {
// Write-only storage textures can be declared in a fragment shader. // Write-only storage textures can be declared in a fragment shader.
{ {
wgpu::ShaderModule fsModule = wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"(
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( [[builtin(frag_coord)]] var<in> frag_coord : vec4<f32>;
#version 450 [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0; [[stage(fragment)]] fn main() -> void {
void main() { textureStore(image0, vec2<i32>(frag_coord.xy), vec4<f32>(1.0, 0.0, 0.0, 1.0));
imageStore(image0, ivec2(gl_FragCoord.xy), vec4(1.f, 0.f, 0.f, 1.f));
})"); })");
utils::ComboRenderPipelineDescriptor descriptor(device); utils::ComboRenderPipelineDescriptor descriptor(device);
@ -219,12 +213,12 @@ TEST_F(StorageTextureValidationTests, ComputePipeline) {
// Write-only storage textures can be declared in a compute shader. // Write-only storage textures can be declared in a compute shader.
{ {
wgpu::ShaderModule csModule = wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, R"(
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
#version 450 [[builtin(local_invocation_id)]] var<in> LocalInvocationID : vec3<u32>;
layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
void main() { [[stage(compute)]] fn main() -> void {
imageStore(image0, ivec2(gl_LocalInvocationID.xy), vec4(0.f, 0.f, 0.f, 0.f)); textureStore(image0, vec2<i32>(LocalInvocationID.xy), vec4<f32>(0.0, 0.0, 0.0, 0.0));
})"); })");
wgpu::ComputePipelineDescriptor descriptor; wgpu::ComputePipelineDescriptor descriptor;
@ -236,40 +230,29 @@ TEST_F(StorageTextureValidationTests, ComputePipeline) {
} }
} }
// Validate read-write storage textures have not been supported yet. // Validate read-write storage textures are not currently supported.
// TODO(cwallez@chromium.org): Convert them to SPIRV ASM to remove the dependency on glslang.
TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) { TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) {
// Read-write storage textures cannot be declared in a vertex shader by default. // Read-write storage textures cannot be declared in a vertex shader by default.
{ {
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
#version 450 [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
layout(set = 0, binding = 0, rgba8) uniform image2D image0; [[stage(vertex)]] fn main() -> void {
void main() {
vec4 pixel = imageLoad(image0, ivec2(gl_VertexIndex, 0));
imageStore(image0, ivec2(gl_VertexIndex, 0), pixel * 2);
})")); })"));
} }
// Read-write storage textures cannot be declared in a fragment shader by default. // Read-write storage textures cannot be declared in a fragment shader by default.
{ {
ASSERT_DEVICE_ERROR( ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
#version 450 [[stage(fragment)]] fn main() -> void {
layout(set = 0, binding = 0, rgba8) uniform image2D image0;
void main() {
vec4 pixel = imageLoad(image0, ivec2(gl_FragCoord.xy));
imageStore(image0, ivec2(gl_FragCoord.xy), pixel * 2);
})")); })"));
} }
// Read-write storage textures cannot be declared in a compute shader by default. // Read-write storage textures cannot be declared in a compute shader by default.
{ {
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
#version 450 [[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
layout(set = 0, binding = 0, rgba8) uniform image2D image0; [[stage(compute)]] fn main() -> void {
void main() {
vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy));
imageStore(image0, ivec2(gl_LocalInvocationID.xy), pixel * 2);
})")); })"));
} }
} }
@ -334,11 +317,10 @@ TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) {
std::string computeShader = std::string computeShader =
CreateComputeShaderWithStorageTexture(storageTextureBindingType, format); CreateComputeShaderWithStorageTexture(storageTextureBindingType, format);
if (utils::TextureFormatSupportsStorageTexture(format)) { if (utils::TextureFormatSupportsStorageTexture(format)) {
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
computeShader.c_str());
} else { } else {
ASSERT_DEVICE_ERROR(utils::CreateShaderModule( ASSERT_DEVICE_ERROR(
device, utils::SingleShaderStage::Compute, computeShader.c_str())); utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
} }
} }
} }
@ -346,33 +328,29 @@ TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) {
// Verify that declaring a storage texture format that is not supported in WebGPU causes validation // Verify that declaring a storage texture format that is not supported in WebGPU causes validation
// error. // error.
TEST_F(StorageTextureValidationTests, UnsupportedSPIRVStorageTextureFormat) { TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {
struct TextureFormatInfo { constexpr std::array<wgpu::TextureFormat, 16> kUnsupportedTextureFormats = {
const char* name; wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm,
const char* componentTypePrefix; wgpu::TextureFormat::R8Uint, wgpu::TextureFormat::R8Sint,
wgpu::TextureFormat::R16Uint, wgpu::TextureFormat::R16Sint,
wgpu::TextureFormat::R16Float, wgpu::TextureFormat::RG8Unorm,
wgpu::TextureFormat::RG8Snorm, wgpu::TextureFormat::RG8Uint,
wgpu::TextureFormat::RG8Sint, wgpu::TextureFormat::RG16Uint,
wgpu::TextureFormat::RG16Sint, wgpu::TextureFormat::RG16Float,
wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Ufloat,
}; };
constexpr std::array<TextureFormatInfo, 7> kUnsupportedTextureFormats = {{{"rgba16", ""},
{"rg16", ""},
{"r16", ""},
{"rgba16_snorm", ""},
{"rg16_snorm", ""},
{"r16_snorm", ""},
{"rgb10_a2ui", "u"}}};
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) { for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
for (const TextureFormatInfo& formatInfo : kUnsupportedTextureFormats) { for (wgpu::TextureFormat format : kUnsupportedTextureFormats) {
std::string computeShader = CreateComputeShaderWithStorageTexture( std::string computeShader = CreateComputeShaderWithStorageTexture(bindingType, format);
bindingType, formatInfo.name, formatInfo.componentTypePrefix); ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute,
computeShader.c_str()));
} }
} }
} }
// Verify that declaring a storage texture dimension that is not supported in WebGPU in shader // Verify that declaring a storage texture dimension that isn't supported by
// causes validation error at the creation of PSO. WebGPU doesn't support using cube map texture // WebGPU causes a compile failure. WebGPU doesn't support using cube map
// views and cube map array texture views as storage textures. // texture views and cube map array texture views as storage textures.
TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) { TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) {
constexpr std::array<wgpu::TextureViewDimension, 2> kUnsupportedTextureViewDimensions = { constexpr std::array<wgpu::TextureViewDimension, 2> kUnsupportedTextureViewDimensions = {
wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray}; wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray};
@ -382,14 +360,7 @@ TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) {
for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) { for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) {
std::string computeShader = std::string computeShader =
CreateComputeShaderWithStorageTexture(bindingType, kFormat, dimension); CreateComputeShaderWithStorageTexture(bindingType, kFormat, dimension);
wgpu::ShaderModule csModule = utils::CreateShaderModule( ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
device, utils::SingleShaderStage::Compute, computeShader.c_str());
wgpu::ComputePipelineDescriptor computePipelineDescriptor;
computePipelineDescriptor.computeStage.module = csModule;
computePipelineDescriptor.computeStage.entryPoint = "main";
computePipelineDescriptor.layout = nullptr;
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));
} }
} }
} }
@ -431,8 +402,8 @@ TEST_F(StorageTextureValidationTests, BindGroupLayoutEntryTypeMatchesShaderDecla
// Create the compute shader with the given binding type. // Create the compute shader with the given binding type.
std::string computeShader = std::string computeShader =
CreateComputeShaderWithStorageTexture(bindingTypeInShader, kStorageTextureFormat); CreateComputeShaderWithStorageTexture(bindingTypeInShader, kStorageTextureFormat);
wgpu::ShaderModule csModule = utils::CreateShaderModule( wgpu::ShaderModule csModule =
device, utils::SingleShaderStage::Compute, computeShader.c_str()); utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
// Set common fields of compute pipeline descriptor. // Set common fields of compute pipeline descriptor.
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@ -508,8 +479,8 @@ TEST_F(StorageTextureValidationTests, BindGroupLayoutStorageTextureFormatMatches
// format. // format.
std::string computeShader = std::string computeShader =
CreateComputeShaderWithStorageTexture(bindingType, storageTextureFormatInShader); CreateComputeShaderWithStorageTexture(bindingType, storageTextureFormatInShader);
wgpu::ShaderModule csModule = utils::CreateShaderModule( wgpu::ShaderModule csModule =
device, utils::SingleShaderStage::Compute, computeShader.c_str()); utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
// Set common fields of compute pipeline descriptor. // Set common fields of compute pipeline descriptor.
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@ -565,8 +536,8 @@ TEST_F(StorageTextureValidationTests, BindGroupLayoutViewDimensionMatchesShaderD
// Create the compute shader with the given texture view dimension. // Create the compute shader with the given texture view dimension.
std::string computeShader = CreateComputeShaderWithStorageTexture( std::string computeShader = CreateComputeShaderWithStorageTexture(
bindingType, kStorageTextureFormat, dimensionInShader); bindingType, kStorageTextureFormat, dimensionInShader);
wgpu::ShaderModule csModule = utils::CreateShaderModule( wgpu::ShaderModule csModule =
device, utils::SingleShaderStage::Compute, computeShader.c_str()); utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
// Set common fields of compute pipeline descriptor. // Set common fields of compute pipeline descriptor.
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
@ -797,9 +768,8 @@ TEST_F(StorageTextureValidationTests, StorageTextureViewDimensionInBindGroup) {
TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) { TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) {
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) { for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
std::string computeShader = std::string computeShader =
CreateComputeShaderWithStorageTexture(bindingType, "rgba8", "", "image2DMS"); CreateComputeShaderWithStorageTexture(bindingType, "", "image2DMS");
ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
computeShader.c_str()));
} }
} }

View File

@ -516,6 +516,42 @@ namespace utils {
return "rgba32sint"; return "rgba32sint";
case wgpu::TextureFormat::RGBA32Float: case wgpu::TextureFormat::RGBA32Float:
return "rgba32float"; return "rgba32float";
// The below do not currently exist in the WGSL spec, but are used
// for tests that expect compilation failure.
case wgpu::TextureFormat::R8Unorm:
return "r8unorm";
case wgpu::TextureFormat::R8Snorm:
return "r8snorm";
case wgpu::TextureFormat::R8Uint:
return "r8uint";
case wgpu::TextureFormat::R8Sint:
return "r8sint";
case wgpu::TextureFormat::R16Uint:
return "r16uint";
case wgpu::TextureFormat::R16Sint:
return "r16sint";
case wgpu::TextureFormat::R16Float:
return "r16float";
case wgpu::TextureFormat::RG8Unorm:
return "rg8unorm";
case wgpu::TextureFormat::RG8Snorm:
return "rg8snorm";
case wgpu::TextureFormat::RG8Uint:
return "rg8uint";
case wgpu::TextureFormat::RG8Sint:
return "rg8sint";
case wgpu::TextureFormat::RG16Uint:
return "rg16uint";
case wgpu::TextureFormat::RG16Sint:
return "rg16sint";
case wgpu::TextureFormat::RG16Float:
return "rg16float";
case wgpu::TextureFormat::RGB10A2Unorm:
return "rgb10a2unorm";
case wgpu::TextureFormat::RG11B10Ufloat:
return "rg11b10ufloat";
default: default:
UNREACHABLE(); UNREACHABLE();
} }