D3D12: Enable `SharedBufferMemoryFromWindowsHandle` on all UMA

This patch enables `SharedBufferMemoryFromWindowsHandle` on all
UMA so that we can test this feature on non-cache-coherent UMA.

This patch also fixes a bug in `CanUseCopyResource()`. When a
buffer is created from shared buffer memory backed by a Windows
file mapping handle, `InitializeAsExternalBuffer()` sets
`mAllocatedSize` to the wgpu buffer descriptor size, while the
underlying D3D12 resource is always the full shared heap
allocation. When a small shared-memory buffer and a small device
buffer had matching `GetAllocatedSize()` values, `CopyResource`
was selected in `CanUseCopyResource()` even though the two D3D12
resources had different widths, triggering a D3D12 validation
error: "Source and Destination buffer resource must have the same
size/width.".

This patch fixes the above issue by comparing the actual D3D12
resource widths `GetDesc().Width` instead of `GetAllocatedSize()`.

This patch also uses `utils::SystemHandle` instead of raw `HANDLE`
in the tests to ensure we never leak any `HANDLE`s in the tests.

Bug: 386255678
Change-Id: I78a9a07c00de4de6e5a1a69dfa6538d6473ea224
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/326495
Commit-Queue: Shao, Jiawei <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/d3d12/BufferD3D12.cpp b/src/dawn/native/d3d12/BufferD3D12.cpp
index b635283..59f3316 100644
--- a/src/dawn/native/d3d12/BufferD3D12.cpp
+++ b/src/dawn/native/d3d12/BufferD3D12.cpp
@@ -163,7 +163,10 @@
 
 MaybeError Buffer::InitializeAsExternalBuffer(ComPtr<ID3D12Resource> d3d12Buffer,
                                               const UnpackedPtr<BufferDescriptor>& descriptor) {
-    mAllocatedSize = descriptor->size;
+    // `mAllocatedSize` should always be the size of the underlying D3D12 buffer resource. Note that
+    // for a buffer backed by shared memory, its wgpu buffer size may be smaller than the width of
+    // the underlying D3D12 buffer resource.
+    mAllocatedSize = d3d12Buffer->GetDesc().Width;
     AllocationInfo info;
     info.mMethod = AllocationMethod::kExternal;
     info.mRequestedSize = mAllocatedSize.value();
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index 146a08d..6e94a1c9 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -241,11 +241,10 @@
         EnableFeature(Feature::BufferMapExtendedUsages);
     }
 
-    // Temporarily only enable SharedBufferMemoryFromWindowsHandle on cache
-    // coherent UMA.
+    // Temporarily only enable SharedBufferMemoryFromWindowsHandle on UMA.
     // TODO(386255678): enable SharedBufferMemoryFromWindowsHandle on other
     // architectures.
-    if (GetDeviceInfo().supportsExistingHeap && SupportsBufferMapExtendedUsages()) {
+    if (GetDeviceInfo().supportsExistingHeap && GetDeviceInfo().isUMA) {
         EnableFeature(Feature::SharedBufferMemoryFromWindowsHandle);
     }
 
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp b/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp
index 356550c..b7d5f7b 100644
--- a/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp
@@ -35,6 +35,7 @@
 #include "src/dawn/tests/DawnTest.h"
 #include "src/dawn/tests/white_box/SharedBufferMemoryTests.h"
 #include "src/dawn/utils/ComboRenderPipelineDescriptor.h"
+#include "src/dawn/utils/SystemHandle.h"
 #include "src/dawn/utils/WGPUHelpers.h"
 #include "src/utils/compiler.h"
 
@@ -81,10 +82,11 @@
     UINT64 signaledValue = 1;
     commandQueue->Signal(fence.Get(), signaledValue);
 
-    HANDLE fenceEvent = nullptr;
+    utils::SystemHandle fenceEvent =
+        utils::SystemHandle::Acquire(CreateEvent(nullptr, FALSE, FALSE, nullptr));
     if (fence->GetCompletedValue() < signaledValue) {
-        fence->SetEventOnCompletion(signaledValue, fenceEvent);
-        WaitForSingleObject(fenceEvent, INFINITE);
+        fence->SetEventOnCompletion(signaledValue, fenceEvent.Get());
+        WaitForSingleObject(fenceEvent.Get(), INFINITE);
     }
 }
 
