Add basic or stub implementations of upstream instance/adapter APIs

Adds upstream instance/adapter APIs. In dawn_native, the basic APIs
to get limits and properties are implemented, but requestAdapter and
requestDevice are not. In dawn_wire, nothing is implemented, but the
stub definitions are put in place, as well the mechanism to inject
WGPUInstance into the wire.

There is a lifetime concern with WGPUInstance and WGPUAdapter on the
wire in that we need to ensure that the client cannot free the
instance or adapter while they are in use. In the near term, this is
not a problem because Chromium will always hold ownership of the
instance and adapters outside of the wire - i.e. it won't inject and
then release ownership.

Bug: dawn:160, dawn:689
Change-Id: Id904272983f23babc9177bc163d78c4fa1044da0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/71520
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2021-12-14 23:22:46 +00:00 committed by Dawn LUCI CQ
parent 0e6f443359
commit 5397f9f9d0
27 changed files with 420 additions and 23 deletions

View File

@ -49,16 +49,14 @@
"request adapter options": {
"category": "structure",
"extensible": "in",
"tags": ["upstream"],
"members": [
{"name": "compatible surface", "type": "surface"},
{"name": "power preference", "type": "power preference", "tags": ["upstream"]},
{"name": "force fallback adapter", "type": "bool", "tags": ["upstream"]}
{"name": "compatible surface", "type": "surface", "optional": true},
{"name": "power preference", "type": "power preference", "default": "undefined"},
{"name": "force fallback adapter", "type": "bool", "default": "false"}
]
},
"request adapter status": {
"category": "enum",
"tags": ["upstream"],
"emscripten_no_enum_table": true,
"values": [
{"value": 0, "name": "success"},
@ -69,29 +67,25 @@
},
"request adapter callback": {
"category": "function pointer",
"tags": ["upstream"],
"args": [
{"name": "status", "type": "request adapter status"},
{"name": "adapter", "type": "adapter"},
{"name": "message", "type": "char", "annotation": "const*"},
{"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},
"adapter": {
"category": "object",
"tags": ["upstream"],
"methods": [
{
"name": "get limits",
"returns": "bool",
"tags": ["upstream"],
"args": [
{"name": "limits", "type": "supported limits", "annotation": "*"}
]
},
{
"name": "get properties",
"tags": ["upstream"],
"args": [
{"name": "properties", "type": "adapter properties", "annotation": "*"}
]
@ -99,7 +93,6 @@
{
"name": "has feature",
"returns": "bool",
"tags": ["upstream"],
"args": [
{"name": "feature", "type": "feature name"}
]
@ -139,13 +132,11 @@
"device descriptor": {
"category": "structure",
"extensible": "in",
"tags": ["upstream"],
"_TODO": "Add requiredFeatures and requiredLimits support",
"members": [
{"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
{"name": "required features count", "type": "uint32_t"},
{"name": "required features", "type": "feature name", "annotation": "const*", "length": "required features count"},
{"name": "required limits", "type": "required limits", "annotation": "const*"}
{"name": "required features count", "type": "uint32_t", "default": 0},
{"name": "required features", "type": "feature name", "annotation": "const*", "length": "required features count", "default": "nullptr"},
{"name": "required limits", "type": "required limits", "annotation": "const*", "optional": true}
]
},
"address mode": {
@ -1295,7 +1286,6 @@
},
"feature name": {
"category": "enum",
"tags": ["upstream"],
"values": [
{"value": 0, "name": "undefined", "jsrepr": "undefined"},
{"value": 1, "name": "depth clip control", "tags": ["upstream"]},
@ -1369,7 +1359,6 @@
},
{
"name": "request adapter",
"tags": ["upstream"],
"args": [
{"name": "options", "type": "request adapter options", "annotation": "const*"},
{"name": "callback", "type": "request adapter callback"},
@ -1475,7 +1464,6 @@
},
"power preference": {
"category": "enum",
"tags": ["upstream"],
"values": [
{"value": 0, "name": "undefined"},
{"value": 1, "name": "low power"},
@ -1994,7 +1982,7 @@
"args": [
{"name": "status", "type": "request device status"},
{"name": "device", "type": "device"},
{"name": "message", "type": "char", "annotation": "const*"},
{"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},

View File

@ -148,6 +148,10 @@
"SurfaceDescriptorFromWindowsSwapChainPanel"
],
"client_side_commands": [
"AdapterGetProperties",
"AdapterGetLimits",
"AdapterHasFeature",
"AdapterRequestDevice",
"BufferMapAsync",
"BufferGetConstMappedRange",
"BufferGetMappedRange",
@ -159,6 +163,7 @@
"DeviceSetDeviceLostCallback",
"DeviceSetUncapturedErrorCallback",
"DeviceSetLoggingCallback",
"InstanceRequestAdapter",
"ShaderModuleGetCompilationInfo",
"QueueOnSubmittedWorkDone",
"QueueWriteBuffer",
@ -173,8 +178,10 @@
"DevicePushErrorScope"
],
"client_special_objects": [
"Adapter",
"Buffer",
"Device",
"Instance",
"Queue",
"ShaderModule"
],

View File

@ -140,7 +140,7 @@ namespace {{metadata.namespace}} {
};
{% macro render_cpp_default_value(member, is_struct=True) -%}
{%- if member.annotation in ["*", "const*"] and member.optional -%}
{%- if member.annotation in ["*", "const*"] and member.optional or member.default_value == "nullptr" -%}
{{" "}}= nullptr
{%- elif member.type.category == "object" and member.optional and is_struct -%}
{{" "}}= nullptr

View File

@ -21,7 +21,7 @@
namespace dawn_native {
{% macro render_cpp_default_value(member) -%}
{%- if member.annotation in ["*", "const*"] and member.optional -%}
{%- if member.annotation in ["*", "const*"] and member.optional or member.default_value == "nullptr" -%}
{{" "}}= nullptr
{%- elif member.type.category == "object" and member.optional -%}
{{" "}}= nullptr

View File

@ -69,6 +69,29 @@ namespace dawn_native {
return {};
}
bool AdapterBase::APIGetLimits(SupportedLimits* limits) const {
return GetLimits(limits);
}
void AdapterBase::APIGetProperties(AdapterProperties* properties) const {
properties->vendorID = mPCIInfo.vendorId;
properties->deviceID = mPCIInfo.deviceId;
properties->name = mPCIInfo.name.c_str();
properties->driverDescription = mDriverDescription.c_str();
properties->adapterType = mAdapterType;
properties->backendType = mBackend;
}
bool AdapterBase::APIHasFeature(wgpu::FeatureName feature) const {
return mSupportedFeatures.IsEnabled(feature);
}
void AdapterBase::APIRequestDevice(const DeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata) {
callback(WGPURequestDeviceStatus_Error, nullptr, "Not implemented", userdata);
}
wgpu::BackendType AdapterBase::GetBackendType() const {
return mBackend;
}

View File

@ -17,6 +17,7 @@
#include "dawn_native/DawnNative.h"
#include "common/RefCounted.h"
#include "dawn_native/Error.h"
#include "dawn_native/Features.h"
#include "dawn_native/Limits.h"
@ -28,13 +29,21 @@ namespace dawn_native {
class DeviceBase;
class AdapterBase {
class AdapterBase : public RefCounted {
public:
AdapterBase(InstanceBase* instance, wgpu::BackendType backend);
virtual ~AdapterBase() = default;
MaybeError Initialize();
// WebGPU API
bool APIGetLimits(SupportedLimits* limits) const;
void APIGetProperties(AdapterProperties* properties) const;
bool APIHasFeature(wgpu::FeatureName feature) const;
void APIRequestDevice(const DeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata);
wgpu::BackendType GetBackendType() const;
wgpu::AdapterType GetAdapterType() const;
const std::string& GetDriverDescription() const;

View File

@ -85,6 +85,34 @@ namespace dawn_native {
"https://bugs.chromium.org/p/dawn/issues/detail?id=551"},
&WGPUDeviceProperties::multiPlanarFormats}}};
Feature FromAPIFeature(wgpu::FeatureName feature) {
switch (feature) {
case wgpu::FeatureName::Undefined:
return Feature::InvalidEnum;
case wgpu::FeatureName::TimestampQuery:
return Feature::TimestampQuery;
case wgpu::FeatureName::PipelineStatisticsQuery:
return Feature::PipelineStatisticsQuery;
case wgpu::FeatureName::TextureCompressionBC:
return Feature::TextureCompressionBC;
case wgpu::FeatureName::TextureCompressionETC2:
return Feature::TextureCompressionETC2;
case wgpu::FeatureName::TextureCompressionASTC:
return Feature::TextureCompressionASTC;
case wgpu::FeatureName::DepthClamping:
return Feature::DepthClamping;
case wgpu::FeatureName::Depth24UnormStencil8:
return Feature::Depth24UnormStencil8;
case wgpu::FeatureName::Depth32FloatStencil8:
return Feature::Depth32FloatStencil8;
case wgpu::FeatureName::IndirectFirstInstance:
return Feature::InvalidEnum;
}
return Feature::InvalidEnum;
}
} // anonymous namespace
void FeaturesSet::EnableFeature(Feature feature) {
@ -99,6 +127,11 @@ namespace dawn_native {
return featuresBitSet[featureIndex];
}
bool FeaturesSet::IsEnabled(wgpu::FeatureName feature) const {
Feature f = FromAPIFeature(feature);
return f != Feature::InvalidEnum && IsEnabled(f);
}
std::vector<const char*> FeaturesSet::GetEnabledFeatureNames() const {
std::vector<const char*> enabledFeatureNames(featuresBitSet.count());

View File

@ -19,6 +19,7 @@
#include <unordered_map>
#include <vector>
#include "dawn/webgpu_cpp.h"
#include "dawn_native/DawnNative.h"
namespace dawn_native {
@ -50,6 +51,7 @@ namespace dawn_native {
void EnableFeature(Feature feature);
bool IsEnabled(Feature feature) const;
bool IsEnabled(wgpu::FeatureName feature) const;
std::vector<const char*> GetEnabledFeatureNames() const;
void InitializeDeviceProperties(WGPUDeviceProperties* properties) const;
};

View File

@ -99,6 +99,12 @@ namespace dawn_native {
return true;
}
void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options,
WGPURequestAdapterCallback callback,
void* userdata) {
callback(WGPURequestAdapterStatus_Error, nullptr, "Not implemented", userdata);
}
void InstanceBase::DiscoverDefaultAdapters() {
for (wgpu::BackendType b : IterateBitSet(GetEnabledBackends())) {
EnsureBackendConnection(b);

View File

@ -45,6 +45,10 @@ namespace dawn_native {
public:
static InstanceBase* Create(const InstanceDescriptor* descriptor = nullptr);
void APIRequestAdapter(const RequestAdapterOptions* options,
WGPURequestAdapterCallback callback,
void* userdata);
void DiscoverDefaultAdapters();
bool DiscoverAdapters(const AdapterDiscoveryOptionsBase* options);

View File

@ -71,6 +71,8 @@ dawn_component("dawn_wire") {
"WireDeserializeAllocator.h",
"WireResult.h",
"WireServer.cpp",
"client/Adapter.cpp",
"client/Adapter.h",
"client/ApiObjects.h",
"client/Buffer.cpp",
"client/Buffer.h",
@ -80,6 +82,8 @@ dawn_component("dawn_wire") {
"client/ClientInlineMemoryTransferService.cpp",
"client/Device.cpp",
"client/Device.h",
"client/Instance.cpp",
"client/Instance.h",
"client/ObjectAllocator.h",
"client/Queue.cpp",
"client/Queue.h",

View File

@ -43,6 +43,8 @@ target_sources(dawn_wire PRIVATE
"WireDeserializeAllocator.h"
"WireResult.h"
"WireServer.cpp"
"client/Adapter.cpp"
"client/Adapter.h"
"client/ApiObjects.h"
"client/Buffer.cpp"
"client/Buffer.h"
@ -52,6 +54,8 @@ target_sources(dawn_wire PRIVATE
"client/ClientInlineMemoryTransferService.cpp"
"client/Device.cpp"
"client/Device.h"
"client/Instance.cpp"
"client/Instance.h"
"client/ObjectAllocator.h"
"client/Queue.cpp"
"client/Queue.h"

View File

@ -41,6 +41,10 @@ namespace dawn_wire {
return mImpl->ReserveDevice();
}
ReservedInstance WireClient::ReserveInstance() {
return mImpl->ReserveInstance();
}
void WireClient::ReclaimTextureReservation(const ReservedTexture& reservation) {
mImpl->ReclaimTextureReservation(reservation);
}
@ -53,6 +57,10 @@ namespace dawn_wire {
mImpl->ReclaimDeviceReservation(reservation);
}
void WireClient::ReclaimInstanceReservation(const ReservedInstance& reservation) {
mImpl->ReclaimInstanceReservation(reservation);
}
void WireClient::Disconnect() {
mImpl->Disconnect();
}

View File

@ -51,6 +51,10 @@ namespace dawn_wire {
return mImpl->InjectDevice(device, id, generation);
}
bool WireServer::InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation) {
return mImpl->InjectInstance(instance, id, generation);
}
WGPUDevice WireServer::GetDevice(uint32_t id, uint32_t generation) {
return mImpl->GetDevice(id, generation);
}

View File

@ -0,0 +1,37 @@
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_wire/client/Adapter.h"
namespace dawn_wire { namespace client {
bool Adapter::GetLimits(WGPUSupportedLimits* limits) const {
UNREACHABLE();
}
void Adapter::GetProperties(WGPUAdapterProperties* properties) const {
UNREACHABLE();
}
bool Adapter::HasFeature(WGPUFeatureName feature) const {
UNREACHABLE();
}
void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata) {
callback(WGPURequestDeviceStatus_Error, nullptr, "Not implemented", nullptr);
}
}} // namespace dawn_wire::client

View File

@ -0,0 +1,39 @@
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DAWNWIRE_CLIENT_ADAPTER_H_
#define DAWNWIRE_CLIENT_ADAPTER_H_
#include <dawn/webgpu.h>
#include "dawn_wire/WireClient.h"
#include "dawn_wire/client/ObjectBase.h"
namespace dawn_wire { namespace client {
class Adapter final : public ObjectBase {
public:
using ObjectBase::ObjectBase;
bool GetLimits(WGPUSupportedLimits* limits) const;
void GetProperties(WGPUAdapterProperties* properties) const;
bool HasFeature(WGPUFeatureName feature) const;
void RequestDevice(const WGPUDeviceDescriptor* descriptor,
WGPURequestDeviceCallback callback,
void* userdata);
};
}} // namespace dawn_wire::client
#endif // DAWNWIRE_CLIENT_ADAPTER_H_

View File

@ -17,8 +17,10 @@
#include "dawn_wire/client/ObjectBase.h"
#include "dawn_wire/client/Adapter.h"
#include "dawn_wire/client/Buffer.h"
#include "dawn_wire/client/Device.h"
#include "dawn_wire/client/Instance.h"
#include "dawn_wire/client/Queue.h"
#include "dawn_wire/client/ShaderModule.h"

View File

@ -118,6 +118,16 @@ namespace dawn_wire { namespace client {
return result;
}
ReservedInstance Client::ReserveInstance() {
auto* allocation = InstanceAllocator().New(this);
ReservedInstance result;
result.instance = ToAPI(allocation->object.get());
result.id = allocation->object->id;
result.generation = allocation->generation;
return result;
}
void Client::ReclaimTextureReservation(const ReservedTexture& reservation) {
TextureAllocator().Free(FromAPI(reservation.texture));
}
@ -130,6 +140,10 @@ namespace dawn_wire { namespace client {
DeviceAllocator().Free(FromAPI(reservation.device));
}
void Client::ReclaimInstanceReservation(const ReservedInstance& reservation) {
InstanceAllocator().Free(FromAPI(reservation.instance));
}
void Client::Disconnect() {
mDisconnected = true;
mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance());

View File

@ -47,10 +47,12 @@ namespace dawn_wire { namespace client {
ReservedTexture ReserveTexture(WGPUDevice device);
ReservedSwapChain ReserveSwapChain(WGPUDevice device);
ReservedDevice ReserveDevice();
ReservedInstance ReserveInstance();
void ReclaimTextureReservation(const ReservedTexture& reservation);
void ReclaimSwapChainReservation(const ReservedSwapChain& reservation);
void ReclaimDeviceReservation(const ReservedDevice& reservation);
void ReclaimInstanceReservation(const ReservedInstance& reservation);
template <typename Cmd>
void SerializeCommand(const Cmd& cmd) {

View File

@ -0,0 +1,25 @@
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_wire/client/Instance.h"
namespace dawn_wire { namespace client {
void Instance::RequestAdapter(const WGPURequestAdapterOptions* options,
WGPURequestAdapterCallback callback,
void* userdata) {
callback(WGPURequestAdapterStatus_Error, nullptr, "Not implemented", nullptr);
}
}} // namespace dawn_wire::client

View File

@ -0,0 +1,36 @@
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DAWNWIRE_CLIENT_INSTANCE_H_
#define DAWNWIRE_CLIENT_INSTANCE_H_
#include <dawn/webgpu.h>
#include "dawn_wire/WireClient.h"
#include "dawn_wire/client/ObjectBase.h"
namespace dawn_wire { namespace client {
class Instance final : public ObjectBase {
public:
using ObjectBase::ObjectBase;
void RequestAdapter(const WGPURequestAdapterOptions* options,
WGPURequestAdapterCallback callback,
void* userdata);
};
}} // namespace dawn_wire::client
#endif // DAWNWIRE_CLIENT_INSTANCE_H_

View File

@ -153,6 +153,24 @@ namespace dawn_wire { namespace server {
return true;
}
bool Server::InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation) {
ASSERT(instance != nullptr);
ObjectData<WGPUInstance>* data = InstanceObjects().Allocate(id);
if (data == nullptr) {
return false;
}
data->handle = instance;
data->generation = generation;
data->state = AllocationState::Allocated;
// The instance is externally owned so it shouldn't be destroyed when we receive a destroy
// message from the client. Add a reference to counterbalance the eventual release.
mProcs.instanceReference(instance);
return true;
}
WGPUDevice Server::GetDevice(uint32_t id, uint32_t generation) {
ObjectData<WGPUDevice>* data = DeviceObjects().Get(id);
if (data == nullptr || data->generation != generation) {

View File

@ -171,6 +171,8 @@ namespace dawn_wire { namespace server {
bool InjectDevice(WGPUDevice device, uint32_t id, uint32_t generation);
bool InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation);
WGPUDevice GetDevice(uint32_t id, uint32_t generation);
template <typename T,

View File

@ -52,6 +52,12 @@ namespace dawn_wire {
uint32_t generation;
};
struct ReservedInstance {
WGPUInstance instance;
uint32_t id;
uint32_t generation;
};
struct DAWN_WIRE_EXPORT WireClientDescriptor {
CommandSerializer* serializer;
client::MemoryTransferService* memoryTransferService = nullptr;
@ -68,10 +74,12 @@ namespace dawn_wire {
ReservedTexture ReserveTexture(WGPUDevice device);
ReservedSwapChain ReserveSwapChain(WGPUDevice device);
ReservedDevice ReserveDevice();
ReservedInstance ReserveInstance();
void ReclaimTextureReservation(const ReservedTexture& reservation);
void ReclaimSwapChainReservation(const ReservedSwapChain& reservation);
void ReclaimDeviceReservation(const ReservedDevice& reservation);
void ReclaimInstanceReservation(const ReservedInstance& reservation);
// Disconnects the client.
// Commands allocated after this point will not be sent.

View File

@ -55,6 +55,8 @@ namespace dawn_wire {
bool InjectDevice(WGPUDevice device, uint32_t id, uint32_t generation);
bool InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation);
// Look up a device by (id, generation) pair. Returns nullptr if the generation
// has expired or the id is not found.
// The Wire does not have destroy hooks to allow an embedder to observe when an object

View File

@ -281,6 +281,7 @@ test("dawn_unittests") {
"unittests/wire/WireErrorCallbackTests.cpp",
"unittests/wire/WireExtensionTests.cpp",
"unittests/wire/WireInjectDeviceTests.cpp",
"unittests/wire/WireInjectInstanceTests.cpp",
"unittests/wire/WireInjectSwapChainTests.cpp",
"unittests/wire/WireInjectTextureTests.cpp",
"unittests/wire/WireMemoryTransferServiceTests.cpp",

View File

@ -0,0 +1,119 @@
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tests/unittests/wire/WireTest.h"
#include "dawn_wire/WireClient.h"
#include "dawn_wire/WireServer.h"
using namespace testing;
using namespace dawn_wire;
namespace {
class WireInjectInstanceTests : public WireTest {
public:
WireInjectInstanceTests() {
}
~WireInjectInstanceTests() override = default;
};
// Test that reserving and injecting an instance makes calls on the client object forward to the
// server object correctly.
TEST_F(WireInjectInstanceTests, CallAfterReserveInject) {
ReservedInstance reservation = GetWireClient()->ReserveInstance();
WGPUInstance serverInstance = api.GetNewInstance();
EXPECT_CALL(api, InstanceReference(serverInstance));
ASSERT_TRUE(
GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
WGPUSurfaceDescriptor surfaceDesc = {};
wgpuInstanceCreateSurface(reservation.instance, &surfaceDesc);
WGPUSurface serverSurface = api.GetNewSurface();
EXPECT_CALL(api, InstanceCreateSurface(serverInstance, NotNull())).WillOnce(Return(serverSurface));
FlushClient();
}
// Test that reserve correctly returns different IDs each time.
TEST_F(WireInjectInstanceTests, ReserveDifferentIDs) {
ReservedInstance reservation1 = GetWireClient()->ReserveInstance();
ReservedInstance reservation2 = GetWireClient()->ReserveInstance();
ASSERT_NE(reservation1.id, reservation2.id);
ASSERT_NE(reservation1.instance, reservation2.instance);
}
// Test that injecting the same id fails.
TEST_F(WireInjectInstanceTests, InjectExistingID) {
ReservedInstance reservation = GetWireClient()->ReserveInstance();
WGPUInstance serverInstance = api.GetNewInstance();
EXPECT_CALL(api, InstanceReference(serverInstance));
ASSERT_TRUE(
GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
// ID already in use, call fails.
ASSERT_FALSE(
GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
}
// Test that the server only borrows the instance and does a single reference-release
TEST_F(WireInjectInstanceTests, InjectedInstanceLifetime) {
ReservedInstance reservation = GetWireClient()->ReserveInstance();
// Injecting the instance adds a reference
WGPUInstance serverInstance = api.GetNewInstance();
EXPECT_CALL(api, InstanceReference(serverInstance));
ASSERT_TRUE(
GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
// Releasing the instance removes a single reference.
wgpuInstanceRelease(reservation.instance);
EXPECT_CALL(api, InstanceRelease(serverInstance));
FlushClient();
// Deleting the server doesn't release a second reference.
DeleteServer();
Mock::VerifyAndClearExpectations(&api);
}
// Test that a device reservation can be reclaimed. This is necessary to
// avoid leaking ObjectIDs for reservations that are never injected.
TEST_F(WireInjectInstanceTests, ReclaimInstanceReservation) {
// Test that doing a reservation and full release is an error.
{
ReservedInstance reservation = GetWireClient()->ReserveInstance();
wgpuInstanceRelease(reservation.instance);
FlushClient(false);
}
// Test that doing a reservation and then reclaiming it recycles the ID.
{
ReservedInstance reservation1 = GetWireClient()->ReserveInstance();
GetWireClient()->ReclaimInstanceReservation(reservation1);
ReservedInstance reservation2 = GetWireClient()->ReserveInstance();
// The ID is the same, but the generation is still different.
ASSERT_EQ(reservation1.id, reservation2.id);
ASSERT_NE(reservation1.generation, reservation2.generation);
// No errors should occur.
FlushClient();
}
}
} // anonymous namespace