Refactor MapRequestTracker to be its own class file.

All the buffer backend files had basically the same implemenations
of MapRequestTracker and the tracker was owned by device backends.
This refactor puts MapRequestTracker into its own file
and has the tracker be owned by DeviceBase and BufferBase.

Bug: dawn:400
Change-Id: Id28422b575e9c04d4435d5f119e0ffe08c2d1ce8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21760
Commit-Queue: Natasha Lee <natlee@microsoft.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index b55c05f..e4f03b5 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -215,6 +215,8 @@
     "Forward.h",
     "Instance.cpp",
     "Instance.h",
+    "MapRequestTracker.cpp",
+    "MapRequestTracker.h",
     "ObjectBase.cpp",
     "ObjectBase.h",
     "PassResourceUsage.h",
diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp
index 8a77618..b293b96 100644
--- a/src/dawn_native/Buffer.cpp
+++ b/src/dawn_native/Buffer.cpp
@@ -18,6 +18,7 @@
 #include "dawn_native/Device.h"
 #include "dawn_native/DynamicUploader.h"
 #include "dawn_native/ErrorData.h"
+#include "dawn_native/MapRequestTracker.h"
 #include "dawn_native/ValidationUtils_autogen.h"
 
 #include <cstdio>
@@ -73,6 +74,9 @@
                 UNREACHABLE();
                 return {};
             }
+            void* GetMappedPointerImpl() override {
+                return mFakeMappedData.get();
+            }
             void UnmapImpl() override {
                 UNREACHABLE();
             }
@@ -267,8 +271,12 @@
         mState = BufferState::Mapped;
 
         if (GetDevice()->ConsumedError(MapReadAsyncImpl(mMapSerial))) {
+            // TODO(natlee@microsoft.com): if map op fails fire callback with DEVICE_LOST status
             return;
         }
+
+        MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker();
+        tracker->Track(this, mMapSerial, false);
     }
 
     MaybeError BufferBase::SetSubDataImpl(uint32_t start, uint32_t count, const void* data) {
@@ -304,8 +312,12 @@
         mState = BufferState::Mapped;
 
         if (GetDevice()->ConsumedError(MapWriteAsyncImpl(mMapSerial))) {
+            // TODO(natlee@microsoft.com): if map op fails fire callback with DEVICE_LOST status
             return;
         }
+
+        MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker();
+        tracker->Track(this, mMapSerial, true);
     }
 
     void BufferBase::Destroy() {
@@ -467,4 +479,13 @@
         return mState == BufferState::Mapped;
     }
 
+    void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) {
+        void* data = GetMappedPointerImpl();
+        if (isWrite) {
+            CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
+        } else {
+            CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
+        }
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/Buffer.h b/src/dawn_native/Buffer.h
index 1d35ff0..4e348fe 100644
--- a/src/dawn_native/Buffer.h
+++ b/src/dawn_native/Buffer.h
@@ -50,6 +50,7 @@
         wgpu::BufferUsage GetUsage() const;
 
         MaybeError MapAtCreation(uint8_t** mappedPointer);
+        void OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite);
 
         MaybeError ValidateCanUseInSubmitNow() const;
 
@@ -84,6 +85,7 @@
         virtual MaybeError MapWriteAsyncImpl(uint32_t serial) = 0;
         virtual void UnmapImpl() = 0;
         virtual void DestroyImpl() = 0;
+        virtual void* GetMappedPointerImpl() = 0;
 
         virtual bool IsMapWritable() const = 0;
         MaybeError CopyFromStagingBuffer();
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index aa5aa70..6232477 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -87,6 +87,8 @@
     "Forward.h"
     "Instance.cpp"
     "Instance.h"
+    "MapRequestTracker.cpp"
+    "MapRequestTracker.h"
     "ObjectBase.cpp"
     "ObjectBase.h"
     "PassResourceUsage.h"
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 4c90c6f..db4936a 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -30,6 +30,7 @@
 #include "dawn_native/Fence.h"
 #include "dawn_native/FenceSignalTracker.h"
 #include "dawn_native/Instance.h"