@@ -384,23 +386,22 @@
 
     LARGE_INTEGER largeSize = {};
     largeSize.QuadPart = kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment;
-    HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
-                                      largeSize.HighPart, largeSize.LowPart, nullptr);
-    EXPECT_NE(handle, nullptr);
+    utils::SystemHandle handle = utils::SystemHandle::Acquire(
+        CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, largeSize.HighPart,
+                          largeSize.LowPart, nullptr));
+    EXPECT_TRUE(handle.IsValid());
 
-    HANDLE duplicatedHandle = nullptr;
+    utils::SystemHandle duplicatedHandle;
     HANDLE process = GetCurrentProcess();
     constexpr DWORD kValidAccess = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY | READ_CONTROL;
-    EXPECT_TRUE(
-        DuplicateHandle(process, handle, process, &duplicatedHandle, kValidAccess, FALSE, 0));
+    EXPECT_TRUE(DuplicateHandle(process, handle.Get(), process, duplicatedHandle.GetMut(),
+                                kValidAccess, FALSE, 0));
 
     // With READ_CONTROL present, OpenExistingHeapFromFileMapping should succeed.
     ComPtr<ID3D12Heap> d3d12Heap;
-    hr = d3d12Device3->OpenExistingHeapFromFileMapping(duplicatedHandle, IID_PPV_ARGS(&d3d12Heap));
+    hr = d3d12Device3->OpenExistingHeapFromFileMapping(duplicatedHandle.Get(),
+                                                       IID_PPV_ARGS(&d3d12Heap));
     EXPECT_EQ(S_OK, hr);
-
-    CloseHandle(duplicatedHandle);
-    CloseHandle(handle);
 }
 
 // Verify missing READ_CONTROL access in DuplicateHandle will cause failure in
@@ -415,43 +416,30 @@
 
     LARGE_INTEGER largeSize = {};
     largeSize.QuadPart = kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment;
-    HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
-                                      largeSize.HighPart, largeSize.LowPart, nullptr);
-    EXPECT_NE(handle, nullptr);
+    utils::SystemHandle handle = utils::SystemHandle::Acquire(
+        CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, largeSize.HighPart,
+                          largeSize.LowPart, nullptr));
+    EXPECT_TRUE(handle.IsValid());
 
     // Import the duplicated handle to align with the behavior in Chromium.
-    HANDLE duplicatedHandle = nullptr;
+    utils::SystemHandle duplicatedHandle;
     HANDLE process = GetCurrentProcess();
     constexpr DWORD kInvalidAccess = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY;
-    EXPECT_TRUE(
-        DuplicateHandle(process, handle, process, &duplicatedHandle, kInvalidAccess, FALSE, 0));
+    EXPECT_TRUE(DuplicateHandle(process, handle.Get(), process, duplicatedHandle.GetMut(),
+                                kInvalidAccess, FALSE, 0));
 
     // Missing read control will cause an error when calling `OpenExistingHeapFromFileMapping`.
     ComPtr<ID3D12Heap> d3d12Heap;
-    HRESULT error_hr =
-        d3d12Device3->OpenExistingHeapFromFileMapping(duplicatedHandle, IID_PPV_ARGS(&d3d12Heap));
+    HRESULT error_hr = d3d12Device3->OpenExistingHeapFromFileMapping(duplicatedHandle.Get(),
+                                                                     IID_PPV_ARGS(&d3d12Heap));
     EXPECT_NE(S_OK, error_hr);
 }
 
