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>
This commit is contained in:
Jiawei Shao 2020-03-11 01:28:48 +00:00 committed by Commit Bot service account
parent 52d0627d56
commit 421684f943
20 changed files with 326 additions and 20 deletions

View File

@ -171,6 +171,7 @@ source_set("libdawn_native_sources") {
"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 @@ test("dawn_unittests") {
"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",

View File

@ -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": {

View File

@ -159,6 +159,12 @@ namespace dawn_native {
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;

View File

@ -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 @@ namespace dawn_native {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
default:

View File

@ -14,23 +14,46 @@
#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) {
// 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 @@ namespace dawn_native {
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 @@ namespace dawn_native {
}
return {};
}
} // namespace dawn_native
namespace {
size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) {
@ -171,6 +196,8 @@ namespace dawn_native {
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}

View File

@ -31,6 +31,7 @@ target_sources(dawn_native PRIVATE
"BackendConnection.h"
"BindGroup.cpp"
"BindGroup.h"
"BindGroupAndStorageBarrierTracker.h"
"BindGroupLayout.cpp"
"BindGroupLayout.h"
"BindGroupTracker.h"

View File

@ -33,6 +33,31 @@ namespace dawn_native {
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 @@ namespace dawn_native {
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;
}
GetShaderStageVisibilityWithBindingType(bindingInfo.type);
bindingSlot.type = bindingInfo.type;
bindingSlot.hasDynamicOffset = false;
bindingSlot.multisampled = bindingInfo.multisampled;

View File

@ -59,6 +59,8 @@ namespace dawn_native {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}

View File

@ -406,6 +406,16 @@ namespace dawn_native {
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 @@ namespace dawn_native {
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) {

View File

@ -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 @@ namespace dawn_native { namespace d3d12 {
} break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;

View File

@ -47,6 +47,8 @@ namespace dawn_native { namespace d3d12 {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}
@ -107,6 +109,8 @@ namespace dawn_native { namespace d3d12 {
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 @@ namespace dawn_native { namespace d3d12 {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;

View File

@ -136,6 +136,8 @@ namespace dawn_native { namespace d3d12 {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
case wgpu::BindingType::UniformBuffer:
@ -224,6 +226,8 @@ namespace dawn_native { namespace d3d12 {
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}

View File

@ -51,6 +51,8 @@ namespace dawn_native { namespace d3d12 {
case wgpu::BindingType::SampledTexture:
case wgpu::BindingType::Sampler:
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
}
}

View File

@ -599,6 +599,8 @@ namespace dawn_native { namespace metal {
} break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}

View File

@ -54,6 +54,8 @@ namespace dawn_native { namespace metal {
textureIndex++;
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;
}

View File

@ -305,6 +305,8 @@ namespace dawn_native { namespace opengl {
} break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;

View File

@ -140,6 +140,8 @@ namespace dawn_native { namespace opengl {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;

View File

@ -56,6 +56,8 @@ namespace dawn_native { namespace opengl {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
UNREACHABLE();
break;

View File

@ -149,6 +149,8 @@ namespace dawn_native { namespace vulkan {
break;
case wgpu::BindingType::StorageTexture:
case wgpu::BindingType::ReadonlyStorageTexture:
case wgpu::BindingType::WriteonlyStorageTexture:
// Not implemented.
case wgpu::BindingType::UniformBuffer:

View File

@ -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));
}
}