| // Copyright 2019 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include <algorithm> | |
| #include <array> | |
| #include <vector> | |
| #include "dawn/native/DawnNative.h" | |
| #include "src/dawn/tests/DawnTest.h" | |
| #include "src/dawn/utils/ComboRenderPipelineDescriptor.h" | |
| #include "src/dawn/utils/WGPUHelpers.h" | |
| #include "src/utils/assert.h" | |
| namespace dawn { | |
| namespace { | |
| enum class PipelineMultisampleLoadOp { | |
| Ignore, | |
| ExpandResolveTarget, | |
| HasResolveTargetButLoadMultisampled, | |
| }; | |
| using PipelineMultisampleLoadOps = std::array<PipelineMultisampleLoadOp, 16>; | |
| class MultisampledRenderingTest : public DawnTest { | |
| protected: | |
| void SetUp() override { | |
| DawnTest::SetUp(); | |
| InitTexturesForTest(); | |
| // TODO(crbug.com/468061892): Fails on Windows 11/NVIDIA GTX 1660. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows11() && IsNvidia() && IsD3D12() && | |
| IsBackendValidationEnabled()); | |
| // TODO(crbug.com/468061892): Fails on Windows 11/AMD RX 5500 XT w/ | |
| // backend validation. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows11() && IsAMD() && IsD3D12() && | |
| IsBackendValidationEnabled()); | |
| } | |
| void InitTexturesForTest() { | |
| mMultisampledColorTexture = CreateTextureForRenderAttachment(kColorFormat, kSampleCount); | |
| mMultisampledColorView = mMultisampledColorTexture.CreateView(); | |
| mResolveTexture = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| mResolveView = mResolveTexture.CreateView(); | |
| mDepthStencilTexture = CreateTextureForRenderAttachment(kDepthStencilFormat, kSampleCount); | |
| mDepthStencilView = mDepthStencilTexture.CreateView(); | |
| } | |
| wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest( | |
| bool testDepth, | |
| uint32_t sampleMask = 0xFFFFFFFF, | |
| bool alphaToCoverageEnabled = false, | |
| bool flipTriangle = false, | |
| bool enableExpandResolveLoadOp = false) { | |
| const char* kFsOneOutputWithDepth = R"( | |
| struct U { | |
| color : vec4f, | |
| depth : f32, | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| struct FragmentOut { | |
| @location(0) color : vec4f, | |
| @builtin(frag_depth) depth : f32, | |
| } | |
| @fragment fn main() -> FragmentOut { | |
| var output : FragmentOut; | |
| output.color = uBuffer.color; | |
| output.depth = uBuffer.depth; | |
| return output; | |
| })"; | |
| const char* kFsOneOutputWithoutDepth = R"( | |
| struct U { | |
| color : vec4f | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| @fragment fn main() -> @location(0) vec4f { | |
| return uBuffer.color; | |
| })"; | |
| const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth; | |
| PipelineMultisampleLoadOps multisampleLoadOps{}; | |
| if (enableExpandResolveLoadOp) { | |
| multisampleLoadOps[0] = PipelineMultisampleLoadOp::ExpandResolveTarget; | |
| } | |
| return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask, alphaToCoverageEnabled, | |
| flipTriangle, multisampleLoadOps); | |
| } | |
| wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest( | |
| uint32_t sampleMask = 0xFFFFFFFF, | |
| bool alphaToCoverageEnabled = false, | |
| bool depthTest = false, | |
| PipelineMultisampleLoadOp loadOpForColor0 = PipelineMultisampleLoadOp::Ignore, | |
| PipelineMultisampleLoadOp loadOpForColor1 = PipelineMultisampleLoadOp::Ignore) { | |
| const char* kFsTwoOutputs = R"( | |
| struct U { | |
| color0 : vec4f, | |
| color1 : vec4f, | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| struct FragmentOut { | |
| @location(0) color0 : vec4f, | |
| @location(1) color1 : vec4f, | |
| } | |
| @fragment fn main() -> FragmentOut { | |
| var output : FragmentOut; | |
| output.color0 = uBuffer.color0; | |
| output.color1 = uBuffer.color1; | |
| return output; | |
| })"; | |
| PipelineMultisampleLoadOps multisampleLoadOps{}; | |
| multisampleLoadOps[0] = loadOpForColor0; | |
| multisampleLoadOps[1] = loadOpForColor1; | |
| return CreateRenderPipelineForTest(kFsTwoOutputs, 2, depthTest, sampleMask, | |
| alphaToCoverageEnabled, /*flipTriangle=*/false, | |
| multisampleLoadOps); | |
| } | |
| wgpu::RenderPipeline CreateRenderPipelineWithNonZeroLocationOutputForTest( | |
| uint32_t sampleMask = 0xFFFFFFFF, | |
| bool alphaToCoverageEnabled = false, | |
| bool enableExpandResolveLoadOp = false) { | |
| const char* kFsNonZeroLocationOutputs = R"( | |
| struct U { | |
| color : vec4f | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| @fragment fn main() -> @location(1) vec4f { | |
| return uBuffer.color; | |
| })"; | |
| PipelineMultisampleLoadOps multisampleLoadOps{}; | |
| if (enableExpandResolveLoadOp) { | |
| multisampleLoadOps[1] = PipelineMultisampleLoadOp::ExpandResolveTarget; | |
| } | |
| return CreateRenderPipelineForTest(kFsNonZeroLocationOutputs, 1, false, sampleMask, | |
| alphaToCoverageEnabled, /*flipTriangle=*/false, | |
| multisampleLoadOps, 1); | |
| } | |
| wgpu::Texture CreateTextureForRenderAttachment(wgpu::TextureFormat format, | |
| uint32_t sampleCount, | |
| uint32_t mipLevelCount = 1, | |
| uint32_t arrayLayerCount = 1, | |
| bool transientAttachment = false, | |
| bool supportsTextureBinding = false, | |
| bool supportsCopyDst = false, | |
| uint32_t width = kWidth, | |
| uint32_t height = kHeight) { | |
| wgpu::TextureDescriptor descriptor; | |
| descriptor.dimension = wgpu::TextureDimension::e2D; | |
| descriptor.size.width = width << (mipLevelCount - 1); | |
| descriptor.size.height = height << (mipLevelCount - 1); | |
| descriptor.size.depthOrArrayLayers = arrayLayerCount; | |
| descriptor.sampleCount = sampleCount; | |
| descriptor.format = format; | |
| descriptor.mipLevelCount = mipLevelCount; | |
| if (transientAttachment) { | |
| descriptor.usage = | |
| wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment; | |
| } else { | |
| descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; | |
| } | |
| if (supportsTextureBinding) { | |
| descriptor.usage |= wgpu::TextureUsage::TextureBinding; | |
| } | |
| if (supportsCopyDst) { | |
| descriptor.usage |= wgpu::TextureUsage::CopyDst; | |
| } | |
| return device.CreateTexture(&descriptor); | |
| } | |
| void EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder, | |
| const wgpu::RenderPassDescriptor& renderPass, | |
| const wgpu::RenderPipeline& pipeline, | |
| const float* uniformData, | |
| uint32_t uniformDataSize) { | |
| wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( | |
| device, uniformData, uniformDataSize, wgpu::BufferUsage::Uniform); | |
| wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), | |
| {{0, uniformBuffer, 0, uniformDataSize}}); | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.SetPipeline(pipeline); | |
| renderPassEncoder.SetBindGroup(0, bindGroup); | |
| renderPassEncoder.Draw(3); | |
| renderPassEncoder.End(); | |
| } | |
| void EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder, | |
| const wgpu::RenderPassDescriptor& renderPass, | |
| const wgpu::RenderPipeline& pipeline, | |
| const wgpu::Color& color) { | |
| const float uniformData[4] = {static_cast<float>(color.r), static_cast<float>(color.g), | |
| static_cast<float>(color.b), static_cast<float>(color.a)}; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, uniformData, | |
| sizeof(float) * 4); | |
| } | |
| utils::ComboRenderPassDescriptor CreateComboRenderPassDescriptorForTest( | |
| std::initializer_list<wgpu::TextureView> colorViews, | |
| std::initializer_list<wgpu::TextureView> resolveTargetViews, | |
| wgpu::LoadOp colorLoadOp, | |
| wgpu::LoadOp depthStencilLoadOp, | |
| bool hasDepthStencilAttachment) { | |
| DAWN_ASSERT(colorViews.size() == resolveTargetViews.size()); | |
| constexpr float kClearDepth = 1.0f; | |
| utils::ComboRenderPassDescriptor renderPass(colorViews); | |
| uint32_t i = 0; | |
| for (const wgpu::TextureView& resolveTargetView : resolveTargetViews) { | |
| renderPass.cColorAttachments[i].loadOp = colorLoadOp; | |
| renderPass.cColorAttachments[i].clearValue = kClearColor; | |
| renderPass.cColorAttachments[i].resolveTarget = resolveTargetView; | |
| ++i; | |
| } | |
| renderPass.cDepthStencilAttachmentInfo.depthClearValue = kClearDepth; | |
| renderPass.cDepthStencilAttachmentInfo.depthLoadOp = depthStencilLoadOp; | |
| if (hasDepthStencilAttachment) { | |
| renderPass.cDepthStencilAttachmentInfo.view = mDepthStencilView; | |
| renderPass.depthStencilAttachment = &renderPass.cDepthStencilAttachmentInfo; | |
| } | |
| return renderPass; | |
| } | |
| void VerifyResolveTarget(const wgpu::Color& inputColor, | |
| wgpu::Texture resolveTexture, | |
| uint32_t mipmapLevel = 0, | |
| uint32_t arrayLayer = 0, | |
| const float msaaCoverage = 0.5f, | |
| uint32_t x = (kWidth - 1) / 2, | |
| uint32_t y = (kHeight - 1) / 2) { | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(inputColor, msaaCoverage); | |
| EXPECT_TEXTURE_EQ(&expectedColor, resolveTexture, {x, y, arrayLayer}, {1, 1}, mipmapLevel, | |
| wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| constexpr static uint32_t kWidth = 3; | |
| constexpr static uint32_t kHeight = 3; | |
| constexpr static uint32_t kSampleCount = 4; | |
| constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; | |
| constexpr static wgpu::TextureFormat kDepthStencilFormat = | |
| wgpu::TextureFormat::Depth24PlusStencil8; | |
| constexpr static uint32_t kFirstSampleMaskBit = 0x00000001; | |
| constexpr static uint32_t kSecondSampleMaskBit = 0x00000002; | |
| constexpr static uint32_t kThirdSampleMaskBit = 0x00000004; | |
| constexpr static uint32_t kFourthSampleMaskBit = 0x00000008; | |
| constexpr static wgpu::Color kClearColor = {0.0f, 0.0f, 0.0f, 0.0f}; | |
| constexpr static wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| constexpr static wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; | |
| wgpu::Texture mMultisampledColorTexture; | |
| wgpu::TextureView mMultisampledColorView; | |
| wgpu::Texture mResolveTexture; | |
| wgpu::TextureView mResolveView; | |
| 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, | |
| PipelineMultisampleLoadOps multisampleLoadOps = {}, | |
| uint32_t firstAttachmentLocation = 0) { | |
| utils::ComboRenderPipelineDescriptor pipelineDescriptor; | |
| // Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal, | |
| // only two of the samples will be touched. | |
| const char* vs = R"( | |
| @vertex | |
| fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { | |
| var pos = array( | |
| vec2f(-1.0, 1.0), | |
| vec2f( 1.0, 1.0), | |
| vec2f( 1.0, -1.0) | |
| ); | |
| return vec4f(pos[VertexIndex], 0.0, 1.0); | |
| })"; | |
| // Draw a bottom-left triangle. | |
| const char* vsFlipped = R"( | |
| @vertex | |
| fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4f { | |
| var pos = array( | |
| vec2f(-1.0, 1.0), | |
| vec2f( 1.0, 1.0), | |
| vec2f(-1.0, -1.0) | |
| ); | |
| return vec4f(pos[VertexIndex], 0.0, 1.0); | |
| })"; | |
| if (flipTriangle) { | |
| pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, vsFlipped); | |
| } else { | |
| pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, vs); | |
| } | |
| pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, fs); | |
| if (hasDepthStencilAttachment) { | |
| wgpu::DepthStencilState* depthStencil = | |
| pipelineDescriptor.EnableDepthStencil(kDepthStencilFormat); | |
| depthStencil->depthWriteEnabled = wgpu::OptionalBool::True; | |
| depthStencil->depthCompare = wgpu::CompareFunction::Less; | |
| } | |
| pipelineDescriptor.multisample.count = kSampleCount; | |
| pipelineDescriptor.multisample.mask = sampleMask; | |
| pipelineDescriptor.multisample.alphaToCoverageEnabled = alphaToCoverageEnabled; | |
| pipelineDescriptor.cFragment.targetCount = numColorAttachments + firstAttachmentLocation; | |
| 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 (multisampleLoadOps[i] != PipelineMultisampleLoadOp::Ignore) { | |
| msaaExpandResolveDescs[i].enabled = | |
| multisampleLoadOps[i] == PipelineMultisampleLoadOp::ExpandResolveTarget; | |
| pipelineDescriptor.cTargets[i].nextInChain = &msaaExpandResolveDescs[i]; | |
| } | |
| } | |
| } | |
| wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); | |
| return pipeline; | |
| } | |
| utils::RGBA8 ExpectedMSAAColor(const wgpu::Color color, const double msaaCoverage) { | |
| utils::RGBA8 result; | |
| result.r = static_cast<uint8_t>(std::min(255.0, 256 * color.r * msaaCoverage)); | |
| result.g = static_cast<uint8_t>(std::min(255.0, 256 * color.g * msaaCoverage)); | |
| result.b = static_cast<uint8_t>(std::min(255.0, 256 * color.b * msaaCoverage)); | |
| result.a = static_cast<uint8_t>(std::min(255.0, 256 * color.a * msaaCoverage)); | |
| return result; | |
| } | |
| }; | |
| // Test using one multisampled color attachment with resolve target can render correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| DAWN_SUPPRESS_TEST_IF(IsWARP()); | |
| constexpr bool kTestDepth = false; | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| // storeOp should not affect the result in the resolve target. | |
| for (wgpu::StoreOp storeOp : {wgpu::StoreOp::Store, wgpu::StoreOp::Discard}) { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| renderPass.cColorAttachments[0].storeOp = storeOp; | |
| std::array<float, 4> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), | |
| kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| } | |
| } | |
| // Test multisampled rendering with depth test works correctly. | |
| TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| constexpr bool kTestDepth = true; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| // In first render pass we draw a green triangle with depth value == 0.2f. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = | |
| CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true); | |
| std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color | |
| 0.2f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In second render pass we draw a red triangle with depth value == 0.5f. | |
| // This red triangle should not be displayed because it is behind the green one that is drawn in | |
| // the last render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color | |
| 0.5f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs | |
| // correctly with depth test. | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| } | |
| // Test rendering into a multisampled color attachment and doing MSAA resolve in another render pass | |
| // works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1549) Fails on Qualcomm-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm()); | |
| constexpr bool kTestDepth = false; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| // In first render pass we draw a green triangle and do not set the resolve target. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass we ony do MSAA resolve with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| } | |
| // Test doing MSAA resolve into multiple resolve targets works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); | |
| constexpr bool kTestDepth = false; | |
| // Draw a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, mResolveTexture); | |
| VerifyResolveTarget(kGreen, resolveTexture2); | |
| } | |
| // Test that resolving only one of multiple MSAA targets works correctly. (dawn:1550) | |
| TEST_P(MultisampledRenderingTest, ResolveOneOfMultipleTargets) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| DAWN_SUPPRESS_TEST_IF(IsWARP()); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); | |
| constexpr bool kTestDepth = false; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), static_cast<float>(kRed.b), | |
| static_cast<float>(kRed.a), static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| // Draws a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment. | |
| // Do MSAA resolve on the first render target. | |
| { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {mResolveView, nullptr}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, mResolveTexture); | |
| } | |
| // Do MSAA resolve on the second render target. | |
| { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {nullptr, mResolveView}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| } | |
| } | |
| // Test that resolving a single render target at a non-zero location works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoNonZeroLocation) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithNonZeroLocationOutputForTest(); | |
| constexpr bool kTestDepth = false; | |
| // Draws a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment. | |
| // Do MSAA resolve on the first render target. | |
| { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {nullptr, mMultisampledColorView}, {nullptr, mResolveView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kRed); | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, mResolveTexture); | |
| } | |
| } | |
| // Test doing MSAA resolve on one multisampled texture twice works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1549) Fails on Qualcomm-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm()); | |
| constexpr bool kTestDepth = false; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| // In first render pass we draw a green triangle and specify mResolveView as the resolve target. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass we do MSAA resolve into resolveTexture2. | |
| { | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {resolveView2}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| VerifyResolveTarget(kGreen, resolveTexture2); | |
| } | |
| // Test using a layer of a 2D texture as resolve target works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| constexpr uint32_t kBaseMipLevel = 2; | |
| wgpu::TextureViewDescriptor textureViewDescriptor; | |
| textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; | |
| textureViewDescriptor.format = kColorFormat; | |
| textureViewDescriptor.baseArrayLayer = 0; | |
| textureViewDescriptor.arrayLayerCount = 1; | |
| textureViewDescriptor.mipLevelCount = 1; | |
| textureViewDescriptor.baseMipLevel = kBaseMipLevel; | |
| wgpu::Texture resolveTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel + 1, 1); | |
| wgpu::TextureView resolveView = resolveTexture.CreateView(&textureViewDescriptor); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| constexpr bool kTestDepth = false; | |
| // Draw a green triangle and do MSAA resolve. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {resolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, resolveTexture, kBaseMipLevel, 0); | |
| } | |
| // Test using a level or a layer of a 2D array texture as resolve target works correctly. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm()); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::TextureViewDescriptor baseTextureViewDescriptor; | |
| baseTextureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; | |
| baseTextureViewDescriptor.format = kColorFormat; | |
| baseTextureViewDescriptor.arrayLayerCount = 1; | |
| baseTextureViewDescriptor.mipLevelCount = 1; | |
| // Create resolveTexture1 with only 1 mipmap level. | |
| constexpr uint32_t kBaseArrayLayer1 = 2; | |
| constexpr uint32_t kBaseMipLevel1 = 0; | |
| wgpu::Texture resolveTexture1 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel1 + 1, kBaseArrayLayer1 + 1); | |
| wgpu::TextureViewDescriptor resolveViewDescriptor1 = baseTextureViewDescriptor; | |
| resolveViewDescriptor1.baseArrayLayer = kBaseArrayLayer1; | |
| resolveViewDescriptor1.baseMipLevel = kBaseMipLevel1; | |
| wgpu::TextureView resolveView1 = resolveTexture1.CreateView(&resolveViewDescriptor1); | |
| // Create resolveTexture2 with (kBaseMipLevel2 + 1) mipmap levels and resolve into its last | |
| // mipmap level. | |
| constexpr uint32_t kBaseArrayLayer2 = 5; | |
| constexpr uint32_t kBaseMipLevel2 = 3; | |
| wgpu::Texture resolveTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, kBaseMipLevel2 + 1, kBaseArrayLayer2 + 1); | |
| wgpu::TextureViewDescriptor resolveViewDescriptor2 = baseTextureViewDescriptor; | |
| resolveViewDescriptor2.baseArrayLayer = kBaseArrayLayer2; | |
| resolveViewDescriptor2.baseMipLevel = kBaseMipLevel2; | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(&resolveViewDescriptor2); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); | |
| constexpr bool kTestDepth = false; | |
| // Draw a red triangle to the first color attachment, and a green triangle to the second color | |
| // attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {resolveView1, resolveView2}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 | |
| kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, resolveTexture1, kBaseMipLevel1, kBaseArrayLayer1); | |
| VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2); | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with a non-default sample mask. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| constexpr bool kTestDepth = false; | |
| // The second and third samples are included, | |
| // only the second one is covered by the triangle. | |
| constexpr uint32_t kSampleMask = kSecondSampleMaskBit | kThirdSampleMaskBit; | |
| constexpr float kMSAACoverage = 0.25f; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage); | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with the final sample mask empty. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithEmptyFinalSampleMask) { | |
| constexpr bool kTestDepth = false; | |
| // The third and fourth samples are included, | |
| // none of which is covered by the triangle. | |
| constexpr uint32_t kSampleMask = kThirdSampleMaskBit | kFourthSampleMaskBit; | |
| constexpr float kMSAACoverage = 0.00f; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage); | |
| } | |
| // Test doing MSAA resolve into multiple resolve targets works correctly with a non-default sample | |
| // mask. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithSampleMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| /// TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| // TODO(crbug.com/454796308): Produces incorrect output on Mac/Intel with | |
| // the WebGPU on WebGPU on Metal backend. | |
| DAWN_SUPPRESS_TEST_IF(IsMacOS() && IsIntel() && IsWebGPUOn(wgpu::BackendType::Metal)); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| // The first and fourth samples are included, | |
| // only the first one is covered by the triangle. | |
| constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kFourthSampleMaskBit; | |
| constexpr float kMSAACoverage = 0.25f; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(kSampleMask); | |
| constexpr bool kTestDepth = false; | |
| // Draw a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 | |
| kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // TODO(crbug.com/dawn/1462): Work around that a sample mask of zero is used for all | |
| // color targets except the last one. | |
| VerifyResolveTarget( | |
| HasToggleEnabled("no_workaround_sample_mask_becomes_zero_for_all_but_last_color_target") | |
| ? wgpu::Color{} | |
| : kRed, | |
| mResolveTexture, 0, 0, kMSAACoverage); | |
| VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage); | |
| } | |
| // Test multisampled rendering with depth test works correctly with a non-default sample mask. | |
| TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndSampleMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1549) Fails on Qualcomm-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm()); | |
| constexpr bool kTestDepth = true; | |
| // The second sample is included in the first render pass and it's covered by the triangle. | |
| constexpr uint32_t kSampleMaskGreen = kSecondSampleMaskBit; | |
| // The first and second samples are included in the second render pass, | |
| // both are covered by the triangle. | |
| constexpr uint32_t kSampleMaskRed = kFirstSampleMaskBit | kSecondSampleMaskBit; | |
| constexpr float kMSAACoverage = 0.50f; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipelineGreen = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskGreen); | |
| wgpu::RenderPipeline pipelineRed = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskRed); | |
| // In first render pass we draw a green triangle with depth value == 0.2f. | |
| // We will only write to the second sample. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = | |
| CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true); | |
| std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color | |
| 0.2f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(), | |
| kSize); | |
| } | |
| // In second render pass we draw a red triangle with depth value == 0.5f. | |
| // We will only write to the first sample, since the second one is red with a smaller depth | |
| // value. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color | |
| 0.5f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(), | |
| kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0, | |
| (kGreen.b + kRed.b) / 2.0, | |
| (kGreen.a + kRed.a) / 2.0}; | |
| // The color of the pixel in the middle of mResolveTexture should be half green and half | |
| // red if MSAA resolve runs correctly with depth test. | |
| VerifyResolveTarget(kHalfGreenHalfRed, mResolveTexture, 0, 0, kMSAACoverage); | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with non-default sample mask and shader-output mask. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMaskAndShaderOutputMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // sample_mask is not supported in compat. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/673): Work around or enforce via validation that sample variables are not | |
| // supported on some platforms. | |
| DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_sample_variables")); | |
| constexpr bool kTestDepth = false; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // The second and third samples are included in the shader-output mask. | |
| // The first and third samples are included in the sample mask. | |
| // Since we're now looking at a fully covered pixel, the rasterization mask | |
| // includes all the samples. | |
| // Thus the final mask includes only the third sample. | |
| constexpr float kMSAACoverage = 0.25f; | |
| constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit; | |
| const char* fs = R"( | |
| struct U { | |
| color : vec4f | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| struct FragmentOut { | |
| @location(0) color : vec4f, | |
| @builtin(sample_mask) sampleMask : u32, | |
| } | |
| @fragment fn main() -> FragmentOut { | |
| var output : FragmentOut; | |
| output.color = uBuffer.color; | |
| output.sampleMask = 6u; | |
| return output; | |
| })"; | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 1, false, kSampleMask); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(kGreen, kMSAACoverage); | |
| EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1}); | |
| } | |
| // Test doing MSAA resolve into multiple resolve targets works correctly with a non-default | |
| // shader-output mask. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithShaderOutputMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // sample_mask is not supported in compat. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/673): Work around or enforce via validation that sample variables are not | |
| // supported on some platforms. | |
| DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_sample_variables")); | |
| // TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| // TODO(crbug.com/454796308): Produces incorrect output on Mac/Intel with | |
| // the WebGPU on WebGPU on Metal backend. | |
| DAWN_SUPPRESS_TEST_IF(IsMacOS() && IsIntel() && IsWebGPUOn(wgpu::BackendType::Metal)); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // The second and third samples are included in the shader-output mask, | |
| // only the first one is covered by the triangle. | |
| constexpr float kMSAACoverage = 0.25f; | |
| const char* fs = R"( | |
| struct U { | |
| color0 : vec4f, | |
| color1 : vec4f, | |
| } | |
| @group(0) @binding(0) var<uniform> uBuffer : U; | |
| struct FragmentOut { | |
| @location(0) color0 : vec4f, | |
| @location(1) color1 : vec4f, | |
| @builtin(sample_mask) sampleMask : u32, | |
| } | |
| @fragment fn main() -> FragmentOut { | |
| var output : FragmentOut; | |
| output.color0 = uBuffer.color0; | |
| output.color1 = uBuffer.color1; | |
| output.sampleMask = 6u; | |
| return output; | |
| })"; | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 2, false); | |
| constexpr bool kTestDepth = false; | |
| // Draw a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 | |
| kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // TODO(crbug.com/dawn/1462): Work around that a sample mask of zero is used for all | |
| // color targets except the last one. | |
| VerifyResolveTarget( | |
| HasToggleEnabled("no_workaround_sample_mask_becomes_zero_for_all_but_last_color_target") | |
| ? wgpu::Color{} | |
| : kRed, | |
| mResolveTexture, 0, 0, kMSAACoverage); | |
| VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage); | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with alphaToCoverageEnabled. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverage) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(crbug.com/458113207): Flaky w/ WARP. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsWARP()); | |
| constexpr bool kTestDepth = false; | |
| constexpr uint32_t kSampleMask = 0xFFFFFFFF; | |
| constexpr bool kAlphaToCoverageEnabled = true; | |
| // Setting alpha <= 0 must result in alpha-to-coverage mask being empty. | |
| // Setting alpha = 0.5f should result in alpha-to-coverage mask including half the samples, | |
| // but this is not guaranteed by the spec. The Metal spec seems to guarantee that this is | |
| // indeed the case. | |
| // Setting alpha >= 1 must result in alpha-to-coverage mask being full. | |
| for (float alpha : {-1.0f, 0.0f, 0.5f, 1.0f, 2.0f}) { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| kTestDepth, kSampleMask, kAlphaToCoverageEnabled); | |
| const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // For alpha = {0, 0.5, 1} we expect msaaCoverage to correspond to the value of alpha. | |
| float msaaCoverage = alpha; | |
| if (alpha < 0.0f) { | |
| msaaCoverage = 0.0f; | |
| } | |
| if (alpha > 1.0f) { | |
| msaaCoverage = 1.0f; | |
| } | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(kGreen, msaaCoverage); | |
| EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1}); | |
| } | |
| } | |
| // Test doing MSAA resolve into multiple resolve targets works correctly with | |
| // alphaToCoverage. The alphaToCoverage mask is computed based on the alpha | |
| // component of the first color render attachment. | |
| TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithAlphaToCoverage) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Workaround introduces a bug on Qualcomm GPUs, but is necessary for ARM GPUs. | |
| DAWN_TEST_UNSUPPORTED_IF(IsAndroid() && IsQualcomm() && | |
| HasToggleEnabled("resolve_multiple_attachments_in_separate_passes")); | |
| // TODO(crbug.com/458113207): Flaky w/ WARP. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsWARP()); | |
| wgpu::TextureView multisampledColorView2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount).CreateView(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| constexpr uint32_t kSampleMask = 0xFFFFFFFF; | |
| constexpr float kMSAACoverage = 0.50f; | |
| constexpr bool kAlphaToCoverageEnabled = true; | |
| // The alpha-to-coverage mask should not depend on the alpha component of the | |
| // second color render attachment. | |
| // We test alpha = 0.51f and 0.99f instead of 0.50f and 1.00f because there are some rounding | |
| // differences on QuadroP400 devices in that case. | |
| for (float alpha : {0.0f, 0.51f, 0.99f}) { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = | |
| CreateRenderPipelineWithTwoOutputsForTest(kSampleMask, kAlphaToCoverageEnabled); | |
| constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.51f}; | |
| const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha}; | |
| constexpr bool kTestDepth = false; | |
| // Draw a red triangle to the first color attachment, and a blue triangle to the second | |
| // color attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), | |
| kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // Alpha to coverage affects both the color outputs, but the mask is computed | |
| // using only the first one. | |
| utils::RGBA8 expectedRed = ExpectedMSAAColor(kRed, kMSAACoverage); | |
| utils::RGBA8 expectedGreen = ExpectedMSAAColor(kGreen, kMSAACoverage); | |
| EXPECT_TEXTURE_EQ(&expectedRed, mResolveTexture, {1, 0}, {1, 1}, | |
| /* level */ 0, wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| EXPECT_TEXTURE_EQ(&expectedGreen, resolveTexture2, {1, 0}, {1, 1}, | |
| /* level */ 0, wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| } | |
| // Test multisampled rendering with depth test works correctly with alphaToCoverage. | |
| TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndAlphaToCoverage) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1549) Fails on Qualcomm-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsQualcomm()); | |
| constexpr bool kTestDepth = true; | |
| constexpr uint32_t kSampleMask = 0xFFFFFFFF; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipelineGreen = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask, true); | |
| wgpu::RenderPipeline pipelineRed = | |
| CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask, false); | |
| // We test alpha = 0.51f and 0.81f instead of 0.50f and 0.80f because there are some | |
| // rounding differences on QuadroP400 devices in that case. | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.51f}; | |
| constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.81f}; | |
| // In first render pass we draw a green triangle with depth value == 0.2f. | |
| // We will only write to half the samples since the alphaToCoverage mode | |
| // is enabled for that render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color | |
| 0.2f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(), | |
| kSize); | |
| } | |
| // In second render pass we draw a red triangle with depth value == 0.5f. | |
| // We will write to all the samples since the alphaToCoverageMode is diabled for | |
| // that render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color | |
| 0.5f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(), | |
| kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0, | |
| (kGreen.b + kRed.b) / 2.0, | |
| (kGreen.a + kRed.a) / 2.0}; | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(kHalfGreenHalfRed, 1.0f); | |
| EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1}, | |
| /* level */ 0, wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with alphaToCoverageEnabled and a sample mask. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverageAndSampleMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Fails on ARM-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsARM()); | |
| // TODO(dawn:491): This doesn't work on non-Apple GPU Metal, because we're using both | |
| // the shader-output mask (emulting the sampleMask from RenderPipeline) and alpha-to-coverage | |
| // at the same time. See the issue: https://github.com/gpuweb/gpuweb/issues/959. | |
| DAWN_SUPPRESS_TEST_IF(IsMetal() && !IsApple()); | |
| // TODO(crbug.com/458113207): Flaky w/ WARP. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsWARP()); | |
| constexpr bool kTestDepth = false; | |
| constexpr float kMSAACoverage = 0.50f; | |
| constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit; | |
| constexpr bool kAlphaToCoverageEnabled = true; | |
| // For those values of alpha we expect the proportion of samples to be covered | |
| // to correspond to the value of alpha. | |
| // We're assuming in the case of alpha = 0.50f that the implementation | |
| // dependendent algorithm will choose exactly one of the first and third samples. | |
| for (float alpha : {0.0f, 0.50f, 1.00f}) { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| kTestDepth, kSampleMask, kAlphaToCoverageEnabled); | |
| const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha - 0.01f}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(kGreen, kMSAACoverage * alpha); | |
| EXPECT_TEXTURE_EQ(&expectedColor, mResolveTexture, {1, 0}, {1, 1}, | |
| /* level */ 0, wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| } | |
| // Test using one multisampled color attachment with resolve target can render correctly | |
| // with alphaToCoverageEnabled and a rasterization mask. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithAlphaToCoverageAndRasterizationMask) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(dawn:1550) Fails on ARM-based Android devices. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsARM()); | |
| // TODO(crbug.com/458113207): Flaky w/ WARP. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsWARP()); | |
| // TODO(crbug.com/500793601): Fails on Windows 11/AMD RX 5500 XT. | |
| DAWN_TEST_UNSUPPORTED_IF(IsWindows11() && IsAMD()); | |
| constexpr bool kTestDepth = false; | |
| constexpr float kMSAACoverage = 0.50f; | |
| constexpr uint32_t kSampleMask = 0xFFFFFFFF; | |
| constexpr bool kAlphaToCoverageEnabled = true; | |
| constexpr bool kFlipTriangle = true; | |
| // For those values of alpha we expect the proportion of samples to be covered | |
| // to correspond to the value of alpha. | |
| // We're assuming in the case of alpha = 0.50f that the implementation | |
| // dependendent algorithm will choose exactly one of the samples covered by the | |
| // triangle. | |
| for (float alpha : {0.0f, 0.50f, 1.00f}) { | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| kTestDepth, kSampleMask, kAlphaToCoverageEnabled, kFlipTriangle); | |
| const wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, alpha - 0.01f}; | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage * alpha); | |
| } | |
| } | |
| // Test that setting a scissor rect does not affect multisample resolve. | |
| TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithScissor) { | |
| // TODO(crbug.com/522869943): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| constexpr bool kTestDepth = false; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| constexpr wgpu::Color kRed = {1.0f, 0.0f, 0.0f, 1.0f}; | |
| constexpr wgpu::Color kGreen = {0.0f, 1.0f, 0.0f, 1.0f}; | |
| // Draw a green triangle, set a scissor for the bottom row of pixels, then draw a red triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| const float redUniformData[4] = {1.0f, 0.0f, 0.0f, 1.0f}; | |
| const float greenUniformData[4] = {0.0f, 1.0f, 0.0f, 1.0f}; | |
| const size_t uniformSize = sizeof(float) * 4; | |
| wgpu::Buffer redUniformBuffer = utils::CreateBufferFromData( | |
| device, redUniformData, uniformSize, wgpu::BufferUsage::Uniform); | |
| wgpu::Buffer greenUniformBuffer = utils::CreateBufferFromData( | |
| device, greenUniformData, uniformSize, wgpu::BufferUsage::Uniform); | |
| wgpu::BindGroup redBindGroup = utils::MakeBindGroup( | |
| device, pipeline.GetBindGroupLayout(0), {{0, redUniformBuffer, 0, uniformSize}}); | |
| wgpu::BindGroup greenBindGroup = utils::MakeBindGroup( | |
| device, pipeline.GetBindGroupLayout(0), {{0, greenUniformBuffer, 0, uniformSize}}); | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.SetPipeline(pipeline); | |
| renderPassEncoder.SetBindGroup(0, greenBindGroup); | |
| renderPassEncoder.Draw(3); | |
| renderPassEncoder.SetScissorRect(0, 0, 3, 1); | |
| renderPassEncoder.SetBindGroup(0, redBindGroup); | |
| renderPassEncoder.Draw(3); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // The bottom-right pixel should be red, but the pixel just above it should be green. | |
| constexpr float kMSAACoverage = 1.0f; | |
| constexpr uint32_t kRedX = 2; | |
| constexpr uint32_t kRedY = 0; | |
| constexpr uint32_t kGreenX = 2; | |
| constexpr uint32_t kGreenY = 1; | |
| VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage, kRedX, kRedY); | |
| VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage, kGreenX, kGreenY); | |
| } | |
| class MultisampledRenderingWithEmptyPassWorkaroundTest : public MultisampledRenderingTest {}; | |
| TEST_P(MultisampledRenderingWithEmptyPassWorkaroundTest, ResolveOneMultisampledTextureTwice) { | |
| constexpr bool kTestDepth = false; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| // In first render pass clear the color view to green triangle resolve. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| kTestDepth); | |
| renderPass.cColorAttachments[0].clearValue = kGreen; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| // No work done during the pass | |
| renderPassEncoder.End(); | |
| } | |
| // In second render pass load the previously cleared color view and resolve. | |
| { | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {mMultisampledColorView}, {resolveView2}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| kTestDepth); | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| // No work done during the pass | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| constexpr float kMSAACoverage = 1.0f; | |
| VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage); | |
| VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage); | |
| } | |
| class MultisampledRenderingWithTransientAttachmentTest : public MultisampledRenderingTest { | |
| protected: | |
| void SetUp() override { | |
| MultisampledRenderingTest::SetUp(); | |
| // TODO(crbug.com/522868204): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| } | |
| }; | |
| // Test using one multisampled color transient attachment with resolve target can render correctly. | |
| TEST_P(MultisampledRenderingWithTransientAttachmentTest, ResolveTransientAttachmentInto2DTexture) { | |
| constexpr bool kTestDepth = false; | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); | |
| auto transientMultisampledColorTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount, | |
| /*mipLevelCount=*/1, | |
| /*arrayLayerCount=*/1, | |
| /*transientAttachment=*/true); | |
| auto transientMultisampledColorView = transientMultisampledColorTexture.CreateView(); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // Draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {transientMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, kTestDepth); | |
| // Note: It is not possible to store into a transient attachment. | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 4> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, mResolveTexture); | |
| } | |
| // Test using multiple multisampled color transient attachment with resolve target can render | |
| // correctly. | |
| TEST_P(MultisampledRenderingWithTransientAttachmentTest, | |
| ResolveMultipleTransientAttachmentsInto2DTexture) { | |
| constexpr bool kTestDepth = false; | |
| auto transientMultisampledColorTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount, | |
| /*mipLevelCount=*/1, | |
| /*arrayLayerCount=*/1, | |
| /*transientAttachment=*/true); | |
| auto transientMultisampledColorView = transientMultisampledColorTexture.CreateView(); | |
| auto transientMultisampledColorTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, kSampleCount, | |
| /*mipLevelCount=*/1, | |
| /*arrayLayerCount=*/1, | |
| /*transientAttachment=*/true); | |
| auto transientMultisampledColorView2 = transientMultisampledColorTexture2.CreateView(); | |
| wgpu::Texture resolveTexture2 = CreateTextureForRenderAttachment(kColorFormat, 1); | |
| wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); | |
| // Draw a red triangle to the first color attachment, and a blue triangle to the second color | |
| // attachment, and do MSAA resolve on two render targets in one render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {transientMultisampledColorView, transientMultisampledColorView2}, | |
| {mResolveView, resolveView2}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); | |
| // Note: It is not possible to store into a transient attachment. | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, mResolveTexture); | |
| VerifyResolveTarget(kGreen, resolveTexture2); | |
| } | |
| class MultisampledRenderToSingleSampledTest : public MultisampledRenderingTest { | |
| void SetUp() override { | |
| MultisampledRenderingTest::SetUp(); | |
| // TODO(crbug.com/522872465): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // Skip all tests if the MSAARenderToSingleSampled feature is not supported. | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::MSAARenderToSingleSampled})); | |
| } | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| std::vector<wgpu::FeatureName> requiredFeatures = {}; | |
| if (SupportsFeatures({wgpu::FeatureName::MSAARenderToSingleSampled})) { | |
| requiredFeatures.push_back(wgpu::FeatureName::MSAARenderToSingleSampled); | |
| } | |
| return requiredFeatures; | |
| } | |
| }; | |
| // Test rendering into a color attachment and start another render pass with LoadOp::Load | |
| // will have the content preserved. | |
| TEST_P(MultisampledRenderToSingleSampledTest, DrawThenLoad) { | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| wgpu::DawnRenderPassSampleCount renderPassSampleCount; | |
| renderPassSampleCount.sampleCount = kSampleCount; | |
| // In first render pass we draw a green triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.nextInChain = &renderPassSampleCount; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass we only use LoadOp::Load with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| /*testDepth=*/false); | |
| renderPass.nextInChain = &renderPassSampleCount; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| } | |
| // Test clear a color attachment (without implicit sample count) and start another render pass (with | |
| // implicit sample count) with LoadOp::Load plus additional drawing works correctly. The final | |
| // result should contain the combination of the loaded content from 1st pass and the 2nd pass. | |
| TEST_P(MultisampledRenderToSingleSampledTest, ClearThenLoadThenDraw) { | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| constexpr wgpu::Color kRed = {1.0f, 0.0f, 0.0f, 1.0f}; | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| wgpu::DawnRenderPassSampleCount renderPassSampleCount; | |
| renderPassSampleCount.sampleCount = kSampleCount; | |
| // In first render pass we clear to red without using implicit sample count. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].clearValue = kRed; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| // In second render pass (with implicit sample count) we use LoadOp::Load then draw a green | |
| // triangle. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| /*testDepth=*/false); | |
| renderPass.nextInChain = &renderPassSampleCount; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| auto commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0, | |
| (kGreen.b + kRed.b) / 2.0, | |
| (kGreen.a + kRed.a) / 2.0}; | |
| utils::RGBA8 expectedColor = ExpectedMSAAColor(kHalfGreenHalfRed, 1.0f); | |
| EXPECT_TEXTURE_EQ(&expectedColor, singleSampledTexture, {1, 1}, {1, 1}, | |
| /* level */ 0, wgpu::TextureAspect::All, /* bytesPerRow */ 0, | |
| /* tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| // Test multisampled rendering with depth test works correctly. | |
| TEST_P(MultisampledRenderToSingleSampledTest, DrawWithDepthTest) { | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/true, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| wgpu::DawnRenderPassSampleCount renderPassSampleCount; | |
| renderPassSampleCount.sampleCount = kSampleCount; | |
| // In first render pass we draw a green triangle with depth value == 0.2f. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| /*testDepth=*/true); | |
| renderPass.nextInChain = &renderPassSampleCount; | |
| std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color | |
| 0.2f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In second render pass we draw a red triangle with depth value == 0.5f. | |
| // This red triangle should not be displayed because it is behind the green one that is drawn in | |
| // the last render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {singleSampledTextureView}, {nullptr}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, | |
| /*testDepth=*/true); | |
| renderPass.nextInChain = &renderPassSampleCount; | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color | |
| 0.5f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs | |
| // correctly with depth test. | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| } | |
| struct Point { | |
| uint32_t x; | |
| uint32_t y; | |
| wgpu::Color color; | |
| float msaaCoverage = 1.0f; | |
| }; | |
| class DawnLoadResolveTextureTest : public MultisampledRenderingTest { | |
| protected: | |
| void SetUp() override { | |
| MultisampledRenderingTest::SetUp(); | |
| // Skip all tests if the DawnLoadResolveTexture feature is not supported. | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnLoadResolveTexture})); | |
| } | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| std::vector<wgpu::FeatureName> requiredFeatures = {}; | |
| if (SupportsFeatures({wgpu::FeatureName::DawnLoadResolveTexture})) { | |
| requiredFeatures.push_back(wgpu::FeatureName::DawnLoadResolveTexture); | |
| } | |
| if (SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})) { | |
| requiredFeatures.push_back(wgpu::FeatureName::DawnPartialLoadResolveTexture); | |
| } | |
| if (SupportsFeatures({wgpu::FeatureName::TransientAttachments})) { | |
| requiredFeatures.push_back(wgpu::FeatureName::TransientAttachments); | |
| } | |
| return requiredFeatures; | |
| } | |
| bool HasResolveMultipleAttachmentInSeparatePassesToggle() { | |
| return HasToggleEnabled("resolve_multiple_attachments_in_separate_passes"); | |
| } | |
| void TestExpandAndResolveWithRect(const wgpu::RenderPassDescriptorResolveRect& rect, | |
| const std::vector<Point>& points) { | |
| DAWN_TEST_UNSUPPORTED_IF( | |
| !SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| const uint32_t resolveWidth = 8; | |
| const uint32_t resolveHeight = 8; | |
| auto multiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ resolveWidth, | |
| /*height*/ resolveHeight); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| const uint32_t approxMsaaWidth = 7; | |
| const uint32_t approxMsaaHeight = 7; | |
| auto approxMultiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ approxMsaaWidth, | |
| /*height*/ approxMsaaHeight); | |
| auto approxMultiSampledTextureView = approxMultiSampledTexture.CreateView(); | |
| mResolveTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ resolveWidth, | |
| /*height*/ resolveHeight); | |
| auto singleSampledTextureView = mResolveTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| wgpu::RenderPipeline redPipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| // In first render pass we draw a red triangle. StoreOp=Discard to discard the MSAA | |
| // texture's content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, redPipeline, kRed); | |
| } | |
| // In the second render pass, we draw a green triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the right and bottom edges untouched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {approxMultiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyPoints(points); | |
| } | |
| // Test that wgpu::LoadOp::Clear or wgpu::LoadOp::Load works with | |
| // wgpu::RenderPassDescriptorResolveRect. | |
| void TestLoadOrClearThenPartialResolve( | |
| const wgpu::RenderPassDescriptorResolveRect& rect, | |
| const std::vector<Point>& points, | |
| wgpu::LoadOp loadOp, | |
| const wgpu::Color& initialColor, | |
| const std::optional<wgpu::Color>& userDrawColor = std::nullopt) { | |
| DAWN_TEST_UNSUPPORTED_IF( | |
| !SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| constexpr uint32_t msaaWidth = 3; | |
| constexpr uint32_t msaaHeight = 3; | |
| constexpr uint32_t resolveWidth = 4; | |
| constexpr uint32_t resolveHeight = 4; | |
| auto multiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ msaaWidth, | |
| /*height*/ msaaHeight); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| mResolveTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ resolveWidth, | |
| /*height*/ resolveHeight); | |
| auto singleSampledTextureView = mResolveTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // Initial clear of MSAA texture if loading. | |
| if (loadOp == wgpu::LoadOp::Load) { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| renderPass.cColorAttachments[0].clearValue = initialColor; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| // Create render pass with configurable wgpu::LoadOp and | |
| // wgpu::RenderPassDescriptorResolveRect. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, loadOp, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| // The clearValue takes effect only when the load operation is set to | |
| // wgpu::LoadOp::Clear. | |
| renderPass.cColorAttachments[0].clearValue = initialColor; | |
| renderPass.nextInChain = ▭ | |
| if (userDrawColor) { | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, | |
| /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, *userDrawColor); | |
| } else { | |
| wgpu::RenderPassEncoder renderPassEncoder = | |
| commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyPoints(points); | |
| // When no user drawing is performed (userDrawColor is not set) verify the entire MSAA | |
| // texture has uniform color (initialColor). | |
| if (!userDrawColor) { | |
| VerifyAllMSAAPointsOfSameColor(initialColor, multiSampledTexture); | |
| } | |
| } | |
| void VerifyPoints(const std::vector<Point>& points, | |
| const std::optional<wgpu::Texture>& resolveTexture = std::nullopt) { | |
| const auto& texture = resolveTexture.value_or(mResolveTexture); | |
| for (const Point& point : points) { | |
| VerifyResolveTarget(point.color, texture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/point.msaaCoverage, /*x=*/point.x, /*y=*/point.y); | |
| } | |
| } | |
| // Verify the points of an MSAA teture. | |
| void VerifyMSAAPoints(const std::vector<Point>& points, const wgpu::Texture& msaaTexture) { | |
| // Resolve the MSAA texture to single sampled texture. | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ msaaTexture.GetWidth(), | |
| /*height*/ msaaTexture.GetHeight()); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {msaaTexture.CreateView()}, {singleSampledTextureView}, wgpu::LoadOp::Load, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyPoints(points, singleSampledTexture); | |
| } | |
| // Verify all the points of an MSAA teture are of the same color. | |
| void VerifyAllMSAAPointsOfSameColor(const wgpu::Color& color, | |
| const wgpu::Texture& msaaTexture) { | |
| auto textureWidth = msaaTexture.GetWidth(); | |
| auto textureHeight = msaaTexture.GetHeight(); | |
| std::vector<Point> points(static_cast<size_t>(textureWidth) * textureHeight); | |
| for (size_t i = 0; i < points.size(); ++i) { | |
| uint32_t x = i % (textureWidth); | |
| uint32_t y = i / (textureWidth); | |
| points[i] = {x, y, color}; | |
| } | |
| VerifyMSAAPoints(points, msaaTexture); | |
| } | |
| void TestLoadOrClearThenPartialResolve(wgpu::LoadOp loadOp, | |
| const wgpu::Color& initialColor, | |
| const wgpu::Color& userDrawColor) { | |
| constexpr wgpu::Color kBlack = {0.0f, 0.0f, 0.0f, 0.0f}; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = 1; | |
| rect.colorOffsetY = 2; | |
| rect.resolveOffsetX = 2; | |
| rect.resolveOffsetY = 2; | |
| rect.width = 1; | |
| rect.height = 1; | |
| // Only the single point at coordinate {resolveOffsetX, resolveOffsetY} in the resolve | |
| // texture is modified. All other points in the resolve texture remain unchanged. | |
| std::vector<Point> points = { | |
| {1, 1, kBlack}, {2, 1, kBlack}, {3, 1, kBlack}, {1, 2, kBlack}, {2, 2, initialColor}, | |
| {3, 2, kBlack}, {1, 3, kBlack}, {2, 3, kBlack}, {3, 3, kBlack}, | |
| }; | |
| // Test wgpu::LoadOp::Clear or wgpu::LoadOp::Load without user draw. | |
| TestLoadOrClearThenPartialResolve(rect, points, loadOp, initialColor); | |
| // If user draws, the resolve texture color is mixed with initial color and user draw color. | |
| const wgpu::Color finalColor = { | |
| (initialColor.r + userDrawColor.r) / 2.0, (initialColor.g + userDrawColor.g) / 2.0, | |
| (initialColor.b + userDrawColor.b) / 2.0, (initialColor.a + userDrawColor.a) / 2.0}; | |
| points[4] = {2, 2, finalColor}; | |
| rect.colorOffsetX = 1; | |
| rect.colorOffsetY = 1; | |
| // Test wgpu::LoadOp::Clear or wgpu::LoadOp::Load with user draw. | |
| TestLoadOrClearThenPartialResolve(rect, points, loadOp, initialColor, userDrawColor); | |
| } | |
| }; | |
| // Test rendering into a resolve texture then start another render pass with | |
| // LoadOp::ExpandResolveTexture. The resolve texture will have its content preserved between | |
| // passes. | |
| TEST_P(DawnLoadResolveTextureTest, DrawThenLoad) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment( | |
| kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments), | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // In first render pass we draw a green triangle. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass, we only use LoadOp::ExpandResolveTexture with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| } | |
| // Test using ExpandResolveTexture load op for non-zero indexed attachment. | |
| TEST_P(DawnLoadResolveTextureTest, DrawThenLoadNonZeroIndexedAttachment) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment( | |
| kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments), | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithNonZeroLocationOutputForTest( | |
| /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*enableExpandResolveLoadOp=*/false); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // In first render pass we draw a green triangle. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {nullptr, multiSampledTextureView}, {nullptr, singleSampledTextureView}, | |
| wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass, we only use LoadOp::ExpandResolveTexture with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {nullptr, multiSampledTextureView}, {nullptr, singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| } | |
| // Test rendering into 2 attachments. The 1st attachment will use | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor0) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working | |
| // with DawnLoadResolveTexture feature if there are more than one attachment. | |
| DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle()); | |
| // TODO(383731610): multiple outputs are not working in compat mode. | |
| DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode()); | |
| auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView1 = multiSampledTexture1.CreateView(); | |
| auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView2 = multiSampledTexture2.CreateView(); | |
| auto singleSampledTexture1 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView1 = singleSampledTexture1.CreateView(); | |
| auto singleSampledTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView2 = singleSampledTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest( | |
| /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false); | |
| // In first render pass: | |
| // - we draw a red triangle to attachment0. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| // - we draw a green triangle to attachment1. StoreOp=Store. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Store; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In second render pass: | |
| // - we only use LoadOp::ExpandResolveTexture for attachment0 with no draw call. | |
| // - we only use LoadOp::Load for attachment1 with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].loadOp = wgpu::LoadOp::Load; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, singleSampledTexture1); | |
| VerifyResolveTarget(kGreen, singleSampledTexture2); | |
| } | |
| // Test rendering into 2 attachments. The 2nd attachment will use | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor1) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working | |
| // with DawnLoadResolveTexture feature if there are more than one attachment. | |
| DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle()); | |
| // TODO(383731610): multiple outputs are not working in compat mode. | |
| DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode()); | |
| auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView1 = multiSampledTexture1.CreateView(); | |
| auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView2 = multiSampledTexture2.CreateView(); | |
| auto singleSampledTexture1 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView1 = singleSampledTexture1.CreateView(); | |
| auto singleSampledTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView2 = singleSampledTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest( | |
| /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false); | |
| // In first render pass: | |
| // - we draw a red triangle to attachment0. StoreOp=Store. | |
| // - we draw a green triangle to attachment1. StoreOp=Discard. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In second render pass: | |
| // - we only use LoadOp::Load for attachment0 with no draw call. | |
| // - we only use LoadOp::ExpandResolveTexture for attachment1 with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, singleSampledTexture1); | |
| VerifyResolveTarget(kGreen, singleSampledTexture2); | |
| } | |
| // Test rendering into 2 attachments. The both attachments will use | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawThenLoadColor0AndColor1) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working | |
| // with DawnLoadResolveTexture feature if there are more than one attachment. | |
| DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle()); | |
| auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView1 = multiSampledTexture1.CreateView(); | |
| auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView2 = multiSampledTexture2.CreateView(); | |
| auto singleSampledTexture1 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView1 = singleSampledTexture1.CreateView(); | |
| auto singleSampledTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView2 = singleSampledTexture2.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest( | |
| /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false); | |
| // In first render pass: | |
| // - we draw a red triangle to attachment0. StoreOp=Discard. | |
| // - we draw a green triangle to attachment1. StoreOp=Discard. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In second render pass: | |
| // - we only use LoadOp::ExpandResolveTexture for attachment0 with no draw call. | |
| // - we only use LoadOp::ExpandResolveTexture for attachment1 with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, singleSampledTexture1); | |
| VerifyResolveTarget(kGreen, singleSampledTexture2); | |
| } | |
| // Test ExpandResolveTexture load op rendering with depth test works correctly. | |
| TEST_P(DawnLoadResolveTextureTest, DrawWithDepthTest) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment( | |
| kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/device.HasFeature(wgpu::FeatureName::TransientAttachments), | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/true, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // In first render pass we clear the render pass without drawing anything | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| // In 2nd render pass we draw a green triangle with depth value == 0.2f. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color | |
| 0.2f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In 3rd render pass we draw a red triangle with depth value == 0.5f. | |
| // This red triangle should not be displayed because it is behind the green one that is drawn in | |
| // the last render pass. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color | |
| 0.5f}; // depth | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs | |
| // correctly with depth test. | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| } | |
| // Test ExpandResolveTexture load op rendering with depth test works correctly with | |
| // two outputs both use ExpandResolveTexture load op. | |
| TEST_P(DawnLoadResolveTextureTest, TwoOutputsDrawWithDepthTestColor0AndColor1) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // TODO(42240662): "resolve_multiple_attachments_in_separate_passes" is currently not working | |
| // with DawnLoadResolveTexture feature if there are more than one attachment. | |
| DAWN_TEST_UNSUPPORTED_IF(HasResolveMultipleAttachmentInSeparatePassesToggle()); | |
| auto multiSampledTexture1 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView1 = multiSampledTexture1.CreateView(); | |
| auto multiSampledTexture2 = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView2 = multiSampledTexture2.CreateView(); | |
| auto singleSampledTexture1 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView1 = singleSampledTexture1.CreateView(); | |
| auto singleSampledTexture2 = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView2 = singleSampledTexture2.CreateView(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest( | |
| /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*depthTest=*/true, | |
| /*loadOpForColor0=*/PipelineMultisampleLoadOp::ExpandResolveTarget, | |
| /*loadOpForColor1=*/PipelineMultisampleLoadOp::ExpandResolveTarget); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| // In first render pass we clear the render pass without drawing anything | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| // In 2nd render pass: | |
| // - we draw a red triangle to attachment0. | |
| // - we draw a green triangle to attachment1. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a), | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| // In 3rd render pass: | |
| // - we draw a green triangle to attachment0. | |
| // - we draw a red triangle to attachment1. | |
| // Both triangles should not pass the depth test. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView1, multiSampledTextureView2}, | |
| {singleSampledTextureView1, singleSampledTextureView2}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load, | |
| /*testDepth=*/true); | |
| renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| renderPass.cColorAttachments[1].loadOp = wgpu::LoadOp::ExpandResolveTexture; | |
| renderPass.cColorAttachments[1].storeOp = wgpu::StoreOp::Discard; | |
| std::array<float, 8> kUniformData = { | |
| static_cast<float>(kGreen.r), static_cast<float>(kGreen.g), | |
| static_cast<float>(kGreen.b), static_cast<float>(kGreen.a), | |
| static_cast<float>(kRed.r), static_cast<float>(kRed.g), | |
| static_cast<float>(kRed.b), static_cast<float>(kRed.a)}; | |
| constexpr uint32_t kSize = sizeof(kUniformData); | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, singleSampledTexture1); | |
| VerifyResolveTarget(kGreen, singleSampledTexture2); | |
| } | |
| // Test rendering into a layer of a 2D array texture and load op=LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, DrawThenLoad2DArrayTextureLayer) { | |
| // TODO(crbug.com/519251257): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| // Creating 2D view from 2D array texture is not supported in compat mode. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = CreateTextureForRenderAttachment( | |
| kColorFormat, 1, 1, /*arrayCount=*/2, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| wgpu::TextureViewDescriptor resolveViewDescriptor2; | |
| resolveViewDescriptor2.dimension = wgpu::TextureViewDimension::e2D; | |
| resolveViewDescriptor2.format = kColorFormat; | |
| resolveViewDescriptor2.baseArrayLayer = 1; | |
| resolveViewDescriptor2.baseMipLevel = 0; | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(&resolveViewDescriptor2); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // In first render pass we draw a green triangle. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In second render pass, we only use LoadOp::ExpandResolveTexture with no draw call. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Load, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, singleSampledTexture, 0, 1); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly | |
| // when rendering with LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ClearThenLoadAndDrawWithRect) { | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; | |
| // In the first render pass we clear the attachment without drawing anything. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| // In the second render pass, we draw a green triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the right and bottom edges untouched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 0; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 0; | |
| rect.width = kWidth - 1; | |
| rect.height = kHeight - 1; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // Verify the center is green. | |
| VerifyResolveTarget(kGreen, singleSampledTexture); | |
| // Verify the right-bottom corner, which is outside the rect, stays untouched. | |
| VerifyResolveTarget(kClearColor, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/kWidth - 1, /*y=*/kHeight - 1); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly with StoreOp::Discard. | |
| TEST_P(DawnLoadResolveTextureTest, StoreOpDiscardWithRect) { | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline greenPipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| wgpu::RenderPipeline redPipelineWithExpandResolve = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| // In the first render pass, we draw a green triangle, and keep it stored in the multisampled | |
| // texture. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, greenPipeline, kGreen); | |
| } | |
| // In the second render pass, we draw a red triangle with LoadOp::ExpandResolveTexture and | |
| // StoreOp::Discard. We also specify a scissor rect requiring the pixels in the right and bottom | |
| // edges untouched during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| // Discard the multisampled texture at the end of this pass. | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 0; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 0; | |
| rect.width = kWidth - 1; | |
| rect.height = kHeight - 1; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, redPipelineWithExpandResolve, kRed); | |
| } | |
| // The third render pass is simply a resolve pass so that we can check the contents of the | |
| // multisample via the resolve target. As the multisampled texture was discarded in the second | |
| // pass, it must be wholly cleared before being resolved into the singlesampled texture. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Load, | |
| wgpu::LoadOp::Clear, | |
| /*testDepth=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); | |
| renderPassEncoder.End(); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| // The center, inside the rect, must be cleared. | |
| VerifyResolveTarget(kClearColor, singleSampledTexture); | |
| // The right-bottom corner, outside of the rect, must be cleared as well. | |
| VerifyResolveTarget(kClearColor, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/kWidth - 1, /*y=*/kHeight - 1); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly when rendering with | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ExpandResolve) { | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| const uint32_t approxMsaaWidth = 3; | |
| const uint32_t approxMsaaHeight = 3; | |
| auto approxMultiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ approxMsaaWidth, | |
| /*height*/ approxMsaaHeight); | |
| auto approxMultiSampledTextureView = approxMultiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| wgpu::RenderPipeline redPipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| // In first render pass we draw a red triangle. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, redPipeline, kRed); | |
| } | |
| // In the second render pass, we draw a green triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the right and bottom edges untouched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {approxMultiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 1; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 1; | |
| rect.width = rect.height = 2; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kRed, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/1, /*y=*/0); | |
| VerifyResolveTarget(kRed, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/2, /*y=*/0); | |
| VerifyResolveTarget(kGreen, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/2, /*y=*/1); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly when rendering at different offset with | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ExpandResolveDifferentOffset) { | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| const uint32_t approxMsaaWidth = 5; | |
| const uint32_t approxMsaaHeight = 5; | |
| auto approxMultiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ approxMsaaWidth, | |
| /*height*/ approxMsaaHeight); | |
| auto approxMultiSampledTextureView = approxMultiSampledTexture.CreateView(); | |
| auto singleSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true); | |
| auto singleSampledTextureView = singleSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| wgpu::RenderPipeline redPipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/false); | |
| // In first render pass we draw a red triangle. StoreOp=Discard to discard the MSAA texture's | |
| // content. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {multiSampledTextureView}, {singleSampledTextureView}, wgpu::LoadOp::Clear, | |
| wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, redPipeline, kRed); | |
| } | |
| // In the second render pass, we draw a green triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the right and bottom edges touched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {approxMultiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 1; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 0; | |
| rect.width = rect.height = 2; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| VerifyResolveTarget(kGreen, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/1, /*y=*/0); | |
| VerifyResolveTarget(kRed, singleSampledTexture, /*mipmapLevel=*/0, /*arrayLayer=*/0, | |
| /*msaaCoverage=*/1.0f, /*x=*/2, /*y=*/1); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly when texture as input with | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ExpandResolveTextureAsInput) { | |
| DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::DawnPartialLoadResolveTexture})); | |
| const uint32_t resolveWidth = 5; | |
| const uint32_t resolveHeight = 5; | |
| const uint32_t updateSize = 2; | |
| mResolveTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 1, 1, 1, /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/true, | |
| /*supportsCopyDst*/ true, | |
| /*width*/ resolveWidth, | |
| /*height*/ resolveHeight); | |
| auto singleSampledTextureView = mResolveTexture.CreateView(); | |
| wgpu::TexelCopyTextureInfo dst = {}; | |
| dst.texture = mResolveTexture; | |
| std::array<utils::RGBA8, static_cast<size_t>(resolveWidth) * resolveHeight> rgbaTextureData; | |
| for (size_t i = 0; i < rgbaTextureData.size(); ++i) { | |
| rgbaTextureData[i] = utils::RGBA8(0, 0, 0, 255); | |
| } | |
| rgbaTextureData[1] = utils::RGBA8(255, 0, 0, 255); | |
| rgbaTextureData[resolveWidth] = utils::RGBA8(0, 255, 0, 255); | |
| rgbaTextureData[resolveWidth + 1] = utils::RGBA8(0, 0, 255, 255); | |
| rgbaTextureData[resolveWidth * 2 + updateSize] = utils::RGBA8(255, 0, 255, 255); | |
| rgbaTextureData[resolveWidth * 2 + updateSize + 1] = utils::RGBA8(255, 255, 0, 255); | |
| rgbaTextureData[resolveWidth * 3 + updateSize] = utils::RGBA8(0, 0, 255, 255); | |
| rgbaTextureData[resolveWidth * 3 + updateSize + 1] = utils::RGBA8(0, 0, 255, 255); | |
| wgpu::TexelCopyBufferLayout dataLayout = {}; | |
| wgpu::TextureDescriptor textureDesc = {}; | |
| textureDesc.size = {resolveWidth, resolveHeight, 1}; | |
| dataLayout.bytesPerRow = textureDesc.size.width * sizeof(utils::RGBA8); | |
| queue.WriteTexture(&dst, rgbaTextureData.data(), rgbaTextureData.size() * sizeof(utils::RGBA8), | |
| &dataLayout, &textureDesc.size); | |
| auto multiSampledTexture = CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false); | |
| auto multiSampledTextureView = multiSampledTexture.CreateView(); | |
| const uint32_t approxMsaaWidth = 6; | |
| const uint32_t approxMsaaHeight = 6; | |
| auto approxMultiSampledTexture = | |
| CreateTextureForRenderAttachment(kColorFormat, 4, 1, 1, | |
| /*transientAttachment=*/false, | |
| /*supportsTextureBinding=*/false, | |
| /*supportsCopyDst=*/false, | |
| /*width*/ approxMsaaWidth, | |
| /*height*/ approxMsaaHeight); | |
| auto approxMultiSampledTextureView = approxMultiSampledTexture.CreateView(); | |
| wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); | |
| wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest( | |
| /*testDepth=*/false, /*sampleMask=*/0xFFFFFFFF, /*alphaToCoverageEnabled=*/false, | |
| /*flipTriangle=*/false, /*enableExpandResolveLoadOp=*/true); | |
| // In the first render pass, we draw a green triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the left and top edges touched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {approxMultiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 0; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 0; | |
| rect.width = rect.height = updateSize; | |
| renderPass.nextInChain = ▭ | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kGreen); | |
| } | |
| // In the second render pass, we draw a blue triangle with LoadOp::ExpandResolveTexture. We | |
| // also specify a scissor rect requiring the pixels in the middle touched | |
| // during expanding and resolving. | |
| { | |
| utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( | |
| {approxMultiSampledTextureView}, {singleSampledTextureView}, | |
| wgpu::LoadOp::ExpandResolveTexture, wgpu::LoadOp::Clear, | |
| /*hasDepthStencilAttachment=*/false); | |
| renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard; | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = 3; | |
| rect.colorOffsetY = 4; | |
| rect.resolveOffsetX = 1; | |
| rect.resolveOffsetY = 2; | |
| rect.width = rect.height = updateSize; | |
| renderPass.nextInChain = ▭ | |
| constexpr wgpu::Color kBlue = {0.0f, 0.0f, 0.8f, 0.8f}; | |
| EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kBlue); | |
| } | |
| wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| std::vector<Point> points = { | |
| {1, 0, kGreen}, | |
| {0, 1, {0.0f, 1.0f, 0.0f, 1.0f}}, | |
| {3, 2, {1.0f, 1.0f, 0.0f, 1.0f}}, | |
| {2, 3, {0.0f, 0.0f, 1.0f, 1.0f}}, | |
| {1, 2, {0.0f, 0.0f, 0.0f, 1.0f}}, | |
| {2, 1, {0.0f, 0.0f, 0.0f, 1.0f}}, | |
| }; | |
| VerifyPoints(points); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly with smaller msaa texture when rendering | |
| // with LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ExpandResolveWithSmallerMSAATexture) { | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = rect.colorOffsetY = 0; | |
| rect.resolveOffsetX = rect.resolveOffsetY = 0; | |
| rect.width = 3; | |
| rect.height = 3; | |
| std::vector<Point> points = { | |
| {1, 0, kGreen}, | |
| {2, 0, kGreen}, | |
| {2, 1, kGreen}, | |
| {3, 0, kRed}, | |
| }; | |
| TestExpandAndResolveWithRect(rect, points); | |
| } | |
| // Test RenderPassDescriptorResolveRect works correctly with non zero offset when rendering with | |
| // LoadOp::ExpandResolveTexture. | |
| TEST_P(DawnLoadResolveTextureTest, ExpandResolveDifferentNonZeroOffsetsWithSmallerMSAATexture) { | |
| wgpu::RenderPassDescriptorResolveRect rect{}; | |
| rect.colorOffsetX = 1; | |
| rect.colorOffsetY = 2; | |
| rect.resolveOffsetX = 3; | |
| rect.resolveOffsetY = 4; | |
| rect.width = 3; | |
| rect.height = 3; | |
| std::vector<Point> points = { | |
| {5, 4, kGreen}, | |
| }; | |
| TestExpandAndResolveWithRect(rect, points); | |
| points = { | |
| {4, 4, kGreen}, | |
| }; | |
| // Test when resolveOffsetX - colorOffsetX and resolveOffsetY - colorOffsetY are different. | |
| rect.resolveOffsetX = 2; | |
| TestExpandAndResolveWithRect(rect, points); | |
| } | |
| // Test that RenderPassDescriptorResolveRect works with wgpu::LoadOp::Clear. | |
| TEST_P(DawnLoadResolveTextureTest, ClearThenPartialResolve) { | |
| TestLoadOrClearThenPartialResolve(wgpu::LoadOp::Clear, /*initialColor*/ kRed, | |
| /*userDrawColor*/ kGreen); | |
| } | |
| // Test that RenderPassDescriptorResolveRect works with wgpu::LoadOp::Load. | |
| TEST_P(DawnLoadResolveTextureTest, LoadThenPartialResolve) { | |
| TestLoadOrClearThenPartialResolve(wgpu::LoadOp::Clear, /*initialColor*/ kGreen, | |
| /*userDrawColor*/ kRed); | |
| } | |
| DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), | |
| D3D12Backend({}, {"use_d3d12_render_pass"}), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| WebGPUBackend(), | |
| VulkanBackend(), | |
| VulkanBackend({"always_resolve_into_zero_level_and_layer"}), | |
| VulkanBackend({"resolve_multiple_attachments_in_separate_passes"}), | |
| MetalBackend({"emulate_store_and_msaa_resolve"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer", | |
| "emulate_store_and_msaa_resolve"})); | |
| DAWN_INSTANTIATE_TEST(MultisampledRenderingWithEmptyPassWorkaroundTest, | |
| VulkanBackend({"vulkan_add_work_to_empty_resolve_pass"})); | |
| DAWN_INSTANTIATE_TEST(MultisampledRenderingWithTransientAttachmentTest, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), | |
| D3D12Backend({}, {"use_d3d12_render_pass"}), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| WebGPUBackend(), | |
| VulkanBackend(), | |
| VulkanBackend({"always_resolve_into_zero_level_and_layer"}), | |
| VulkanBackend({"resolve_multiple_attachments_in_separate_passes"}), | |
| MetalBackend({"emulate_store_and_msaa_resolve"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer", | |
| "emulate_store_and_msaa_resolve"})); | |
| DAWN_INSTANTIATE_TEST(MultisampledRenderToSingleSampledTest, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), | |
| D3D12Backend({}, {"use_d3d12_render_pass"}), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| WebGPUBackend(), | |
| VulkanBackend(), | |
| VulkanBackend({"always_resolve_into_zero_level_and_layer"}), | |
| VulkanBackend({"resolve_multiple_attachments_in_separate_passes"}), | |
| MetalBackend({"emulate_store_and_msaa_resolve"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer", | |
| "emulate_store_and_msaa_resolve"})); | |
| DAWN_INSTANTIATE_TEST(DawnLoadResolveTextureTest, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), | |
| D3D12Backend({}, {"use_d3d12_render_pass"}), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| WebGPUBackend(), | |
| VulkanBackend(), | |
| VulkanBackend({"always_resolve_into_zero_level_and_layer"}), | |
| VulkanBackend({"resolve_multiple_attachments_in_separate_passes"}), | |
| MetalBackend({"emulate_store_and_msaa_resolve"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer"}), | |
| MetalBackend({"always_resolve_into_zero_level_and_layer", | |
| "emulate_store_and_msaa_resolve"})); | |
| } // anonymous namespace | |
| } // namespace dawn |