-class D3D12SharedMemoryFileHandleBackend : public SharedBufferMemoryTestBackend {
+// Base backend for SharedBufferMemory backed by a Windows file mapping handle. Subclasses only
+// need to override RequiredFeatures() to declare the features they require.
+class D3D12SharedMemoryFileHandleBackendBase : public SharedBufferMemoryTestBackend {
   public:
-    static Backend GetInstance() {
-        static D3D12SharedMemoryFileHandleBackend b;
-        return &b;
-    }
-
-    void TearDown() override {
-        if (mSharedMemoryHandle != nullptr) {
-            CloseHandle(mSharedMemoryHandle);
-            mSharedMemoryHandle = nullptr;
-        }
-    }
-
-    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override {
-        return {wgpu::FeatureName::SharedBufferMemoryFromWindowsHandle,
-                wgpu::FeatureName::SharedFenceDXGISharedHandle,
-                wgpu::FeatureName::BufferMapExtendedUsages, wgpu::FeatureName::HostMappedPointer};
-    }
+    void TearDown() override { mSharedMemoryHandle.Close(); }
 
     wgpu::SharedBufferMemory CreateSharedBufferMemory(const wgpu::Device& device,
                                                       wgpu::BufferUsage usages,
@@ -464,13 +452,14 @@
         largeSize.QuadPart = alignedHeapSize;
         // Create a named shared memory object by using INVALID_HANDLE_VALUE as input file handle.
         // See https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory.
-        mSharedMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
-                                                largeSize.HighPart, largeSize.LowPart, nullptr);
-        EXPECT_NE(mSharedMemoryHandle, nullptr);
+        mSharedMemoryHandle = utils::SystemHandle::Acquire(
+            CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, largeSize.HighPart,
+                              largeSize.LowPart, nullptr));
+        EXPECT_TRUE(mSharedMemoryHandle.IsValid());
 
         if (initializationData) {
             // Get mapped pointer to the file mapping object for test
-            void* ptr = MapViewOfFile(mSharedMemoryHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+            void* ptr = MapViewOfFile(mSharedMemoryHandle.Get(), FILE_MAP_ALL_ACCESS, 0, 0, 0);
             EXPECT_NE(ptr, nullptr);
 
             DAWN_UNSAFE_TODO(memcpy(ptr, &initializationData, sizeof(initializationData)));
@@ -480,23 +469,43 @@
 
         wgpu::SharedBufferMemoryDescriptor desc;
         wgpu::SharedBufferMemoryFromWindowsHandleDescriptor sharedFileHandleDesc;
-        sharedFileHandleDesc.handle = mSharedMemoryHandle;
+        sharedFileHandleDesc.handle = mSharedMemoryHandle.Get();
         sharedFileHandleDesc.size = alignedHeapSize;
         desc.nextInChain = &sharedFileHandleDesc;
 
         return device.ImportSharedBufferMemory(&desc);
     }
 
-  private:
-    D3D12SharedMemoryFileHandleBackend() {}
+  protected:
+    D3D12SharedMemoryFileHandleBackendBase() {}
 
-    HANDLE mSharedMemoryHandle = nullptr;
+  private:
+    utils::SystemHandle mSharedMemoryHandle;
 };
 
-class SharedBufferMemoryD3D12SharedFileHandleTests : public SharedBufferMemoryTests {};
+class D3D12SharedMemoryFileHandleWithExtendedUsagesBackend
+    : public D3D12SharedMemoryFileHandleBackendBase {
+  public:
+    static Backend GetInstance() {
+        static D3D12SharedMemoryFileHandleWithExtendedUsagesBackend b;
+        return &b;
+    }
+
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override {
+        return {wgpu::FeatureName::SharedBufferMemoryFromWindowsHandle,
+                wgpu::FeatureName::SharedFenceDXGISharedHandle,
+                wgpu::FeatureName::BufferMapExtendedUsages, wgpu::FeatureName::HostMappedPointer};
+    }
+
+  private:
+    D3D12SharedMemoryFileHandleWithExtendedUsagesBackend() {}
+};
+
+class SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests
+    : public SharedBufferMemoryTests {};
 
 // Ensure that importing a nullptr handle results in error.
-TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests, nullResourceFailure) {
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests, nullResourceFailure) {
     wgpu::SharedBufferMemoryFromWindowsHandleDescriptor sharedFileHandleDesc;
     sharedFileHandleDesc.handle = nullptr;
     sharedFileHandleDesc.size = kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment;
@@ -507,16 +516,17 @@
 
 // Ensure that heap size not being a multiple of 65536 (D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
 // results in error.
-TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests, MemorySizeNotAlignFailure) {
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests, MemorySizeNotAlignFailure) {
     constexpr uint32_t kUnAlignedSize = kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment / 2;
 
     LARGE_INTEGER largeSize = {};
     largeSize.QuadPart = kUnAlignedSize;
     // Create a named shared memory object by using INVALID_HANDLE_VALUE as input file handle.
     // See https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory.
-    HANDLE sharedMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
-                                                  largeSize.HighPart, largeSize.LowPart, nullptr);
-    EXPECT_NE(sharedMemoryHandle, nullptr);
+    utils::SystemHandle sharedMemoryHandle = utils::SystemHandle::Acquire(
+        CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, largeSize.HighPart,
+                          largeSize.LowPart, nullptr));
+    EXPECT_TRUE(sharedMemoryHandle.IsValid());
 
     wgpu::SharedBufferMemoryFromWindowsHandleDescriptor sharedFileHandleDesc;
     sharedFileHandleDesc.handle = nullptr;
