Add SharedBufferMemoryBase CreateBuffer Implementation

Adds base and D3D12 implementation of SharedBufferMemory APICreateBuffer
and APIGetProperties. Includes tests.

Bug: dawn:2382
Change-Id: I8b2c30d48f65f5bb9fc3cbbd6c78aeef75a6409e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/175963
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brandon1 Jones <brandon1.jones@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index ec44f26..6c0d6dd 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -408,6 +408,7 @@
             return wgpu::BufferMapState::Pending;
         case BufferState::Unmapped:
         case BufferState::Destroyed:
+        case BufferState::SharedMemoryNoAccess:
             return wgpu::BufferMapState::Unmapped;
         default:
             DAWN_UNREACHABLE();
@@ -497,6 +498,8 @@
             return DAWN_VALIDATION_ERROR("%s used in submit while mapped.", this);
         case BufferState::PendingMap:
             return DAWN_VALIDATION_ERROR("%s used in submit while pending map.", this);
+        case BufferState::SharedMemoryNoAccess:
+            return DAWN_VALIDATION_ERROR("%s used in submit without shared memory access.", this);
         case BufferState::HostMappedPersistent:
         case BufferState::Unmapped:
             return {};
@@ -760,6 +763,8 @@
             return DAWN_VALIDATION_ERROR("%s is destroyed.", this);
         case BufferState::HostMappedPersistent:
             return DAWN_VALIDATION_ERROR("Host-mapped %s cannot be mapped again.", this);
+        case BufferState::SharedMemoryNoAccess:
+            return DAWN_VALIDATION_ERROR("%s used in submit without shared memory access.", this);
         case BufferState::Unmapped:
             break;
     }
@@ -825,6 +830,7 @@
 
         case BufferState::PendingMap:
         case BufferState::Unmapped:
+        case BufferState::SharedMemoryNoAccess:
         case BufferState::Destroyed:
             return false;
     }
@@ -872,6 +878,10 @@
     mLastUsageSerial = serial;
 }
 
+void BufferBase::SetHasAccess(bool hasAccess) {
+    mState = hasAccess ? BufferState::Unmapped : BufferState::SharedMemoryNoAccess;
+}
+
 bool BufferBase::IsFullBufferRange(uint64_t offset, uint64_t size) const {
     return offset == 0 && size == GetSize();
 }
diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h
index 5b14d90..b21db0d 100644
--- a/src/dawn/native/Buffer.h
+++ b/src/dawn/native/Buffer.h
@@ -39,6 +39,7 @@
 #include "dawn/native/Forward.h"
 #include "dawn/native/IntegerTypes.h"
 #include "dawn/native/ObjectBase.h"
+#include "dawn/native/SharedBufferMemory.h"
 #include "dawn/native/UsageValidationMode.h"
 
 #include "dawn/native/dawn_platform.h"
@@ -76,6 +77,7 @@
         Mapped,
         MappedAtCreation,
         HostMappedPersistent,
+        SharedMemoryNoAccess,
         Destroyed,
     };
     static Ref<BufferBase> MakeError(DeviceBase* device, const BufferDescriptor* descriptor);
@@ -102,6 +104,9 @@
     void SetIsDataInitialized();
     void MarkUsedInPendingCommands();
 
+    // SetHasAccess determines Dawn's ability to access SharedBufferMemory.
+    void SetHasAccess(bool hasAccess);
+
     virtual void* GetMappedPointer() = 0;
     void* GetMappedRange(size_t offset, size_t size, bool writable = true);
     MaybeError Unmap();
@@ -138,6 +143,9 @@
 
     ExecutionSerial mLastUsageSerial = ExecutionSerial(0);
 
+    // The shared buffer memory state the buffer was created from. May be null.
+    Ref<SharedBufferMemoryContents> mSharedBufferMemoryContents;
+
   private:
     std::function<void()> PrepareMappingCallback(MapRequestID mapID,
                                                  WGPUBufferMapAsyncStatus status);
diff --git a/src/dawn/native/SharedBufferMemory.cpp b/src/dawn/native/SharedBufferMemory.cpp
index 7ae842e..384e290 100644
--- a/src/dawn/native/SharedBufferMemory.cpp
+++ b/src/dawn/native/SharedBufferMemory.cpp
@@ -27,6 +27,10 @@
 
 #include "dawn/native/SharedBufferMemory.h"
 
