d3d11: support synchronization with keyed mutex

This CL adds keyed mutex sychronization temporarily. It is needed
for unblocking chrome graphite dogfooding chrome on Windows. This
change will be reverted when chrome stops using keyed mutex.

Bug: dawn:1906
Change-Id: I9777e98e83d6f4bbd670accd1380accf29da4d46
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/140520
Kokoro: Peng Huang <penghuang@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/src/dawn/native/d3d/ExternalImageDXGIImpl.cpp b/src/dawn/native/d3d/ExternalImageDXGIImpl.cpp
index d747bf1..6a2010e 100644
--- a/src/dawn/native/d3d/ExternalImageDXGIImpl.cpp
+++ b/src/dawn/native/d3d/ExternalImageDXGIImpl.cpp
@@ -66,10 +66,15 @@
                              textureDescriptor->nextInChain)
                              ->internalUsage;
     }
+
+    // If the resource has IDXGIKeyedMutex interface, it will be used for synchronization.
+    // TODO(dawn:1906): remove the mDXGIKeyedMutex when it is not used in chrome.
+    mD3DResource.As(&mDXGIKeyedMutex);
 }
 
 ExternalImageDXGIImpl::~ExternalImageDXGIImpl() {
     ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
+    mDXGIKeyedMutexReleaser.reset();
     DestroyInternal();
 }
 
@@ -150,6 +155,16 @@
             ->CreateD3DExternalTexture(&textureDescriptor, mD3DResource, std::move(waitFences),
                                        descriptor->isSwapChainTexture, descriptor->isInitialized);
 
+    if (mDXGIKeyedMutex && mAccessCount == 0) {
+        HRESULT hr = mDXGIKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireKey, INFINITE);
+        if (FAILED(hr)) {
+            dawn::ErrorLog() << "Failed to acquire keyed mutex for external image";
+            return nullptr;
+        }
+        mDXGIKeyedMutexReleaser.emplace(mDXGIKeyedMutex);
+    }
+    ++mAccessCount;
+
     return ToAPI(texture.Detach());
 }
 
@@ -175,6 +190,11 @@
     }
     signalFence->fenceHandle = ToBackend(mBackendDevice.Get())->GetFenceHandle();
     signalFence->fenceValue = static_cast<uint64_t>(fenceValue);
+
+    --mAccessCount;
+    if (mDXGIKeyedMutexReleaser && mAccessCount == 0) {
+        mDXGIKeyedMutexReleaser.reset();
+    }
 }
 
 }  // namespace dawn::native::d3d
diff --git a/src/dawn/native/d3d/ExternalImageDXGIImpl.h b/src/dawn/native/d3d/ExternalImageDXGIImpl.h
index 5d358a7..59cd4fa 100644
--- a/src/dawn/native/d3d/ExternalImageDXGIImpl.h
+++ b/src/dawn/native/d3d/ExternalImageDXGIImpl.h
@@ -18,10 +18,13 @@
 #include <wrl/client.h>
 
 #include <memory>
+#include <optional>
+#include <utility>
 #include <vector>
 
 #include "dawn/common/LinkedList.h"
 #include "dawn/common/Mutex.h"
+#include "dawn/common/NonCopyable.h"
 #include "dawn/native/Error.h"
 #include "dawn/native/Forward.h"
 #include "dawn/native/IntegerTypes.h"
@@ -61,6 +64,7 @@
   protected:
     Ref<DeviceBase> mBackendDevice;
     ComPtr<IUnknown> mD3DResource;
+    ComPtr<IDXGIKeyedMutex> mDXGIKeyedMutex;
     wgpu::TextureUsage mUsage;
     wgpu::TextureUsage mUsageInternal = wgpu::TextureUsage::None;
     wgpu::TextureDimension mDimension;
@@ -69,6 +73,20 @@
     uint32_t mMipLevelCount;
     uint32_t mSampleCount;
     std::vector<wgpu::TextureFormat> mViewFormats;
+    uint32_t mAccessCount = 0;
+
+    // Chrome uses 0 as acquire key.
+    static constexpr UINT64 kDXGIKeyedMutexAcquireKey = 0;
+    class KeyedMutexReleaser : public NonCopyable {
+      public:
+        explicit KeyedMutexReleaser(ComPtr<IDXGIKeyedMutex> keyedMutex)
+            : mDXGIKeyedMutex(std::move(keyedMutex)) {}
+        ~KeyedMutexReleaser() { mDXGIKeyedMutex->ReleaseSync(kDXGIKeyedMutexAcquireKey); }
+
+      private:
+        const ComPtr<IDXGIKeyedMutex> mDXGIKeyedMutex;
+    };
+    std::optional<KeyedMutexReleaser> mDXGIKeyedMutexReleaser;
 };
 
 }  // namespace dawn::native::d3d