@@ -527,7 +537,7 @@
 }
 
 // Tests that no error occurs when we create a SharedBufferMemory with Uniform usage.
-TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests, UniformUsageValidation) {
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests, UniformUsageValidation) {
     constexpr wgpu::BufferUsage kMapWriteUsages =
         wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
     wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(
@@ -542,12 +552,12 @@
     memory.CreateBuffer(&bufferDesc);
 }
 
-DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D12,
-                                 SharedBufferMemoryTests,
-                                 {D3D12Backend()},
-                                 {ExistingD3D12ResourceBackend::GetInstance(),
-                                  D3D12SharedMemoryFileHandleBackend::GetInstance()});
-
+DAWN_INSTANTIATE_PREFIXED_TEST_P(
+    D3D12,
+    SharedBufferMemoryTests,
+    {D3D12Backend()},
+    {ExistingD3D12ResourceBackend::GetInstance(),
+     D3D12SharedMemoryFileHandleWithExtendedUsagesBackend::GetInstance()});
 // As D3D12 backend is filtered out on Windows x86, we need below to allow uninstantiated gtests.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SharedBufferMemoryExistingD3D12ResourceTests);
 DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D12,
@@ -556,6 +566,183 @@
                                  {ExistingD3D12ResourceBackend::GetInstance()});
 
 // As D3D12 backend is filtered out on Windows x86, we need below to allow uninstantiated gtests.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests);
