Updates D3D12 device allocators for thread-safety.

Bug: dawn:1662
Change-Id: I3b7913c15a887e45ff5c4387eb50a7f399b3362a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/144208
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/d3d12/BindGroupD3D12.cpp b/src/dawn/native/d3d12/BindGroupD3D12.cpp
index 8100cc8..e510b79 100644
--- a/src/dawn/native/d3d12/BindGroupD3D12.cpp
+++ b/src/dawn/native/d3d12/BindGroupD3D12.cpp
@@ -212,7 +212,7 @@
     ASSERT(!mCPUViewAllocation.IsValid());
 }
 
-bool BindGroup::PopulateViews(ShaderVisibleDescriptorAllocator* viewAllocator) {
+bool BindGroup::PopulateViews(MutexProtected<ShaderVisibleDescriptorAllocator>& viewAllocator) {
     const BindGroupLayout* bgl = ToBackend(GetLayout());
 
     const uint32_t descriptorCount = bgl->GetCbvUavSrvDescriptorCount();
@@ -249,8 +249,9 @@
     return mSamplerAllocationEntry->GetBaseDescriptor();
 }
 
-bool BindGroup::PopulateSamplers(Device* device,
-                                 ShaderVisibleDescriptorAllocator* samplerAllocator) {
+bool BindGroup::PopulateSamplers(
+    Device* device,
+    MutexProtected<ShaderVisibleDescriptorAllocator>& samplerAllocator) {
     if (mSamplerAllocationEntry == nullptr) {
         return true;
     }
diff --git a/src/dawn/native/d3d12/BindGroupD3D12.h b/src/dawn/native/d3d12/BindGroupD3D12.h
index 3faafba..b4e6c66 100644
--- a/src/dawn/native/d3d12/BindGroupD3D12.h
+++ b/src/dawn/native/d3d12/BindGroupD3D12.h
@@ -15,6 +15,7 @@
 #ifndef SRC_DAWN_NATIVE_D3D12_BINDGROUPD3D12_H_
 #define SRC_DAWN_NATIVE_D3D12_BINDGROUPD3D12_H_
 
+#include "dawn/common/MutexProtected.h"
 #include "dawn/common/PlacementAllocated.h"
 #include "dawn/common/ityp_span.h"
 #include "dawn/common/ityp_stack_vec.h"
@@ -39,8 +40,9 @@
               const CPUDescriptorHeapAllocation& viewAllocation);
 
     // Returns true if the BindGroup was successfully populated.
-    bool PopulateViews(ShaderVisibleDescriptorAllocator* viewAllocator);
-    bool PopulateSamplers(Device* device, ShaderVisibleDescriptorAllocator* samplerAllocator);
+    bool PopulateViews(MutexProtected<ShaderVisibleDescriptorAllocator>& viewAllocator);
+    bool PopulateSamplers(Device* device,
+                          MutexProtected<ShaderVisibleDescriptorAllocator>& samplerAllocator);
 
     D3D12_GPU_DESCRIPTOR_HANDLE GetBaseViewDescriptor() const;
     D3D12_GPU_DESCRIPTOR_HANDLE GetBaseSamplerDescriptor() const;
diff --git a/src/dawn/native/d3d12/BindGroupLayoutD3D12.cpp b/src/dawn/native/d3d12/BindGroupLayoutD3D12.cpp
index 3c24d7b..ed66d7f 100644
--- a/src/dawn/native/d3d12/BindGroupLayoutD3D12.cpp
+++ b/src/dawn/native/d3d12/BindGroupLayoutD3D12.cpp
@@ -159,17 +159,22 @@
     uint32_t viewSizeIncrement = 0;
     CPUDescriptorHeapAllocation viewAllocation;
     if (GetCbvUavSrvDescriptorCount() > 0) {
-        DAWN_TRY_ASSIGN(viewAllocation, mViewAllocator->AllocateCPUDescriptors());
-        viewSizeIncrement = mViewAllocator->GetSizeIncrement();
+        ASSERT(mViewAllocator != nullptr);
+        DAWN_TRY((*mViewAllocator).Use([&](auto viewAllocator) -> MaybeError {
+            DAWN_TRY_ASSIGN(viewAllocation, viewAllocator->AllocateCPUDescriptors());
+            viewSizeIncrement = viewAllocator->GetSizeIncrement();
+            return {};
+        }));
     }
 
     Ref<BindGroup> bindGroup = AcquireRef<BindGroup>(
         mBindGroupAllocator->Allocate(device, descriptor, viewSizeIncrement, viewAllocation));
 
     if (GetSamplerDescriptorCount() > 0) {
+        ASSERT(mSamplerAllocator != nullptr);
         Ref<SamplerHeapCacheEntry> samplerHeapCacheEntry;
         DAWN_TRY_ASSIGN(samplerHeapCacheEntry, device->GetSamplerHeapCache()->GetOrCreate(
-                                                   bindGroup.Get(), mSamplerAllocator));
+                                                   bindGroup.Get(), *mSamplerAllocator));
         bindGroup->SetSamplerAllocationEntry(std::move(samplerHeapCacheEntry));
     }
 