+#include "dawn_native/MapRequestTracker.h"
 #include "dawn_native/PipelineLayout.h"
 #include "dawn_native/Queue.h"
 #include "dawn_native/RenderBundleEncoder.h"
@@ -102,6 +103,7 @@
         mCaches = std::make_unique<DeviceBase::Caches>();
         mErrorScopeTracker = std::make_unique<ErrorScopeTracker>(this);
         mFenceSignalTracker = std::make_unique<FenceSignalTracker>(this);
+        mMapRequestTracker = std::make_unique<MapRequestTracker>(this);
         mDynamicUploader = std::make_unique<DynamicUploader>(this);
         mDeprecationWarnings = std::make_unique<DeprecationWarnings>();
 
@@ -146,6 +148,7 @@
             // pending callbacks.
             mErrorScopeTracker->Tick(GetCompletedCommandSerial());
             mFenceSignalTracker->Tick(GetCompletedCommandSerial());
+            mMapRequestTracker->Tick(GetCompletedCommandSerial());
         }
 
         // At this point GPU operations are always finished, so we are in the disconnected state.
@@ -155,6 +158,7 @@
         mCurrentErrorScope->UnlinkForShutdown();
         mFenceSignalTracker = nullptr;
         mDynamicUploader = nullptr;
+        mMapRequestTracker = nullptr;
 
         // Tell the backend that it can free all the objects now that the GPU timeline is empty.
         ShutDownImpl();
@@ -301,6 +305,10 @@
         return mFenceSignalTracker.get();
     }
 
+    MapRequestTracker* DeviceBase::GetMapRequestTracker() const {
+        return mMapRequestTracker.get();
+    }
+
     Serial DeviceBase::GetCompletedCommandSerial() const {
         return mCompletedSerial;
     }
@@ -711,6 +719,7 @@
         mDynamicUploader->Deallocate(GetCompletedCommandSerial());
         mErrorScopeTracker->Tick(GetCompletedCommandSerial());
         mFenceSignalTracker->Tick(GetCompletedCommandSerial());
