Validate a subresource can't be an attachment multiple times in a pass
Bug: dawn:998, dawn:1001
Change-Id: Id7cd4964ebf9589c599059cc0f853c0125fa725a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/58361
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/PassResourceUsageTracker.cpp b/src/dawn_native/PassResourceUsageTracker.cpp
index 3aa90bf..c9b8e97 100644
--- a/src/dawn_native/PassResourceUsageTracker.cpp
+++ b/src/dawn_native/PassResourceUsageTracker.cpp
@@ -46,12 +46,23 @@
textureUsage.Update(range,
[usage](const SubresourceRange&, wgpu::TextureUsage* storedUsage) {
+ // TODO(crbug.com/dawn/1001): Consider optimizing to have fewer
+ // branches.
+ if ((*storedUsage & wgpu::TextureUsage::RenderAttachment) != 0 &&
+ (usage & wgpu::TextureUsage::RenderAttachment) != 0) {
+ // Using the same subresource as an attachment for two different
+ // render attachments is a write-write hazard. Add this internal
+ // usage so we will fail the check that a subresource with
+ // writable usage is the single usage.
+ *storedUsage |= kAgainAsRenderAttachment;
+ }
*storedUsage |= usage;
});
}
- void SyncScopeUsageTracker::AddTextureUsage(TextureBase* texture,
- const TextureSubresourceUsage& textureUsage) {
+ void SyncScopeUsageTracker::AddRenderBundleTextureUsage(
+ TextureBase* texture,
+ const TextureSubresourceUsage& textureUsage) {
// Get or create a new TextureSubresourceUsage for that texture (initially filled with
// wgpu::TextureUsage::None)
auto it = mTextureUsages.emplace(
@@ -62,7 +73,10 @@
passTextureUsage->Merge(
textureUsage, [](const SubresourceRange&, wgpu::TextureUsage* storedUsage,
- const wgpu::TextureUsage& addedUsage) { *storedUsage |= addedUsage; });
+ const wgpu::TextureUsage& addedUsage) {
+ ASSERT((addedUsage & wgpu::TextureUsage::RenderAttachment) == 0);
+ *storedUsage |= addedUsage;
+ });
}
void SyncScopeUsageTracker::AddBindGroup(BindGroupBase* group) {
diff --git a/src/dawn_native/PassResourceUsageTracker.h b/src/dawn_native/PassResourceUsageTracker.h
index d4de8fa..33f33ff 100644
--- a/src/dawn_native/PassResourceUsageTracker.h
+++ b/src/dawn_native/PassResourceUsageTracker.h
@@ -36,7 +36,8 @@
public:
void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage);
void TextureViewUsedAs(TextureViewBase* texture, wgpu::TextureUsage usage);
- void AddTextureUsage(TextureBase* texture, const TextureSubresourceUsage& textureUsage);
+ void AddRenderBundleTextureUsage(TextureBase* texture,
+ const TextureSubresourceUsage& textureUsage);
// Walks the bind groups and tracks all its resources.
void AddBindGroup(BindGroupBase* group);
diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp
index a7b6abc..7c1a81a 100644
--- a/src/dawn_native/RenderPassEncoder.cpp
+++ b/src/dawn_native/RenderPassEncoder.cpp
@@ -227,7 +227,8 @@
}
for (uint32_t i = 0; i < usages.textures.size(); ++i) {
- mUsageTracker.AddTextureUsage(usages.textures[i], usages.textureUsages[i]);
+ mUsageTracker.AddRenderBundleTextureUsage(usages.textures[i],
+ usages.textureUsages[i]);
}
}
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index d981acc..d26053c 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -42,10 +42,6 @@
static constexpr wgpu::TextureUsage kReadOnlyTextureUsages =
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled | kReadOnlyStorageTexture;
- static constexpr wgpu::TextureUsage kWritableTextureUsages =
- wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage |
- wgpu::TextureUsage::RenderAttachment;
-
class TextureBase : public ObjectBase {
public:
enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
diff --git a/src/dawn_native/dawn_platform.h b/src/dawn_native/dawn_platform.h
index 9e75de4..537a502 100644
--- a/src/dawn_native/dawn_platform.h
+++ b/src/dawn_native/dawn_platform.h
@@ -30,6 +30,11 @@
static constexpr wgpu::TextureUsage kReadOnlyStorageTexture =
static_cast<wgpu::TextureUsage>(0x80000000);
+ // Internal usage to help tracking when a subresource is used as render attachment usage
+ // more than once in a render pass.
+ static constexpr wgpu::TextureUsage kAgainAsRenderAttachment =
+ static_cast<wgpu::TextureUsage>(0x80000001);
+
// Add an extra texture usage for textures that will be presented, for use in backends
// that needs to transition to present usage.
// This currently aliases wgpu::TextureUsage::Present, we would assign it
diff --git a/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp b/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
index ffc1fe4..0cf64d8 100644
--- a/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
+++ b/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
@@ -990,6 +990,70 @@
}
}
+ // Test that a single subresource of a texture cannot be used as a render attachment more than
+ // once in the same pass.
+ TEST_F(ResourceUsageTrackingTest, TextureWithMultipleRenderAttachmentUsage) {
+ // Create a texture with two array layers
+ wgpu::TextureDescriptor descriptor;
+ descriptor.dimension = wgpu::TextureDimension::e2D;
+ descriptor.size = {1, 1, 2};
+ descriptor.usage = wgpu::TextureUsage::RenderAttachment;
+ descriptor.format = kFormat;
+
+ wgpu::Texture texture = device.CreateTexture(&descriptor);
+
+ wgpu::TextureViewDescriptor viewDesc = {};
+ viewDesc.arrayLayerCount = 1;
+
+ wgpu::TextureView viewLayer0 = texture.CreateView(&viewDesc);
+
+ viewDesc.baseArrayLayer = 1;
+ wgpu::TextureView viewLayer1 = texture.CreateView(&viewDesc);
+
+ // Control: It is valid to use layer0 as a render target for one attachment, and
+ // layer1 as the second attachment in the same pass
+ {
+ utils::ComboRenderPassDescriptor renderPass({viewLayer0, viewLayer1});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+ pass.EndPass();
+ encoder.Finish();
+ }
+
+ // Control: It is valid to use layer0 as a render target in separate passes.
+ {
+ utils::ComboRenderPassDescriptor renderPass({viewLayer0});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass);
+ pass0.EndPass();
+ wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass);
+ pass1.EndPass();
+ encoder.Finish();
+ }
+
+ // It is invalid to use layer0 as a render target for both attachments in the same pass
+ {
+ utils::ComboRenderPassDescriptor renderPass({viewLayer0, viewLayer0});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(encoder.Finish());
+ }
+
+ // It is invalid to use layer1 as a render target for both attachments in the same pass
+ {
+ utils::ComboRenderPassDescriptor renderPass({viewLayer1, viewLayer1});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+ pass.EndPass();
+ ASSERT_DEVICE_ERROR(encoder.Finish());
+ }
+ }
+
// Test that using the same texture as both readable and writable in different passes is
// allowed
TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageInDifferentPasses) {