+DAWN_INSTANTIATE_PREFIXED_TEST_P(
+    D3D12,
+    SharedBufferMemoryD3D12SharedFileHandleWithExtendedUsagesTests,
+    {D3D12Backend()},
+    {D3D12SharedMemoryFileHandleWithExtendedUsagesBackend::GetInstance()});
+
+// Backend for platforms that support SharedBufferMemoryFromWindowsHandle and
+// SharedFenceDXGISharedHandle but don't support BufferMapExtendedUsages.
+class D3D12SharedMemoryFileHandleBackend : public D3D12SharedMemoryFileHandleBackendBase {
+  public:
+    static Backend GetInstance() {
+        static D3D12SharedMemoryFileHandleBackend b;
+        return &b;
+    }
+
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override {
+        return {wgpu::FeatureName::SharedBufferMemoryFromWindowsHandle,
+                wgpu::FeatureName::SharedFenceDXGISharedHandle};
+    }
+
+  private:
+    D3D12SharedMemoryFileHandleBackend() {}
+};
+
+class SharedBufferMemoryD3D12SharedFileHandleTests : public SharedBufferMemoryTests {};
+
+// Tests that a buffer with MapWrite|CopySrc usages can be created from shared buffer memory,
+// written via mappedAtCreation, copied to a destination buffer, and the destination contains
+// the expected data.
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests, MapWriteCopySrcUsageSucceeds) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(
+        device, wgpu::BufferUsage::None, kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment);
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+
+    DAWN_TEST_UNSUPPORTED_IF(!(properties.usage & wgpu::BufferUsage::MapWrite));
+
+    constexpr uint32_t kTestData = 0x12345678;
+    constexpr uint64_t kTestDataSize = sizeof(kTestData);
+
+    // Create the source buffer from shared memory with mappedAtCreation = true.
+    wgpu::BufferDescriptor srcDesc = {};
+    srcDesc.size = kTestDataSize;
+    srcDesc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
+    srcDesc.mappedAtCreation = true;
+    wgpu::Buffer srcBuffer = memory.CreateBuffer(&srcDesc);
+    ASSERT_TRUE(srcBuffer.Get());
+
+    wgpu::SharedBufferMemoryBeginAccessDescriptor beginDesc = {};
+    beginDesc.initialized = false;
+    ASSERT_EQ(wgpu::Status::Success, memory.BeginAccess(srcBuffer, &beginDesc));
+
+    // Write the test data through the mapped range and then unmap.
+    uint32_t* mappedData = static_cast<uint32_t*>(srcBuffer.GetMappedRange(0, kTestDataSize));
+    ASSERT_NE(nullptr, mappedData);
+    *mappedData = kTestData;
+    srcBuffer.Unmap();
+
+    // Copy the source buffer to a regular device destination buffer.
+    wgpu::BufferDescriptor dstDesc = {};
+    dstDesc.size = kTestDataSize;
+    dstDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
+    wgpu::Buffer dstBuffer = device.CreateBuffer(&dstDesc);
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kTestDataSize);
+    wgpu::CommandBuffer commandBuffer = encoder.Finish();
+    queue.Submit(1, &commandBuffer);
+
+    wgpu::SharedBufferMemoryEndAccessState endState = {};
+    ASSERT_EQ(wgpu::Status::Success, memory.EndAccess(srcBuffer, &endState));
+
+    // Verify the destination buffer contains the data that was written to the source.
+    EXPECT_BUFFER_U32_EQ(kTestData, dstBuffer, 0);
+}
+
+// Tests that a buffer with MapRead|CopyDst usages can be created from shared buffer memory,
+// receive a copy from a source buffer, and the mapped contents match the source data.
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests, MapReadCopyDstUsageSucceeds) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(
+        device, wgpu::BufferUsage::None, kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment);
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+
+    DAWN_TEST_UNSUPPORTED_IF(!(properties.usage & wgpu::BufferUsage::MapRead));
+
+    constexpr uint32_t kTestData = 0x87654321;
+    constexpr uint64_t kTestDataSize = sizeof(kTestData);
+
+    // Create the destination buffer from shared memory with `MapRead|CopyDst` usages.
+    wgpu::BufferDescriptor dstDesc = {};
+    dstDesc.size = kTestDataSize;
+    dstDesc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
+    wgpu::Buffer dstBuffer = memory.CreateBuffer(&dstDesc);
+    ASSERT_TRUE(dstBuffer.Get());
+
+    // Create a device-owned source buffer pre-filled with the test data.
+    wgpu::Buffer srcBuffer =
+        utils::CreateBufferFromData(device, &kTestData, kTestDataSize, wgpu::BufferUsage::CopySrc);
+
+    wgpu::SharedBufferMemoryBeginAccessDescriptor beginDesc = {};
+    beginDesc.initialized = false;
+    ASSERT_EQ(wgpu::Status::Success, memory.BeginAccess(dstBuffer, &beginDesc));
+
+    // Copy the source data into the destination shared buffer and then map it to verify.
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kTestDataSize);
+    wgpu::CommandBuffer commandBuffer = encoder.Finish();
+    queue.Submit(1, &commandBuffer);
+
+    MapAsyncAndWait(dstBuffer, wgpu::MapMode::Read, 0, kTestDataSize);
+    const uint32_t* mappedData =
+        static_cast<const uint32_t*>(dstBuffer.GetConstMappedRange(0, kTestDataSize));
+    ASSERT_NE(nullptr, mappedData);
+    EXPECT_EQ(kTestData, *mappedData);
+    dstBuffer.Unmap();
+
+    wgpu::SharedBufferMemoryEndAccessState endState = {};
+    ASSERT_EQ(wgpu::Status::Success, memory.EndAccess(dstBuffer, &endState));
+}
+
+// A regression test against `CanUseCopyResource()` with the the buffers created from shared buffer
+// memory. `CanUseCopyResource()` must compare actual D3D12 resource widths instead of
+// `GetAllocatedSize()` because for an external buffer, GetAllocatedSize() equals the WebGPU buffer
+// size, which may be smaller than the D3D12 resource backing it (65536-byte aligned heap here).
+TEST_P(SharedBufferMemoryD3D12SharedFileHandleTests,
+       CopyFromSubsizedExternalBufferDoesNotUseCopyResource) {
+    // The SharedBufferMemory is backed by a 65536-byte D3D12 resource (heap alignment requirement).
+    // A 4-byte WebGPU buffer created from it has GetAllocatedSize() = 4, D3D12 width = 65536.
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(
+        device, wgpu::BufferUsage::None, kD3D12SharedBufferMemoryFileMappingHandleSizeAlignment);
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+    DAWN_TEST_UNSUPPORTED_IF(!(properties.usage & wgpu::BufferUsage::MapWrite));
+
+    constexpr uint32_t kTestData = 0xDEADBEEF;
+    constexpr uint64_t kTestDataSize = sizeof(kTestData);
+
+    wgpu::BufferDescriptor srcDesc = {};
+    srcDesc.size = kTestDataSize;
+    srcDesc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
+    srcDesc.mappedAtCreation = true;
+    wgpu::Buffer srcBuffer = memory.CreateBuffer(&srcDesc);
+    ASSERT_TRUE(srcBuffer.Get());
+
+    wgpu::SharedBufferMemoryBeginAccessDescriptor beginDesc = {};
+    beginDesc.initialized = false;
+    ASSERT_EQ(wgpu::Status::Success, memory.BeginAccess(srcBuffer, &beginDesc));
+
+    uint32_t* mapped = static_cast<uint32_t*>(srcBuffer.GetMappedRange(0, kTestDataSize));
+    ASSERT_NE(nullptr, mapped);
+    *mapped = kTestData;
+    srcBuffer.Unmap();
+
+    // Regular Dawn CopyDst buffer: D3D12 resource width = kTestDataSize.
+    // Old bug: GetAllocatedSize() both = 4 → CanUseCopyResource = true
+    //          → CopyResource(4-byte D3D12, 65536-byte D3D12) → D3D12 validation error.
+    // Fixed:   D3D12 widths 4 ≠ 65536 → CanUseCopyResource = false → CopyBufferRegion.
+    wgpu::BufferDescriptor dstDesc = {};
+    dstDesc.size = kTestDataSize;
+    dstDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
+    wgpu::Buffer dstBuffer = device.CreateBuffer(&dstDesc);
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kTestDataSize);
+    wgpu::CommandBuffer commandBuffer = encoder.Finish();
+    queue.Submit(1, &commandBuffer);
+
+    wgpu::SharedBufferMemoryEndAccessState endState = {};
+    ASSERT_EQ(wgpu::Status::Success, memory.EndAccess(srcBuffer, &endState));
+
+    EXPECT_BUFFER_U32_EQ(kTestData, dstBuffer, 0);
+}
+
+// As D3D12 backend is filtered out on Windows x86, we need below to allow uninstantiated gtests.
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SharedBufferMemoryD3D12SharedFileHandleTests);
 DAWN_INSTANTIATE_PREFIXED_TEST_P(D3D12,
                                  SharedBufferMemoryD3D12SharedFileHandleTests,