+        mMapRequestTracker->Tick(GetCompletedCommandSerial());
     }
 
     void DeviceBase::Reference() {
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 0a270a5..3aa5920 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -37,6 +37,7 @@
     class ErrorScope;
     class ErrorScopeTracker;
     class FenceSignalTracker;
+    class MapRequestTracker;
     class StagingBufferBase;
 
     class DeviceBase {
@@ -71,6 +72,7 @@
 
         ErrorScopeTracker* GetErrorScopeTracker() const;
         FenceSignalTracker* GetFenceSignalTracker() const;
+        MapRequestTracker* GetMapRequestTracker() const;
 
         // Returns the Format corresponding to the wgpu::TextureFormat or an error if the format
         // isn't a valid wgpu::TextureFormat or isn't supported by this device.
@@ -331,6 +333,7 @@
         std::unique_ptr<DynamicUploader> mDynamicUploader;
         std::unique_ptr<ErrorScopeTracker> mErrorScopeTracker;
         std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
+        std::unique_ptr<MapRequestTracker> mMapRequestTracker;
         Ref<QueueBase> mDefaultQueue;
 
         struct DeprecationWarnings;
diff --git a/src/dawn_native/MapRequestTracker.cpp b/src/dawn_native/MapRequestTracker.cpp
new file mode 100644
index 0000000..fefcabc
--- /dev/null
+++ b/src/dawn_native/MapRequestTracker.cpp
@@ -0,0 +1,45 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/MapRequestTracker.h"
+#include "dawn_native/Buffer.h"
+#include "dawn_native/Device.h"
+
+namespace dawn_native {
+    struct Request;
+    class DeviceBase;
+
+    MapRequestTracker::MapRequestTracker(DeviceBase* device) : mDevice(device) {
+    }
+
+    MapRequestTracker::~MapRequestTracker() {
+        ASSERT(mInflightRequests.Empty());
+    }
+
+    void MapRequestTracker::Track(BufferBase* buffer, uint32_t mapSerial, bool isWrite) {
+        Request request;
+        request.buffer = buffer;
+        request.mapSerial = mapSerial;
+        request.isWrite = isWrite;
+
+        mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial());
+    }
+
+    void MapRequestTracker::Tick(Serial finishedSerial) {
+        for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
+            request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.isWrite);
+        }
+        mInflightRequests.ClearUpTo(finishedSerial);
+    }
+}  // namespace dawn_native
\ No newline at end of file
diff --git a/src/dawn_native/MapRequestTracker.h b/src/dawn_native/MapRequestTracker.h
new file mode 100644
index 0000000..0dffca1
--- /dev/null
+++ b/src/dawn_native/MapRequestTracker.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_MAPREQUESTTRACKER_H_
+#define DAWNNATIVE_MAPREQUESTTRACKER_H_
+
+#include "common/SerialQueue.h"
+#include "dawn_native/Device.h"
+
+namespace dawn_native {
+
+    class MapRequestTracker {
+      public:
+        MapRequestTracker(DeviceBase* device);
+        ~MapRequestTracker();
+
+        void Track(BufferBase* buffer, uint32_t mapSerial, bool isWrite);
+        void Tick(Serial finishedSerial);
+
+      private:
+        DeviceBase* mDevice;
+
+        struct Request {
+            Ref<BufferBase> buffer;
+            uint32_t mapSerial;
+            bool isWrite;
+        };
+        SerialQueue<Request> mInflightRequests;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_MAPREQUESTTRACKER_H
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp
index fa68ef7..ba79052 100644
--- a/src/dawn_native/d3d12/BufferD3D12.cpp
+++ b/src/dawn_native/d3d12/BufferD3D12.cpp
@@ -233,14 +233,6 @@
         return mResourceAllocation.GetGPUPointer();
     }
 
-    void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite) {
-        if (isWrite) {
-            CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
-        } else {
-            CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
-        }
-    }
-
     bool Buffer::IsMapWritable() const {
         // TODO(enga): Handle CPU-visible memory on UMA
         return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0;
@@ -256,6 +248,7 @@
         DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
                                                       reinterpret_cast<void**>(mappedPointer)),
                               "D3D12 map at creation"));
+        mMappedData = reinterpret_cast<char*>(mappedPointer);
         return {};
     }
 
@@ -267,14 +260,11 @@
 
         mWrittenMappedRange = {};
         D3D12_RANGE readRange = {0, static_cast<size_t>(GetSize())};
-        char* data = nullptr;
-        DAWN_TRY(
-            CheckHRESULT(GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&data)),
-                         "D3D12 map read async"));
+        DAWN_TRY(CheckHRESULT(
+            GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&mMappedData)),
+            "D3D12 map read async"));
         // There is no need to transition the resource to a new state: D3D12 seems to make the GPU
         // writes available when the fence is passed.
-        MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
-        tracker->Track(this, serial, data, false);
         return {};
     }
 
@@ -285,14 +275,11 @@
         DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockHeap(heap));
 
         mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
-        char* data = nullptr;
-        DAWN_TRY(CheckHRESULT(
-            GetD3D12Resource()->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(&data)),
-            "D3D12 map write async"));
+        DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
+                                                      reinterpret_cast<void**>(&mMappedData)),
+                              "D3D12 map write async"));
         // There is no need to transition the resource to a new state: D3D12 seems to make the CPU
         // writes available on queue submission.
-        MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
-        tracker->Track(this, serial, data, true);
         return {};
     }
 
@@ -303,6 +290,11 @@
         Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
         ToBackend(GetDevice())->GetResidencyManager()->UnlockHeap(heap);
         mWrittenMappedRange = {};
+        mMappedData = nullptr;
+    }
+
+    void* Buffer::GetMappedPointerImpl() {
+        return mMappedData;
     }
 
     void Buffer::DestroyImpl() {
@@ -325,29 +317,4 @@
         return mResourceAllocation.GetInfo().mMethod == allocationMethod;
     }
 
