Support Storage Textures as Valid Binding Types
This patch adds the basic validation of read-only storage texture,
write-only storage texture and read-write storage texture as new
binding types with no bind group layout provided in the creation of
pipeline state objects.
- Read-only storage textures can be used in vertex, fragment and
compute shaders.
- Write-only storage textures can only be used in compute shaders
due to the limitation on Metal.
- Read-write storage textures are not allowed now and they are
reserved to be supported as an extension in the future.
BUG=dawn:267
TEST=dawn_unittests
Change-Id: Iffc432f29a855b85d59451cb3c50269e03b84627
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/16661
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index f09523e..6a4f381 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -171,6 +171,7 @@
"src/dawn_native/BackendConnection.h",
"src/dawn_native/BindGroup.cpp",
"src/dawn_native/BindGroup.h",
+ "src/dawn_native/BindGroupAndStorageBarrierTracker.h",
"src/dawn_native/BindGroupLayout.cpp",
"src/dawn_native/BindGroupLayout.h",
"src/dawn_native/BindGroupTracker.h",
@@ -863,6 +864,7 @@
"src/tests/unittests/validation/RenderPipelineValidationTests.cpp",
"src/tests/unittests/validation/SamplerValidationTests.cpp",
"src/tests/unittests/validation/ShaderModuleValidationTests.cpp",
+ "src/tests/unittests/validation/StorageTextureValidationTests.cpp",
"src/tests/unittests/validation/TextureValidationTests.cpp",
"src/tests/unittests/validation/TextureViewValidationTests.cpp",
"src/tests/unittests/validation/ToggleValidationTests.cpp",
diff --git a/dawn.json b/dawn.json
index 434ad14..bec9c9e 100644
--- a/dawn.json
+++ b/dawn.json
@@ -114,7 +114,9 @@
{"value": 2, "name": "readonly storage buffer"},
{"value": 3, "name": "sampler"},
{"value": 4, "name": "sampled texture"},
- {"value": 5, "name": "storage texture"}
+ {"value": 5, "name": "storage texture"},
+ {"value": 6, "name": "readonly storage texture"},
+ {"value": 7, "name": "writeonly storage texture"}
]
},
"blend descriptor": {
diff --git a/src/dawn_native/BindGroup.cpp b/src/dawn_native/BindGroup.cpp
index 9e85be1..fe8166f 100644
--- a/src/dawn_native/BindGroup.cpp
+++ b/src/dawn_native/BindGroup.cpp
@@ -159,6 +159,12 @@
case wgpu::BindingType::Sampler:
DAWN_TRY(ValidateSamplerBinding(device, binding));
break;
+ // TODO(jiawei.shao@intel.com): support creating bind group with read-only and
+ // write-only storage textures.
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
+ return DAWN_VALIDATION_ERROR(
+ "Readonly and writeonly storage textures are not supported.");
case wgpu::BindingType::StorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/BindGroupAndStorageBarrierTracker.h b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
index 0c016e1..06fc268 100644
--- a/src/dawn_native/BindGroupAndStorageBarrierTracker.h
+++ b/src/dawn_native/BindGroupAndStorageBarrierTracker.h
@@ -15,9 +15,8 @@
#ifndef DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
#define DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
-#include "dawn_native/BindGroupTracker.h"
-
#include "dawn_native/BindGroup.h"
+#include "dawn_native/BindGroupTracker.h"
namespace dawn_native {
@@ -62,6 +61,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
default:
diff --git a/src/dawn_native/BindGroupLayout.cpp b/src/dawn_native/BindGroupLayout.cpp
index d16330a..c583137 100644
--- a/src/dawn_native/BindGroupLayout.cpp
+++ b/src/dawn_native/BindGroupLayout.cpp
@@ -14,22 +14,45 @@
#include "dawn_native/BindGroupLayout.h"
+#include <functional>
+
#include "common/BitSetIterator.h"
#include "common/HashUtils.h"
#include "dawn_native/Device.h"
#include "dawn_native/ValidationUtils_autogen.h"
-#include <functional>
-
namespace dawn_native {
MaybeError ValidateBindingTypeWithShaderStageVisibility(
wgpu::BindingType bindingType,
wgpu::ShaderStage shaderStageVisibility) {
- if (bindingType == wgpu::BindingType::StorageBuffer &&
- (shaderStageVisibility & wgpu::ShaderStage::Vertex) != 0) {
- return DAWN_VALIDATION_ERROR(
- "storage buffer binding is not supported in vertex shader");
+ // TODO(jiawei.shao@intel.com): support read-write storage textures.
+ switch (bindingType) {
+ case wgpu::BindingType::StorageBuffer: {
+ if ((shaderStageVisibility & wgpu::ShaderStage::Vertex) != 0) {
+ return DAWN_VALIDATION_ERROR(
+ "storage buffer binding is not supported in vertex shader");
+ }
+ } break;
+
+ case wgpu::BindingType::WriteonlyStorageTexture: {
+ if ((shaderStageVisibility &
+ (wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment)) != 0) {
+ return DAWN_VALIDATION_ERROR(
+ "write-only storage texture binding is only supported in compute shader");
+ }
+ } break;
+
+ case wgpu::BindingType::StorageTexture: {
+ return DAWN_VALIDATION_ERROR("Read-write storage texture binding is not supported");
+ } break;
+
+ case wgpu::BindingType::UniformBuffer:
+ case wgpu::BindingType::ReadonlyStorageBuffer:
+ case wgpu::BindingType::Sampler:
+ case wgpu::BindingType::SampledTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ break;
}
return {};
@@ -78,6 +101,8 @@
break;
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
if (binding.hasDynamicOffset) {
return DAWN_VALIDATION_ERROR("Samplers and textures cannot be dynamic");
}
@@ -105,7 +130,7 @@
}
return {};
- }
+ } // namespace dawn_native
namespace {
size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) {
@@ -171,6 +196,8 @@
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index e90f790..b3e4a89 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -31,6 +31,7 @@
"BackendConnection.h"
"BindGroup.cpp"
"BindGroup.h"
+ "BindGroupAndStorageBarrierTracker.h"
"BindGroupLayout.cpp"
"BindGroupLayout.h"
"BindGroupTracker.h"
diff --git a/src/dawn_native/PipelineLayout.cpp b/src/dawn_native/PipelineLayout.cpp
index 5cdf781..6f22685 100644
--- a/src/dawn_native/PipelineLayout.cpp
+++ b/src/dawn_native/PipelineLayout.cpp
@@ -33,6 +33,31 @@
lhs.textureComponentType == rhs.textureComponentType;
}
+ wgpu::ShaderStage GetShaderStageVisibilityWithBindingType(wgpu::BindingType bindingType) {
+ // TODO(jiawei.shao@intel.com): support read-only and read-write storage textures.
+ switch (bindingType) {
+ case wgpu::BindingType::StorageBuffer:
+ return wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute;
+
+ case wgpu::BindingType::WriteonlyStorageTexture:
+ return wgpu::ShaderStage::Compute;
+
+ case wgpu::BindingType::StorageTexture:
+ UNREACHABLE();
+ return wgpu::ShaderStage::None;
+
+ case wgpu::BindingType::UniformBuffer:
+ case wgpu::BindingType::ReadonlyStorageBuffer:
+ case wgpu::BindingType::Sampler:
+ case wgpu::BindingType::SampledTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ return wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment |
+ wgpu::ShaderStage::Compute;
+ }
+
+ return {};
+ }
+
} // anonymous namespace
MaybeError ValidatePipelineLayoutDescriptor(DeviceBase* device,
@@ -135,14 +160,9 @@
DAWN_TRY(ValidateBindingTypeWithShaderStageVisibility(
bindingInfo.type, StageBit(module->GetExecutionModel())));
- if (bindingInfo.type == wgpu::BindingType::StorageBuffer) {
- bindingSlot.visibility =
- wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute;
- } else {
- bindingSlot.visibility = wgpu::ShaderStage::Vertex |
- wgpu::ShaderStage::Fragment |
- wgpu::ShaderStage::Compute;
- }
+ bindingSlot.visibility =
+ GetShaderStageVisibilityWithBindingType(bindingInfo.type);
+
bindingSlot.type = bindingInfo.type;
bindingSlot.hasDynamicOffset = false;
bindingSlot.multisampled = bindingInfo.multisampled;
diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp
index bedf0c4..a68c58f 100644
--- a/src/dawn_native/ProgrammablePassEncoder.cpp
+++ b/src/dawn_native/ProgrammablePassEncoder.cpp
@@ -59,6 +59,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index eca1733..a6a12c5 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -406,6 +406,16 @@
info->type = wgpu::BindingType::StorageBuffer;
}
} break;
+ case wgpu::BindingType::StorageTexture: {
+ spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id);
+ if (flags.get(spv::DecorationNonReadable)) {
+ info->type = wgpu::BindingType::WriteonlyStorageTexture;
+ } else if (flags.get(spv::DecorationNonWritable)) {
+ info->type = wgpu::BindingType::ReadonlyStorageTexture;
+ } else {
+ info->type = wgpu::BindingType::StorageTexture;
+ }
+ } break;
default:
info->type = bindingType;
}
@@ -421,6 +431,8 @@
wgpu::BindingType::Sampler));
DAWN_TRY(ExtractResourcesBinding(resources.storage_buffers, compiler,
wgpu::BindingType::StorageBuffer));
+ DAWN_TRY(ExtractResourcesBinding(resources.storage_images, compiler,
+ wgpu::BindingType::StorageTexture));
// Extract the vertex attributes
if (mExecutionModel == SingleShaderStage::Vertex) {
diff --git a/src/dawn_native/d3d12/BindGroupD3D12.cpp b/src/dawn_native/d3d12/BindGroupD3D12.cpp
index c8138b2..0f9ea86 100644
--- a/src/dawn_native/d3d12/BindGroupD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupD3D12.cpp
@@ -13,15 +13,15 @@
// limitations under the License.
#include "dawn_native/d3d12/BindGroupD3D12.h"
+
#include "common/BitSetIterator.h"
#include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
#include "dawn_native/d3d12/BufferD3D12.h"
+#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/SamplerD3D12.h"
#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "dawn_native/d3d12/TextureD3D12.h"
-#include "dawn_native/d3d12/DeviceD3D12.h"
-
namespace dawn_native { namespace d3d12 {
BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor)
@@ -161,6 +161,8 @@
} break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
index cce9196..80e9d8c 100644
--- a/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp
@@ -47,6 +47,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
@@ -107,6 +109,8 @@
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
@@ -129,6 +133,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index c7c42fe..7f7bc6a 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -136,6 +136,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
case wgpu::BindingType::UniformBuffer:
@@ -224,6 +226,8 @@
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
diff --git a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
index 8d3dd17..7afadc7 100644
--- a/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
+++ b/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp
@@ -51,6 +51,8 @@
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
}
}
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 628f77b..7958217 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -599,6 +599,8 @@
} break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
diff --git a/src/dawn_native/metal/PipelineLayoutMTL.mm b/src/dawn_native/metal/PipelineLayoutMTL.mm
index 1d3e220..3a09238 100644
--- a/src/dawn_native/metal/PipelineLayoutMTL.mm
+++ b/src/dawn_native/metal/PipelineLayoutMTL.mm
@@ -54,6 +54,8 @@
textureIndex++;
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 2194016..25d3c81 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -305,6 +305,8 @@
} break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index e280875..71e2b83 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -140,6 +140,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/opengl/PipelineLayoutGL.cpp b/src/dawn_native/opengl/PipelineLayoutGL.cpp
index 530e7d0..18fd4f2 100644
--- a/src/dawn_native/opengl/PipelineLayoutGL.cpp
+++ b/src/dawn_native/opengl/PipelineLayoutGL.cpp
@@ -56,6 +56,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 7e12806..ab70419 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -149,6 +149,8 @@
break;
case wgpu::BindingType::StorageTexture:
+ case wgpu::BindingType::ReadonlyStorageTexture:
+ case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
case wgpu::BindingType::UniformBuffer:
diff --git a/src/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
new file mode 100644
index 0000000..56cf999
--- /dev/null
+++ b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -0,0 +1,207 @@
+// 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 "tests/unittests/validation/ValidationTest.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/WGPUHelpers.h"
+
+class StorageTextureValidationTests : public ValidationTest {
+ protected:
+ wgpu::ShaderModule mDefaultVSModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+ #version 450
+ void main() {
+ gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
+ })");
+ wgpu::ShaderModule mDefaultFSModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
+ #version 450
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ fragColor = vec4(1.f, 0.f, 0.f, 1.f);
+ })");
+};
+
+// Validate read-only storage textures can be declared in vertex and fragment
+// shaders, while writeonly storage textures can't.
+TEST_F(StorageTextureValidationTests, RenderPipeline) {
+ // Readonly storage texture can be declared in a vertex shader.
+ {
+ wgpu::ShaderModule vsModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0;
+ void main() {
+ gl_Position = imageLoad(image0, ivec2(gl_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::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0;
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ fragColor = imageLoad(image0, ivec2(gl_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.
+ {
+ wgpu::ShaderModule vsModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
+ void main() {
+ imageStore(image0, ivec2(gl_VertexIndex, 0), vec4(1.f, 0.f, 0.f, 1.f));
+ })");
+
+ 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 cannot be declared in a fragment shader.
+ {
+ wgpu::ShaderModule fsModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
+ void main() {
+ imageStore(image0, ivec2(gl_FragCoord.xy), vec4(1.f, 0.f, 0.f, 1.f));
+ })");
+
+ utils::ComboRenderPipelineDescriptor descriptor(device);
+ descriptor.layout = nullptr;
+ descriptor.vertexStage.module = mDefaultVSModule;
+ descriptor.cFragmentStage.module = fsModule;
+ ASSERT_DEVICE_ERROR(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::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0;
+ layout(std430, set = 0, binding = 0) buffer Buf { uint buf; };
+ void main() {
+ vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy));
+ buf = uint(pixel.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::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0;
+ void main() {
+ imageStore(image0, ivec2(gl_LocalInvocationID.xy), vec4(0.f, 0.f, 0.f, 0.f));
+ })");
+
+ wgpu::ComputePipelineDescriptor descriptor;
+ descriptor.layout = nullptr;
+ descriptor.computeStage.module = csModule;
+ descriptor.computeStage.entryPoint = "main";
+
+ device.CreateComputePipeline(&descriptor);
+ }
+}
+
+// Validate read-write storage textures have not been supported yet.
+TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) {
+ // Read-write storage textures cannot be declared in a vertex shader by default.
+ {
+ wgpu::ShaderModule vsModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform image2D image0;
+ void main() {
+ vec4 pixel = imageLoad(image0, ivec2(gl_VertexIndex, 0));
+ imageStore(image0, ivec2(gl_VertexIndex, 0), pixel * 2);
+ })");
+
+ utils::ComboRenderPipelineDescriptor descriptor(device);
+ descriptor.layout = nullptr;
+ descriptor.vertexStage.module = vsModule;
+ descriptor.cFragmentStage.module = mDefaultFSModule;
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+
+ // Read-write storage textures cannot be declared in a fragment shader by default.
+ {
+ wgpu::ShaderModule fsModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
+ #version 450
+ 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);
+ })");
+
+ utils::ComboRenderPipelineDescriptor descriptor(device);
+ descriptor.layout = nullptr;
+ descriptor.vertexStage.module = mDefaultVSModule;
+ descriptor.cFragmentStage.module = fsModule;
+ ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+ }
+
+ // Read-write storage textures cannot be declared in a compute shader by default.
+ {
+ wgpu::ShaderModule csModule =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
+ #version 450
+ layout(set = 0, binding = 0, rgba8) uniform image2D image0;
+ void main() {
+ vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy));
+ imageStore(image0, ivec2(gl_LocalInvocationID.xy), pixel * 2);
+ })");
+
+ wgpu::ComputePipelineDescriptor descriptor;
+ descriptor.layout = nullptr;
+ descriptor.computeStage.module = csModule;
+ descriptor.computeStage.entryPoint = "main";
+
+ ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor));
+ }
+}