Add more FramebufferFetch tests RenderPassWithMixedPipelines mixes pipeline using @color and pipeline without the attribute in the same render pass. MultisampledAttachmentNoSampleShading ensures that FramebufferFetch being enabled doesn't increase sample shading rate when color attribute isn't used. Bug: 42241389 Change-Id: I77c90199b991e7631ae95be9f24ca1990f0351ed Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/302855 Commit-Queue: Kyle Charbonneau <kylechar@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/docs/tint/extensions/chromium_experimental_framebuffer_fetch.md b/docs/tint/extensions/chromium_experimental_framebuffer_fetch.md index 45b4909..e64d859 100644 --- a/docs/tint/extensions/chromium_experimental_framebuffer_fetch.md +++ b/docs/tint/extensions/chromium_experimental_framebuffer_fetch.md
@@ -13,7 +13,8 @@ ## Pseudo-specification This adds a new builtin attribute `@color` which takes a `u32` argument and is only allowed on `@fragment` entry point scalar / vector inputs. -Two input variables cannot have the same `@color` attribute. +* Two input variables cannot have the same `@color` attribute. +* The use of `@color(N)` with a multi-sampled color attachment will cause the fragment shader to run once per sample. ## Example usage @@ -27,4 +28,3 @@ ## To-dos - Are f16 types allowed for the `@color` inputs? - - Are any changes needed for Vulkan support?
diff --git a/src/dawn/tests/end2end/FramebufferFetchTests.cpp b/src/dawn/tests/end2end/FramebufferFetchTests.cpp index b32cce7..e8a984a 100644 --- a/src/dawn/tests/end2end/FramebufferFetchTests.cpp +++ b/src/dawn/tests/end2end/FramebufferFetchTests.cpp
@@ -105,6 +105,59 @@ EXPECT_TEXTURE_EQ(uint32_t(10), texture, {0, 0}); } +// Check that FramebufferFetch works correctly when switching between pipelines that use and do not +// use FramebufferFetch within the same render pass. +TEST_P(FramebufferFetchTests, RenderPassWithMixedPipelines) { + // Pipeline without FramebufferFetch that uses blending to add 10 (10/255.0) to the attachment. + utils::ComboRenderPipelineDescriptor pDesc1; + InitForSinglePoint(&pDesc1); + pDesc1.cFragment.module = utils::CreateShaderModule(device, R"( + @fragment fn main() -> @location(0) vec4f { + return vec4f(10.0 / 255.0, 0.0, 0.0, 0.0); + } + )"); + pDesc1.cBlends[0].color.srcFactor = wgpu::BlendFactor::One; + pDesc1.cBlends[0].color.dstFactor = wgpu::BlendFactor::One; + pDesc1.cBlends[0].color.operation = wgpu::BlendOperation::Add; + pDesc1.cBlends[0].alpha = pDesc1.cBlends[0].color; + pDesc1.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm; + pDesc1.cTargets[0].blend = &pDesc1.cBlends[0]; + wgpu::RenderPipeline pipeline1 = device.CreateRenderPipeline(&pDesc1); + + // Pipeline with FramebufferFetch that adds one (1/255.0) to the attachment. + utils::ComboRenderPipelineDescriptor pDesc2; + InitForSinglePoint(&pDesc2); + pDesc2.cFragment.module = utils::CreateShaderModule(device, kExt + R"( + @fragment fn main(@color(0) in : vec4f) -> @location(0) vec4f { + return in + vec4f(1.0 / 255.0, 0.0, 0.0, 0.0); + } + )"); + pDesc2.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm; + wgpu::RenderPipeline pipeline2 = device.CreateRenderPipeline(&pDesc2); + + wgpu::Texture texture = MakeAttachment(wgpu::TextureFormat::RGBA8Unorm); + + utils::ComboRenderPassDescriptor passDesc({texture.CreateView()}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDesc); + // 1st invocation of pipeline1: adds 10. (Value: 10) + pass.SetPipeline(pipeline1); + pass.Draw(1); + // Invocation of pipeline2: increments by 1 five times. (Value: 15) + pass.SetPipeline(pipeline2); + pass.Draw(5); + // 2nd invocation of pipeline1: adds 10. (Value: 25) + pass.SetPipeline(pipeline1); + pass.Draw(1); + pass.End(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // The result should be 10 + 5 + 10 = 25. + EXPECT_TEXTURE_EQ(utils::RGBA8(25, 0, 0, 0), texture, {0, 0}); +} + // Check that it is post-blend framebuffer fetch TEST_P(FramebufferFetchTests, PostBlend) { // Pipeline that draw a point at the center that outputs the current attachment value with @@ -322,6 +375,60 @@ EXPECT_TEXTURE_EQ(uint32_t(10 + 1789), texture1, {0, 0}); } +// Checks that with the framebuffer fetch feature enabled and a multisampled color attachment, a +// fragment shader that doesn't use @color doesn't increase the sample shading rate. +TEST_P(FramebufferFetchTests, MultisampledAttachmentNoSampleShading) { + // Metal doesn't guarantee that the fragment shader will only run once for a multi-sampled draw. + // The atomic variable causes the shader to run 4x with Metal+ARM and this test fails. + DAWN_SUPPRESS_TEST_IF(IsMacOS()); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}}); + + utils::ComboRenderPipelineDescriptor pDesc; + InitForSinglePoint(&pDesc); + pDesc.layout = utils::MakePipelineLayout(device, {bgl}); + pDesc.multisample.count = 4; + pDesc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm; + pDesc.cFragment.module = utils::CreateShaderModule(device, kExt + R"( + @group(0) @binding(0) var<storage, read_write> invocationCount : atomic<u32>; + + @fragment fn addOne() -> @location(0) vec4f { + atomicAdd(&invocationCount, 1); + return vec4(1 / 255.0); + } + )"); + + pDesc.cFragment.entryPoint = "addOne"; + wgpu::RenderPipeline addOnePipeline = device.CreateRenderPipeline(&pDesc); + + // Create the buffer that will contain the number of invocations. + wgpu::BufferDescriptor bufDesc; + bufDesc.size = 4; + bufDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc; + wgpu::Buffer invocationBuffer = device.CreateBuffer(&bufDesc); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, invocationBuffer}}); + + wgpu::Texture attachment = MakeAttachment(wgpu::TextureFormat::RGBA8Unorm, {1, 1}, 4); + wgpu::Texture resolve = MakeAttachment(wgpu::TextureFormat::RGBA8Unorm); + + utils::ComboRenderPassDescriptor passDesc({attachment.CreateView()}); + passDesc.cColorAttachments[0].resolveTarget = resolve.CreateView(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDesc); + pass.SetBindGroup(0, bg); + // This FS shouldn't use sample shading and should run once. + pass.SetPipeline(addOnePipeline); + pass.Draw(1); + pass.End(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_EQ(1, invocationBuffer, 0); + EXPECT_TEXTURE_EQ(utils::RGBA8(1, 1, 1, 1), resolve, {0, 0}); +} + // Test the behavior with multisampling. // Checks that each sample has its own framebuffer fetch by add sample_index to that sample multiple // times. Checks that any framebuffer fetch forces sample shading, even if all the samples have the