@@ -179,7 +184,7 @@
 void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup,
                                           CPUDescriptorHeapAllocation* viewAllocation) {
     if (viewAllocation->IsValid()) {
-        mViewAllocator->Deallocate(viewAllocation);
+        (*mViewAllocator)->Deallocate(viewAllocation);
     }
 
     mBindGroupAllocator->Deallocate(bindGroup);
diff --git a/src/dawn/native/d3d12/BindGroupLayoutD3D12.h b/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
index 7357ddc..b082c7e 100644
--- a/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
+++ b/src/dawn/native/d3d12/BindGroupLayoutD3D12.h
@@ -83,8 +83,8 @@
 
     MutexProtected<SlabAllocator<BindGroup>> mBindGroupAllocator;
 
-    StagingDescriptorAllocator* mSamplerAllocator = nullptr;
-    StagingDescriptorAllocator* mViewAllocator = nullptr;
+    MutexProtected<StagingDescriptorAllocator>* mSamplerAllocator = nullptr;
+    MutexProtected<StagingDescriptorAllocator>* mViewAllocator = nullptr;
 };
 
 }  // namespace dawn::native::d3d12
diff --git a/src/dawn/native/d3d12/CommandBufferD3D12.cpp b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
index 6c57af3..32af020 100644
--- a/src/dawn/native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
@@ -18,6 +18,7 @@
 #include <utility>
 #include <vector>
 
+#include "dawn/common/MutexProtected.h"
 #include "dawn/native/BindGroupTracker.h"
 #include "dawn/native/CommandValidation.h"
 #include "dawn/native/DynamicUploader.h"
@@ -605,8 +606,8 @@
     ityp::array<BindGroupIndex, D3D12_GPU_DESCRIPTOR_HANDLE, kMaxBindGroups>
         mBoundRootSamplerTables = {};
 