-    MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
-    }
-
-    MapRequestTracker::~MapRequestTracker() {
-        ASSERT(mInflightRequests.Empty());
-    }
-
-    void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) {
-        Request request;
-        request.buffer = buffer;
-        request.mapSerial = mapSerial;
-        request.data = data;
-        request.isWrite = isWrite;
-
-        mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial());
-    }
-
-    void MapRequestTracker::Tick(Serial finishedSerial) {
-        for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
-            request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.data,
-                                                       request.isWrite);
-        }
-        mInflightRequests.ClearUpTo(finishedSerial);
-    }
-
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/BufferD3D12.h b/src/dawn_native/d3d12/BufferD3D12.h
index 02ec403..5410738 100644
--- a/src/dawn_native/d3d12/BufferD3D12.h
+++ b/src/dawn_native/d3d12/BufferD3D12.h
@@ -34,7 +34,6 @@
 
         ComPtr<ID3D12Resource> GetD3D12Resource() const;
         D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
-        void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite);
 
         bool TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
                                              D3D12_RESOURCE_BARRIER* barrier,
@@ -55,6 +54,7 @@
 
         bool IsMapWritable() const override;
         virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
+        void* GetMappedPointerImpl() override;
 
         bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
                                                   D3D12_RESOURCE_BARRIER* barrier,
@@ -65,26 +65,7 @@
         wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None;
         Serial mLastUsedSerial = UINT64_MAX;
         D3D12_RANGE mWrittenMappedRange;
-    };
-
-    class MapRequestTracker {
-      public:
-        MapRequestTracker(Device* device);
-        ~MapRequestTracker();
-
-        void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite);
-        void Tick(Serial finishedSerial);
-
-      private:
-        Device* mDevice;
-
-        struct Request {
-            Ref<Buffer> buffer;
-            uint32_t mapSerial;
-            void* data;
-            bool isWrite;
-        };
-        SerialQueue<Request> mInflightRequests;
+        char* mMappedData = nullptr;
     };
 
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 9f4d887..b46000e 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -112,7 +112,6 @@
 
         mSamplerHeapCache = std::make_unique<SamplerHeapCache>(this);
 
-        mMapRequestTracker = std::make_unique<MapRequestTracker>(this);
         mResidencyManager = std::make_unique<ResidencyManager>(this);
         mResourceAllocatorManager = std::make_unique<ResourceAllocatorManager>(this);
 
@@ -189,10 +188,6 @@
         return ToBackend(GetAdapter())->GetBackend()->GetFunctions();
     }
 
-    MapRequestTracker* Device::GetMapRequestTracker() const {
-        return mMapRequestTracker.get();
-    }
-
     CommandAllocatorManager* Device::GetCommandAllocatorManager() const {
         return mCommandAllocatorManager.get();
     }
@@ -222,7 +217,6 @@
         mSamplerShaderVisibleDescriptorAllocator->Tick(completedSerial);
         mRenderTargetViewAllocator->Tick(completedSerial);
         mDepthStencilViewAllocator->Tick(completedSerial);
-        mMapRequestTracker->Tick(completedSerial);
         mUsedComObjectRefs.ClearUpTo(completedSerial);
         DAWN_TRY(ExecutePendingCommandContext());
         DAWN_TRY(NextSerial());
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index c55f8f7..5f0fafc 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -31,7 +31,6 @@
 
     class CommandAllocatorManager;
     class DescriptorHeapAllocator;
-    class MapRequestTracker;
     class PlatformFunctions;
     class ResidencyManager;
     class ResourceAllocatorManager;
@@ -66,7 +65,6 @@
         ComPtr<ID3D12CommandSignature> GetDrawIndirectSignature() const;
         ComPtr<ID3D12CommandSignature> GetDrawIndexedIndirectSignature() const;
 
-        MapRequestTracker* GetMapRequestTracker() const;
         CommandAllocatorManager* GetCommandAllocatorManager() const;
         ResidencyManager* GetResidencyManager() const;
 
@@ -179,7 +177,6 @@
         SerialQueue<ComPtr<IUnknown>> mUsedComObjectRefs;
 
         std::unique_ptr<CommandAllocatorManager> mCommandAllocatorManager;
