Validate texture usage scope with storage textures in one render pass

This patch adds the validation rules on the texture usage scope with
storage textures in one render pass.
1. Write-only storage cannot be used in combination with anything else
in the same render pass.
2. Sampled and read-only storage are allowed to be used in the same
render pass.

This patch also adds dawn_unittests to test the storage texture usage
scope in one render pass:
1. read-only or write-only storage only
2. read-only or write-only storage + sampled
3. read-only or write-only storage + output attachment
4. read-only + write-only

This patch also removes kWritableBufferUsages as it is not used in Dawn
at all.

BUG=dawn:267
TEST=dawn_unittests

Change-Id: Ib2a0f06ec8d183c5f812f87459c6b1b8f79937e0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19820
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Jiawei Shao
2020-04-21 00:48:10 +00:00
committed by Commit Bot service account
parent d543d58760
commit e89b48768b
13 changed files with 234 additions and 21 deletions

View File

@@ -339,6 +339,8 @@ source_set("dawn_white_box_tests_sources") {
}
}
sources += [ "white_box/InternalResourceUsageTests.cpp" ]
if (dawn_enable_d3d12) {
sources += [
"white_box/D3D12DescriptorHeapTests.cpp",

View File

@@ -863,3 +863,142 @@ TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) {
ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor));
}
}
// 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::OutputAttachment, kFormat);
utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()});
for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) {
// Create a bind group that contains a storage texture.
wgpu::BindGroupLayout bindGroupLayout =
utils::MakeBindGroupLayout(device, {{.binding = 0,
.visibility = wgpu::ShaderStage::Fragment,
.type = storageTextureType,
.storageTextureFormat = 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::OutputAttachment, kFormat);
utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()});
// Create a bind group that contains a storage texture and a sampled texture.
for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) {
// Create a bind group that binds the same texture as both storage texture and sampled
// texture.
wgpu::BindGroupLayout bindGroupLayout =
utils::MakeBindGroupLayout(device, {{.binding = 0,
.visibility = wgpu::ShaderStage::Fragment,
.type = storageTextureType,
.storageTextureFormat = kFormat},
{.binding = 1,
.visibility = wgpu::ShaderStage::Fragment,
.type = wgpu::BindingType::SampledTexture,
.storageTextureFormat = kFormat}});
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::BindingType::ReadonlyStorageTexture:
encoder.Finish();
break;
case wgpu::BindingType::WriteonlyStorageTexture:
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, StorageTextureAndOutputAttachmentInOneRenderPass) {
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
wgpu::Texture storageTexture =
CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::OutputAttachment, kFormat);
utils::ComboRenderPassDescriptor renderPassDescriptor({storageTexture.CreateView()});
for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) {
// Create a bind group that contains a storage texture.
wgpu::BindGroupLayout bindGroupLayout =
utils::MakeBindGroupLayout(device, {{.binding = 0,
.visibility = wgpu::ShaderStage::Fragment,
.type = storageTextureType,
.storageTextureFormat = 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, ReadOnlyStorageTextureAndWriteOnlyStorageTexture) {
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, {{.binding = 0,
.visibility = wgpu::ShaderStage::Fragment,
.type = wgpu::BindingType::ReadonlyStorageTexture,
.storageTextureFormat = kFormat},
{.binding = 1,
.visibility = wgpu::ShaderStage::Fragment,
.type = wgpu::BindingType::WriteonlyStorageTexture,
.storageTextureFormat = kFormat}});
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, bindGroupLayout,
{{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}});
// It is invalid to use a a texture as both read-only storage texture and write-only storage
// texture in one render pass.
wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::OutputAttachment, 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());
}

View File

@@ -0,0 +1,45 @@
// 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/DawnTest.h"
#include "dawn_native/dawn_platform.h"
class InternalResourceUsageTests : public DawnTest {};
// Verify it is an error to create a buffer with a buffer usage that should only be used
// internally.
TEST_P(InternalResourceUsageTests, InternalBufferUsage) {
DAWN_SKIP_TEST_IF(IsDawnValidationSkipped());
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn_native::kReadOnlyStorageBuffer;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
// Verify it is an error to create a texture with a texture usage that should only be used
// internally.
TEST_P(InternalResourceUsageTests, InternalTextureUsage) {
DAWN_SKIP_TEST_IF(IsDawnValidationSkipped());
wgpu::TextureDescriptor descriptor;
descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
descriptor.size = {1, 1, 1};
descriptor.usage = dawn_native::kReadonlyStorageTexture;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
}
DAWN_INSTANTIATE_TEST(InternalResourceUsageTests, NullBackend());