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,