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