+#include <utility>
+
+#include "dawn/native/Buffer.h"
+#include "dawn/native/ChainUtils.h"
 #include "dawn/native/Device.h"
 
 namespace dawn::native {
@@ -37,6 +41,11 @@
   public:
     ErrorSharedBufferMemory(DeviceBase* device, const SharedBufferMemoryDescriptor* descriptor)
         : SharedBufferMemoryBase(device, descriptor, ObjectBase::kError) {}
+
+    ResultOrError<Ref<BufferBase>> CreateBufferImpl(
+        const UnpackedPtr<BufferDescriptor>& descriptor) override {
+        DAWN_UNREACHABLE();
+    }
 };
 
 }  // namespace
@@ -51,12 +60,15 @@
 SharedBufferMemoryBase::SharedBufferMemoryBase(DeviceBase* device,
                                                const SharedBufferMemoryDescriptor* descriptor,
                                                ObjectBase::ErrorTag tag)
-    : ApiObjectBase(device, tag, descriptor->label) {}
+    : ApiObjectBase(device, tag, descriptor->label),
+      mProperties{nullptr, wgpu::BufferUsage::None, 0} {}
 
 SharedBufferMemoryBase::SharedBufferMemoryBase(DeviceBase* device,
                                                const char* label,
                                                const SharedBufferMemoryProperties& properties)
