GL: implement buffer shadowing for MapAsync()

Adds a new toggle, ShadowCopyMapWrite, that uses the host-side shadow
copy when MapAsync() is called with MapWrite. This allows the specific
code sequence of MapAsync() immediately followed by WaitAny() (as used
by Graphite) to be called on any thread even in deferral, since the
serial is not incremented between those two calls.

Change-Id: I2e575d3bed090e5c695347be1f01967caa1a60bd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/293455
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Stephen White <senorblanco@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index 635230d..0f31d17 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -817,6 +817,13 @@
       "Allows the GL backend to defer GL commands until Queue::Submit is called. This includes "
       "backend resource creations which will also be deferred.",
       "https://crbug.com/dawn/451928481", ToggleStage::Device}},
+    {Toggle::ShadowCopyMapWrite,
+     {"shadow_copy_map_write",
+      "By shadowing mapped buffers on the host, this allows the specific "
+      "code sequence of MapAsync(wgpu::MapMode::Write, ...) / WaitAny() "
+      "to be called on any thread, even in deferral mode. "
+      "This may incur a performance penalty due to the host-side copy.",
+      "https://crbug.com/dawn/451928481", ToggleStage::Device}},
     {Toggle::DisableTransientAttachment,
      {"disable_transient_attachment", "Disable TextureUsage TransientAttachment usage.",
       "https://crbug.com/dawn/462620664", ToggleStage::Device}},
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 59a6db5..c9e6a8a 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -199,6 +199,7 @@
     IgnoreImportedAHardwareBufferVulkanImageSize,
     GLAllowContextOnMultiThreads,
     GLDefer,
+    ShadowCopyMapWrite,
     DisableTransientAttachment,
     AutoMapBackendBuffer,
 
diff --git a/src/dawn/native/opengl/BufferGL.cpp b/src/dawn/native/opengl/BufferGL.cpp
index 0c5c3dd..a91a687 100644
--- a/src/dawn/native/opengl/BufferGL.cpp
+++ b/src/dawn/native/opengl/BufferGL.cpp
@@ -219,6 +219,15 @@
 }
 
 MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) {
+    // This allows the specific code sequence of MapAsync(Write) / WaitAny() to work in deferral
+    // at the cost of a host-side copy. This should be removed in favour of eager buffer mapping,
+    // once that's implemented.
+    if ((mode & wgpu::MapMode::Write) && GetDevice()->IsToggleEnabled(Toggle::ShadowCopyMapWrite)) {
+        mCPUStaging.resize(GetSize());
+        mMappedData = mCPUStaging.data();
+        return {};
+    }
+
     // It is an error to map an empty range in OpenGL. We always have at least a 4-byte buffer
     // so we extend the range to be 4 bytes.
     if (size == 0) {