Implement RequestAdapterF and RequestDeviceF in native/wire
- implement RequestAdapterF and RequestDeviceF in native
- implement RequestDeviceF in dawn wire
- rewrite legacy non-future methods on top of the future ones
- Updates wire and native adpater/device creation tests to
test all future callback modes, as well as the legacy path
Bug: dawn:1987
Change-Id: I70495d2d59a2bedd90e39d177a4c6a0666e2fdbd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/166220
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 73424db..e4f23b1 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -145,6 +145,16 @@
]
},
{
+ "name": "request device f",
+ "_comment": "TODO(crbug.com/dawn/2021): This is dawn/emscripten-only until we rename it to replace the old API. See bug for details.",
+ "tags": ["dawn", "emscripten"],
+ "returns": "future",
+ "args": [
+ {"name": "options", "type": "device descriptor", "annotation": "const*", "optional": true, "no_default": true},
+ {"name": "callback info", "type": "request device callback info"}
+ ]
+ },
+ {
"name": "create device",
"tags": ["dawn"],
"returns": "device",
@@ -2891,6 +2901,15 @@
{"name": "userdata", "type": "void *"}
]
},
+ "request device callback info": {
+ "category": "structure",
+ "extensible": "in",
+ "members": [
+ {"name": "mode", "type": "callback mode"},
+ {"name": "callback", "type": "request device callback"},
+ {"name": "userdata", "type": "void *"}
+ ]
+ },
"request device status": {
"category": "enum",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index d0ded98..f37d6ac 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -108,7 +108,8 @@
],
"adapter request device": [
{ "name": "adapter id", "type": "ObjectId", "id_type": "adapter" },
- { "name": "request serial", "type": "uint64_t" },
+ { "name": "event manager handle", "type": "ObjectHandle" },
+ { "name": "future", "type": "future" },
{ "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
{ "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
]
@@ -177,8 +178,8 @@
{ "name": "features", "type": "feature name", "annotation": "const*", "length": "features count"}
],
"adapter request device callback": [
- { "name": "adapter", "type": "ObjectHandle", "handle_type": "adapter" },
- { "name": "request serial", "type": "uint64_t" },
+ { "name": "event manager", "type": "ObjectHandle" },
+ { "name": "future", "type": "future" },
{ "name": "status", "type": "request device status" },
{ "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
{ "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
@@ -203,6 +204,7 @@
"AdapterHasFeature",
"AdapterEnumerateFeatures",
"AdapterRequestDevice",
+ "AdapterRequestDeviceF",
"BufferMapAsync",
"BufferMapAsyncF",
"BufferGetConstMappedRange",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 32f4f49..e34ad50 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -262,6 +262,49 @@
callback(status, ToAPI(ReturnToAPI(std::move(device))), nullptr, userdata);
}
+Future AdapterBase::APIRequestDeviceF(const DeviceDescriptor* descriptor,
+ const RequestDeviceCallbackInfo& callbackInfo) {
+ struct RequestDeviceEvent final : public EventManager::TrackedEvent {
+ WGPURequestDeviceCallback mCallback;
+ void* mUserdata;
+ ResultOrError<Ref<DeviceBase>> mDeviceOrError;
+
+ RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo,
+ ResultOrError<Ref<DeviceBase>> deviceOrError)
+ : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+ mCallback(callbackInfo.callback),
+ mUserdata(callbackInfo.userdata),
+ mDeviceOrError(std::move(deviceOrError)) {
+ CompleteIfSpontaneous();
+ }
+
+ ~RequestDeviceEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+ void Complete(EventCompletionType completionType) override {
+ if (mDeviceOrError.IsError()) {
+ std::unique_ptr<ErrorData> errorData = mDeviceOrError.AcquireError();
+ mCallback(WGPURequestDeviceStatus_Error, nullptr,
+ errorData->GetFormattedMessage().c_str(), mUserdata);
+ return;
+ }
+ Ref<DeviceBase> device = mDeviceOrError.AcquireSuccess();
+ WGPURequestDeviceStatus status = device == nullptr ? WGPURequestDeviceStatus_Unknown
+ : WGPURequestDeviceStatus_Success;
+ mCallback(status, ToAPI(device.Detach()), nullptr, mUserdata);
+ }
+ };
+
+ constexpr DeviceDescriptor kDefaultDescriptor = {};
+ if (descriptor == nullptr) {
+ descriptor = &kDefaultDescriptor;
+ }
+
+ FutureID futureID = mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent(
+ callbackInfo.mode,
+ AcquireRef(new RequestDeviceEvent(callbackInfo, CreateDevice(descriptor))));
+ return {futureID};
+}
+
const TogglesState& AdapterBase::GetTogglesState() const {
return mTogglesState;
}
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index 6170358..2f8a405 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -59,6 +59,8 @@
void APIRequestDevice(const DeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata);
+ Future APIRequestDeviceF(const DeviceDescriptor* descriptor,
+ const RequestDeviceCallbackInfo& callbackInfo);
DeviceBase* APICreateDevice(const DeviceDescriptor* descriptor = nullptr);
ResultOrError<Ref<DeviceBase>> CreateDevice(const DeviceDescriptor* rawDescriptor);
diff --git a/src/dawn/native/EventManager.cpp b/src/dawn/native/EventManager.cpp
index 37f21cc..aeaada8 100644
--- a/src/dawn/native/EventManager.cpp
+++ b/src/dawn/native/EventManager.cpp
@@ -437,6 +437,14 @@
ExecutionSerial completionSerial)
: mCallbackMode(callbackMode), mCompletionData(QueueAndSerial{queue, completionSerial}) {}
+EventManager::TrackedEvent::TrackedEvent(wgpu::CallbackMode callbackMode, Completed tag)
+ : TrackedEvent(callbackMode, [] {
+ // Make a system event that is already signaled.
+ // Note that this won't make a real OS event because the OS
+ // event is lazily created when waited on.
+ return SystemEvent::CreateSignaled();
+ }()) {}
+
EventManager::TrackedEvent::~TrackedEvent() {
DAWN_ASSERT(mCompleted);
}
diff --git a/src/dawn/native/EventManager.h b/src/dawn/native/EventManager.h
index fec82b4..c0d4668 100644
--- a/src/dawn/native/EventManager.h
+++ b/src/dawn/native/EventManager.h
@@ -108,8 +108,8 @@
// completed) will be cleaned up at that time.
class EventManager::TrackedEvent : public RefCounted {
protected:
- // Note: TrackedEvents are (currently) only for Device events. Events like RequestAdapter and
- // RequestDevice complete immediately in dawn native, so should never need to be tracked.
+ // Create an event from a SystemEvent. Note that events like RequestAdapter and
+ // RequestDevice complete immediately in dawn native, and may use an already-completed event.
TrackedEvent(wgpu::CallbackMode callbackMode, Ref<SystemEvent> completionEvent);
// Create a TrackedEvent from a queue completion serial.
@@ -117,6 +117,10 @@
QueueBase* queue,
ExecutionSerial completionSerial);
+ struct Completed {};
+ // Create a TrackedEvent that is already completed.
+ TrackedEvent(wgpu::CallbackMode callbackMode, Completed tag);
+
public:
// Subclasses must implement this to complete the event (if not completed) with
// EventCompletionType::Shutdown.
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 56f53f7..d720bae 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -251,19 +251,50 @@
void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options,
WGPURequestAdapterCallback callback,
void* userdata) {
- auto adapters = EnumerateAdapters(options);
- if (adapters.empty()) {
- callback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapters.", userdata);
- } else {
- callback(WGPURequestAdapterStatus_Success, ToAPI(ReturnToAPI(std::move(adapters[0]))),
- nullptr, userdata);
- }
+ APIRequestAdapterF(
+ options, RequestAdapterCallbackInfo{nullptr, wgpu::CallbackMode::AllowSpontaneous, callback,
+ userdata});
}
Future InstanceBase::APIRequestAdapterF(const RequestAdapterOptions* options,
const RequestAdapterCallbackInfo& callbackInfo) {
- // TODO(dawn:1987) Implement this.
- DAWN_UNREACHABLE();
+ struct RequestAdapterEvent final : public EventManager::TrackedEvent {
+ WGPURequestAdapterCallback mCallback;
+ void* mUserdata;
+ Ref<AdapterBase> mAdapter;
+
+ RequestAdapterEvent(const RequestAdapterCallbackInfo& callbackInfo,
+ Ref<AdapterBase> adapter)
+ : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+ mCallback(callbackInfo.callback),
+ mUserdata(callbackInfo.userdata),
+ mAdapter(std::move(adapter)) {
+ CompleteIfSpontaneous();
+ }
+
+ ~RequestAdapterEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+ void Complete(EventCompletionType completionType) override {
+ WGPUAdapter adapter = ToAPI(ReturnToAPI(std::move(mAdapter)));
+ if (adapter == nullptr) {
+ mCallback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapters",
+ mUserdata);
+ } else {
+ mCallback(WGPURequestAdapterStatus_Success, adapter, nullptr, mUserdata);
+ }
+ }
+ };
+
+ static constexpr RequestAdapterOptions kDefaultOptions = {};
+ if (options == nullptr) {
+ options = &kDefaultOptions;
+ }
+ auto adapters = EnumerateAdapters(options);
+
+ FutureID futureID = GetEventManager()->TrackEvent(
+ callbackInfo.mode, AcquireRef(new RequestAdapterEvent(
+ callbackInfo, adapters.empty() ? nullptr : std::move(adapters[0]))));
+ return {futureID};
}
Ref<AdapterBase> InstanceBase::CreateAdapter(Ref<PhysicalDeviceBase> physicalDevice,
diff --git a/src/dawn/native/SystemEvent.cpp b/src/dawn/native/SystemEvent.cpp
index e08ff7f..6d2a3a6 100644
--- a/src/dawn/native/SystemEvent.cpp
+++ b/src/dawn/native/SystemEvent.cpp
@@ -123,6 +123,13 @@
// SystemEvent
+// static
+Ref<SystemEvent> SystemEvent::CreateSignaled() {
+ auto ev = AcquireRef(new SystemEvent());
+ ev->Signal();
+ return ev;
+}
+
bool SystemEvent::IsSignaled() const {
return mSignaled.load(std::memory_order_acquire);
}
diff --git a/src/dawn/native/SystemEvent.h b/src/dawn/native/SystemEvent.h
index 2a3d0c5..dfce08c 100644
--- a/src/dawn/native/SystemEvent.h
+++ b/src/dawn/native/SystemEvent.h
@@ -104,6 +104,8 @@
class SystemEvent : public RefCounted {
public:
+ static Ref<SystemEvent> CreateSignaled();
+
bool IsSignaled() const;
void Signal();
diff --git a/src/dawn/tests/end2end/AdapterCreationTests.cpp b/src/dawn/tests/end2end/AdapterCreationTests.cpp
index 22cc222..3461561 100644
--- a/src/dawn/tests/end2end/AdapterCreationTests.cpp
+++ b/src/dawn/tests/end2end/AdapterCreationTests.cpp
@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
+#include <optional>
#include <string>
#include <utility>
@@ -43,7 +44,7 @@
using testing::MockCallback;
using testing::SaveArg;
-class AdapterCreationTest : public ::testing::Test {
+class AdapterCreationTest : public ::testing::TestWithParam<std::optional<wgpu::CallbackMode>> {
protected:
void SetUp() override {
dawnProcSetProcs(&native::GetProcs());
@@ -74,6 +75,35 @@
dawnProcSetProcs(nullptr);
}
+ void RequestAdapter(const wgpu::Instance& inst,
+ const wgpu::RequestAdapterOptions* options,
+ WGPURequestAdapterCallback callback,
+ void* userdata) {
+ if (GetParam() == std::nullopt) {
+ // Legacy RequestAdapter. It should call the callback immediately.
+ inst.RequestAdapter(options, callback, userdata);
+ return;
+ }
+
+ wgpu::Future future =
+ inst.RequestAdapterF(options, {nullptr, *GetParam(), callback, userdata});
+ switch (*GetParam()) {
+ case wgpu::CallbackMode::WaitAnyOnly: {
+ // Callback should complete as soon as poll once.
+ wgpu::FutureWaitInfo waitInfo = {future};
+ EXPECT_EQ(inst.WaitAny(1, &waitInfo, 0), wgpu::WaitStatus::Success);
+ ASSERT_TRUE(waitInfo.completed);
+ break;
+ }
+ case wgpu::CallbackMode::AllowSpontaneous:
+ // Callback should already be called.
+ break;
+ case wgpu::CallbackMode::AllowProcessEvents:
+ inst.ProcessEvents();
+ break;
+ }
+ }
+
wgpu::Instance instance;
bool anyAdapterAvailable = false;
bool swiftShaderAvailable = false;
@@ -81,8 +111,15 @@
bool integratedGPUAvailable = false;
};
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ AdapterCreationTest,
+ ::testing::ValuesIn(std::initializer_list<std::optional<wgpu::CallbackMode>>{
+ wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+ wgpu::CallbackMode::AllowSpontaneous, std::nullopt}));
+
// Test that requesting the default adapter works
-TEST_F(AdapterCreationTest, DefaultAdapter) {
+TEST_P(AdapterCreationTest, DefaultAdapter) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -90,14 +127,14 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
}
// Test that passing nullptr for the options gets the default adapter
-TEST_F(AdapterCreationTest, NullGivesDefaultAdapter) {
+TEST_P(AdapterCreationTest, NullGivesDefaultAdapter) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -105,21 +142,21 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this + 1))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(nullptr, cb.Callback(), cb.MakeUserdata(this + 1));
+ RequestAdapter(instance, nullptr, cb.Callback(), cb.MakeUserdata(this + 1));
wgpu::Adapter adapter2 = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter2 != nullptr, anyAdapterAvailable);
}
// Test that requesting the fallback adapter returns SwiftShader.
-TEST_F(AdapterCreationTest, FallbackAdapter) {
+TEST_P(AdapterCreationTest, FallbackAdapter) {
wgpu::RequestAdapterOptions options = {};
options.forceFallbackAdapter = true;
@@ -133,7 +170,7 @@
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
.WillOnce(SaveArg<1>(&cAdapter));
}
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, swiftShaderAvailable);
@@ -147,7 +184,7 @@
}
// Test that requesting a high performance GPU works
-TEST_F(AdapterCreationTest, PreferHighPerformance) {
+TEST_P(AdapterCreationTest, PreferHighPerformance) {
wgpu::RequestAdapterOptions options = {};
options.powerPreference = wgpu::PowerPreference::HighPerformance;
@@ -161,7 +198,7 @@
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
.WillOnce(SaveArg<1>(&cAdapter));
}
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -176,7 +213,7 @@
}
// Test that requesting a low power GPU works
-TEST_F(AdapterCreationTest, PreferLowPower) {
+TEST_P(AdapterCreationTest, PreferLowPower) {
wgpu::RequestAdapterOptions options = {};
options.powerPreference = wgpu::PowerPreference::LowPower;
@@ -190,7 +227,7 @@
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
.WillOnce(SaveArg<1>(&cAdapter));
}
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -205,7 +242,7 @@
}
// Test that requesting a Compatibility adapter is supported.
-TEST_F(AdapterCreationTest, Compatibility) {
+TEST_P(AdapterCreationTest, Compatibility) {
wgpu::RequestAdapterOptions options = {};
options.compatibilityMode = true;
@@ -214,7 +251,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -225,7 +262,7 @@
}
// Test that requesting a Non-Compatibility adapter is supported and is default.
-TEST_F(AdapterCreationTest, NonCompatibility) {
+TEST_P(AdapterCreationTest, NonCompatibility) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -233,7 +270,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -244,7 +281,7 @@
}
// Test that GetInstance() returns the correct Instance.
-TEST_F(AdapterCreationTest, GetInstance) {
+TEST_P(AdapterCreationTest, GetInstance) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -252,7 +289,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -262,7 +299,7 @@
// Test that calling AdapterGetProperties returns separate allocations for strings.
// However, the string contents are equivalent.
-TEST_F(AdapterCreationTest, PropertiesUnique) {
+TEST_P(AdapterCreationTest, PropertiesUnique) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -270,7 +307,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -294,7 +331,7 @@
}
// Test move assignment of the adapter properties.
-TEST_F(AdapterCreationTest, PropertiesMoveAssign) {
+TEST_P(AdapterCreationTest, PropertiesMoveAssign) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -302,7 +339,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -351,7 +388,7 @@
}
// Test move construction of the adapter properties.
-TEST_F(AdapterCreationTest, PropertiesMoveConstruct) {
+TEST_P(AdapterCreationTest, PropertiesMoveConstruct) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -359,7 +396,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -406,7 +443,7 @@
}
// Test that the adapter properties can outlive the adapter.
-TEST_F(AdapterCreationTest, PropertiesOutliveAdapter) {
+TEST_P(AdapterCreationTest, PropertiesOutliveAdapter) {
wgpu::RequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -414,7 +451,7 @@
WGPUAdapter cAdapter = nullptr;
EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
.WillOnce(SaveArg<1>(&cAdapter));
- instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+ RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
diff --git a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
index 6c76506..3ed0d71 100644
--- a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
+++ b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
+#include <optional>
#include <vector>
#include "dawn/dawn_proc.h"
@@ -272,8 +273,51 @@
}
}
+class DeviceCreationFutureTest
+ : public DeviceCreationTest,
+ public ::testing::WithParamInterface<std::optional<wgpu::CallbackMode>> {
+ protected:
+ void RequestDevice(const Adapter& a,
+ const wgpu::DeviceDescriptor* descriptor,
+ WGPURequestDeviceCallback callback,
+ void* userdata) {
+ wgpu::Adapter wgpuAdapter(a.Get());
+ if (GetParam() == std::nullopt) {
+ // Legacy RequestDevice. It should call the callback immediately.
+ wgpuAdapter.RequestDevice(descriptor, callback, userdata);
+ return;
+ }
+
+ wgpu::Future future =
+ wgpuAdapter.RequestDeviceF(descriptor, {nullptr, *GetParam(), callback, userdata});
+ wgpu::Instance wgpuInstance(instance->Get());
+ switch (*GetParam()) {
+ case wgpu::CallbackMode::WaitAnyOnly: {
+ // Callback should complete as soon as poll once.
+ wgpu::FutureWaitInfo waitInfo = {future};
+ EXPECT_EQ(wgpuInstance.WaitAny(1, &waitInfo, 0), wgpu::WaitStatus::Success);
+ ASSERT_TRUE(waitInfo.completed);
+ break;
+ }
+ case wgpu::CallbackMode::AllowSpontaneous:
+ // Callback should already be called.
+ break;
+ case wgpu::CallbackMode::AllowProcessEvents:
+ wgpuInstance.ProcessEvents();
+ break;
+ }
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ DeviceCreationFutureTest,
+ ::testing::ValuesIn(std::initializer_list<std::optional<wgpu::CallbackMode>>{
+ wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+ wgpu::CallbackMode::AllowSpontaneous, std::nullopt}));
+
// Test successful call to RequestDevice with descriptor
-TEST_F(DeviceCreationTest, RequestDeviceSuccess) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceSuccess) {
WGPUDevice cDevice;
{
MockCallback<WGPURequestDeviceCallback> cb;
@@ -281,7 +325,7 @@
.WillOnce(SaveArg<1>(&cDevice));
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), cb.MakeUserdata(this));
+ RequestDevice(adapter, &desc, cb.Callback(), cb.MakeUserdata(this));
}
wgpu::Device device = wgpu::Device::Acquire(cDevice);
@@ -289,14 +333,14 @@
}
// Test successful call to RequestDevice with a null descriptor
-TEST_F(DeviceCreationTest, RequestDeviceNullDescriptorSuccess) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceNullDescriptorSuccess) {
WGPUDevice cDevice;
{
MockCallback<WGPURequestDeviceCallback> cb;
EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
.WillOnce(SaveArg<1>(&cDevice));
- adapter.RequestDevice(nullptr, cb.Callback(), cb.MakeUserdata(this));
+ RequestDevice(adapter, nullptr, cb.Callback(), cb.MakeUserdata(this));
}
wgpu::Device device = wgpu::Device::Acquire(cDevice);
@@ -304,7 +348,7 @@
}
// Test failing call to RequestDevice with invalid feature
-TEST_F(DeviceCreationTest, RequestDeviceFailure) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceFailure) {
MockCallback<WGPURequestDeviceCallback> cb;
EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
@@ -313,7 +357,7 @@
desc.requiredFeatures = &invalidFeature;
desc.requiredFeatureCount = 1;
- adapter.RequestDevice(&desc, cb.Callback(), cb.MakeUserdata(this));
+ RequestDevice(adapter, &desc, cb.Callback(), cb.MakeUserdata(this));
}
} // anonymous namespace
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 469ef93..1f6b889 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
@@ -29,6 +29,7 @@
#include <vector>
#include "dawn/tests/MockCallback.h"
+#include "dawn/tests/unittests/wire/WireFutureTest.h"
#include "dawn/tests/unittests/wire/WireTest.h"
#include "dawn/wire/WireClient.h"
@@ -49,11 +50,24 @@
using testing::StrEq;
using testing::WithArg;
-class WireAdapterTests : public WireTest {
+using WireAdapterTestBase = WireFutureTestWithParams<WGPURequestDeviceCallback,
+ WGPURequestDeviceCallbackInfo,
+ wgpuAdapterRequestDevice,
+ wgpuAdapterRequestDeviceF>;
+
+class WireAdapterTests : public WireAdapterTestBase {
protected:
+ // Overriden version of wgpuAdapterRequestDevice that defers to the API call based on the
+ // test callback mode.
+ void AdapterRequestDevice(const wgpu::Adapter& a,
+ const wgpu::DeviceDescriptor* descriptor,
+ void* userdata = nullptr) {
+ CallImpl(userdata, a.Get(), reinterpret_cast<WGPUDeviceDescriptor const*>(descriptor));
+ }
+
// Bootstrap the tests and create a fake adapter.
void SetUp() override {
- WireTest::SetUp();
+ WireAdapterTestBase::SetUp();
WGPURequestAdapterOptions options = {};
MockCallback<WGPURequestAdapterCallback> cb;
@@ -102,83 +116,72 @@
void TearDown() override {
adapter = nullptr;
- WireTest::TearDown();
+ WireAdapterTestBase::TearDown();
}
WGPUAdapter apiAdapter;
wgpu::Adapter adapter;
};
+DAWN_INSTANTIATE_WIRE_FUTURE_TEST_P(WireAdapterTests);
-// Test that the DeviceDescriptor is passed from the client to the server.
-TEST_F(WireAdapterTests, RequestDevicePassesDescriptor) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
+// Test that an empty DeviceDescriptor is passed from the client to the server.
+TEST_P(WireAdapterTests, RequestDeviceEmptyDescriptor) {
+ wgpu::DeviceDescriptor desc = {};
+ AdapterRequestDevice(adapter, &desc);
- // Test an empty descriptor
- {
- wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+ .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
+ EXPECT_EQ(apiDesc->label, nullptr);
+ EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
+ EXPECT_EQ(apiDesc->requiredLimits, nullptr);
- EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
- .WillOnce(WithArg<1>(Invoke([](const WGPUDeviceDescriptor* apiDesc) {
- EXPECT_EQ(apiDesc->label, nullptr);
- EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
- EXPECT_EQ(apiDesc->requiredLimits, nullptr);
- })));
- FlushClient();
- }
+ // Call the callback so the test doesn't wait indefinitely.
+ api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+ nullptr);
+ })));
+ FlushClient();
+ FlushFutures();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call).Times(1);
- // Test a non-empty descriptor
- {
- wgpu::RequiredLimits limits = {};
- limits.limits.maxStorageTexturesPerShaderStage = 5;
+ FlushCallbacks();
+ });
+}
- std::vector<wgpu::FeatureName> features = {wgpu::FeatureName::TextureCompressionETC2,
- wgpu::FeatureName::TextureCompressionASTC};
+// Test that a null DeviceDescriptor is passed from the client to the server as an empty one.
+TEST_P(WireAdapterTests, RequestDeviceNullDescriptor) {
+ AdapterRequestDevice(adapter, nullptr);
- wgpu::DeviceDescriptor desc = {};
- desc.label = "hello device";
- desc.requiredLimits = &limits;
- desc.requiredFeatureCount = features.size();
- desc.requiredFeatures = features.data();
+ EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+ .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
+ EXPECT_EQ(apiDesc->label, nullptr);
+ EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
+ EXPECT_EQ(apiDesc->requiredLimits, nullptr);
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ // Call the callback so the test doesn't wait indefinitely.
+ api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+ nullptr);
+ })));
+ FlushClient();
+ FlushFutures();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call).Times(1);
- EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
- .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
- EXPECT_STREQ(apiDesc->label, desc.label);
-
- ASSERT_EQ(apiDesc->requiredFeatureCount, features.size());
- for (uint32_t i = 0; i < features.size(); ++i) {
- EXPECT_EQ(apiDesc->requiredFeatures[i],
- static_cast<WGPUFeatureName>(features[i]));
- }
-
- ASSERT_NE(apiDesc->requiredLimits, nullptr);
- EXPECT_EQ(apiDesc->requiredLimits->nextInChain, nullptr);
- EXPECT_EQ(apiDesc->requiredLimits->limits.maxStorageTexturesPerShaderStage,
- limits.limits.maxStorageTexturesPerShaderStage);
- })));
- FlushClient();
- }
-
- // Delete the adapter now, or it'll call the mock callback after it's deleted.
- adapter = nullptr;
+ FlushCallbacks();
+ });
}
static void DeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void* userdata) {}
// Test that the DeviceDescriptor is not allowed to pass a device lost callback from the client to
// the server.
-TEST_F(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) {
+ int userdata = 1337;
wgpu::DeviceDescriptor desc = {};
desc.deviceLostCallback = DeviceLostCallback;
- desc.deviceLostUserdata = userdata;
+ desc.deviceLostUserdata = &userdata;
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ AdapterRequestDevice(adapter, &desc);
EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
.WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
@@ -187,18 +190,22 @@
// The callback should not be passed through to the server.
ASSERT_EQ(apiDesc->deviceLostCallback, nullptr);
ASSERT_EQ(apiDesc->deviceLostUserdata, nullptr);
+
+ // Call the callback so the test doesn't wait indefinitely.
+ api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+ nullptr);
})));
FlushClient();
+ FlushFutures();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call).Times(1);
- // Delete the adapter now, or it'll call the mock callback after it's deleted.
- adapter = nullptr;
+ FlushCallbacks();
+ });
}
// Test that RequestDevice forwards the device information to the client.
-TEST_F(WireAdapterTests, RequestDeviceSuccess) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceSuccess) {
wgpu::SupportedLimits fakeLimits = {};
fakeLimits.limits.maxTextureDimension1D = 433;
fakeLimits.limits.maxVertexAttributes = 1243;
@@ -209,14 +216,13 @@
};
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ AdapterRequestDevice(adapter, &desc, this);
// Expect the server to receive the message. Then, mock a fake reply.
WGPUDevice apiDevice = api.GetNewDevice();
// The backend device should not be known by the wire server.
EXPECT_FALSE(GetWireServer()->IsDeviceKnown(apiDevice));
- wgpu::Device device;
EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
.WillOnce(InvokeWithoutArgs([&] {
// Set on device creation to forward callbacks to the client.
@@ -251,29 +257,35 @@
// After the callback is called, the backend device is now known by the server.
EXPECT_TRUE(GetWireServer()->IsDeviceKnown(apiDevice));
}));
+
FlushClient();
+ FlushFutures();
+ wgpu::Device device;
// Expect the callback in the client and all the device information to match.
- EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
- .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
- device = wgpu::Device::Acquire(cDevice);
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+ .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
+ device = wgpu::Device::Acquire(cDevice);
- wgpu::SupportedLimits limits;
- EXPECT_TRUE(device.GetLimits(&limits));
- EXPECT_EQ(limits.limits.maxTextureDimension1D, fakeLimits.limits.maxTextureDimension1D);
- EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
+ wgpu::SupportedLimits limits;
+ EXPECT_TRUE(device.GetLimits(&limits));
+ EXPECT_EQ(limits.limits.maxTextureDimension1D,
+ fakeLimits.limits.maxTextureDimension1D);
+ EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
- std::vector<wgpu::FeatureName> features;
- features.resize(device.EnumerateFeatures(nullptr));
- ASSERT_EQ(features.size(), fakeFeatures.size());
- EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
+ std::vector<wgpu::FeatureName> features;
+ features.resize(device.EnumerateFeatures(nullptr));
+ ASSERT_EQ(features.size(), fakeFeatures.size());
+ EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
- std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
- for (wgpu::FeatureName feature : features) {
- EXPECT_EQ(featureSet.erase(feature), 1u);
- }
- })));
- FlushServer();
+ std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
+ for (wgpu::FeatureName feature : features) {
+ EXPECT_EQ(featureSet.erase(feature), 1u);
+ }
+ })));
+ FlushCallbacks();
+ });
// Test that callbacks can propagate from server to client.
MockCallback<WGPUErrorCallback> errorCb;
@@ -283,7 +295,7 @@
EXPECT_CALL(errorCb, Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
.Times(1);
- FlushServer();
+ FlushCallbacks();
device = nullptr;
// Cleared when the device is destroyed.
@@ -301,10 +313,7 @@
// Test that features requested that the implementation supports, but not the
// wire reject the callback.
-TEST_F(WireAdapterTests, RequestFeatureUnsupportedByWire) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestFeatureUnsupportedByWire) {
std::initializer_list<wgpu::FeatureName> fakeFeatures = {
// Some value that is not a valid feature
static_cast<wgpu::FeatureName>(-2),
@@ -312,7 +321,7 @@
};
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ AdapterRequestDevice(adapter, &desc, this);
// Expect the server to receive the message. Then, mock a fake reply.
// The reply contains features that the device implementation supports, but the
@@ -341,19 +350,19 @@
apiDevice, nullptr);
}));
FlushClient();
+ FlushFutures();
// Expect an error callback since the feature is not supported.
- EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
- FlushServer();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
+ FlushCallbacks();
+ });
}
// Test that RequestDevice errors forward to the client.
-TEST_F(WireAdapterTests, RequestDeviceError) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceError) {
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ AdapterRequestDevice(adapter, &desc, this);
// Expect the server to receive the message. Then, mock an error.
EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
@@ -362,38 +371,98 @@
"Request device failed");
}));
FlushClient();
+ FlushFutures();
// Expect the callback in the client.
- EXPECT_CALL(cb,
- Call(WGPURequestDeviceStatus_Error, nullptr, StrEq("Request device failed"), this))
- .Times(1);
- FlushServer();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr,
+ StrEq("Request device failed"), this))
+ .Times(1);
+ FlushCallbacks();
+ });
}
-// Test that RequestDevice receives unknown status if the adapter is deleted
+// Test that RequestDevice can complete successfully even if the adapter is deleted
// before the callback happens.
-TEST_F(WireAdapterTests, RequestDeviceAdapterDestroyedBeforeCallback) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceAdapterDestroyedBeforeCallback) {
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
-
- EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+ AdapterRequestDevice(adapter, &desc, this);
adapter = nullptr;
+
+ wgpu::SupportedLimits fakeLimits = {};
+ fakeLimits.limits.maxTextureDimension1D = 433;
+ fakeLimits.limits.maxVertexAttributes = 1243;
+
+ std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+ wgpu::FeatureName::Depth32FloatStencil8,
+ wgpu::FeatureName::TextureCompressionBC,
+ };
+
+ // Mock a reply from the server.
+ WGPUDevice apiDevice = api.GetNewDevice();
+ EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+ .WillOnce(InvokeWithoutArgs([&] {
+ // Set on device creation to forward callbacks to the client.
+ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
+ .Times(1);
+ EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
+ EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull()))
+ .Times(1);
+
+ EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
+ .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+ *reinterpret_cast<wgpu::SupportedLimits*>(limits) = fakeLimits;
+ return true;
+ })));
+
+ EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
+ .WillOnce(Return(fakeFeatures.size()));
+
+ EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
+ .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+ for (wgpu::FeatureName feature : fakeFeatures) {
+ *(features++) = static_cast<WGPUFeatureName>(feature);
+ }
+ return fakeFeatures.size();
+ })));
+
+ api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
+ apiDevice, nullptr);
+ }));
+ EXPECT_CALL(api, AdapterRelease(apiAdapter));
+ FlushClient();
+ FlushFutures();
+
+ wgpu::Device device;
+ // Expect the callback in the client.
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+ .WillOnce(WithArg<1>(
+ Invoke([&](WGPUDevice cDevice) { device = wgpu::Device::Acquire(cDevice); })));
+ FlushCallbacks();
+ });
+
+ device = nullptr;
+ // Cleared when the device is destroyed.
+ EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
+ EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
+ EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1);
+ EXPECT_CALL(api, DeviceRelease(apiDevice));
+ FlushClient();
}
// Test that RequestDevice receives unknown status if the wire is disconnected
// before the callback happens.
-TEST_F(WireAdapterTests, RequestDeviceWireDisconnectedBeforeCallback) {
- MockCallback<WGPURequestDeviceCallback> cb;
- auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceWireDisconnectedBeforeCallback) {
wgpu::DeviceDescriptor desc = {};
- adapter.RequestDevice(&desc, cb.Callback(), userdata);
+ AdapterRequestDevice(adapter, &desc, this);
- EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
- GetWireClient()->Disconnect();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this))
+ .Times(1);
+
+ GetWireClient()->Disconnect();
+ });
}
} // anonymous namespace
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 3a85769..fabaffc 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -27,10 +27,76 @@
#include "dawn/wire/client/Adapter.h"
+#include <memory>
+#include <string>
+
#include "dawn/common/Log.h"
#include "dawn/wire/client/Client.h"
namespace dawn::wire::client {
+namespace {
+
+class RequestDeviceEvent : public TrackedEvent {
+ public:
+ static constexpr EventType kType = EventType::RequestDevice;
+
+ RequestDeviceEvent(const WGPURequestDeviceCallbackInfo& callbackInfo, Device* device)
+ : TrackedEvent(callbackInfo.mode),
+ mCallback(callbackInfo.callback),
+ mUserdata(callbackInfo.userdata),
+ mDevice(device) {}
+
+ EventType GetType() override { return kType; }
+
+ void ReadyHook(WGPURequestDeviceStatus status,
+ const char* message,
+ const WGPUSupportedLimits* limits,
+ uint32_t featuresCount,
+ const WGPUFeatureName* features) {
+ DAWN_ASSERT(mDevice != nullptr);
+ mStatus = status;
+ if (message != nullptr) {
+ mMessage = message;
+ }
+ if (status == WGPURequestDeviceStatus_Success) {
+ mDevice->SetLimits(limits);
+ mDevice->SetFeatures(features, featuresCount);
+ }
+ }
+
+ private:
+ void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
+ if (completionType == EventCompletionType::Shutdown) {
+ mStatus = WGPURequestDeviceStatus_Unknown;
+ mMessage = "GPU connection lost";
+ }
+ if (mStatus != WGPURequestDeviceStatus_Success && mDevice != nullptr) {
+ // If there was an error, we may need to reclaim the device allocation, otherwise the
+ // device is returned to the user who owns it.
+ mDevice->GetClient()->Free(mDevice);
+ mDevice = nullptr;
+ }
+ if (mCallback) {
+ mCallback(mStatus, ToAPI(mDevice), mMessage ? mMessage->c_str() : nullptr, mUserdata);
+ }
+ }
+
+ WGPURequestDeviceCallback mCallback;
+ void* mUserdata;
+
+ // Note that the message is optional because we want to return nullptr when it wasn't set
+ // instead of a pointer to an empty string.
+ WGPURequestDeviceStatus mStatus;
+ std::optional<std::string> mMessage;
+
+ // The device is created when we call RequestDevice(F). It is guaranteed to be alive
+ // throughout the duration of a RequestDeviceEvent because the Event essentially takes
+ // ownership of it until either an error occurs at which point the Event cleans it up, or it
+ // returns the device to the user who then takes ownership as the Event goes away.
+ Device* mDevice = nullptr;
+};
+
+} // anonymous namespace
Adapter::~Adapter() {
mRequestDeviceRequests.CloseAll([](RequestDeviceData* request) {
@@ -155,16 +221,22 @@
void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata) {
- Client* client = GetClient();
- if (client->IsDisconnected()) {
- callback(WGPURequestDeviceStatus_Error, nullptr, "GPU connection lost", userdata);
- return;
- }
+ WGPURequestDeviceCallbackInfo callbackInfo = {};
+ callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
+ callbackInfo.callback = callback;
+ callbackInfo.userdata = userdata;
+ RequestDeviceF(descriptor, callbackInfo);
+}
- // The descriptor is passed so that the deviceLostCallback can be tracked client-side and called
- // when the device is lost.
+WGPUFuture Adapter::RequestDeviceF(const WGPUDeviceDescriptor* descriptor,
+ const WGPURequestDeviceCallbackInfo& callbackInfo) {
+ Client* client = GetClient();
Device* device = client->Make<Device>(GetEventManagerHandle(), descriptor);
- uint64_t serial = mRequestDeviceRequests.Add({callback, device->GetWireId(), userdata});
+ auto [futureIDInternal, tracked] =
+ GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(callbackInfo, device));
+ if (!tracked) {
+ return {futureIDInternal};
+ }
// Ensure the device lost callback isn't serialized as part of the command, as it cannot be
// passed between processes.
@@ -177,55 +249,25 @@
AdapterRequestDeviceCmd cmd;
cmd.adapterId = GetWireId();
- cmd.requestSerial = serial;
+ cmd.eventManagerHandle = GetEventManagerHandle();
+ cmd.future = {futureIDInternal};
cmd.deviceObjectHandle = device->GetWireHandle();
cmd.descriptor = &wireDescriptor;
client->SerializeCommand(cmd);
+ return {futureIDInternal};
}
-bool Client::DoAdapterRequestDeviceCallback(Adapter* adapter,
- uint64_t requestSerial,
+bool Client::DoAdapterRequestDeviceCallback(ObjectHandle eventManager,
+ WGPUFuture future,
WGPURequestDeviceStatus status,
const char* message,
const WGPUSupportedLimits* limits,
uint32_t featuresCount,
const WGPUFeatureName* features) {
- // May have been deleted or recreated so this isn't an error.
- if (adapter == nullptr) {
- return true;
- }
- return adapter->OnRequestDeviceCallback(requestSerial, status, message, limits, featuresCount,
- features);
-}
-
-bool Adapter::OnRequestDeviceCallback(uint64_t requestSerial,
- WGPURequestDeviceStatus status,
- const char* message,
- const WGPUSupportedLimits* limits,
- uint32_t featuresCount,
- const WGPUFeatureName* features) {
- RequestDeviceData request;
- if (!mRequestDeviceRequests.Acquire(requestSerial, &request)) {
- return false;
- }
-
- Client* client = GetClient();
- Device* device = client->Get<Device>(request.deviceObjectId);
-
- // If the return status is a failure we should give a null device to the callback and
- // free the allocation.
- if (status != WGPURequestDeviceStatus_Success) {
- client->Free(device);
- request.callback(status, nullptr, message, request.userdata);
- return true;
- }
-
- device->SetLimits(limits);
- device->SetFeatures(features, featuresCount);
-
- request.callback(status, ToAPI(device), message, request.userdata);
- return true;
+ return GetEventManager(eventManager)
+ .SetFutureReady<RequestDeviceEvent>(future.id, status, message, limits,
+ featuresCount, features) == WireResult::Success;
}
WGPUInstance Adapter::GetInstance() const {
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index b625698..79021e9 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -57,13 +57,8 @@
void RequestDevice(const WGPUDeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata);
-
- bool OnRequestDeviceCallback(uint64_t requestSerial,
- WGPURequestDeviceStatus status,
- const char* message,
- const WGPUSupportedLimits* limits,
- uint32_t featuresCount,
- const WGPUFeatureName* features);
+ WGPUFuture RequestDeviceF(const WGPUDeviceDescriptor* descriptor,
+ const WGPURequestDeviceCallbackInfo& callbackInfo);
// Unimplementable. Only availale in dawn_native.
WGPUInstance GetInstance() const;
diff --git a/src/dawn/wire/client/EventManager.h b/src/dawn/wire/client/EventManager.h
index 6577f9e..071b825 100644
--- a/src/dawn/wire/client/EventManager.h
+++ b/src/dawn/wire/client/EventManager.h
@@ -48,6 +48,7 @@
enum class EventType {
MapAsync,
RequestAdapter,
+ RequestDevice,
WorkDone,
};
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index 96f1523..f78f2d3 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -146,7 +146,6 @@
struct RequestAdapterUserdata : CallbackUserdata {
using CallbackUserdata::CallbackUserdata;
- ObjectHandle instance;
ObjectHandle eventManager;
WGPUFuture future;
ObjectId adapterObjectId;
@@ -155,8 +154,8 @@
struct RequestDeviceUserdata : CallbackUserdata {
using CallbackUserdata::CallbackUserdata;
- ObjectHandle adapter;
- uint64_t requestSerial;
+ ObjectHandle eventManager;
+ WGPUFuture future;
ObjectId deviceObjectId;
};
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 4a5626e..a95e78c 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -33,15 +33,16 @@
namespace dawn::wire::server {
WireResult Server::DoAdapterRequestDevice(Known<WGPUAdapter> adapter,
- uint64_t requestSerial,
+ ObjectHandle eventManager,
+ WGPUFuture future,
ObjectHandle deviceHandle,
const WGPUDeviceDescriptor* descriptor) {
Known<WGPUDevice> device;
WIRE_TRY(DeviceObjects().Allocate(&device, deviceHandle, AllocationState::Reserved));
auto userdata = MakeUserdata<RequestDeviceUserdata>();
- userdata->adapter = adapter.AsHandle();
- userdata->requestSerial = requestSerial;
+ userdata->eventManager = eventManager;
+ userdata->future = future;
userdata->deviceObjectId = device.id;
mProcs.adapterRequestDevice(adapter->handle, descriptor,
@@ -55,8 +56,8 @@
WGPUDevice device,
const char* message) {
ReturnAdapterRequestDeviceCallbackCmd cmd = {};
- cmd.adapter = data->adapter;
- cmd.requestSerial = data->requestSerial;
+ cmd.eventManager = data->eventManager;
+ cmd.future = data->future;
cmd.status = status;
cmd.message = message;
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 660c339..24a69a2 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -42,7 +42,6 @@
WIRE_TRY(AdapterObjects().Allocate(&adapter, adapterHandle, AllocationState::Reserved));
auto userdata = MakeUserdata<RequestAdapterUserdata>();
- userdata->instance = instance.AsHandle();
userdata->eventManager = eventManager;
userdata->future = future;
userdata->adapterObjectId = adapter.id;