-    : ApiObjectBase(device, label), mProperties(properties) {}
+    : ApiObjectBase(device, label), mProperties(properties) {
+    GetObjectTrackingList()->Track(this);
+}
 
 ObjectType SharedBufferMemoryBase::GetType() const {
     return ObjectType::SharedBufferMemory;
@@ -64,12 +76,73 @@
 
 void SharedBufferMemoryBase::DestroyImpl() {}
 
+void SharedBufferMemoryBase::Initialize() {
+    DAWN_ASSERT(!IsError());
+    mContents = CreateContents();
+}
+
 void SharedBufferMemoryBase::APIGetProperties(SharedBufferMemoryProperties* properties) const {
-    return;
+    properties->usage = mProperties.usage;
+    properties->size = mProperties.size;
+
+    UnpackedPtr<SharedBufferMemoryProperties> unpacked;
+    if (GetDevice()->ConsumedError(ValidateAndUnpack(properties), &unpacked,
+                                   "calling %s.GetProperties", this)) {
+        return;
+    }
 }
 
 BufferBase* SharedBufferMemoryBase::APICreateBuffer(const BufferDescriptor* descriptor) {
-    return nullptr;
+    Ref<BufferBase> result;
+
+    // Provide the defaults if no descriptor is provided.
+    BufferDescriptor defaultDescriptor;
+    if (descriptor == nullptr) {
+        defaultDescriptor = {};
+        defaultDescriptor.size = mProperties.size;
+        defaultDescriptor.usage = mProperties.usage;
+        descriptor = &defaultDescriptor;
+    }
+
+    if (GetDevice()->ConsumedError(CreateBuffer(descriptor), &result,
+                                   InternalErrorType::OutOfMemory, "calling %s.CreateBuffer(%s).",
+                                   this, descriptor)) {
+        result = BufferBase::MakeError(GetDevice(), descriptor);
+    }
+    return ReturnToAPI(std::move(result));
+}
+
+ResultOrError<Ref<BufferBase>> SharedBufferMemoryBase::CreateBuffer(
+    const BufferDescriptor* rawDescriptor) {
+    DAWN_TRY(GetDevice()->ValidateIsAlive());
+    DAWN_TRY(GetDevice()->ValidateObject(this));
+    // Validate the buffer descriptor.
+    UnpackedPtr<BufferDescriptor> descriptor;
+    DAWN_TRY_ASSIGN(descriptor, ValidateBufferDescriptor(GetDevice(), rawDescriptor));
+
+    // Ensure the buffer descriptor usage is a subset of the shared buffer memory's usage.
+    DAWN_INVALID_IF(!IsSubset(descriptor->usage, mProperties.usage),
+                    "The buffer usage (%s) is incompatible with the SharedBufferMemory usage (%s).",
+                    descriptor->usage, mProperties.usage);
+
+    // Validate that the buffer size exactly matches the shared buffer memory's size.
+    DAWN_INVALID_IF(descriptor->size != mProperties.size,
+                    "SharedBufferMemory size (%u) doesn't match descriptor size (%u).",
+                    mProperties.size, descriptor->size);
+
+    Ref<BufferBase> buffer;
+    DAWN_TRY_ASSIGN(buffer, CreateBufferImpl(descriptor));
+    // Access is not allowed until BeginAccess has been called.
+    buffer->SetHasAccess(false);
+    return buffer;
+}
+
+Ref<SharedBufferMemoryContents> SharedBufferMemoryBase::CreateContents() {
+    return AcquireRef(new SharedBufferMemoryContents(GetWeakRef(this)));
+}
+
+SharedBufferMemoryContents* SharedBufferMemoryBase::GetContents() const {
+    return mContents.Get();
 }
 
 bool SharedBufferMemoryBase::APIBeginAccess(BufferBase* buffer,
@@ -82,7 +155,15 @@
 }
 
 bool SharedBufferMemoryBase::APIIsDeviceLost() {
-    return false;
+    return GetDevice()->IsLost();
+}
+
+SharedBufferMemoryContents::SharedBufferMemoryContents(
+    WeakRef<SharedBufferMemoryBase> sharedBufferMemory)
+    : mSharedBufferMemory(std::move(sharedBufferMemory)) {}
+
+const WeakRef<SharedBufferMemoryBase>& SharedBufferMemoryContents::GetSharedBufferMemory() const {
+    return mSharedBufferMemory;
 }
 
 void APISharedBufferMemoryEndAccessStateFreeMembers(WGPUSharedBufferMemoryEndAccessState cState) {
diff --git a/src/dawn/native/SharedBufferMemory.h b/src/dawn/native/SharedBufferMemory.h
index 09bf6eb..fac946e 100644
--- a/src/dawn/native/SharedBufferMemory.h
+++ b/src/dawn/native/SharedBufferMemory.h
@@ -33,10 +33,12 @@
 #include "dawn/native/Error.h"
 #include "dawn/native/Forward.h"
 #include "dawn/native/ObjectBase.h"
+#include "dawn/native/SharedFence.h"
 #include "dawn/native/dawn_platform.h"
 
 namespace dawn::native {
 
+class SharedBufferMemoryContents;
 struct SharedBufferMemoryDescriptor;
 struct SharedBufferMemoryBeginAccessDescriptor;
 struct SharedBufferMemoryEndAccessState;
@@ -51,6 +53,8 @@
     static SharedBufferMemoryBase* MakeError(DeviceBase* device,
                                              const SharedBufferMemoryDescriptor* descriptor);
 
+    void Initialize();
+
     void APIGetProperties(SharedBufferMemoryProperties* properties) const;
     BufferBase* APICreateBuffer(const BufferDescriptor* descriptor);
     // Returns true if access was acquired. If it returns true, then APIEndAccess must
@@ -66,6 +70,8 @@
 
     ObjectType GetType() const override;
 
+    SharedBufferMemoryContents* GetContents() const;
+
   protected:
     SharedBufferMemoryBase(DeviceBase* device,
                            const char* label,
@@ -77,6 +83,26 @@
     void DestroyImpl() override;
 
     SharedBufferMemoryProperties mProperties;
+
+  private:
+    virtual Ref<SharedBufferMemoryContents> CreateContents();
+
+    ResultOrError<Ref<BufferBase>> CreateBuffer(const BufferDescriptor* rawDescriptor);
+
+    virtual ResultOrError<Ref<BufferBase>> CreateBufferImpl(
+        const UnpackedPtr<BufferDescriptor>& descriptor) = 0;
+
+    Ref<SharedBufferMemoryContents> mContents;
+};
+
+class SharedBufferMemoryContents : public RefCounted {
+  public:
+    explicit SharedBufferMemoryContents(WeakRef<SharedBufferMemoryBase> sharedBufferMemory);
+
+    const WeakRef<SharedBufferMemoryBase>& GetSharedBufferMemory() const;
+
+  private:
+    WeakRef<SharedBufferMemoryBase> mSharedBufferMemory;
 };
 
 }  // namespace dawn::native
diff --git a/src/dawn/native/d3d12/BufferD3D12.cpp b/src/dawn/native/d3d12/BufferD3D12.cpp
index 747746c..fc1ad9b 100644
--- a/src/dawn/native/d3d12/BufferD3D12.cpp
+++ b/src/dawn/native/d3d12/BufferD3D12.cpp
@@ -43,6 +43,7 @@
 #include "dawn/native/d3d12/HeapD3D12.h"
 #include "dawn/native/d3d12/QueueD3D12.h"
 #include "dawn/native/d3d12/ResidencyManagerD3D12.h"
+#include "dawn/native/d3d12/SharedBufferMemoryD3D12.h"
 #include "dawn/native/d3d12/UtilsD3D12.h"
 #include "dawn/platform/DawnPlatform.h"
 #include "dawn/platform/tracing/TraceEvent.h"
@@ -130,6 +131,26 @@
     return buffer;
 }
 
+// static
+ResultOrError<Ref<Buffer>> Buffer::CreateFromSharedBufferMemory(
+    SharedBufferMemory* memory,
+    const UnpackedPtr<BufferDescriptor>& descriptor) {
+    Device* device = ToBackend(memory->GetDevice());
+    Ref<Buffer> buffer = AcquireRef(new Buffer(device, descriptor));
+    DAWN_TRY(buffer->InitializeAsExternalBuffer(memory->GetD3DResource(), descriptor));
+    buffer->mSharedBufferMemoryContents = memory->GetContents();
+    return buffer;
+}
+
+MaybeError Buffer::InitializeAsExternalBuffer(ComPtr<ID3D12Resource> d3d12Buffer,
+                                              const UnpackedPtr<BufferDescriptor>& descriptor) {
+    AllocationInfo info;
+    info.mMethod = AllocationMethod::kExternal;
+    mResourceAllocation = {info, 0, std::move(d3d12Buffer), nullptr};
+    mAllocatedSize = descriptor->size;
+    return {};
+}
+
 Buffer::Buffer(Device* device, const UnpackedPtr<BufferDescriptor>& descriptor)
     : BufferBase(device, descriptor) {}
 
diff --git a/src/dawn/native/d3d12/BufferD3D12.h b/src/dawn/native/d3d12/BufferD3D12.h
index bd0c13e..3d22ca7 100644
--- a/src/dawn/native/d3d12/BufferD3D12.h
+++ b/src/dawn/native/d3d12/BufferD3D12.h
@@ -41,11 +41,15 @@
 
 class CommandRecordingContext;
 class Device;
+class SharedBufferMemory;
 
 class Buffer final : public BufferBase {
   public:
     static ResultOrError<Ref<Buffer>> Create(Device* device,
                                              const UnpackedPtr<BufferDescriptor>& descriptor);
+    static ResultOrError<Ref<Buffer>> CreateFromSharedBufferMemory(
+        SharedBufferMemory* memory,
+        const UnpackedPtr<BufferDescriptor>& descriptor);
 
     ID3D12Resource* GetD3D12Resource() const;
     D3D12_GPU_VIRTUAL_ADDRESS GetVA() const;
@@ -75,6 +79,8 @@
 
     MaybeError Initialize(bool mappedAtCreation);
     MaybeError InitializeHostMapped(const BufferHostMappedPointer* hostMappedDesc);
+    MaybeError InitializeAsExternalBuffer(ComPtr<ID3D12Resource> d3dBuffer,
+                                          const UnpackedPtr<BufferDescriptor>& descriptor);
     MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override;
     void UnmapImpl() override;
     void DestroyImpl() override;
diff --git a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
index fb6cb21..1a43eb8 100644
--- a/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/PhysicalDeviceD3D12.cpp
@@ -147,6 +147,7 @@
     EnableFeature(Feature::AdapterPropertiesD3D);
     EnableFeature(Feature::MultiPlanarRenderTargets);
     EnableFeature(Feature::R8UnormStorage);
+    EnableFeature(Feature::SharedBufferMemoryD3D12Resource);
 
     if (AreTimestampQueriesSupported()) {
         EnableFeature(Feature::TimestampQuery);
diff --git a/src/dawn/native/d3d12/SharedBufferMemoryD3D12.cpp b/src/dawn/native/d3d12/SharedBufferMemoryD3D12.cpp
index d486c17..a242520 100644
--- a/src/dawn/native/d3d12/SharedBufferMemoryD3D12.cpp
+++ b/src/dawn/native/d3d12/SharedBufferMemoryD3D12.cpp
@@ -25,16 +25,72 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#include <utility>
+
+#include "dawn/native/d3d12/BufferD3D12.h"
+#include "dawn/native/d3d12/DeviceD3D12.h"
 #include "dawn/native/d3d12/SharedBufferMemoryD3D12.h"
 
 namespace dawn::native::d3d12 {
 
+SharedBufferMemory::SharedBufferMemory(Device* device,
+                                       const char* label,
+                                       SharedBufferMemoryProperties properties,
+                                       ComPtr<ID3D12Resource> resource)
+    : SharedBufferMemoryBase(device, label, properties), mResource(std::move(resource)) {}
+
 // static
 ResultOrError<Ref<SharedBufferMemory>> SharedBufferMemory::Create(
     Device* device,
     const char* label,
     const SharedBufferMemoryD3D12ResourceDescriptor* descriptor) {
-    return DAWN_UNIMPLEMENTED_ERROR("Not implemented");
+    DAWN_INVALID_IF(!descriptor->resource, "D3D12 resource is missing.");
+
+    ComPtr<ID3D12Resource> d3d12Resource = descriptor->resource;
+
+    ID3D12Device* resourceDevice = nullptr;
+    d3d12Resource->GetDevice(__uuidof(*resourceDevice), reinterpret_cast<void**>(&resourceDevice));
+    DAWN_INVALID_IF(resourceDevice != device->GetD3D12Device(),
+                    "The D3D12 device of the resource and the D3D12 device of %s must be same.",
+                    device);
+    resourceDevice->Release();
+
+    D3D12_RESOURCE_DESC desc = d3d12Resource->GetDesc();
+    DAWN_INVALID_IF(desc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER,
+                    "Resource dimension (%d) was not Buffer", desc.Dimension);
+
+    D3D12_HEAP_PROPERTIES heapProperties;
+    D3D12_HEAP_FLAGS heapFlags;
+    d3d12Resource->GetHeapProperties(&heapProperties, &heapFlags);
+
+    wgpu::BufferUsage usages = wgpu::BufferUsage::None;
+
+    if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) {
+        usages |=
+            wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
+    } else if (heapProperties.Type == D3D12_HEAP_TYPE_UPLOAD) {
+        usages |= wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
+    } else if (heapProperties.Type == D3D12_HEAP_TYPE_READBACK) {
+        usages |= wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
+    }
+
+    SharedBufferMemoryProperties properties;
+    properties.size = desc.Width;
+    properties.usage = usages;
+
+    auto result =
+        AcquireRef(new SharedBufferMemory(device, label, properties, std::move(d3d12Resource)));
+    result->Initialize();
+    return result;
+}
+
+ResultOrError<Ref<BufferBase>> SharedBufferMemory::CreateBufferImpl(
+    const UnpackedPtr<BufferDescriptor>& descriptor) {
+    return Buffer::CreateFromSharedBufferMemory(this, descriptor);
+}
+
+ID3D12Resource* SharedBufferMemory::GetD3DResource() const {
+    return mResource.Get();
 }
 
 }  // namespace dawn::native::d3d12
diff --git a/src/dawn/native/d3d12/SharedBufferMemoryD3D12.h b/src/dawn/native/d3d12/SharedBufferMemoryD3D12.h
index cae0327..8f0fc38 100644
--- a/src/dawn/native/d3d12/SharedBufferMemoryD3D12.h
+++ b/src/dawn/native/d3d12/SharedBufferMemoryD3D12.h
@@ -42,6 +42,19 @@
         Device* device,
         const char* label,
         const SharedBufferMemoryD3D12ResourceDescriptor* descriptor);
+
+    ID3D12Resource* GetD3DResource() const;
+
+  private:
+    SharedBufferMemory(Device* device,
+                       const char* label,
+                       SharedBufferMemoryProperties properties,
+                       ComPtr<ID3D12Resource> resource);
+
+    ResultOrError<Ref<BufferBase>> CreateBufferImpl(
+        const UnpackedPtr<BufferDescriptor>& descriptor) override;
+
+    ComPtr<ID3D12Resource> mResource;
 };
 
 }  // namespace dawn::native::d3d12
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index d395f65..2e442ad 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -671,6 +671,7 @@
   if (dawn_enable_d3d11 || dawn_enable_d3d12) {
     libs += [
       "d3d11.lib",
+      "d3d12.lib",
       "dxgi.lib",
     ]
 
@@ -746,6 +747,8 @@
 
   sources = [
     "white_box/ShaderModuleTests.cpp",
+    "white_box/SharedBufferMemoryTests.cpp",
+    "white_box/SharedBufferMemoryTests.h",
     "white_box/SharedTextureMemoryTests.cpp",
     "white_box/SharedTextureMemoryTests.h",
   ]
@@ -756,7 +759,10 @@
   }
 
   if (is_win) {
-    sources += [ "white_box/SharedTextureMemoryTests_win.cpp" ]
+    sources += [
+      "white_box/SharedBufferMemoryTests_win.cpp",
+      "white_box/SharedTextureMemoryTests_win.cpp",
+    ]
   }
 
   if (dawn_enable_vulkan) {
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp b/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp
new file mode 100644
index 0000000..635f17d
--- /dev/null
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp
@@ -0,0 +1,144 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "dawn/tests/white_box/SharedBufferMemoryTests.h"
+#include <gtest/gtest.h>
+#include <vector>
+#include "dawn/tests/DawnTest.h"
+#include "dawn/utils/WGPUHelpers.h"
+
+namespace dawn {
+
+void SharedBufferMemoryTests::SetUp() {
+    DAWN_TEST_UNSUPPORTED_IF(UsesWire());
+    DawnTestWithParams<SharedBufferMemoryTestParams>::SetUp();
+}
+
+std::vector<wgpu::FeatureName> SharedBufferMemoryTests::GetRequiredFeatures() {
+    auto features = GetParam().mBackend->RequiredFeatures(GetAdapter().Get());
+    if (!SupportsFeatures(features)) {
+        return {};
+    }
+
+    return features;
+}
+
+namespace {
+
+using ::testing::HasSubstr;
+
+// Test that it is an error to import shared buffer memory without a chained struct.
+TEST_P(SharedBufferMemoryTests, ImportSharedBufferMemoryNoChain) {
+    wgpu::SharedBufferMemoryDescriptor desc;
+    ASSERT_DEVICE_ERROR_MSG(
+        wgpu::SharedBufferMemory memory = device.ImportSharedBufferMemory(&desc),
+        HasSubstr("chain"));
+}
+
+// Test that it is an error to import shared buffer memory when the device is destroyed.
+TEST_P(SharedBufferMemoryTests, ImportSharedBufferMemoryDeviceDestroy) {
+    device.Destroy();
+
+    wgpu::SharedBufferMemoryDescriptor desc;
+    ASSERT_DEVICE_ERROR_MSG(
+        wgpu::SharedBufferMemory memory = device.ImportSharedBufferMemory(&desc),
+        HasSubstr("lost"));
+}
+
+// Test that SharedBufferMemory::IsDeviceLost() returns the expected value before and
+// after destroying the device.
+TEST_P(SharedBufferMemoryTests, CheckIsDeviceLostBeforeAndAfterDestroyingDevice) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(device);
+
+    EXPECT_FALSE(memory.IsDeviceLost());
+    device.Destroy();
+    EXPECT_TRUE(memory.IsDeviceLost());
+}
+
+// Test that SharedBufferMemory::IsDeviceLost() returns the expected value before and
+// after losing the device.
+TEST_P(SharedBufferMemoryTests, CheckIsDeviceLostBeforeAndAfterLosingDevice) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(device);
+
+    EXPECT_FALSE(memory.IsDeviceLost());
+    LoseDeviceForTesting(device);
+    EXPECT_TRUE(memory.IsDeviceLost());
+}
+
+// Test calling GetProperties on SharedBufferMemory after an error.
+TEST_P(SharedBufferMemoryTests, GetPropertiesErrorMemory) {
+    wgpu::SharedBufferMemoryDescriptor desc;
+    ASSERT_DEVICE_ERROR(wgpu::SharedBufferMemory memory = device.ImportSharedBufferMemory(&desc));
+
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+
+    EXPECT_EQ(properties.usage, wgpu::BufferUsage::None);
+    EXPECT_EQ(properties.size, 0u);
+}
+
+// Tests that creating SharedBufferMemory validates buffer size.
+TEST_P(SharedBufferMemoryTests, SizeValidation) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(device);
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+
+    wgpu::BufferDescriptor bufferDesc = {};
+    bufferDesc.usage = properties.usage;
+    bufferDesc.size = properties.size + 1;
+    ASSERT_DEVICE_ERROR_MSG(memory.CreateBuffer(&bufferDesc),
+                            HasSubstr("doesn't match descriptor size"));
+}
+
+// Tests that creating SharedBufferMemory validates buffer usages.
+TEST_P(SharedBufferMemoryTests, UsageValidation) {
+    wgpu::SharedBufferMemory memory = GetParam().mBackend->CreateSharedBufferMemory(device);
+    wgpu::SharedBufferMemoryProperties properties;
+    memory.GetProperties(&properties);
+
+    wgpu::BufferDescriptor bufferDesc = {};
+    bufferDesc.size = properties.size;
+
+    for (wgpu::BufferUsage usage :
+         {wgpu::BufferUsage::MapRead, wgpu::BufferUsage::MapWrite, wgpu::BufferUsage::CopySrc,
+          wgpu::BufferUsage::CopyDst, wgpu::BufferUsage::Index, wgpu::BufferUsage::Vertex,
+          wgpu::BufferUsage::Uniform, wgpu::BufferUsage::Storage, wgpu::BufferUsage::Indirect,
+          wgpu::BufferUsage::QueryResolve}) {
+        bufferDesc.usage = usage;
+        if (usage & properties.usage) {
+            wgpu::Buffer b = memory.CreateBuffer(&bufferDesc);
+            EXPECT_EQ(b.GetUsage(), usage);
+        } else {
+            ASSERT_DEVICE_ERROR(memory.CreateBuffer(&bufferDesc));
+        }
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SharedBufferMemoryTests);
+
+}  // anonymous namespace
+}  // namespace dawn
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests.h b/src/dawn/tests/white_box/SharedBufferMemoryTests.h
new file mode 100644
index 0000000..b1490af
--- /dev/null
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests.h
@@ -0,0 +1,63 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_TESTS_WHITE_BOX_SHAREDBUFFERMEMORYTESTS_H_
+#define SRC_DAWN_TESTS_WHITE_BOX_SHAREDBUFFERMEMORYTESTS_H_
+
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "dawn/tests/DawnTest.h"
+
+namespace dawn {
+
+class SharedBufferMemoryTestBackend {
+  public:
+    virtual void SetUp() {}
+    virtual void TearDown() {}
+
+    // The required features for testing this backend.
+    virtual std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& device) const = 0;
+
+    // Create one basic shared buffer memory. It should support most operations.
+    virtual wgpu::SharedBufferMemory CreateSharedBufferMemory(const wgpu::Device& device) = 0;
+};
+
+using Backend = SharedBufferMemoryTestBackend*;
+DAWN_TEST_PARAM_STRUCT(SharedBufferMemoryTestParams, Backend);
+
+class SharedBufferMemoryTests : public DawnTestWithParams<SharedBufferMemoryTestParams> {
+  public:
+    void SetUp() override;
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override;
+};
+}  // namespace dawn
+
+#endif  // SRC_DAWN_TESTS_WHITE_BOX_SHAREDBUFFERMEMORYTESTS_H_
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp b/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp
new file mode 100644
index 0000000..06f5049
--- /dev/null
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests_win.cpp
@@ -0,0 +1,141 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <d3d12.h>
+#include <vector>
+#include "dawn/native/D3D12Backend.h"
+#include "dawn/native/d3d12/DeviceD3D12.h"
+#include "dawn/tests/DawnTest.h"
+#include "dawn/tests/white_box/SharedBufferMemoryTests.h"
+#include "dawn/utils/ComboRenderPipelineDescriptor.h"
+#include "dawn/utils/WGPUHelpers.h"
+
+namespace dawn {
+namespace {
+
+constexpr uint32_t kBufferWidth = 32;
+
+class Backend : public SharedBufferMemoryTestBackend {
+  public:
+    static Backend* GetInstance() {
+        static Backend b;
+        return &b;
+    }
+
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter& adapter) const override {
+        return {wgpu::FeatureName::SharedBufferMemoryD3D12Resource};
+    }
+
+    wgpu::SharedBufferMemory CreateSharedBufferMemory(const wgpu::Device& device) override {
+        ComPtr<ID3D12Device> d3d12Device = CreateD3D12Device(device);
+        ComPtr<ID3D12Resource> d3d12Resource =
+            CreateD3D12Buffer(d3d12Device.Get(), D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_FLAG_NONE);
+
+        wgpu::SharedBufferMemoryDescriptor desc;
+        native::d3d12::SharedBufferMemoryD3D12ResourceDescriptor sharedD3d12ResourceDesc;
+        sharedD3d12ResourceDesc.resource = d3d12Resource.Get();
+        desc.nextInChain = &sharedD3d12ResourceDesc;
+        return device.ImportSharedBufferMemory(&desc);
+    }
+
+  private:
+    ComPtr<ID3D12Device> CreateD3D12Device(const wgpu::Device& device) {
+        ComPtr<IDXGIAdapter> dxgiAdapter = native::d3d::GetDXGIAdapter(device.GetAdapter().Get());
+        DXGI_ADAPTER_DESC adapterDesc;
+        dxgiAdapter->GetDesc(&adapterDesc);
+
+        ComPtr<IDXGIFactory4> dxgiFactory;
+        CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory));
+        dxgiAdapter = nullptr;
+        dxgiFactory->EnumAdapterByLuid(adapterDesc.AdapterLuid, IID_PPV_ARGS(&dxgiAdapter));
+
+        ComPtr<ID3D12Device> d3d12Device;
+
+        D3D12CreateDevice(dxgiAdapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device),
+                          &d3d12Device);
+
+        return d3d12Device;
+    }
+
+    ComPtr<ID3D12Resource> CreateD3D12Buffer(ID3D12Device* device,
+                                             D3D12_HEAP_TYPE heapType,
+                                             D3D12_RESOURCE_FLAGS resourceFlags) {
+        D3D12_RESOURCE_STATES initialResourceState = D3D12_RESOURCE_STATE_COMMON;
+        if (heapType == D3D12_HEAP_TYPE_UPLOAD) {
+            initialResourceState = D3D12_RESOURCE_STATE_GENERIC_READ;
+        }
+
+        D3D12_HEAP_PROPERTIES heapProperties = {heapType, D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
+                                                D3D12_MEMORY_POOL_UNKNOWN, 0, 0};
+
+        D3D12_RESOURCE_DESC descriptor;
+        descriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+        descriptor.Alignment = 0;
+        descriptor.Width = kBufferWidth;
+        descriptor.Height = 1;
+        descriptor.DepthOrArraySize = 1;
+        descriptor.MipLevels = 1;
+        descriptor.Format = DXGI_FORMAT_UNKNOWN;
+        descriptor.SampleDesc.Count = 1;
+        descriptor.SampleDesc.Quality = 0;
+        descriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+        descriptor.Flags = resourceFlags;
+
+        ComPtr<ID3D12Resource> resource;
+
+        device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &descriptor,
+                                        initialResourceState, {}, IID_PPV_ARGS(&resource));
+        return resource;
+    }
+
+  private:
+    Backend() {}
+};
+
+// TODO(dawn:2382): Add D3D12-specific tests for:
+// - Test importing an {UPLOAD, READBACK, DEFAULT} buffer.
+// - Test reading to an {UPLOAD, READBACK, DEFAULT} buffer.
+// - Test Writing to an {UPLOAD, READBACK, DEFAULT} buffer
+// - Ensure BeginAccess works with SharedFence.
+// - Ensure EndAccess works with SharedFence.
+// - Validate that importing a nullptr ID3D12Resource results in error.
+// - Validate that importing an ID3D12Resource from another device results in error.
+// - Check using a non-mappable buffers between multiple devices.
+// - Check using the mappable buffers between multiple devices
+// - Check validation that isInitialized must be true (for now).
+
+// TODO(dawn:2382): Add backend-agnostic tests for:
+// - Ensure that EndAccess cannot be called on a mapped buffer.
+// - Ensure no operations {mapping, use on queue} can occur before calling BeginAccess.
+// - Ensure multiple buffers created from a SharedBufferMemory cannot be accessed simultaneously.
+// - Validate that calling EndAccess before BeginAccess produces an error.
+// - Validate that calling BeginAccess twice produces an error.
+
+DAWN_INSTANTIATE_TEST_P(SharedBufferMemoryTests, {D3D12Backend()}, {Backend::GetInstance()});
+
+}  // anonymous namespace
+}  // namespace dawn