Initial FramebufferFetch support on Metal.

The code in the Metal backend should be complete, but we need a lot more
tests to check that the feature is indeed correct in a variety of use
cases.

Bug: dawn:2195
Change-Id: Iec717ddc898fab2eb0add8eb1f6ed2553019e38b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/160564
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 5d73162..5184c3c 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -560,9 +560,10 @@
         }
 
         if (@available(macOS 11.0, iOS 10.0, *)) {
-            // Memoryless storage mode for Metal textures is available only
-            // from the Apple2 family of GPUs on.
+            // Memoryless storage mode and programmable blending are available only from the Apple2
+            // family of GPUs on.
             if ([*mDevice supportsFamily:MTLGPUFamilyApple2]) {
+                EnableFeature(Feature::FramebufferFetch);
                 EnableFeature(Feature::TransientAttachments);
             }
         }
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 1b22bb2..28055ba 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -560,6 +560,7 @@
     "end2end/ExternalTextureTests.cpp",
     "end2end/FirstIndexOffsetTests.cpp",
     "end2end/FragDepthTests.cpp",
+    "end2end/FramebufferFetchTests.cpp",
     "end2end/GpuMemorySynchronizationTests.cpp",
     "end2end/HistogramTests.cpp",
     "end2end/IndexFormatTests.cpp",
diff --git a/src/dawn/tests/end2end/FramebufferFetchTests.cpp b/src/dawn/tests/end2end/FramebufferFetchTests.cpp
new file mode 100644
index 0000000..d9cf791
--- /dev/null
+++ b/src/dawn/tests/end2end/FramebufferFetchTests.cpp
@@ -0,0 +1,107 @@
+// Copyright 2023 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 <vector>
+
+#include "dawn/tests/DawnTest.h"
+#include "dawn/utils/ComboRenderPipelineDescriptor.h"
+#include "dawn/utils/WGPUHelpers.h"
+
+namespace dawn {
+namespace {
+
+class FramebufferFetchTests : public DawnTest {
+  protected:
+    void SetUp() override {
+        DawnTest::SetUp();
+        DAWN_TEST_UNSUPPORTED_IF(!device.HasFeature(wgpu::FeatureName::FramebufferFetch));
+    }
+
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        std::vector<wgpu::FeatureName> requiredFeatures = {};
+        if (SupportsFeatures({wgpu::FeatureName::FramebufferFetch})) {
+            requiredFeatures.push_back(wgpu::FeatureName::FramebufferFetch);
+        }
+        return requiredFeatures;
+    }
+};
+
+// A basic test of framebuffer fetch to help get folks started before a more complete set of tests
+// is added.
+TEST_P(FramebufferFetchTests, Basic) {
+    // A shader using framebuffer fetch.
+    wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
+        enable chromium_experimental_framebuffer_fetch;
+
+        @vertex fn vs() -> @builtin(position) vec4f {
+            return vec4f(0, 0, 0, 1);
+        }
+
+        @fragment fn fs(@color(0) in : u32) -> @location(0) u32 {
+            return in + 1;
+        }
+    )");
+
+    // The pipeline using the shader, drawing points at 0, 0.
+    utils::ComboRenderPipelineDescriptor pDesc;
+    pDesc.vertex.module = module;
+    pDesc.vertex.entryPoint = "vs";
+    pDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
+    pDesc.cFragment.module = module;
+    pDesc.cFragment.entryPoint = "fs";
+    pDesc.cTargets[0].format = wgpu::TextureFormat::R32Uint;
+    wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pDesc);
+
+    // Our render target.
+    wgpu::TextureDescriptor tDesc;
+    tDesc.size = {1, 1};
+    tDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
+    tDesc.format = wgpu::TextureFormat::R32Uint;
+    wgpu::Texture texture = device.CreateTexture(&tDesc);
+
+    // Draw 10 points with the pipeline on the render target.
+    utils::ComboRenderPassDescriptor passDesc({texture.CreateView()});
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&passDesc);
+    pass.SetPipeline(pipeline);
+    pass.Draw(10);
+    pass.End();
+
+    wgpu::CommandBuffer commands = encoder.Finish();
+    queue.Submit(1, &commands);
+
+    // The 10 points should have successfully used framebuffer fetch to do increment ten times
+    // without races.
+    EXPECT_TEXTURE_EQ(uint32_t(10), texture, {0, 0});
+}
+
+// TODO(dawn:2195): Make a test plan.
+
+DAWN_INSTANTIATE_TEST(FramebufferFetchTests, MetalBackend());
+
+}  // anonymous namespace
+}  // namespace dawn