-    ShaderVisibleDescriptorAllocator* mViewAllocator;
-    ShaderVisibleDescriptorAllocator* mSamplerAllocator;
+    MutexProtected<ShaderVisibleDescriptorAllocator>& mViewAllocator;
+    MutexProtected<ShaderVisibleDescriptorAllocator>& mSamplerAllocator;
 };
 
 class DescriptorHeapState {
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp
index 44ac992..7835b1b 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -117,21 +117,23 @@
 
     // Zero sized allocator is never requested and does not need to exist.
     for (uint32_t countIndex = 0; countIndex < kNumViewDescriptorAllocators; countIndex++) {
-        mViewAllocators[countIndex + 1] = std::make_unique<StagingDescriptorAllocator>(
-            this, 1u << countIndex, kShaderVisibleDescriptorHeapSize,
-            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+        mViewAllocators[countIndex + 1] =
+            std::make_unique<MutexProtected<StagingDescriptorAllocator>>(
+                this, 1u << countIndex, kShaderVisibleDescriptorHeapSize,
+                D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
     }
 
     for (uint32_t countIndex = 0; countIndex < kNumSamplerDescriptorAllocators; countIndex++) {
-        mSamplerAllocators[countIndex + 1] = std::make_unique<StagingDescriptorAllocator>(
-            this, 1u << countIndex, kShaderVisibleDescriptorHeapSize,
-            D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+        mSamplerAllocators[countIndex + 1] =
+            std::make_unique<MutexProtected<StagingDescriptorAllocator>>(
+                this, 1u << countIndex, kShaderVisibleDescriptorHeapSize,
+                D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
     }
 
-    mRenderTargetViewAllocator = std::make_unique<StagingDescriptorAllocator>(
+    mRenderTargetViewAllocator = std::make_unique<MutexProtected<StagingDescriptorAllocator>>(
         this, 1, kAttachmentDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
 
-    mDepthStencilViewAllocator = std::make_unique<StagingDescriptorAllocator>(
+    mDepthStencilViewAllocator = std::make_unique<MutexProtected<StagingDescriptorAllocator>>(
         this, 1, kAttachmentDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
 
     mSamplerHeapCache = std::make_unique<SamplerHeapCache>(this);
@@ -312,10 +314,10 @@
 
     mResourceAllocatorManager->Tick(completedSerial);
     DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial));
-    mViewShaderVisibleDescriptorAllocator->Tick(completedSerial);
-    mSamplerShaderVisibleDescriptorAllocator->Tick(completedSerial);
-    mRenderTargetViewAllocator->Tick(completedSerial);
-    mDepthStencilViewAllocator->Tick(completedSerial);
+    (*mViewShaderVisibleDescriptorAllocator)->Tick(completedSerial);
+    (*mSamplerShaderVisibleDescriptorAllocator)->Tick(completedSerial);
+    (*mRenderTargetViewAllocator)->Tick(completedSerial);
+    (*mDepthStencilViewAllocator)->Tick(completedSerial);
     mUsedComObjectRefs.ClearUpTo(completedSerial);
 
     if (mPendingCommands.IsOpen() && mPendingCommands.NeedsSubmit()) {
@@ -721,15 +723,17 @@
     mCommandQueue.Reset();
 }
 
-ShaderVisibleDescriptorAllocator* Device::GetViewShaderVisibleDescriptorAllocator() const {
-    return mViewShaderVisibleDescriptorAllocator.get();
+MutexProtected<ShaderVisibleDescriptorAllocator>& Device::GetViewShaderVisibleDescriptorAllocator()
+    const {
+    return *mViewShaderVisibleDescriptorAllocator.get();
 }
 
-ShaderVisibleDescriptorAllocator* Device::GetSamplerShaderVisibleDescriptorAllocator() const {
-    return mSamplerShaderVisibleDescriptorAllocator.get();
+MutexProtected<ShaderVisibleDescriptorAllocator>&
+Device::GetSamplerShaderVisibleDescriptorAllocator() const {
+    return *mSamplerShaderVisibleDescriptorAllocator.get();
 }
 
-StagingDescriptorAllocator* Device::GetViewStagingDescriptorAllocator(
+MutexProtected<StagingDescriptorAllocator>* Device::GetViewStagingDescriptorAllocator(
     uint32_t descriptorCount) const {
     ASSERT(descriptorCount <= kMaxViewDescriptorsPerBindGroup);
     // This is Log2 of the next power of two, plus 1.
@@ -737,7 +741,7 @@
     return mViewAllocators[allocatorIndex].get();
 }
 
-StagingDescriptorAllocator* Device::GetSamplerStagingDescriptorAllocator(
+MutexProtected<StagingDescriptorAllocator>* Device::GetSamplerStagingDescriptorAllocator(
     uint32_t descriptorCount) const {
     ASSERT(descriptorCount <= kMaxSamplerDescriptorsPerBindGroup);
     // This is Log2 of the next power of two, plus 1.
@@ -745,12 +749,12 @@
     return mSamplerAllocators[allocatorIndex].get();
 }
 
-StagingDescriptorAllocator* Device::GetRenderTargetViewAllocator() const {
-    return mRenderTargetViewAllocator.get();
+MutexProtected<StagingDescriptorAllocator>& Device::GetRenderTargetViewAllocator() const {
+    return *mRenderTargetViewAllocator.get();
 }
 
-StagingDescriptorAllocator* Device::GetDepthStencilViewAllocator() const {
-    return mDepthStencilViewAllocator.get();
+MutexProtected<StagingDescriptorAllocator>& Device::GetDepthStencilViewAllocator() const {
+    return *mDepthStencilViewAllocator.get();
 }
 
 SamplerHeapCache* Device::GetSamplerHeapCache() {
diff --git a/src/dawn/native/d3d12/DeviceD3D12.h b/src/dawn/native/d3d12/DeviceD3D12.h
index 4d1e3c3..7db7d37 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.h
+++ b/src/dawn/native/d3d12/DeviceD3D12.h
@@ -18,6 +18,7 @@
 #include <memory>
 #include <vector>
 
+#include "dawn/common/MutexProtected.h"
 #include "dawn/common/SerialQueue.h"
 #include "dawn/native/d3d/DeviceD3D.h"
 #include "dawn/native/d3d12/CommandRecordingContext.h"
@@ -120,20 +121,23 @@
 
     void DeallocateMemory(ResourceHeapAllocation& allocation);
 
-    ShaderVisibleDescriptorAllocator* GetViewShaderVisibleDescriptorAllocator() const;
-    ShaderVisibleDescriptorAllocator* GetSamplerShaderVisibleDescriptorAllocator() const;
+    MutexProtected<ShaderVisibleDescriptorAllocator>& GetViewShaderVisibleDescriptorAllocator()
+        const;
+    MutexProtected<ShaderVisibleDescriptorAllocator>& GetSamplerShaderVisibleDescriptorAllocator()
+        const;
 
     // Returns nullptr when descriptor count is zero.
-    StagingDescriptorAllocator* GetViewStagingDescriptorAllocator(uint32_t descriptorCount) const;
+    MutexProtected<StagingDescriptorAllocator>* GetViewStagingDescriptorAllocator(
+        uint32_t descriptorCount) const;
 
-    StagingDescriptorAllocator* GetSamplerStagingDescriptorAllocator(
+    MutexProtected<StagingDescriptorAllocator>* GetSamplerStagingDescriptorAllocator(
         uint32_t descriptorCount) const;
 
     SamplerHeapCache* GetSamplerHeapCache();
 
-    StagingDescriptorAllocator* GetRenderTargetViewAllocator() const;
+    MutexProtected<StagingDescriptorAllocator>& GetRenderTargetViewAllocator() const;
 
-    StagingDescriptorAllocator* GetDepthStencilViewAllocator() const;
+    MutexProtected<StagingDescriptorAllocator>& GetDepthStencilViewAllocator() const;
 
     ResultOrError<Ref<d3d::Fence>> CreateFence(
         const d3d::ExternalImageDXGIFenceDescriptor* descriptor) override;
@@ -246,21 +250,25 @@
 
     // Index corresponds to Log2Ceil(descriptorCount) where descriptorCount is in
     // the range [0, kMaxSamplerDescriptorsPerBindGroup].
-    std::array<std::unique_ptr<StagingDescriptorAllocator>, kNumViewDescriptorAllocators + 1>
+    std::array<std::unique_ptr<MutexProtected<StagingDescriptorAllocator>>,
+               kNumViewDescriptorAllocators + 1>
         mViewAllocators;
 
     // Index corresponds to Log2Ceil(descriptorCount) where descriptorCount is in
     // the range [0, kMaxViewDescriptorsPerBindGroup].
-    std::array<std::unique_ptr<StagingDescriptorAllocator>, kNumSamplerDescriptorAllocators + 1>
+    std::array<std::unique_ptr<MutexProtected<StagingDescriptorAllocator>>,
+               kNumSamplerDescriptorAllocators + 1>
         mSamplerAllocators;
 
-    std::unique_ptr<StagingDescriptorAllocator> mRenderTargetViewAllocator;
+    std::unique_ptr<MutexProtected<StagingDescriptorAllocator>> mRenderTargetViewAllocator;
 
-    std::unique_ptr<StagingDescriptorAllocator> mDepthStencilViewAllocator;
+    std::unique_ptr<MutexProtected<StagingDescriptorAllocator>> mDepthStencilViewAllocator;
 
-    std::unique_ptr<ShaderVisibleDescriptorAllocator> mViewShaderVisibleDescriptorAllocator;
+    std::unique_ptr<MutexProtected<ShaderVisibleDescriptorAllocator>>
+        mViewShaderVisibleDescriptorAllocator;
 
-    std::unique_ptr<ShaderVisibleDescriptorAllocator> mSamplerShaderVisibleDescriptorAllocator;
+    std::unique_ptr<MutexProtected<ShaderVisibleDescriptorAllocator>>
+        mSamplerShaderVisibleDescriptorAllocator;
 
     // Sampler cache needs to be destroyed before the CPU sampler allocator to ensure the final
     // release is called.
diff --git a/src/dawn/native/d3d12/SamplerHeapCacheD3D12.cpp b/src/dawn/native/d3d12/SamplerHeapCacheD3D12.cpp
index f1d12e3..434e80b 100644
--- a/src/dawn/native/d3d12/SamplerHeapCacheD3D12.cpp
+++ b/src/dawn/native/d3d12/SamplerHeapCacheD3D12.cpp
@@ -28,11 +28,12 @@
 
 namespace dawn::native::d3d12 {
 
-SamplerHeapCacheEntry::SamplerHeapCacheEntry(std::vector<Sampler*> samplers)
-    : mSamplers(std::move(samplers)) {}
+SamplerHeapCacheEntry::SamplerHeapCacheEntry(MutexProtected<StagingDescriptorAllocator>& allocator,
+                                             std::vector<Sampler*> samplers)
+    : mSamplers(std::move(samplers)), mAllocator(allocator) {}
 
 SamplerHeapCacheEntry::SamplerHeapCacheEntry(SamplerHeapCache* cache,
-                                             StagingDescriptorAllocator* allocator,
+                                             MutexProtected<StagingDescriptorAllocator>& allocator,
                                              std::vector<Sampler*> samplers,
                                              CPUDescriptorHeapAllocation allocation)
     : mCPUAllocation(std::move(allocation)),
@@ -58,7 +59,8 @@
     ASSERT(!mCPUAllocation.IsValid());
 }
 
-bool SamplerHeapCacheEntry::Populate(Device* device, ShaderVisibleDescriptorAllocator* allocator) {
+bool SamplerHeapCacheEntry::Populate(Device* device,
+                                     MutexProtected<ShaderVisibleDescriptorAllocator>& allocator) {
     if (allocator->IsAllocationStillValid(mGPUAllocation)) {
         return true;
     }
@@ -90,7 +92,7 @@
 
 ResultOrError<Ref<SamplerHeapCacheEntry>> SamplerHeapCache::GetOrCreate(
     const BindGroup* group,
-    StagingDescriptorAllocator* samplerAllocator) {
+    MutexProtected<StagingDescriptorAllocator>& samplerAllocator) {
     const BindGroupLayout* bgl = ToBackend(group->GetLayout());
 
     // If a previously created bindgroup used the same samplers, the backing sampler heap
@@ -110,7 +112,7 @@
 
     // Check the cache if there exists a sampler heap allocation that corresponds to the
     // samplers.
-    SamplerHeapCacheEntry blueprint(std::move(samplers));
+    SamplerHeapCacheEntry blueprint(samplerAllocator, std::move(samplers));
     auto iter = mCache.find(&blueprint);
     if (iter != mCache.end()) {
         return Ref<SamplerHeapCacheEntry>(*iter);
@@ -120,10 +122,14 @@
     // real entry below.
     samplers = std::move(blueprint.AcquireSamplers());
 
+    uint32_t samplerSizeIncrement = 0;
     CPUDescriptorHeapAllocation allocation;
-    DAWN_TRY_ASSIGN(allocation, samplerAllocator->AllocateCPUDescriptors());
+    DAWN_TRY(samplerAllocator.Use([&](auto allocator) -> MaybeError {
+        DAWN_TRY_ASSIGN(allocation, allocator->AllocateCPUDescriptors());
+        samplerSizeIncrement = allocator->GetSizeIncrement();
+        return {};
+    }));
 
-    const uint32_t samplerSizeIncrement = samplerAllocator->GetSizeIncrement();
     ID3D12Device* d3d12Device = mDevice->GetD3D12Device();
 
     for (uint32_t i = 0; i < samplers.size(); ++i) {
diff --git a/src/dawn/native/d3d12/SamplerHeapCacheD3D12.h b/src/dawn/native/d3d12/SamplerHeapCacheD3D12.h
index 934ff1d..dbb8894 100644
--- a/src/dawn/native/d3d12/SamplerHeapCacheD3D12.h
+++ b/src/dawn/native/d3d12/SamplerHeapCacheD3D12.h
@@ -18,6 +18,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "dawn/common/MutexProtected.h"
 #include "dawn/common/Ref.h"
 #include "dawn/common/RefCounted.h"
 #include "dawn/native/BindingInfo.h"
@@ -46,10 +47,10 @@
 // Wraps sampler descriptor heap allocations in a cache.
 class SamplerHeapCacheEntry : public RefCounted {
   public:
-    SamplerHeapCacheEntry() = default;
-    explicit SamplerHeapCacheEntry(std::vector<Sampler*> samplers);
+    explicit SamplerHeapCacheEntry(MutexProtected<StagingDescriptorAllocator>& allocator,
+                                   std::vector<Sampler*> samplers);
     SamplerHeapCacheEntry(SamplerHeapCache* cache,
-                          StagingDescriptorAllocator* allocator,
+                          MutexProtected<StagingDescriptorAllocator>& allocator,
                           std::vector<Sampler*> samplers,
                           CPUDescriptorHeapAllocation allocation);
     ~SamplerHeapCacheEntry() override;
@@ -58,7 +59,7 @@
 
     std::vector<Sampler*>&& AcquireSamplers();
 
-    bool Populate(Device* device, ShaderVisibleDescriptorAllocator* allocator);
+    bool Populate(Device* device, MutexProtected<ShaderVisibleDescriptorAllocator>& allocator);
 
     // Functors necessary for the unordered_map<SamplerHeapCacheEntry*>-based cache.
     struct HashFunc {
@@ -77,7 +78,7 @@
     // by the device and will already be unique.
     std::vector<Sampler*> mSamplers;
 
-    StagingDescriptorAllocator* mAllocator = nullptr;
+    MutexProtected<StagingDescriptorAllocator>& mAllocator;
     SamplerHeapCache* mCache = nullptr;
 };
 
@@ -90,7 +91,7 @@
 
     ResultOrError<Ref<SamplerHeapCacheEntry>> GetOrCreate(
         const BindGroup* group,
-        StagingDescriptorAllocator* samplerAllocator);
+        MutexProtected<StagingDescriptorAllocator>& samplerAllocator);
 
     void RemoveCacheEntry(SamplerHeapCacheEntry* entry);
 
diff --git a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
index dd111af..a7e1ab0 100644
--- a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
+++ b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
@@ -77,11 +77,11 @@
 }
 
 // static
-ResultOrError<std::unique_ptr<ShaderVisibleDescriptorAllocator>>
+ResultOrError<std::unique_ptr<MutexProtected<ShaderVisibleDescriptorAllocator>>>
 ShaderVisibleDescriptorAllocator::Create(Device* device, D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
-    std::unique_ptr<ShaderVisibleDescriptorAllocator> allocator =
-        std::make_unique<ShaderVisibleDescriptorAllocator>(device, heapType);
-    DAWN_TRY(allocator->AllocateAndSwitchShaderVisibleHeap());
+    std::unique_ptr<MutexProtected<ShaderVisibleDescriptorAllocator>> allocator =
+        std::make_unique<MutexProtected<ShaderVisibleDescriptorAllocator>>(device, heapType);
+    DAWN_TRY((*allocator)->AllocateAndSwitchShaderVisibleHeap());
     return std::move(allocator);
 }
 
diff --git a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h
index cf09f9d..3f5a7c7 100644
--- a/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h
+++ b/src/dawn/native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h
@@ -18,6 +18,7 @@
 #include <list>
 #include <memory>
 
+#include "dawn/common/MutexProtected.h"
 #include "dawn/native/Error.h"
 #include "dawn/native/RingBufferAllocator.h"
 #include "dawn/native/d3d12/IntegerTypes.h"
@@ -46,7 +47,7 @@
 
 class ShaderVisibleDescriptorAllocator {
   public:
-    static ResultOrError<std::unique_ptr<ShaderVisibleDescriptorAllocator>> Create(
+    static ResultOrError<std::unique_ptr<MutexProtected<ShaderVisibleDescriptorAllocator>>> Create(
         Device* device,
         D3D12_DESCRIPTOR_HEAP_TYPE heapType);
 
diff --git a/src/dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h b/src/dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h
index 0940867..cc91aaa 100644
--- a/src/dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h
+++ b/src/dawn/native/d3d12/StagingDescriptorAllocatorD3D12.h
@@ -70,11 +70,11 @@
     std::vector<uint32_t> mAvailableHeaps;  // Indices into the pool.
     std::vector<NonShaderVisibleBuffer> mPool;
 
-    Device* mDevice;
+    Device const* mDevice = nullptr;
 
-    uint32_t mSizeIncrement;  // Size of the descriptor (in bytes).
-    uint32_t mBlockSize;      // Size of the block of descriptors (in bytes).
-    uint32_t mHeapSize;       // Size of the heap (in number of descriptors).
+    const uint32_t mSizeIncrement = 0;  // Size of the descriptor (in bytes).
+    const uint32_t mBlockSize = 0;      // Size of the block of descriptors (in bytes).
+    const uint32_t mHeapSize = 0;       // Size of the heap (in number of descriptors).
 
     D3D12_DESCRIPTOR_HEAP_TYPE mHeapType;
 
diff --git a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
index 1222fa1..21bf85d 100644
--- a/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
+++ b/src/dawn/tests/white_box/D3D12DescriptorHeapTests.cpp
@@ -138,8 +138,7 @@
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
     Device* d3dDevice = reinterpret_cast<Device*>(device.Get());
-    ShaderVisibleDescriptorAllocator* allocator =
-        d3dDevice->GetViewShaderVisibleDescriptorAllocator();
+    auto& allocator = d3dDevice->GetViewShaderVisibleDescriptorAllocator();
     const uint64_t heapSize = allocator->GetShaderVisibleHeapSizeForTesting();
 
     const HeapVersionID heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
@@ -194,8 +193,7 @@
     wgpu::Sampler sampler = device.CreateSampler();
 
     Device* d3dDevice = reinterpret_cast<Device*>(device.Get());
-    ShaderVisibleDescriptorAllocator* allocator =
-        d3dDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = d3dDevice->GetSamplerShaderVisibleDescriptorAllocator();
     const uint64_t samplerHeapSize = allocator->GetShaderVisibleHeapSizeForTesting();
 
     const HeapVersionID HeapVersionID = allocator->GetShaderVisibleHeapSerialForTesting();
@@ -227,8 +225,7 @@
     DAWN_TEST_UNSUPPORTED_IF(
         !mD3DDevice->IsToggleEnabled(native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
 
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
     std::list<ComPtr<ID3D12DescriptorHeap>> heaps = {allocator->GetShaderVisibleHeap()};
 
@@ -271,8 +268,7 @@
 
     constexpr uint32_t kNumOfSwitches = 5;
 
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
     const HeapVersionID heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
 
@@ -303,8 +299,7 @@
 
     constexpr uint32_t kNumOfSwitches = 5;
 
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
     const HeapVersionID heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
 
     std::set<ComPtr<ID3D12DescriptorHeap>> heaps = {allocator->GetShaderVisibleHeap()};
@@ -342,8 +337,7 @@
 
 // Verify shader-visible heaps do not recycle in multiple submits.
 TEST_P(D3D12DescriptorHeapTests, GrowHeapsInMultipleSubmits) {
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
     const HeapVersionID heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
 
@@ -368,8 +362,7 @@
 
 // Verify shader-visible heaps do not recycle in a pending submit.
 TEST_P(D3D12DescriptorHeapTests, GrowHeapsInPendingSubmit) {
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
     const HeapVersionID heapSerial = allocator->GetShaderVisibleHeapSerialForTesting();
 
@@ -395,8 +388,7 @@
 // once no longer pending.
 // Switches over many times until |kNumOfPooledHeaps| heaps are pool-allocated.
 TEST_P(D3D12DescriptorHeapTests, GrowAndPoolHeapsInPendingAndMultipleSubmits) {
-    ShaderVisibleDescriptorAllocator* allocator =
-        mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+    auto& allocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
     std::set<ComPtr<ID3D12DescriptorHeap>> heaps = {allocator->GetShaderVisibleHeap()};
 
@@ -818,11 +810,9 @@
         wgpu::SamplerDescriptor samplerDescriptor;
         wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor);
 
-        ShaderVisibleDescriptorAllocator* viewAllocator =
-            mD3DDevice->GetViewShaderVisibleDescriptorAllocator();
+        auto& viewAllocator = mD3DDevice->GetViewShaderVisibleDescriptorAllocator();
 
-        ShaderVisibleDescriptorAllocator* samplerAllocator =
-            mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
+        auto& samplerAllocator = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator();
 
         const HeapVersionID viewHeapSerial = viewAllocator->GetShaderVisibleHeapSerialForTesting();
         const HeapVersionID samplerHeapSerial =
@@ -1048,8 +1038,7 @@
 // on.
 TEST_P(D3D12DescriptorHeapTests, InvalidateAllocationAfterSerial) {
     Device* d3dDevice = reinterpret_cast<Device*>(device.Get());
-    ShaderVisibleDescriptorAllocator* gpuAllocator =
-        d3dDevice->GetViewShaderVisibleDescriptorAllocator();
+    auto& gpuAllocator = d3dDevice->GetViewShaderVisibleDescriptorAllocator();
 
     GPUDescriptorHeapAllocation gpuHeapDescAllocation;
 
diff --git a/src/dawn/tests/white_box/D3D12ResidencyTests.cpp b/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
index db4e0de..e3cc08e 100644
--- a/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
+++ b/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
@@ -373,8 +373,7 @@
 
     native::d3d12::Device* d3dDevice = native::d3d12::ToBackend(native::FromAPI(device.Get()));
 
-    native::d3d12::ShaderVisibleDescriptorAllocator* allocator =
-        d3dDevice->GetViewShaderVisibleDescriptorAllocator();
+    auto& allocator = d3dDevice->GetViewShaderVisibleDescriptorAllocator();
     const uint64_t heapSize = allocator->GetShaderVisibleHeapSizeForTesting();
 
     const native::d3d12::HeapVersionID heapSerial =