-        std::unique_ptr<MapRequestTracker> mMapRequestTracker;
         std::unique_ptr<ResourceAllocatorManager> mResourceAllocatorManager;
         std::unique_ptr<ResidencyManager> mResidencyManager;
 
diff --git a/src/dawn_native/metal/BufferMTL.h b/src/dawn_native/metal/BufferMTL.h
index 081503b..afd123e 100644
--- a/src/dawn_native/metal/BufferMTL.h
+++ b/src/dawn_native/metal/BufferMTL.h
@@ -29,8 +29,6 @@
         static ResultOrError<Buffer*> Create(Device* device, const BufferDescriptor* descriptor);
         id<MTLBuffer> GetMTLBuffer() const;
 
-        void OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite);
-
       private:
         using BufferBase::BufferBase;
         MaybeError Initialize();
@@ -40,6 +38,7 @@
         MaybeError MapWriteAsyncImpl(uint32_t serial) override;
         void UnmapImpl() override;
         void DestroyImpl() override;
+        void* GetMappedPointerImpl() override;
 
         bool IsMapWritable() const override;
         MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
@@ -47,25 +46,6 @@
         id<MTLBuffer> mMtlBuffer = nil;
     };
 
-    class MapRequestTracker {
-      public:
-        MapRequestTracker(Device* device);
-        ~MapRequestTracker();
-
-        void Track(Buffer* buffer, uint32_t mapSerial, bool isWrite);
-        void Tick(Serial finishedSerial);
-
-      private:
-        Device* mDevice;
-
-        struct Request {
-            Ref<Buffer> buffer;
-            uint32_t mapSerial;
-            bool isWrite;
-        };
-        SerialQueue<Request> mInflightRequests;
-    };
-
 }}  // namespace dawn_native::metal
 
 #endif  // DAWNNATIVE_METAL_BUFFERMTL_H_
diff --git a/src/dawn_native/metal/BufferMTL.mm b/src/dawn_native/metal/BufferMTL.mm
index 5b57778..0f84467 100644
--- a/src/dawn_native/metal/BufferMTL.mm
+++ b/src/dawn_native/metal/BufferMTL.mm
@@ -67,15 +67,6 @@
         return mMtlBuffer;
     }
 
-    void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) {
-        char* data = reinterpret_cast<char*>([mMtlBuffer contents]);
-        if (isWrite) {
-            CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
-        } else {
-            CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
-        }
-    }
-
     bool Buffer::IsMapWritable() const {
         // TODO(enga): Handle CPU-visible memory on UMA
         return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0;
@@ -87,17 +78,17 @@
     }
 
     MaybeError Buffer::MapReadAsyncImpl(uint32_t serial) {
-        MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker();
-        tracker->Track(this, serial, false);
         return {};
     }
 
     MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) {
-        MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker();
-        tracker->Track(this, serial, true);
         return {};
     }
 
+    void* Buffer::GetMappedPointerImpl() {
+        return reinterpret_cast<uint8_t*>([mMtlBuffer contents]);
+    }
+
     void Buffer::UnmapImpl() {
         // Nothing to do, Metal StorageModeShared buffers are always mapped.
     }
@@ -107,29 +98,4 @@
         mMtlBuffer = nil;
     }
 
-    MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
-    }
-
-    MapRequestTracker::~MapRequestTracker() {
-        ASSERT(mInflightRequests.Empty());
-    }
-
-    void MapRequestTracker::Track(Buffer* buffer,
-                                  uint32_t mapSerial,
-                                  bool isWrite) {
-        Request request;
-        request.buffer = buffer;
-        request.mapSerial = mapSerial;
-        request.isWrite = isWrite;
-
-        mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial());
-    }
-
-    void MapRequestTracker::Tick(Serial finishedSerial) {
-        for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
-            request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.isWrite);
-        }
-        mInflightRequests.ClearUpTo(finishedSerial);
-    }
-
 }}  // namespace dawn_native::metal
diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h
index b09926a..33ce4b6 100644
--- a/src/dawn_native/metal/DeviceMTL.h
+++ b/src/dawn_native/metal/DeviceMTL.h
@@ -32,8 +32,6 @@
 
 namespace dawn_native { namespace metal {
 
-    class MapRequestTracker;
-
     class Device : public DeviceBase {
       public:
         static ResultOrError<Device*> Create(AdapterBase* adapter,
@@ -54,8 +52,6 @@
         CommandRecordingContext* GetPendingCommandContext();
         void SubmitPendingCommandBuffer();
 
-        MapRequestTracker* GetMapTracker() const;
-
         TextureBase* CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor,
                                                     IOSurfaceRef ioSurface,
                                                     uint32_t plane);
@@ -104,7 +100,6 @@
 
         id<MTLDevice> mMtlDevice = nil;
         id<MTLCommandQueue> mCommandQueue = nil;
-        std::unique_ptr<MapRequestTracker> mMapTracker;
 
         CommandRecordingContext mCommandContext;
 
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index 905832e..f396f58 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -51,7 +51,6 @@
                    const DeviceDescriptor* descriptor)
         : DeviceBase(adapter, descriptor),
           mMtlDevice([mtlDevice retain]),
-          mMapTracker(new MapRequestTracker(this)),
           mCompletedSerial(0) {
         [mMtlDevice retain];
     }
@@ -170,8 +169,6 @@
         CheckPassedSerials();
         Serial completedSerial = GetCompletedCommandSerial();
 
-        mMapTracker->Tick(completedSerial);
-
         if (mCommandContext.GetCommands() != nil) {
             SubmitPendingCommandBuffer();
         } else if (completedSerial == GetLastSubmittedCommandSerial()) {
@@ -245,10 +242,6 @@
         [pendingCommands release];
     }
 
-    MapRequestTracker* Device::GetMapTracker() const {
-        return mMapTracker.get();
-    }
-
     ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
         std::unique_ptr<StagingBufferBase> stagingBuffer =
             std::make_unique<StagingBuffer>(size, this);
@@ -323,8 +316,6 @@
 
         [mCommandContext.AcquireCommands() release];
 
-        mMapTracker = nullptr;
-
         [mCommandQueue release];
         mCommandQueue = nil;
 
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index 0dac43e..77ef491 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -266,7 +266,7 @@
 
     struct BufferMapOperation : PendingOperation {
         virtual void Execute() {
-            buffer->MapOperationCompleted(serial, ptr, isWrite);
+            buffer->OnMapCommandSerialFinished(serial, isWrite);
         }
 
         Ref<Buffer> buffer;
@@ -296,14 +296,6 @@
         return {};
     }
 
-    void Buffer::MapOperationCompleted(uint32_t serial, void* ptr, bool isWrite) {
-        if (isWrite) {
-            CallMapWriteCallback(serial, WGPUBufferMapAsyncStatus_Success, ptr, GetSize());
-        } else {
-            CallMapReadCallback(serial, WGPUBufferMapAsyncStatus_Success, ptr, GetSize());
-        }
-    }
-
     void Buffer::CopyFromStaging(StagingBufferBase* staging,
                                  uint64_t sourceOffset,
                                  uint64_t destinationOffset,
@@ -341,6 +333,10 @@
         ToBackend(GetDevice())->AddPendingOperation(std::move(operation));
     }
 
+    void* Buffer::GetMappedPointerImpl() {
+        return mBackingData.get();
+    }
+
     void Buffer::UnmapImpl() {
     }
 
diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h
index a78c8a3..8d2eeee 100644
--- a/src/dawn_native/null/DeviceNull.h
+++ b/src/dawn_native/null/DeviceNull.h
@@ -182,7 +182,6 @@
       public:
         Buffer(Device* device, const BufferDescriptor* descriptor);
 
-        void MapOperationCompleted(uint32_t serial, void* ptr, bool isWrite);
         void CopyFromStaging(StagingBufferBase* staging,
                              uint64_t sourceOffset,
                              uint64_t destinationOffset,
@@ -201,6 +200,7 @@
         bool IsMapWritable() const override;
         MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
         void MapAsyncImplCommon(uint32_t serial, bool isWrite);
+        void* GetMappedPointerImpl() override;
 
         std::unique_ptr<uint8_t[]> mBackingData;
     };
diff --git a/src/dawn_native/opengl/BufferGL.cpp b/src/dawn_native/opengl/BufferGL.cpp
index 80311af..0ccb726 100644
--- a/src/dawn_native/opengl/BufferGL.cpp
+++ b/src/dawn_native/opengl/BufferGL.cpp
@@ -45,8 +45,8 @@
         const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
 
         gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
-        void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
-        *mappedPointer = reinterpret_cast<uint8_t*>(data);
+        mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+        *mappedPointer = reinterpret_cast<uint8_t*>(mMappedData);
         return {};
     }
 
@@ -64,8 +64,7 @@
         // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high
         // version of OpenGL that would let us map the buffer unsynchronized.
         gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
-        void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
-        CallMapReadCallback(serial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
+        mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
         return {};
     }
 
@@ -75,16 +74,20 @@
         // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high
         // version of OpenGL that would let us map the buffer unsynchronized.
         gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
-        void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
-        CallMapWriteCallback(serial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
+        mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
         return {};
     }
 
+    void* Buffer::GetMappedPointerImpl() {
+        return mMappedData;
+    }
+
     void Buffer::UnmapImpl() {
         const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
 
         gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
         gl.UnmapBuffer(GL_ARRAY_BUFFER);
+        mMappedData = nullptr;
     }
 
     void Buffer::DestroyImpl() {
diff --git a/src/dawn_native/opengl/BufferGL.h b/src/dawn_native/opengl/BufferGL.h
index 2211a5c..01177f3 100644
--- a/src/dawn_native/opengl/BufferGL.h
+++ b/src/dawn_native/opengl/BufferGL.h
@@ -40,8 +40,10 @@
 
         bool IsMapWritable() const override;
         MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
+        void* GetMappedPointerImpl() override;
 
         GLuint mBuffer = 0;
+        void* mMappedData = nullptr;
     };
 
 }}  // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index 4508176..7e9e42d 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -153,6 +153,11 @@
 
     MaybeError Device::TickImpl() {
         CheckPassedSerials();
+        if (GetCompletedCommandSerial() == GetLastSubmittedCommandSerial()) {
+            // If there's no GPU work in flight we still need to artificially increment the serial
+            // so that CPU operations waiting on GPU completion can know they don't have to wait.
+            ArtificiallyIncrementSerials();
+        }
         return {};
     }
 
diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp
index 4bc09e0..0b4584b 100644
--- a/src/dawn_native/vulkan/BufferVk.cpp
+++ b/src/dawn_native/vulkan/BufferVk.cpp
@@ -236,12 +236,6 @@
 
         CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
         TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapRead);
-
-        uint8_t* memory = mMemoryAllocation.GetMappedPointer();
-        ASSERT(memory != nullptr);
-
-        MapRequestTracker* tracker = device->GetMapRequestTracker();
-        tracker->Track(this, serial, memory, false);
         return {};
     }
 
@@ -250,12 +244,6 @@
 
         CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
         TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapWrite);
-
-        uint8_t* memory = mMemoryAllocation.GetMappedPointer();
-        ASSERT(memory != nullptr);
-
-        MapRequestTracker* tracker = device->GetMapRequestTracker();
-        tracker->Track(this, serial, memory, true);
         return {};
     }
 
@@ -263,6 +251,12 @@
         // No need to do anything, we keep CPU-visible memory mapped at all time.
     }
 
+    void* Buffer::GetMappedPointerImpl() {
+        uint8_t* memory = mMemoryAllocation.GetMappedPointer();
+        ASSERT(memory != nullptr);
+        return memory;
+    }
+
     void Buffer::DestroyImpl() {
         ToBackend(GetDevice())->DeallocateMemory(&mMemoryAllocation);
 
@@ -272,34 +266,4 @@
         }
     }
 
