Update DawnLoadResolveTexture's render pass compatibility spec
Because Vulkan's render pass compatibility requires that if the
render passes have more than one subpass then:
- All attachments (color, input, resolve, depth/stencil) **must** be the
same.
We need to know that a render pipeline's color target has any resolve
target or not in order to query the appropriate render pass.
Previously, `ColorTargetStateExpandResolveTextureDawn` only told us
whether a color target uses `ExpandResolveTexture` load op or not. But
it didn't tell us whether the color target has a resolve target but
uses another type of load op (e.g. `Load`). Consequencely this only
enabled us to query a vulkan render pass with correct input attachments,
but not resolve attachments.
This CL updates the DawnLoadResolveTexture's spec:
- If `ColorTargetStateExpandResolveTextureDawn` is chained to a render
pipeline's ColorTargetState. Then it is implicitly implied that a
resolve target is present at that location.
- If the struct's enabled flag is true then it's understood that the
respective color target does use `ExpandResolveTexture` load op.
- If `ColorTargetStateExpandResolveTextureDawn` is not chained. Then
it is implied that the corresponding color target doesn't have resolve
texture.
Bug: dawn:1710
Change-Id: I23115930dafdaf506995eaafa9bd4d0200e1f278
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/189980
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
diff --git a/docs/dawn/features/dawn_load_resolve_texture.md b/docs/dawn/features/dawn_load_resolve_texture.md
index 880f52d..7da8a3f 100644
--- a/docs/dawn/features/dawn_load_resolve_texture.md
+++ b/docs/dawn/features/dawn_load_resolve_texture.md
@@ -65,7 +65,12 @@
Notes:
- If a resolve texture is used in a `wgpu::LoadOp::ExpandResolveTexture` operation, it must have `wgpu::TextureUsage::TextureBinding` usage.
- - If `wgpu::ColorTargetStateExpandResolveTextureDawn` chained struct is not included in a `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState` or if it is included but `enabled` boolean flag is false, then the result render pipeline cannot be used in a render pass using `ExpandResolveTexture` load op for that respective color attachment.
- - Similarly, a render pipeline created with `wgpu::ColorTargetStateExpandResolveTextureDawn`'s `enabled` flag = `true` won't be able to be used in render passes that don't use `ExpandResolveTexture` load op for the respective color attachment.
+ - The `wgpu::ColorTargetStateExpandResolveTextureDawn` chained struct controls the compatibility between a render pipeline and a render pass:
+ - If the chained struct is not included in any `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState` then the render pipeline **can only** be used on any render pass not using any `ExpandResolveTexture` load op. Whether the render pass has any resolve target doesn't matter.
+ - If the chained struct is included in some color targets but **none** of their `enabled` boolean flags are true, then it's the same as above case.
+ - If at least one included chained struct has `enabled` = true, then the compatibility's requirements are stricter:
+ - If render pipeline's color target `i` has `wgpu::ColorTargetStateExpandResolveTextureDawn.enabled` = true, then the compatible render pass **must** have `ExpandResolveTexture` load op on attachment `i`.
+ - If render pipeline's color target `i` has `wgpu::ColorTargetStateExpandResolveTextureDawn.enabled` = false, then the compatible render pass's attachment `i` must have a resolve target and its load op **must not** be `ExpandResolveTexture`.
+ - If render pipeline's color target `i` has no `wgpu::ColorTargetStateExpandResolveTextureDawn` included, then the compatible render pass's attachment `i` **must not** have any resolve target.
- Currently the `ExpandResolveTexture` LoadOp only works on color attachment, this could be changed in future.
- The texture is not supported if it is not resolvable by WebGPU standard. This means this feature currently doesn't work with integer textures.
diff --git a/src/dawn/native/AttachmentState.cpp b/src/dawn/native/AttachmentState.cpp
index 75ac8dc..6432976 100644
--- a/src/dawn/native/AttachmentState.cpp
+++ b/src/dawn/native/AttachmentState.cpp
@@ -78,11 +78,26 @@
UnpackedPtr<ColorTargetState> unpackedTarget = Unpack(&target);
if (auto* expandResolveState =
unpackedTarget.Get<ColorTargetStateExpandResolveTextureDawn>()) {
- mAttachmentsToExpandResolve.set(i, expandResolveState->enabled);
+ mExpandResolveInfo.attachmentsToExpandResolve.set(i,
+ expandResolveState->enabled);
+ // The presence of ColorTargetStateExpandResolveTextureDawn implies that
+ // this color target has a resolve target. Doesn't matter `enabled` is true or
+ // not.
+ mExpandResolveInfo.resolveTargetsMask.set(i);
}
}
}
}
+ if (!mExpandResolveInfo.attachmentsToExpandResolve.any()) {
+ // If render pipeline doesn't have any color target using ExpandResolveTexture load op then
+ // ignore resolve targets. This is to relax compatibility requirement for common cases
+ // where ExpandResolveTexture is not used.
+ mExpandResolveInfo.resolveTargetsMask.reset();
+ }
+
+ DAWN_ASSERT(IsSubset(mExpandResolveInfo.attachmentsToExpandResolve,
+ mExpandResolveInfo.resolveTargetsMask));
+
if (descriptor->depthStencil != nullptr) {
mDepthStencilFormat = descriptor->depthStencil->format;
}
@@ -124,8 +139,9 @@
}
if (colorAttachment.loadOp == wgpu::LoadOp::ExpandResolveTexture) {
- mAttachmentsToExpandResolve.set(i);
+ mExpandResolveInfo.attachmentsToExpandResolve.set(i);
}
+ mExpandResolveInfo.resolveTargetsMask.set(i, colorAttachment.resolveTarget);
}
// Gather the depth-stencil information.
@@ -139,6 +155,15 @@
}
}
+ if (!mExpandResolveInfo.attachmentsToExpandResolve.any()) {
+ // If render pass doesn't have any color attachment using ExpandResolveTexture load op then
+ // ignore resolve targets. This is to relax compatibility requirement for common cases
+ // where ExpandResolveTexture is not used.
+ mExpandResolveInfo.resolveTargetsMask.reset();
+ }
+ DAWN_ASSERT(IsSubset(mExpandResolveInfo.attachmentsToExpandResolve,
+ mExpandResolveInfo.resolveTargetsMask));
+
// Gather the PLS information.
if (auto* pls = descriptor.Get<RenderPassPixelLocalStorage>()) {
mHasPLS = true;
@@ -167,9 +192,11 @@
mColorFormats = blueprint.mColorFormats;
mDepthStencilFormat = blueprint.mDepthStencilFormat;
mSampleCount = blueprint.mSampleCount;
- mAttachmentsToExpandResolve = blueprint.mAttachmentsToExpandResolve;
+ mExpandResolveInfo = blueprint.mExpandResolveInfo;
mHasPLS = blueprint.mHasPLS;
mStorageAttachmentSlots = blueprint.mStorageAttachmentSlots;
+ DAWN_ASSERT(IsSubset(mExpandResolveInfo.attachmentsToExpandResolve,
+ mExpandResolveInfo.resolveTargetsMask));
SetContentHash(blueprint.GetContentHash());
}
@@ -202,8 +229,13 @@
return false;
}
- // Both attachment state must either enable MSAA render to single sampled or disable it.
- if (a->mAttachmentsToExpandResolve != b->mAttachmentsToExpandResolve) {
+ // Both attachment state must have the same `ExpandResolveTexture` load ops.
+ if (a->mExpandResolveInfo.attachmentsToExpandResolve !=
+ b->mExpandResolveInfo.attachmentsToExpandResolve) {
+ return false;
+ }
+
+ if (a->mExpandResolveInfo.resolveTargetsMask != b->mExpandResolveInfo.resolveTargetsMask) {
return false;
}
@@ -238,8 +270,9 @@
// Hash sample count
HashCombine(&hash, mSampleCount);
- // Hash MSAA render to single sampled flag
- HashCombine(&hash, mAttachmentsToExpandResolve);
+ // Hash expand resolve load op bits
+ HashCombine(&hash, mExpandResolveInfo.attachmentsToExpandResolve);
+ HashCombine(&hash, mExpandResolveInfo.resolveTargetsMask);
// Hash the PLS state
HashCombine(&hash, mHasPLS);
@@ -272,8 +305,8 @@
return mSampleCount;
}
-ColorAttachmentMask AttachmentState::GetExpandResolveUsingAttachmentsMask() const {
- return mAttachmentsToExpandResolve;
+const AttachmentState::ExpandResolveInfo& AttachmentState::GetExpandResolveInfo() const {
+ return mExpandResolveInfo;
}
bool AttachmentState::HasPixelLocalStorage() const {
diff --git a/src/dawn/native/AttachmentState.h b/src/dawn/native/AttachmentState.h
index a320818..4dcbf98 100644
--- a/src/dawn/native/AttachmentState.h
+++ b/src/dawn/native/AttachmentState.h
@@ -50,6 +50,14 @@
public CachedObject,
public ContentLessObjectCacheable<AttachmentState> {
public:
+ struct ExpandResolveInfo {
+ // Mask indicates which attachments use `ExpandResolveTexture` load op.
+ ColorAttachmentMask attachmentsToExpandResolve;
+ // Mask indicates which attachments have resolve target. Only enabled if
+ // attachmentsToExpandResolve has any bit set.
+ ColorAttachmentMask resolveTargetsMask;
+ };
+
// Note: Descriptors must be validated before the AttachmentState is constructed.
explicit AttachmentState(DeviceBase* device, const RenderBundleEncoderDescriptor* descriptor);
explicit AttachmentState(DeviceBase* device,
@@ -66,7 +74,7 @@
bool HasDepthStencilAttachment() const;
wgpu::TextureFormat GetDepthStencilFormat() const;
uint32_t GetSampleCount() const;
- ColorAttachmentMask GetExpandResolveUsingAttachmentsMask() const;
+ const ExpandResolveInfo& GetExpandResolveInfo() const;
bool HasPixelLocalStorage() const;
const std::vector<wgpu::TextureFormat>& GetStorageAttachmentSlots() const;
std::vector<ColorAttachmentIndex> ComputeStorageAttachmentPackingInColorAttachments() const;
@@ -86,7 +94,8 @@
wgpu::TextureFormat mDepthStencilFormat = wgpu::TextureFormat::Undefined;
uint32_t mSampleCount = 0;
- ColorAttachmentMask mAttachmentsToExpandResolve;
+ ExpandResolveInfo mExpandResolveInfo;
+
bool mHasPLS = false;
std::vector<wgpu::TextureFormat> mStorageAttachmentSlots;
};
diff --git a/src/dawn/native/BlitColorToColorWithDraw.cpp b/src/dawn/native/BlitColorToColorWithDraw.cpp
index cd382db..9c78f70 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.cpp
+++ b/src/dawn/native/BlitColorToColorWithDraw.cpp
@@ -120,14 +120,19 @@
// Color target states.
PerColorAttachment<ColorTargetState> colorTargets = {};
- wgpu::ColorTargetStateExpandResolveTextureDawn msaaExpandResolveState;
- msaaExpandResolveState.enabled = true;
+ PerColorAttachment<wgpu::ColorTargetStateExpandResolveTextureDawn> msaaExpandResolveStates;
for (auto [i, target] : Enumerate(colorTargets)) {
target.format = pipelineKey.colorTargetFormats[i];
// We shouldn't change the color targets that are not involved in.
- if (pipelineKey.attachmentsToExpandResolve[i]) {
- target.nextInChain = &msaaExpandResolveState;
+ if (pipelineKey.resolveTargetsMask[i]) {
+ target.nextInChain = &msaaExpandResolveStates[i];
+ msaaExpandResolveStates[i].enabled = pipelineKey.attachmentsToExpandResolve[i];
+ if (msaaExpandResolveStates[i].enabled) {
+ target.writeMask = wgpu::ColorWriteMask::All;
+ } else {
+ target.writeMask = wgpu::ColorWriteMask::None;
+ }
} else {
target.writeMask = wgpu::ColorWriteMask::None;
}
@@ -212,6 +217,7 @@
wgpu::TextureViewDimension::e2D);
pipelineKey.attachmentsToExpandResolve.set(colorIdx);
}
+ pipelineKey.resolveTargetsMask.set(colorIdx, colorAttachment.resolveTarget);
pipelineKey.colorTargetFormats[colorIdx] = format.format;
pipelineKey.sampleCount = view->GetTexture()->GetSampleCount();
@@ -267,6 +273,7 @@
size_t hash = 0;
HashCombine(&hash, key.attachmentsToExpandResolve);
+ HashCombine(&hash, key.resolveTargetsMask);
for (auto format : key.colorTargetFormats) {
HashCombine(&hash, format);
@@ -284,6 +291,9 @@
if (a.attachmentsToExpandResolve != b.attachmentsToExpandResolve) {
return false;
}
+ if (a.resolveTargetsMask != b.resolveTargetsMask) {
+ return false;
+ }
for (auto [i, format] : Enumerate(a.colorTargetFormats)) {
if (format != b.colorTargetFormats[i]) {
diff --git a/src/dawn/native/BlitColorToColorWithDraw.h b/src/dawn/native/BlitColorToColorWithDraw.h
index d418e40..ac5463a 100644
--- a/src/dawn/native/BlitColorToColorWithDraw.h
+++ b/src/dawn/native/BlitColorToColorWithDraw.h
@@ -50,6 +50,7 @@
PerColorAttachment<wgpu::TextureFormat> colorTargetFormats;
ColorAttachmentMask attachmentsToExpandResolve;
+ ColorAttachmentMask resolveTargetsMask;
wgpu::TextureFormat depthStencilFormat = wgpu::TextureFormat::Undefined;
uint32_t sampleCount = 1;
diff --git a/src/dawn/native/webgpu_absl_format.cpp b/src/dawn/native/webgpu_absl_format.cpp
index 4b78122..90bc866 100644
--- a/src/dawn/native/webgpu_absl_format.cpp
+++ b/src/dawn/native/webgpu_absl_format.cpp
@@ -336,16 +336,20 @@
}
while (nextColorIndex < i) {
- s->Append(absl::StrFormat("{format: %s}, ", wgpu::TextureFormat::Undefined));
+ s->Append(absl::StrFormat("%d={format: %s}, ", nextColorIndex,
+ wgpu::TextureFormat::Undefined));
nextColorIndex++;
needsComma = false;
}
- s->Append(absl::StrFormat("{format:%s", value->GetColorAttachmentFormat(i)));
+ s->Append(absl::StrFormat("%d={format:%s", i, value->GetColorAttachmentFormat(i)));
- if (value->GetDevice()->HasFeature(Feature::DawnLoadResolveTexture)) {
- s->Append(absl::StrFormat(", expandResolveTexture:%v",
- value->GetExpandResolveUsingAttachmentsMask().test(i)));
+ if (value->GetDevice()->HasFeature(Feature::DawnLoadResolveTexture) &&
+ value->GetExpandResolveInfo().attachmentsToExpandResolve.any()) {
+ s->Append(
+ absl::StrFormat(", resolve:%v, expandResolve:%v",
+ value->GetExpandResolveInfo().resolveTargetsMask.test(i),
+ value->GetExpandResolveInfo().attachmentsToExpandResolve.test(i)));
}
s->Append("}");
diff --git a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
index fefc26f..e72e23a 100644
--- a/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
+++ b/src/dawn/tests/end2end/MultisampledRenderingTests.cpp
@@ -26,7 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
-#include <bitset>
+#include <array>
#include <vector>
#include "dawn/common/Assert.h"
@@ -37,7 +37,12 @@
namespace dawn {
namespace {
-using AttachmentMask = std::bitset<16>;
+enum class PipelineMultisampleLoadOp {
+ Ignore,
+ ExpandResolveTarget,
+ HasResolveTargetButLoadMultisampled,
+};
+using PipelineMultisampleLoadOps = std::array<PipelineMultisampleLoadOp, 16>;
class MultisampledRenderingTest : public DawnTest {
protected:
@@ -97,18 +102,20 @@
const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
- AttachmentMask enableExpandResolveLoadOps;
- enableExpandResolveLoadOps.set(0, enableExpandResolveLoadOp);
+ PipelineMultisampleLoadOps multisampleLoadOps{};
+ if (enableExpandResolveLoadOp) {
+ multisampleLoadOps[0] = PipelineMultisampleLoadOp::ExpandResolveTarget;
+ }
return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask, alphaToCoverageEnabled,
- flipTriangle, enableExpandResolveLoadOps);
+ flipTriangle, multisampleLoadOps);
}
wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest(
uint32_t sampleMask = 0xFFFFFFFF,
bool alphaToCoverageEnabled = false,
bool depthTest = false,
- bool enableExpandResolveLoadOpForColor0 = false,
- bool enableExpandResolveLoadOpForColor1 = false) {
+ PipelineMultisampleLoadOp loadOpForColor0 = PipelineMultisampleLoadOp::Ignore,
+ PipelineMultisampleLoadOp loadOpForColor1 = PipelineMultisampleLoadOp::Ignore) {
const char* kFsTwoOutputs = R"(
struct U {
color0 : vec4f,
@@ -128,13 +135,13 @@
return output;
})";
- AttachmentMask enableExpandResolveLoadOps;
- enableExpandResolveLoadOps.set(0, enableExpandResolveLoadOpForColor0);
- enableExpandResolveLoadOps.set(1, enableExpandResolveLoadOpForColor1);
+ PipelineMultisampleLoadOps multisampleLoadOps{};
+ multisampleLoadOps[0] = loadOpForColor0;
+ multisampleLoadOps[1] = loadOpForColor1;
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, depthTest, sampleMask,
alphaToCoverageEnabled, /*flipTriangle=*/false,
- enableExpandResolveLoadOps);
+ multisampleLoadOps);
}
wgpu::RenderPipeline CreateRenderPipelineWithNonZeroLocationOutputForTest(
@@ -151,11 +158,13 @@
return uBuffer.color;
})";
- AttachmentMask enableExpandResolveLoadOps = {};
- enableExpandResolveLoadOps.set(1, enableExpandResolveLoadOp);
+ PipelineMultisampleLoadOps multisampleLoadOps{};
+ if (enableExpandResolveLoadOp) {
+ multisampleLoadOps[1] = PipelineMultisampleLoadOp::ExpandResolveTarget;
+ }
return CreateRenderPipelineForTest(kFsNonZeroLocationOutputs, 1, false, sampleMask,
alphaToCoverageEnabled, /*flipTriangle=*/false,
- enableExpandResolveLoadOps, 1);
+ multisampleLoadOps, 1);
}
wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format,
@@ -278,14 +287,15 @@
wgpu::Texture mDepthStencilTexture;
wgpu::TextureView mDepthStencilView;
- wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs,
- uint32_t numColorAttachments,
- bool hasDepthStencilAttachment,
- uint32_t sampleMask = 0xFFFFFFFF,
- bool alphaToCoverageEnabled = false,
- bool flipTriangle = false,
- AttachmentMask enableExpandResolveLoadOps = {},
- uint32_t firstAttachmentLocation = 0) {
+ wgpu::RenderPipeline CreateRenderPipelineForTest(
+ const char* fs,
+ uint32_t numColorAttachments,
+ bool hasDepthStencilAttachment,
+ uint32_t sampleMask = 0xFFFFFFFF,
+ bool alphaToCoverageEnabled = false,
+ bool flipTriangle = false,
+ PipelineMultisampleLoadOps multisampleLoadOps = {},
+ uint32_t firstAttachmentLocation = 0) {
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
// Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
@@ -334,16 +344,17 @@
pipelineDescriptor.cFragment.targetCount = numColorAttachments + firstAttachmentLocation;
- wgpu::ColorTargetStateExpandResolveTextureDawn msaaExpandResolveDesc;
- msaaExpandResolveDesc.enabled = true;
+ std::array<wgpu::ColorTargetStateExpandResolveTextureDawn, 16> msaaExpandResolveDescs;
for (uint32_t i = 0; i < numColorAttachments + firstAttachmentLocation; ++i) {
if (i < firstAttachmentLocation) {
pipelineDescriptor.cTargets[i].writeMask = wgpu::ColorWriteMask::None;
pipelineDescriptor.cTargets[i].format = wgpu::TextureFormat::Undefined;
} else {
pipelineDescriptor.cTargets[i].format = kColorFormat;
- if (enableExpandResolveLoadOps.test(i)) {
- pipelineDescriptor.cTargets[i].nextInChain = &msaaExpandResolveDesc;
+ if (multisampleLoadOps[i] != PipelineMultisampleLoadOp::Ignore) {
+ msaaExpandResolveDescs[i].enabled =
+ multisampleLoadOps[i] == PipelineMultisampleLoadOp::ExpandResolveTarget;
+ pipelineDescriptor.cTargets[i].nextInChain = &msaaExpandResolveDescs[i];
}
}
}
@@ -1982,8 +1993,8 @@
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(
/*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false,
/*depthTest=*/true,
- /*enableExpandResolveLoadOpForColor0=*/true,
- /*enableExpandResolveLoadOpForColor1=*/true);
+ /*loadOpForColor0=*/PipelineMultisampleLoadOp::ExpandResolveTarget,
+ /*loadOpForColor1=*/PipelineMultisampleLoadOp::ExpandResolveTarget);
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
index fb26be8..c83130c 100644
--- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -2329,6 +2329,19 @@
@fragment fn main() -> @location(1) vec4f {
return textureLoad(src_tex, vec2u(0, 0), 0);
})");
+
+ fs2TargetsModule = utils::CreateShaderModule(device, R"(
+ struct FragmentOut {
+ @location(0) color0 : vec4f,
+ @location(1) color1 : vec4f,
+ }
+
+ @fragment fn main() -> FragmentOut {
+ var output : FragmentOut;
+ output.color0 = vec4f(0.85);
+ output.color1 = output.color0 * 0.5;
+ return output;
+ })");
}
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
@@ -2349,6 +2362,7 @@
wgpu::ShaderModule fsWithTextureModule;
wgpu::ShaderModule fsWithTextureToTarget1Module;
+ wgpu::ShaderModule fs2TargetsModule;
};
// Test that creating and using a render pipeline with ColorTargetStateExpandResolveTextureDawn
@@ -2529,6 +2543,172 @@
ASSERT_DEVICE_ERROR(encoder.Finish());
}
+// Test that the following scenario works:
+// - Render pipeline and render pass both have two color outputs with resolve targets.
+// - Only second output uses ExpandResolveTexture.
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
+ RenderPipelineAndRenderPassTwoOuputsLoadColor1) {
+ constexpr uint32_t kSampleCount = 4;
+
+ auto msaaTexture1 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+ auto msaaTexture2 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
+ // Create single sampled textures.
+ auto texture1 =
+ CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
+ auto texture2 =
+ CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
+
+ // Create render pass with two outputs. Second one uses ExpandResolveTexture.
+ utils::ComboRenderPassDescriptor renderPassDescriptor(
+ {msaaTexture1.CreateView(), msaaTexture2.CreateView()});
+ renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
+ renderPassDescriptor.cColorAttachments[0].resolveTarget = texture1.CreateView();
+ renderPassDescriptor.cColorAttachments[1].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+ renderPassDescriptor.cColorAttachments[1].resolveTarget = texture2.CreateView();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
+
+ // Create render pipeline with two chained ColorTargetStateExpandResolveTextureDawn.
+ // The first one's enabled flag = true.
+ utils::ComboRenderPipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.vertex.module = vsModule;
+ pipelineDescriptor.cFragment.module = fs2TargetsModule;
+ pipelineDescriptor.multisample.count = kSampleCount;
+
+ wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc[2];
+ pipelineMSAAExpandResolveDesc[0].enabled = false;
+ pipelineDescriptor.cTargets[0].format = kColorFormat;
+ pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc[0];
+ pipelineMSAAExpandResolveDesc[1].enabled = true;
+ pipelineDescriptor.cTargets[1].format = kColorFormat;
+ pipelineDescriptor.cTargets[1].nextInChain = &pipelineMSAAExpandResolveDesc[1];
+ pipelineDescriptor.cFragment.targetCount = 2;
+
+ wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
+ renderPass.SetPipeline(pipeline);
+ renderPass.End();
+
+ encoder.Finish();
+}
+
+// Test that the following render pipeline and render pass are incompatible.
+// - Render pass:
+// - colorAttachments[0]
+// - has resolveTarget.
+// - loadOp = ExpandResolveTexture.
+// - colorAttachments[1]
+// - has resolveTarget.
+// - loadOp = Load.
+// - Render pipeline:
+// - colorTargets[0].nextInChain contains ColorTargetStateExpandResolveTextureDawn.
+// - ColorTargetStateExpandResolveTextureDawn.enabled = true.
+// - colorTargets[1].nextInChain = null.
+// They are incompatible by spec's requirements.
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
+ RenderPipelineAndRenderPassMismatchResolveTargetsError) {
+ constexpr uint32_t kSampleCount = 4;
+
+ auto msaaTexture1 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+ auto msaaTexture2 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
+ // Create single sampled textures.
+ auto texture1 =
+ CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
+ auto texture2 =
+ CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
+
+ // Create render pass with two resolve targets. First one uses ExpandResolveTexture.
+ utils::ComboRenderPassDescriptor renderPassDescriptor(
+ {msaaTexture1.CreateView(), msaaTexture2.CreateView()});
+ renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+ renderPassDescriptor.cColorAttachments[0].resolveTarget = texture1.CreateView();
+ renderPassDescriptor.cColorAttachments[1].loadOp = wgpu::LoadOp::Load;
+ renderPassDescriptor.cColorAttachments[1].resolveTarget = texture2.CreateView();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
+
+ // Create render pipeline (without ColorTargetStateExpandResolveTextureDawn chained to
+ // colorTargets[1])
+ utils::ComboRenderPipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.vertex.module = vsModule;
+ pipelineDescriptor.cFragment.module = fs2TargetsModule;
+ pipelineDescriptor.multisample.count = kSampleCount;
+
+ wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc;
+ pipelineMSAAExpandResolveDesc.enabled = true;
+ pipelineDescriptor.cTargets[0].format = kColorFormat;
+ pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc;
+ pipelineDescriptor.cTargets[1].format = kColorFormat;
+ pipelineDescriptor.cFragment.targetCount = 2;
+
+ wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
+ renderPass.SetPipeline(pipeline);
+ renderPass.End();
+
+ ASSERT_DEVICE_ERROR(encoder.Finish(), testing::HasSubstr("not compatible"));
+}
+
+// Test that the following render pipeline and render pass are incompatible.
+// - Render pass:
+// - colorAttachments[0]
+// - has resolveTarget.
+// - loadOp = ExpandResolveTexture.
+// - colorAttachments[1]
+// - has **no** resolveTarget.
+// - Render pipeline:
+// - colorTargets[0].nextInChain = ColorTargetStateExpandResolveTextureDawn.
+// - ColorTargetStateExpandResolveTextureDawn.enabled = true.
+// - colorTargets[1].nextInChain = ColorTargetStateExpandResolveTextureDawn.
+// - ColorTargetStateExpandResolveTextureDawn.enabled = false.
+// They are incompatible by spec's requirements.
+TEST_F(LoadResolveTexturePipelineDescriptorValidationTest,
+ RenderPipelineAndRenderPassMismatchResolveTargetsError2) {
+ constexpr uint32_t kSampleCount = 4;
+
+ auto msaaTexture1 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+ auto msaaTexture2 = CreateTexture(wgpu::TextureUsage::RenderAttachment, kSampleCount);
+
+ // Create single sampled textures.
+ auto texture1 =
+ CreateTexture(wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, 1);
+
+ // Create render pass with two outputs but one resolve target. First one uses
+ // ExpandResolveTexture.
+ utils::ComboRenderPassDescriptor renderPassDescriptor(
+ {msaaTexture1.CreateView(), msaaTexture2.CreateView()});
+ renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture;
+ renderPassDescriptor.cColorAttachments[0].resolveTarget = texture1.CreateView();
+ renderPassDescriptor.cColorAttachments[1].loadOp = wgpu::LoadOp::Load;
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor);
+
+ // Create render pipeline with two chained ColorTargetStateExpandResolveTextureDawn.
+ // The first one's enabled flag = true.
+ utils::ComboRenderPipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.vertex.module = vsModule;
+ pipelineDescriptor.cFragment.module = fs2TargetsModule;
+ pipelineDescriptor.multisample.count = kSampleCount;
+
+ wgpu::ColorTargetStateExpandResolveTextureDawn pipelineMSAAExpandResolveDesc[2];
+ pipelineMSAAExpandResolveDesc[0].enabled = true;
+ pipelineDescriptor.cTargets[0].format = kColorFormat;
+ pipelineDescriptor.cTargets[0].nextInChain = &pipelineMSAAExpandResolveDesc[0];
+ pipelineMSAAExpandResolveDesc[1].enabled = false;
+ pipelineDescriptor.cTargets[1].format = kColorFormat;
+ pipelineDescriptor.cTargets[1].nextInChain = &pipelineMSAAExpandResolveDesc[1];
+ pipelineDescriptor.cFragment.targetCount = 2;
+
+ wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
+ renderPass.SetPipeline(pipeline);
+ renderPass.End();
+
+ ASSERT_DEVICE_ERROR(encoder.Finish(), testing::HasSubstr("not compatible"));
+}
+
// Bind resolve attachment in a ExpandResolveTexture render pass as texture should result
// in error.
TEST_F(LoadResolveTexturePipelineDescriptorValidationTest, BindColorAttachmentAsTextureError) {