mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 19:31:25 +00:00
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>
947 lines
48 KiB
C++
947 lines
48 KiB
C++
// Copyright 2020 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "common/Assert.h"
|
|
#include "tests/unittests/validation/ValidationTest.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/TextureFormatUtils.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
class StorageTextureValidationTests : public ValidationTest {
|
|
protected:
|
|
void SetUp() override {
|
|
ValidationTest::SetUp();
|
|
|
|
mDefaultVSModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[builtin(position)]] var<out> Position : vec4<f32>;
|
|
[[stage(vertex)]] fn main() -> void {
|
|
Position = vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
})");
|
|
mDefaultFSModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[location(0)]] var<out> fragColor : vec4<f32>;
|
|
[[stage(fragment)]] fn main() -> void {
|
|
fragColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
|
})");
|
|
}
|
|
|
|
static const char* GetFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) {
|
|
// TODO(bclayton): Support and test texture_storage_1d_array
|
|
switch (dimension) {
|
|
case wgpu::TextureViewDimension::e1D:
|
|
return "texture_storage_1d";
|
|
case wgpu::TextureViewDimension::e2D:
|
|
return "texture_storage_2d";
|
|
case wgpu::TextureViewDimension::e2DArray:
|
|
return "texture_storage_2d_array";
|
|
case wgpu::TextureViewDimension::e3D:
|
|
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:
|
|
default:
|
|
UNREACHABLE();
|
|
return "";
|
|
}
|
|
}
|
|
|
|
static std::string CreateComputeShaderWithStorageTexture(
|
|
wgpu::StorageTextureAccess storageTextureBindingType,
|
|
wgpu::TextureFormat textureFormat,
|
|
wgpu::TextureViewDimension textureViewDimension = wgpu::TextureViewDimension::e2D) {
|
|
return CreateComputeShaderWithStorageTexture(
|
|
storageTextureBindingType, utils::GetWGSLImageFormatQualifier(textureFormat),
|
|
GetFloatImageTypeDeclaration(textureViewDimension));
|
|
}
|
|
|
|
static std::string CreateComputeShaderWithStorageTexture(
|
|
wgpu::StorageTextureAccess storageTextureBindingType,
|
|
const char* imageFormatQualifier,
|
|
const char* imageTypeDeclaration = "texture_storage_2d") {
|
|
const char* access = "";
|
|
switch (storageTextureBindingType) {
|
|
case wgpu::StorageTextureAccess::ReadOnly:
|
|
access = "read";
|
|
break;
|
|
case wgpu::StorageTextureAccess::WriteOnly:
|
|
access = "write";
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
std::ostringstream ostream;
|
|
ostream << "[[group(0), binding(0)]] var<uniform_constant> image0 : "
|
|
<< "[[access(" << access << ")]] " << imageTypeDeclaration << "<"
|
|
<< imageFormatQualifier
|
|
<< ">;\n"
|
|
"[[stage(compute)]] fn main() -> void {\n"
|
|
"}\n";
|
|
|
|
return ostream.str();
|
|
}
|
|
|
|
wgpu::Texture CreateTexture(wgpu::TextureUsage usage,
|
|
wgpu::TextureFormat format,
|
|
uint32_t sampleCount = 1,
|
|
uint32_t arrayLayerCount = 1) {
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
|
descriptor.size = {16, 16, arrayLayerCount};
|
|
descriptor.sampleCount = sampleCount;
|
|
descriptor.format = format;
|
|
descriptor.mipLevelCount = 1;
|
|
descriptor.usage = usage;
|
|
return device.CreateTexture(&descriptor);
|
|
}
|
|
|
|
wgpu::ShaderModule mDefaultVSModule;
|
|
wgpu::ShaderModule mDefaultFSModule;
|
|
|
|
const std::array<wgpu::StorageTextureAccess, 2> kSupportedStorageTextureAccess = {
|
|
wgpu::StorageTextureAccess::ReadOnly, wgpu::StorageTextureAccess::WriteOnly};
|
|
};
|
|
|
|
// Validate read-only storage textures can be declared in vertex and fragment shaders, while
|
|
// writeonly storage textures cannot be used in vertex shaders.
|
|
TEST_F(StorageTextureValidationTests, RenderPipeline) {
|
|
// Readonly storage texture can be declared in a vertex shader.
|
|
{
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>;
|
|
[[builtin(vertex_index)]] var<in> VertexIndex : u32;
|
|
[[builtin(position)]] var<out> Position : vec4<f32>;
|
|
[[stage(vertex)]] fn main() -> void {
|
|
Position = textureLoad(image0, vec2<i32>(i32(VertexIndex), 0));
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
|
descriptor.layout = nullptr;
|
|
descriptor.vertexStage.module = vsModule;
|
|
descriptor.cFragmentStage.module = mDefaultFSModule;
|
|
device.CreateRenderPipeline(&descriptor);
|
|
}
|
|
|
|
// Read-only storage textures can be declared in a fragment shader.
|
|
{
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>;
|
|
[[builtin(frag_coord)]] var<in> FragCoord : vec4<f32>;
|
|
[[location(0)]] var<out> fragColor : vec4<f32>;
|
|
[[stage(fragment)]] fn main() -> void {
|
|
fragColor = textureLoad(image0, vec2<i32>(FragCoord.xy));
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
|
descriptor.layout = nullptr;
|
|
descriptor.vertexStage.module = mDefaultVSModule;
|
|
descriptor.cFragmentStage.module = fsModule;
|
|
device.CreateRenderPipeline(&descriptor);
|
|
}
|
|
|
|
// Write-only storage textures cannot be declared in a vertex shader.
|
|
if ((false) /* TODO(https://crbug.com/tint/449) */) {
|
|
wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[builtin(vertex_index)]] var<in> vertex_index : u32;
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
|
|
[[stage(vertex)]] fn main() -> void {
|
|
textureStore(image0, vec2<i32>(i32(vertex_index), 0), vec4<f32>(1.0, 0.0, 0.0, 1.0));
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
|
descriptor.layout = nullptr;
|
|
descriptor.vertexStage.module = vsModule;
|
|
descriptor.cFragmentStage.module = mDefaultFSModule;
|
|
ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
|
|
}
|
|
|
|
// Write-only storage textures can be declared in a fragment shader.
|
|
{
|
|
wgpu::ShaderModule fsModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[builtin(frag_coord)]] var<in> frag_coord : vec4<f32>;
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
|
|
[[stage(fragment)]] fn main() -> void {
|
|
textureStore(image0, vec2<i32>(frag_coord.xy), vec4<f32>(1.0, 0.0, 0.0, 1.0));
|
|
})");
|
|
|
|
utils::ComboRenderPipelineDescriptor descriptor(device);
|
|
descriptor.layout = nullptr;
|
|
descriptor.vertexStage.module = mDefaultVSModule;
|
|
descriptor.cFragmentStage.module = fsModule;
|
|
device.CreateRenderPipeline(&descriptor);
|
|
}
|
|
}
|
|
|
|
// Validate both read-only and write-only storage textures can be declared in
|
|
// compute shaders.
|
|
TEST_F(StorageTextureValidationTests, ComputePipeline) {
|
|
// Read-only storage textures can be declared in a compute shader.
|
|
{
|
|
wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read)]] texture_storage_2d<rgba8unorm>;
|
|
[[builtin(local_invocation_id)]] var<in> LocalInvocationID : vec3<u32>;
|
|
|
|
[[block]] struct Buf {
|
|
[[offset(0)]] data : f32;
|
|
};
|
|
[[group(0), binding(1)]] var<storage_buffer> buf : [[access(read_write)]] Buf;
|
|
|
|
[[stage(compute)]] fn main() -> void {
|
|
buf.data = textureLoad(image0, vec2<i32>(LocalInvocationID.xy)).x;
|
|
})");
|
|
|
|
wgpu::ComputePipelineDescriptor descriptor;
|
|
descriptor.layout = nullptr;
|
|
descriptor.computeStage.module = csModule;
|
|
descriptor.computeStage.entryPoint = "main";
|
|
|
|
device.CreateComputePipeline(&descriptor);
|
|
}
|
|
|
|
// Write-only storage textures can be declared in a compute shader.
|
|
{
|
|
wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(write)]] texture_storage_2d<rgba8unorm>;
|
|
[[builtin(local_invocation_id)]] var<in> LocalInvocationID : vec3<u32>;
|
|
|
|
[[stage(compute)]] fn main() -> void {
|
|
textureStore(image0, vec2<i32>(LocalInvocationID.xy), vec4<f32>(0.0, 0.0, 0.0, 0.0));
|
|
})");
|
|
|
|
wgpu::ComputePipelineDescriptor descriptor;
|
|
descriptor.layout = nullptr;
|
|
descriptor.computeStage.module = csModule;
|
|
descriptor.computeStage.entryPoint = "main";
|
|
|
|
device.CreateComputePipeline(&descriptor);
|
|
}
|
|
}
|
|
|
|
// Validate read-write storage textures are not currently supported.
|
|
TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) {
|
|
// Read-write storage textures cannot be declared in a vertex shader by default.
|
|
{
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
|
|
[[stage(vertex)]] fn main() -> void {
|
|
})"));
|
|
}
|
|
|
|
// Read-write storage textures cannot be declared in a fragment shader by default.
|
|
{
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
|
|
[[stage(fragment)]] fn main() -> void {
|
|
})"));
|
|
}
|
|
|
|
// Read-write storage textures cannot be declared in a compute shader by default.
|
|
{
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, R"(
|
|
[[group(0), binding(0)]] var<uniform_constant> image0 : [[access(read_write)]] texture_storage_2d<rgba8unorm>;
|
|
[[stage(compute)]] fn main() -> void {
|
|
})"));
|
|
}
|
|
}
|
|
|
|
// Test that using read-only storage texture and write-only storage texture in
|
|
// BindGroupLayout is valid, while using read-write storage texture is not allowed now.
|
|
TEST_F(StorageTextureValidationTests, BindGroupLayoutWithStorageTextureBindingType) {
|
|
struct TestSpec {
|
|
wgpu::ShaderStage stage;
|
|
wgpu::StorageTextureAccess type;
|
|
bool valid;
|
|
};
|
|
constexpr std::array<TestSpec, 6> kTestSpecs = {
|
|
{{wgpu::ShaderStage::Vertex, wgpu::StorageTextureAccess::ReadOnly, true},
|
|
{wgpu::ShaderStage::Vertex, wgpu::StorageTextureAccess::WriteOnly, false},
|
|
{wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::ReadOnly, true},
|
|
{wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly, true},
|
|
{wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::ReadOnly, true},
|
|
{wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, true}}};
|
|
|
|
for (const auto& testSpec : kTestSpecs) {
|
|
wgpu::BindGroupLayoutEntry entry = utils::BindingLayoutEntryInitializationHelper(
|
|
0, testSpec.stage, testSpec.type, wgpu::TextureFormat::R32Uint);
|
|
|
|
wgpu::BindGroupLayoutDescriptor descriptor;
|
|
descriptor.entryCount = 1;
|
|
descriptor.entries = &entry;
|
|
|
|
if (testSpec.valid) {
|
|
device.CreateBindGroupLayout(&descriptor);
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate it is an error to declare a read-only or write-only storage texture in shaders with any
|
|
// format that doesn't support TextureUsage::Storage texture usages.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) {
|
|
// Not include RGBA8UnormSrgb, BGRA8Unorm, BGRA8UnormSrgb because they are not related to any
|
|
// SPIR-V Image Formats.
|
|
constexpr std::array<wgpu::TextureFormat, 32> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
|
|
wgpu::TextureFormat::R32Uint, wgpu::TextureFormat::R32Sint,
|
|
wgpu::TextureFormat::R32Float, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureFormat::RGBA8Snorm, wgpu::TextureFormat::RGBA8Uint,
|
|
wgpu::TextureFormat::RGBA8Sint, wgpu::TextureFormat::RG32Uint,
|
|
wgpu::TextureFormat::RG32Sint, wgpu::TextureFormat::RG32Float,
|
|
wgpu::TextureFormat::RGBA16Uint, wgpu::TextureFormat::RGBA16Sint,
|
|
wgpu::TextureFormat::RGBA16Float, wgpu::TextureFormat::RGBA32Uint,
|
|
wgpu::TextureFormat::RGBA32Sint, wgpu::TextureFormat::RGBA32Float,
|
|
wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm,
|
|
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};
|
|
|
|
for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureFormat format : kWGPUTextureFormatSupportedAsSPIRVImageFormats) {
|
|
std::string computeShader =
|
|
CreateComputeShaderWithStorageTexture(storageTextureBindingType, format);
|
|
if (utils::TextureFormatSupportsStorageTexture(format)) {
|
|
utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(
|
|
utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that declaring a storage texture format that is not supported in WebGPU causes validation
|
|
// error.
|
|
TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {
|
|
constexpr std::array<wgpu::TextureFormat, 16> kUnsupportedTextureFormats = {
|
|
wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm,
|
|
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,
|
|
};
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureFormat format : kUnsupportedTextureFormats) {
|
|
std::string computeShader = CreateComputeShaderWithStorageTexture(bindingType, format);
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that declaring a storage texture dimension that isn't supported by
|
|
// WebGPU causes a compile failure. WebGPU doesn't support using cube map
|
|
// texture views and cube map array texture views as storage textures.
|
|
TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) {
|
|
constexpr std::array<wgpu::TextureViewDimension, 2> kUnsupportedTextureViewDimensions = {
|
|
wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray};
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::R32Float;
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) {
|
|
std::string computeShader =
|
|
CreateComputeShaderWithStorageTexture(bindingType, kFormat, dimension);
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that declaring a texture view dimension that is not supported to be used as storage
|
|
// textures in WebGPU in bind group layout causes validation error. WebGPU doesn't support using
|
|
// cube map texture views and cube map array texture views as storage textures.
|
|
TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInBindGroupLayout) {
|
|
constexpr std::array<wgpu::TextureViewDimension, 2> kUnsupportedTextureViewDimensions = {
|
|
wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray};
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::R32Float;
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) {
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Compute, bindingType, kFormat, dimension}}));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify when we create and use a bind group layout with storage textures in the creation of
|
|
// render and compute pipeline, the binding type in the bind group layout must match the
|
|
// declaration in the shader.
|
|
TEST_F(StorageTextureValidationTests, BindGroupLayoutEntryTypeMatchesShaderDeclaration) {
|
|
constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
|
|
std::initializer_list<utils::BindingLayoutEntryInitializationHelper> kSupportedBindingTypes = {
|
|
{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::ReadOnlyStorage},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::SamplerBindingType::Filtering},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::ReadOnly,
|
|
kStorageTextureFormat},
|
|
{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly,
|
|
kStorageTextureFormat}};
|
|
|
|
for (wgpu::StorageTextureAccess bindingTypeInShader : kSupportedStorageTextureAccess) {
|
|
// Create the compute shader with the given binding type.
|
|
std::string computeShader =
|
|
CreateComputeShaderWithStorageTexture(bindingTypeInShader, kStorageTextureFormat);
|
|
wgpu::ShaderModule csModule =
|
|
utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
|
|
|
|
// Set common fields of compute pipeline descriptor.
|
|
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
|
|
defaultComputePipelineDescriptor.computeStage.module = csModule;
|
|
defaultComputePipelineDescriptor.computeStage.entryPoint = "main";
|
|
|
|
for (utils::BindingLayoutEntryInitializationHelper bindingLayoutEntry :
|
|
kSupportedBindingTypes) {
|
|
wgpu::ComputePipelineDescriptor computePipelineDescriptor =
|
|
defaultComputePipelineDescriptor;
|
|
|
|
// Create bind group layout with different binding types.
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindingLayoutEntry});
|
|
computePipelineDescriptor.layout =
|
|
utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
|
|
|
|
// The binding type in the bind group layout must the same as the related image object
|
|
// declared in shader.
|
|
if (bindingLayoutEntry.storageTexture.access == bindingTypeInShader) {
|
|
device.CreateComputePipeline(&computePipelineDescriptor);
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify it is invalid not to set a valid texture format in a bind group layout when the binding
|
|
// type is read-only or write-only storage texture.
|
|
TEST_F(StorageTextureValidationTests, UndefinedStorageTextureFormatInBindGroupLayout) {
|
|
wgpu::BindGroupLayoutEntry errorBindGroupLayoutEntry;
|
|
errorBindGroupLayoutEntry.binding = 0;
|
|
errorBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute;
|
|
errorBindGroupLayoutEntry.storageTexture.format = wgpu::TextureFormat::Undefined;
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
errorBindGroupLayoutEntry.storageTexture.access = bindingType;
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {errorBindGroupLayoutEntry}));
|
|
}
|
|
}
|
|
|
|
// Verify it is invalid to create a bind group layout with storage textures and an unsupported
|
|
// storage texture format.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureFormatInBindGroupLayout) {
|
|
wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry;
|
|
defaultBindGroupLayoutEntry.binding = 0;
|
|
defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute;
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureFormat textureFormat : utils::kAllTextureFormats) {
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
|
|
bindGroupLayoutBinding.storageTexture.access = bindingType;
|
|
bindGroupLayoutBinding.storageTexture.format = textureFormat;
|
|
if (utils::TextureFormatSupportsStorageTexture(textureFormat)) {
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify the storage texture format in the bind group layout must match the declaration in shader.
|
|
TEST_F(StorageTextureValidationTests, BindGroupLayoutStorageTextureFormatMatchesShaderDeclaration) {
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureFormat storageTextureFormatInShader : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(storageTextureFormatInShader)) {
|
|
continue;
|
|
}
|
|
|
|
// Create the compute shader module with the given binding type and storage texture
|
|
// format.
|
|
std::string computeShader =
|
|
CreateComputeShaderWithStorageTexture(bindingType, storageTextureFormatInShader);
|
|
wgpu::ShaderModule csModule =
|
|
utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
|
|
|
|
// Set common fields of compute pipeline descriptor.
|
|
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
|
|
defaultComputePipelineDescriptor.computeStage.module = csModule;
|
|
defaultComputePipelineDescriptor.computeStage.entryPoint = "main";
|
|
|
|
// Set common fileds of bind group layout binding.
|
|
utils::BindingLayoutEntryInitializationHelper defaultBindGroupLayoutEntry = {
|
|
0, wgpu::ShaderStage::Compute, bindingType, utils::kAllTextureFormats[0]};
|
|
|
|
for (wgpu::TextureFormat storageTextureFormatInBindGroupLayout :
|
|
utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(
|
|
storageTextureFormatInBindGroupLayout)) {
|
|
continue;
|
|
}
|
|
|
|
// Create the bind group layout with the given storage texture format.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
|
|
bindGroupLayoutBinding.storageTexture.format =
|
|
storageTextureFormatInBindGroupLayout;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
// Create the compute pipeline with the bind group layout.
|
|
wgpu::ComputePipelineDescriptor computePipelineDescriptor =
|
|
defaultComputePipelineDescriptor;
|
|
computePipelineDescriptor.layout =
|
|
utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
|
|
|
|
// The storage texture format in the bind group layout must be the same as the one
|
|
// declared in the shader.
|
|
if (storageTextureFormatInShader == storageTextureFormatInBindGroupLayout) {
|
|
device.CreateComputePipeline(&computePipelineDescriptor);
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify the dimension of the bind group layout with storage textures must match the one declared
|
|
// in shader.
|
|
TEST_F(StorageTextureValidationTests, BindGroupLayoutViewDimensionMatchesShaderDeclaration) {
|
|
constexpr std::array<wgpu::TextureViewDimension, 4> kSupportedDimensions = {
|
|
wgpu::TextureViewDimension::e1D, wgpu::TextureViewDimension::e2D,
|
|
wgpu::TextureViewDimension::e2DArray, wgpu::TextureViewDimension::e3D};
|
|
constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
for (wgpu::TextureViewDimension dimensionInShader : kSupportedDimensions) {
|
|
// Create the compute shader with the given texture view dimension.
|
|
std::string computeShader = CreateComputeShaderWithStorageTexture(
|
|
bindingType, kStorageTextureFormat, dimensionInShader);
|
|
wgpu::ShaderModule csModule =
|
|
utils::CreateShaderModuleFromWGSL(device, computeShader.c_str());
|
|
|
|
// Set common fields of compute pipeline descriptor.
|
|
wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor;
|
|
defaultComputePipelineDescriptor.computeStage.module = csModule;
|
|
defaultComputePipelineDescriptor.computeStage.entryPoint = "main";
|
|
|
|
// Set common fields of bind group layout binding.
|
|
utils::BindingLayoutEntryInitializationHelper defaultBindGroupLayoutEntry = {
|
|
0, wgpu::ShaderStage::Compute, bindingType, kStorageTextureFormat};
|
|
|
|
for (wgpu::TextureViewDimension dimensionInBindGroupLayout : kSupportedDimensions) {
|
|
// Create the bind group layout with the given texture view dimension.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
|
|
bindGroupLayoutBinding.storageTexture.viewDimension = dimensionInBindGroupLayout;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
// Create the compute pipeline with the bind group layout.
|
|
wgpu::ComputePipelineDescriptor computePipelineDescriptor =
|
|
defaultComputePipelineDescriptor;
|
|
computePipelineDescriptor.layout =
|
|
utils::MakeBasicPipelineLayout(device, &bindGroupLayout);
|
|
|
|
// The texture dimension in the bind group layout must be the same as the one
|
|
// declared in the shader.
|
|
if (dimensionInShader == dimensionInBindGroupLayout) {
|
|
device.CreateComputePipeline(&computePipelineDescriptor);
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that in a bind group layout binding neither read-only nor write-only storage textures
|
|
// are allowed to have dynamic offsets.
|
|
// TODO(dawn:527): No longer be applicable after changes to BindGroupLayoutEntry are complete.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureCannotHaveDynamicOffsets) {
|
|
const std::array<wgpu::BindingType, 2> kSupportedStorageTextureBindingTypes = {
|
|
wgpu::BindingType::ReadonlyStorageTexture, wgpu::BindingType::WriteonlyStorageTexture};
|
|
for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) {
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding;
|
|
bindGroupLayoutBinding.binding = 0;
|
|
bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute;
|
|
bindGroupLayoutBinding.type = storageBindingType;
|
|
bindGroupLayoutBinding.storageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
|
|
bindGroupLayoutBinding.hasDynamicOffset = true;
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding})));
|
|
}
|
|
}
|
|
|
|
// Verify that only a texture view can be used as a read-only or write-only storage texture in a
|
|
// bind group.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureBindingTypeInBindGroup) {
|
|
constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
for (wgpu::StorageTextureAccess storageBindingType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group layout.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding;
|
|
bindGroupLayoutBinding.binding = 0;
|
|
bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute;
|
|
bindGroupLayoutBinding.storageTexture.access = storageBindingType;
|
|
bindGroupLayoutBinding.storageTexture.format = kStorageTextureFormat;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
// Buffers are not allowed to be used as storage textures in a bind group.
|
|
{
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 1024;
|
|
descriptor.usage = wgpu::BufferUsage::Uniform;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer}}));
|
|
}
|
|
|
|
// Samplers are not allowed to be used as storage textures in a bind group.
|
|
{
|
|
wgpu::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor();
|
|
wgpu::Sampler sampler = device.CreateSampler(&descriptor);
|
|
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}}));
|
|
}
|
|
|
|
// Texture views are allowed to be used as storage textures in a bind group.
|
|
{
|
|
wgpu::TextureView textureView =
|
|
CreateTexture(wgpu::TextureUsage::Storage, kStorageTextureFormat).CreateView();
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that a texture used as read-only or write-only storage texture in a bind group must be
|
|
// created with the texture usage wgpu::TextureUsage::STORAGE.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureUsageInBindGroup) {
|
|
constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
constexpr std::array<wgpu::TextureUsage, 6> kTextureUsages = {
|
|
wgpu::TextureUsage::CopySrc, wgpu::TextureUsage::CopyDst,
|
|
wgpu::TextureUsage::Sampled, wgpu::TextureUsage::Storage,
|
|
wgpu::TextureUsage::RenderAttachment, wgpu::TextureUsage::Present};
|
|
|
|
for (wgpu::StorageTextureAccess storageBindingType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group layout.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding;
|
|
bindGroupLayoutBinding.binding = 0;
|
|
bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute;
|
|
bindGroupLayoutBinding.storageTexture.access = storageBindingType;
|
|
bindGroupLayoutBinding.storageTexture.format = wgpu::TextureFormat::R32Float;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
for (wgpu::TextureUsage usage : kTextureUsages) {
|
|
// Create texture views with different texture usages
|
|
wgpu::TextureView textureView =
|
|
CreateTexture(usage, kStorageTextureFormat).CreateView();
|
|
|
|
// Verify that the texture used as storage texture must be created with the texture
|
|
// usage wgpu::TextureUsage::STORAGE.
|
|
if (usage & wgpu::TextureUsage::Storage) {
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}});
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the format of a texture used as read-only or write-only storage texture in a bind
|
|
// group must match the corresponding bind group binding.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureFormatInBindGroup) {
|
|
for (wgpu::StorageTextureAccess storageBindingType : kSupportedStorageTextureAccess) {
|
|
wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry;
|
|
defaultBindGroupLayoutEntry.binding = 0;
|
|
defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute;
|
|
defaultBindGroupLayoutEntry.storageTexture.access = storageBindingType;
|
|
|
|
for (wgpu::TextureFormat formatInBindGroupLayout : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(formatInBindGroupLayout)) {
|
|
continue;
|
|
}
|
|
|
|
// Create a bind group layout with given storage texture format.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
|
|
bindGroupLayoutBinding.storageTexture.format = formatInBindGroupLayout;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
for (wgpu::TextureFormat textureViewFormat : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(textureViewFormat)) {
|
|
continue;
|
|
}
|
|
|
|
// Create texture views with different texture formats.
|
|
wgpu::TextureView storageTextureView =
|
|
CreateTexture(wgpu::TextureUsage::Storage, textureViewFormat).CreateView();
|
|
|
|
// Verify that the format of the texture view used as storage texture in a bind
|
|
// group must match the storage texture format declaration in the bind group layout.
|
|
if (textureViewFormat == formatInBindGroupLayout) {
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}});
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that the dimension of a texture view used as read-only or write-only storage texture in a
|
|
// bind group must match the corresponding bind group binding.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureViewDimensionInBindGroup) {
|
|
constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
constexpr uint32_t kArrayLayerCount = 6u;
|
|
|
|
// Currently we only support creating 2D-compatible texture view dimensions.
|
|
// TODO(jiawei.shao@intel.com): test the use of 1D and 3D texture view dimensions when they are
|
|
// supported in Dawn.
|
|
constexpr std::array<wgpu::TextureViewDimension, 2> kSupportedDimensions = {
|
|
wgpu::TextureViewDimension::e2D, wgpu::TextureViewDimension::e2DArray};
|
|
|
|
wgpu::Texture texture =
|
|
CreateTexture(wgpu::TextureUsage::Storage, kStorageTextureFormat, 1, kArrayLayerCount);
|
|
|
|
wgpu::TextureViewDescriptor kDefaultTextureViewDescriptor;
|
|
kDefaultTextureViewDescriptor.format = kStorageTextureFormat;
|
|
kDefaultTextureViewDescriptor.baseMipLevel = 0;
|
|
kDefaultTextureViewDescriptor.mipLevelCount = 1;
|
|
kDefaultTextureViewDescriptor.baseArrayLayer = 0;
|
|
kDefaultTextureViewDescriptor.arrayLayerCount = 1u;
|
|
|
|
for (wgpu::StorageTextureAccess storageBindingType : kSupportedStorageTextureAccess) {
|
|
wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry;
|
|
defaultBindGroupLayoutEntry.binding = 0;
|
|
defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute;
|
|
defaultBindGroupLayoutEntry.storageTexture.access = storageBindingType;
|
|
defaultBindGroupLayoutEntry.storageTexture.format = kStorageTextureFormat;
|
|
|
|
for (wgpu::TextureViewDimension dimensionInBindGroupLayout : kSupportedDimensions) {
|
|
// Create a bind group layout with given texture view dimension.
|
|
wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry;
|
|
bindGroupLayoutBinding.storageTexture.viewDimension = dimensionInBindGroupLayout;
|
|
wgpu::BindGroupLayout bindGroupLayout =
|
|
utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
|
|
|
|
for (wgpu::TextureViewDimension dimensionOfTextureView : kSupportedDimensions) {
|
|
// Create a texture view with given texture view dimension.
|
|
wgpu::TextureViewDescriptor textureViewDescriptor = kDefaultTextureViewDescriptor;
|
|
textureViewDescriptor.dimension = dimensionOfTextureView;
|
|
wgpu::TextureView storageTextureView = texture.CreateView(&textureViewDescriptor);
|
|
|
|
// Verify that the dimension of the texture view used as storage texture in a bind
|
|
// group must match the texture view dimension declaration in the bind group layout.
|
|
if (dimensionInBindGroupLayout == dimensionOfTextureView) {
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}});
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify multisampled storage textures cannot be supported now.
|
|
TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) {
|
|
for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
|
|
std::string computeShader =
|
|
CreateComputeShaderWithStorageTexture(bindingType, "", "image2DMS");
|
|
ASSERT_DEVICE_ERROR(utils::CreateShaderModuleFromWGSL(device, computeShader.c_str()));
|
|
}
|
|
}
|
|
|
|
// Verify it is valid to use a texture as either read-only storage texture or write-only storage
|
|
// texture in a render pass.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureInRenderPass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat);
|
|
|
|
wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::RenderAttachment, kFormat);
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()});
|
|
|
|
for (wgpu::StorageTextureAccess storageTextureType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group that contains a storage texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Fragment, storageTextureType, kFormat}});
|
|
|
|
wgpu::BindGroup bindGroupWithStorageTexture =
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTexture.CreateView()}});
|
|
|
|
// It is valid to use a texture as read-only or write-only storage texture in the render
|
|
// pass.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroupWithStorageTexture);
|
|
renderPassEncoder.EndPass();
|
|
encoder.Finish();
|
|
}
|
|
}
|
|
|
|
// Verify it is valid to use a a texture as both read-only storage texture and sampled texture in
|
|
// one render pass, while it is invalid to use a texture as both write-only storage texture and
|
|
// sampled texture in one render pass.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureAndSampledTextureInOneRenderPass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture =
|
|
CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled, kFormat);
|
|
|
|
wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::RenderAttachment, kFormat);
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()});
|
|
|
|
// Create a bind group that contains a storage texture and a sampled texture.
|
|
for (wgpu::StorageTextureAccess storageTextureType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group that binds the same texture as both storage texture and sampled
|
|
// texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Fragment, storageTextureType, kFormat},
|
|
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, bindGroupLayout,
|
|
{{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}});
|
|
|
|
// It is valid to use a a texture as both read-only storage texture and sampled texture in
|
|
// one render pass, while it is invalid to use a texture as both write-only storage
|
|
// texture an sampled texture in one render pass.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroup);
|
|
renderPassEncoder.EndPass();
|
|
switch (storageTextureType) {
|
|
case wgpu::StorageTextureAccess::ReadOnly:
|
|
encoder.Finish();
|
|
break;
|
|
case wgpu::StorageTextureAccess::WriteOnly:
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify it is invalid to use a a texture as both storage texture (either read-only or write-only)
|
|
// and output attachment in one render pass.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureAndRenderAttachmentInOneRenderPass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture =
|
|
CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::RenderAttachment, kFormat);
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({storageTexture.CreateView()});
|
|
|
|
for (wgpu::StorageTextureAccess storageTextureType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group that contains a storage texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Fragment, storageTextureType, kFormat}});
|
|
wgpu::BindGroup bindGroupWithStorageTexture =
|
|
utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTexture.CreateView()}});
|
|
|
|
// It is invalid to use a texture as both storage texture and output attachment in one
|
|
// render pass.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroupWithStorageTexture);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Verify it is invalid to use a a texture as both read-only storage texture and write-only storage
|
|
// texture in one render pass.
|
|
TEST_F(StorageTextureValidationTests, ReadOnlyAndWriteOnlyStorageTextureInOneRenderPass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat);
|
|
|
|
// Create a bind group that uses the same texture as both read-only and write-only storage
|
|
// texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::ReadOnly, kFormat},
|
|
{1, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, bindGroupLayout,
|
|
{{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}});
|
|
|
|
// It is invalid to use a texture as both read-only storage texture and write-only storage
|
|
// texture in one render pass.
|
|
wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::RenderAttachment, kFormat);
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()});
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroup);
|
|
renderPassEncoder.EndPass();
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Verify it is valid to use a texture as both storage texture (read-only or write-only) and
|
|
// sampled texture in one compute pass.
|
|
TEST_F(StorageTextureValidationTests, StorageTextureAndSampledTextureInOneComputePass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture =
|
|
CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled, kFormat);
|
|
|
|
for (wgpu::StorageTextureAccess storageTextureType : kSupportedStorageTextureAccess) {
|
|
// Create a bind group that binds the same texture as both storage texture and sampled
|
|
// texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Compute, storageTextureType, kFormat},
|
|
{1, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Float}});
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, bindGroupLayout,
|
|
{{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}});
|
|
|
|
// It is valid to use a a texture as both storage texture (read-only or write-only) and
|
|
// sampled texture in one compute pass.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass();
|
|
computePassEncoder.SetBindGroup(0, bindGroup);
|
|
computePassEncoder.EndPass();
|
|
encoder.Finish();
|
|
}
|
|
}
|
|
|
|
// Verify it is valid to use a texture as both read-only storage texture and write-only storage
|
|
// texture in one compute pass.
|
|
TEST_F(StorageTextureValidationTests, ReadOnlyAndWriteOnlyStorageTextureInOneComputePass) {
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat);
|
|
|
|
// Create a bind group that uses the same texture as both read-only and write-only storage
|
|
// texture.
|
|
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
|
|
device, {{0, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::ReadOnly, kFormat},
|
|
{1, wgpu::ShaderStage::Compute, wgpu::StorageTextureAccess::WriteOnly, kFormat}});
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, bindGroupLayout,
|
|
{{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}});
|
|
|
|
// It is valid to use a texture as both read-only storage texture and write-only storage
|
|
// texture in one compute pass.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass();
|
|
computePassEncoder.SetBindGroup(0, bindGroup);
|
|
computePassEncoder.EndPass();
|
|
encoder.Finish();
|
|
}
|