-    // MapRequestTracker
-
-    MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
-    }
-
-    MapRequestTracker::~MapRequestTracker() {
-        ASSERT(mInflightRequests.Empty());
-    }
-
-    void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) {
-        Request request;
-        request.buffer = buffer;
-        request.mapSerial = mapSerial;
-        request.data = data;
-        request.isWrite = isWrite;
-
-        mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial());
-    }
-
-    void MapRequestTracker::Tick(Serial finishedSerial) {
-        for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
-            if (request.isWrite) {
-                request.buffer->OnMapWriteCommandSerialFinished(request.mapSerial, request.data);
-            } else {
-                request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data);
-            }
-        }
-        mInflightRequests.ClearUpTo(finishedSerial);
-    }
-
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/BufferVk.h b/src/dawn_native/vulkan/BufferVk.h
index 3d7fdf9..021e4ab 100644
--- a/src/dawn_native/vulkan/BufferVk.h
+++ b/src/dawn_native/vulkan/BufferVk.h
@@ -53,6 +53,7 @@
 
         bool IsMapWritable() const override;
         MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
+        void* GetMappedPointerImpl() override;
 
         VkBuffer mHandle = VK_NULL_HANDLE;
         ResourceMemoryAllocation mMemoryAllocation;
@@ -60,26 +61,6 @@
         wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None;
     };
 
-    class MapRequestTracker {
-      public:
-        MapRequestTracker(Device* device);
-        ~MapRequestTracker();
-
-        void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite);
-        void Tick(Serial finishedSerial);
-
-      private:
-        Device* mDevice;
-
-        struct Request {
-            Ref<Buffer> buffer;
-            uint32_t mapSerial;
-            void* data;
-            bool isWrite;
-        };
-        SerialQueue<Request> mInflightRequests;
-    };
-
 }}  // namespace dawn_native::vulkan
 
 #endif  // DAWNNATIVE_VULKAN_BUFFERVK_H_
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index a2ba72a..9edefcb 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -82,7 +82,6 @@
             mDeleter = std::make_unique<FencedDeleter>(this);
         }
 
-        mMapRequestTracker = std::make_unique<MapRequestTracker>(this);
         mRenderPassCache = std::make_unique<RenderPassCache>(this);
         mResourceMemoryAllocator = std::make_unique<ResourceMemoryAllocator>(this);
 
@@ -167,7 +166,6 @@
         }
         mBindGroupLayoutsPendingDeallocation.ClearUpTo(completedSerial);
 
-        mMapRequestTracker->Tick(completedSerial);
         mResourceMemoryAllocator->Tick(completedSerial);
         mDeleter->Tick(completedSerial);
 
@@ -201,10 +199,6 @@
         return mQueue;
     }
 
-    MapRequestTracker* Device::GetMapRequestTracker() const {
-        return mMapRequestTracker.get();
-    }
-
     FencedDeleter* Device::GetFencedDeleter() const {
         return mDeleter.get();
     }
@@ -802,8 +796,6 @@
         // Call Tick() again to clear them before releasing the deleter.
         mDeleter->Tick(GetCompletedCommandSerial());
 
-        mMapRequestTracker = nullptr;
-
         // The VkRenderPasses in the cache can be destroyed immediately since all commands referring
         // to them are guaranteed to be finished executing.
         mRenderPassCache = nullptr;
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index 585d7f0..799f7f2 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -37,7 +37,6 @@
     class BindGroupLayout;
     class BufferUploader;
     class FencedDeleter;
-    class MapRequestTracker;
     class RenderPassCache;
     class ResourceMemoryAllocator;
 
@@ -59,7 +58,6 @@
 
         BufferUploader* GetBufferUploader() const;
         FencedDeleter* GetFencedDeleter() const;
-        MapRequestTracker* GetMapRequestTracker() const;
         RenderPassCache* GetRenderPassCache() const;
 
         CommandRecordingContext* GetPendingRecordingContext();
@@ -147,7 +145,6 @@
 
         SerialQueue<Ref<BindGroupLayout>> mBindGroupLayoutsPendingDeallocation;
         std::unique_ptr<FencedDeleter> mDeleter;
-        std::unique_ptr<MapRequestTracker> mMapRequestTracker;
         std::unique_ptr<ResourceMemoryAllocator> mResourceMemoryAllocator;
         std::unique_ptr<RenderPassCache> mRenderPassCache;