dawn_native: Add Instance and Adapters

New objects are introduced to control what happens before device
creation in dawn_native:
 - Instance: a connection from the application to dawn_native that is
used for dependency injection and to discover adapters.
 - Adapters: represents the possibility of device creation for a specific
(GPU, backend) pair.
 - BackendConnection: an internal object that standardizes the interface
between the frontend and backends.

The BackendConnection interface is implemented for the Null backend and
stubbed out in other backends. This allows this change to port the
ValidationTests to use the new Instance and Adapters concept and deal
with other backends later.

BUG=dawn:29

Change-Id: I19719a9342b4af091accc0c02fb6b9697eadde7b
Reviewed-on: https://dawn-review.googlesource.com/c/3500
Reviewed-by: Stephen White <senorblanco@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2019-01-04 10:30:40 +00:00 committed by Commit Bot service account
parent e9212dfe30
commit ac67fec1c9
17 changed files with 546 additions and 12 deletions

View File

@ -359,6 +359,10 @@ source_set("libdawn_native_sources") {
sources = get_target_outputs(":libdawn_native_utils_gen")
sources += [
"src/dawn_native/Adapter.cpp",
"src/dawn_native/Adapter.h",
"src/dawn_native/BackendConnection.cpp",
"src/dawn_native/BackendConnection.h",
"src/dawn_native/BindGroup.cpp",
"src/dawn_native/BindGroup.h",
"src/dawn_native/BindGroupLayout.cpp",
@ -393,6 +397,8 @@ source_set("libdawn_native_sources") {
"src/dawn_native/Forward.h",
"src/dawn_native/InputState.cpp",
"src/dawn_native/InputState.h",
"src/dawn_native/Instance.cpp",
"src/dawn_native/Instance.h",
"src/dawn_native/ObjectBase.cpp",
"src/dawn_native/ObjectBase.h",
"src/dawn_native/PassResourceUsage.h",

View File

@ -0,0 +1,54 @@
// Copyright 2018 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_native/Adapter.h"
#include "dawn_native/Instance.h"
namespace dawn_native {
AdapterBase::AdapterBase(InstanceBase* instance, BackendType backend)
: mInstance(instance), mBackend(backend) {
}
BackendType AdapterBase::GetBackendType() const {
return mBackend;
}
const PCIInfo& AdapterBase::GetPCIInfo() const {
return mPCIInfo;
}
InstanceBase* AdapterBase::GetInstance() const {
return mInstance;
}
DeviceBase* AdapterBase::CreateDevice() {
DeviceBase* result = nullptr;
if (mInstance->ConsumedError(CreateDeviceInternal(&result))) {
return nullptr;
}
return result;
}
MaybeError AdapterBase::CreateDeviceInternal(DeviceBase** result) {
// TODO(cwallez@chromium.org): This will eventually have validation that the device
// descriptor is valid and is a subset what's allowed on this adapter.
DAWN_TRY_ASSIGN(*result, CreateDeviceImpl());
return {};
}
} // namespace dawn_native

49
src/dawn_native/Adapter.h Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2018 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 DAWNNATIVE_ADAPTER_H_
#define DAWNNATIVE_ADAPTER_H_
#include "dawn_native/DawnNative.h"
#include "dawn_native/Error.h"
namespace dawn_native {
class DeviceBase;
class AdapterBase {
public:
AdapterBase(InstanceBase* instance, BackendType backend);
virtual ~AdapterBase() = default;
BackendType GetBackendType() const;
const PCIInfo& GetPCIInfo() const;
InstanceBase* GetInstance() const;
DeviceBase* CreateDevice();
private:
virtual ResultOrError<DeviceBase*> CreateDeviceImpl() = 0;
MaybeError CreateDeviceInternal(DeviceBase** result);
InstanceBase* mInstance = nullptr;
BackendType mBackend;
PCIInfo mPCIInfo;
};
} // namespace dawn_native
#endif // DAWNNATIVE_ADAPTER_H_

View File

@ -0,0 +1,31 @@
// Copyright 2018 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_native/BackendConnection.h"
namespace dawn_native {
BackendConnection::BackendConnection(InstanceBase* instance, BackendType type)
: mInstance(instance), mType(type) {
}
BackendType BackendConnection::GetType() const {
return mType;
}
InstanceBase* BackendConnection::GetInstance() const {
return mInstance;
}
} // namespace dawn_native

View File

@ -0,0 +1,44 @@
// Copyright 2018 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 DAWNNATIVE_BACKENDCONNECTION_H_
#define DAWNNATIVE_BACKENDCONNECTION_H_
#include "dawn_native/Adapter.h"
#include "dawn_native/DawnNative.h"
namespace dawn_native {
// An common interface for all backends. Mostly used to create adapters for a particular
// backend.
class BackendConnection {
public:
BackendConnection(InstanceBase* instance, BackendType type);
virtual ~BackendConnection() = default;
BackendType GetType() const;
InstanceBase* GetInstance() const;
// Returns all the adapters for the system that can be created by the backend, without extra
// options (such as debug adapters, custom driver libraries, etc.)
virtual std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() = 0;
private:
InstanceBase* mInstance = nullptr;
BackendType mType;
};
} // namespace dawn_native
#endif // DAWNNATIVE_BACKENDCONNECTION_H_

View File

@ -14,6 +14,7 @@
#include "dawn_native/DawnNative.h"
#include "dawn_native/Device.h"
#include "dawn_native/Instance.h"
// Contains the entry-points into dawn_native
@ -30,4 +31,50 @@ namespace dawn_native {
return deviceBase->GetPCIInfo();
}
// Adapter
Adapter::Adapter() = default;
Adapter::Adapter(AdapterBase* impl) : mImpl(impl) {
}
Adapter::~Adapter() {
mImpl = nullptr;
}
BackendType Adapter::GetBackendType() const {
return mImpl->GetBackendType();
}
const PCIInfo& Adapter::GetPCIInfo() const {
return mImpl->GetPCIInfo();
}
dawnDevice Adapter::CreateDevice() {
return reinterpret_cast<dawnDevice>(mImpl->CreateDevice());
}
// Instance
Instance::Instance() : mImpl(new InstanceBase()) {
}
Instance::~Instance() {
delete mImpl;
mImpl = nullptr;
}
void Instance::DiscoverDefaultAdapters() {
mImpl->DiscoverDefaultAdapters();
}
std::vector<Adapter> Instance::GetAdapters() const {
// Adapters are owned by mImpl so it is safe to return non RAII pointers to them
std::vector<Adapter> adapters;
for (const std::unique_ptr<AdapterBase>& adapter : mImpl->GetAdapters()) {
adapters.push_back({adapter.get()});
}
return adapters;
}
} // namespace dawn_native

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DAWNNATIVE_DEVICEBASE_H_
#define DAWNNATIVE_DEVICEBASE_H_
#ifndef DAWNNATIVE_DEVICE_H_
#define DAWNNATIVE_DEVICE_H_
#include "common/Serial.h"
#include "dawn_native/Error.h"
@ -170,4 +170,4 @@ namespace dawn_native {
} // namespace dawn_native
#endif // DAWNNATIVE_DEVICEBASE_H_
#endif // DAWNNATIVE_DEVICE_H_

View File

@ -0,0 +1,119 @@
// Copyright 2018 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_native/Instance.h"
#include "common/Assert.h"
#include "dawn_native/ErrorData.h"
#include <iostream>
namespace dawn_native {
// Forward definitions of each backend's "Connect" function that creates new BackendConnection.
// Conditionally compiled declarations are used to avoid using static constructors instead.
#if defined(DAWN_ENABLE_BACKEND_D3D12)
namespace d3d12 {
BackendConnection* Connect(InstanceBase* instance);
}
#endif // defined(DAWN_ENABLE_BACKEND_D3D12)
#if defined(DAWN_ENABLE_BACKEND_METAL)
namespace metal {
BackendConnection* Connect(InstanceBase* instance);
}
#endif // defined(DAWN_ENABLE_BACKEND_METAL)
#if defined(DAWN_ENABLE_BACKEND_NULL)
namespace null {
BackendConnection* Connect(InstanceBase* instance);
}
#endif // defined(DAWN_ENABLE_BACKEND_NULL)
#if defined(DAWN_ENABLE_BACKEND_OPENGL)
namespace opengl {
BackendConnection* Connect(InstanceBase* instance);
}
#endif // defined(DAWN_ENABLE_BACKEND_OPENGL)
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
namespace vulkan {
BackendConnection* Connect(InstanceBase* instance);
}
#endif // defined(DAWN_ENABLE_BACKEND_VULKAN)
// InstanceBase
void InstanceBase::DiscoverDefaultAdapters() {
EnsureBackendConnections();
// Query and merge all default adapters for all backends
for (std::unique_ptr<BackendConnection>& backend : mBackends) {
std::vector<std::unique_ptr<AdapterBase>> backendAdapters =
backend->DiscoverDefaultAdapters();
for (std::unique_ptr<AdapterBase>& adapter : backendAdapters) {
ASSERT(adapter->GetBackendType() == backend->GetType());
ASSERT(adapter->GetInstance() == this);
mAdapters.push_back(std::move(adapter));
}
}
}
const std::vector<std::unique_ptr<AdapterBase>>& InstanceBase::GetAdapters() const {
return mAdapters;
}
void InstanceBase::EnsureBackendConnections() {
if (mBackendsConnected) {
return;
}
auto Register = [this](BackendConnection* connection, BackendType expectedType) {
if (connection != nullptr) {
ASSERT(connection->GetType() == expectedType);
ASSERT(connection->GetInstance() == this);
mBackends.push_back(std::unique_ptr<BackendConnection>(connection));
}
};
#if defined(DAWN_ENABLE_BACKEND_D3D12)
Register(d3d12::Connect(this), BackendType::D3D12);
#endif // defined(DAWN_ENABLE_BACKEND_D3D12)
#if defined(DAWN_ENABLE_BACKEND_METAL)
Register(metal::Connect(this), BackendType::Metal);
#endif // defined(DAWN_ENABLE_BACKEND_METAL)
#if defined(DAWN_ENABLE_BACKEND_NULL)
Register(null::Connect(this), BackendType::Null);
#endif // defined(DAWN_ENABLE_BACKEND_NULL)
#if defined(DAWN_ENABLE_BACKEND_OPENGL)
Register(opengl::Connect(this), BackendType::OpenGL);
#endif // defined(DAWN_ENABLE_BACKEND_OPENGL)
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
Register(vulkan::Connect(this), BackendType::Vulkan);
#endif // defined(DAWN_ENABLE_BACKEND_VULKAN)
mBackendsConnected = true;
}
bool InstanceBase::ConsumedError(MaybeError maybeError) {
if (maybeError.IsError()) {
ErrorData* error = maybeError.AcquireError();
ASSERT(error != nullptr);
std::cout << error->GetMessage() << std::endl;
delete error;
return true;
}
return false;
}
} // namespace dawn_native

View File

@ -0,0 +1,53 @@
// Copyright 2018 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 DAWNNATIVE_INSTANCE_H_
#define DAWNNATIVE_INSTANCE_H_
#include "dawn_native/Adapter.h"
#include "dawn_native/BackendConnection.h"
#include <memory>
#include <vector>
namespace dawn_native {
// This is called InstanceBase for consistency across the frontend, even if the backends don't
// specialize this class.
class InstanceBase final {
public:
InstanceBase() = default;
~InstanceBase() = default;
InstanceBase(const InstanceBase& other) = delete;
InstanceBase& operator=(const InstanceBase& other) = delete;
void DiscoverDefaultAdapters();
const std::vector<std::unique_ptr<AdapterBase>>& GetAdapters() const;
// Used to handle error that happen up to device creation.
bool ConsumedError(MaybeError maybeError);
private:
void EnsureBackendConnections();
bool mBackendsConnected = false;
std::vector<std::unique_ptr<BackendConnection>> mBackends;
std::vector<std::unique_ptr<AdapterBase>> mAdapters;
};
} // namespace dawn_native
#endif // DAWNNATIVE_INSTANCE_H_

View File

@ -16,6 +16,7 @@
#include "common/Assert.h"
#include "common/SwapChainUtils.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/D3D12Backend.h"
#include "dawn_native/d3d12/BindGroupD3D12.h"
#include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
@ -66,6 +67,10 @@ namespace dawn_native { namespace d3d12 {
ASSERT(SUCCEEDED(hr));
}
BackendConnection* Connect(InstanceBase* instance) {
return nullptr;
}
namespace {
ComPtr<IDXGIFactory4> CreateFactory(const PlatformFunctions* functions) {
ComPtr<IDXGIFactory4> factory;

View File

@ -14,6 +14,7 @@
#include "dawn_native/metal/DeviceMTL.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/BindGroup.h"
#include "dawn_native/BindGroupLayout.h"
#include "dawn_native/MetalBackend.h"
@ -125,6 +126,10 @@ namespace dawn_native { namespace metal {
return reinterpret_cast<dawnDevice>(new Device(metalDevice));
}
BackendConnection* Connect(InstanceBase* instance) {
return nullptr;
}
// Device
Device::Device(id<MTLDevice> mtlDevice)

View File

@ -14,6 +14,7 @@
#include "dawn_native/null/NullBackend.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/Commands.h"
#include "dawn_native/NullBackend.h"
@ -21,10 +22,42 @@
namespace dawn_native { namespace null {
// Implementation of pre-Device objects: the null adapter, null backend connection and Connect()
class Adapter : public AdapterBase {
public:
Adapter(InstanceBase* instance) : AdapterBase(instance, BackendType::Null) {
}
virtual ~Adapter() = default;
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override {
return {new Device};
}
};
class NullBackend : public BackendConnection {
public:
NullBackend(InstanceBase* instance) : BackendConnection(instance, BackendType::Null) {
}
std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() override {
// There is always a single Null adapter because it is purely CPU based and doesn't
// depend on the system.
std::vector<std::unique_ptr<AdapterBase>> adapters;
adapters.push_back(std::make_unique<Adapter>(GetInstance()));
return adapters;
}
};
dawnDevice CreateDevice() {
return reinterpret_cast<dawnDevice>(new Device);
}
BackendConnection* Connect(InstanceBase* instance) {
return new NullBackend(instance);
}
// Device
Device::Device() {

View File

@ -14,6 +14,7 @@
#include "dawn_native/opengl/DeviceGL.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/BindGroup.h"
#include "dawn_native/BindGroupLayout.h"
#include "dawn_native/OpenGLBackend.h"
@ -42,6 +43,10 @@ namespace dawn_native { namespace opengl {
return reinterpret_cast<dawnDevice>(new Device);
}
BackendConnection* Connect(InstanceBase* instance) {
return nullptr;
}
// Device
Device::Device() {

View File

@ -16,6 +16,7 @@
#include "common/Platform.h"
#include "common/SwapChainUtils.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/Commands.h"
#include "dawn_native/ErrorData.h"
#include "dawn_native/VulkanBackend.h"
@ -80,6 +81,10 @@ namespace dawn_native { namespace vulkan {
return static_cast<dawnTextureFormat>(impl->GetPreferredFormat());
}
BackendConnection* Connect(InstanceBase* instance) {
return nullptr;
}
// Device
Device::Device(const std::vector<const char*>& requiredInstanceExtensions) {

View File

@ -19,14 +19,75 @@
#include <dawn_native/dawn_native_export.h>
#include <string>
#include <vector>
namespace dawn_native {
struct PCIInfo {
uint32_t deviceId = 0;
uint32_t vendorId = 0;
std::string name;
};
enum class BackendType {
D3D12,
Metal,
Null,
OpenGL,
Vulkan,
};
class InstanceBase;
class AdapterBase;
// An adapter is an object that represent on possibility of creating devices in the system.
// Most of the time it will represent a combination of a physical GPU and an API. Not that the
// same GPU can be represented by multiple adapters but on different APIs.
//
// The underlying Dawn adapter is owned by the Dawn instance so this class is not RAII but just
// a reference to an underlying adapter.
class DAWN_NATIVE_EXPORT Adapter {
public:
Adapter();
Adapter(AdapterBase* impl);
~Adapter();
BackendType GetBackendType() const;
const PCIInfo& GetPCIInfo() const;
// Create a device on this adapter, note that the interface will change to include at least
// a device descriptor and a pointer to backend specific options.
// On an error, nullptr is returned.
dawnDevice CreateDevice();
private:
AdapterBase* mImpl = nullptr;
};
// Represents a connection to dawn_native and is used for dependency injection, discovering
// system adapters and injecting custom adapters (like a Swiftshader Vulkan adapter).
//
// This is an RAII class for Dawn instances and also controls the lifetime of all adapters
// for this instance.
class DAWN_NATIVE_EXPORT Instance {
public:
Instance();
~Instance();
Instance(const Instance& other) = delete;
Instance& operator=(const Instance& other) = delete;
// Gather all adapters in the system that can be accessed with no special options. These
// adapters will later be returned by GetAdapters.
void DiscoverDefaultAdapters();
// Returns all the adapters that the instance knows about.
std::vector<Adapter> GetAdapters() const;
private:
InstanceBase* mImpl = nullptr;
};
// Backend-agnostic API for dawn_native
DAWN_NATIVE_EXPORT dawnProcTable GetProcs();

View File

@ -14,22 +14,33 @@
#include "tests/unittests/validation/ValidationTest.h"
#include "common/Assert.h"
#include "dawn/dawn.h"
#include "dawn_native/DawnNative.h"
#include "dawn_native/NullBackend.h"
namespace dawn_native {
namespace null {
void Init(dawnProcTable* procs, dawnDevice* device);
}
}
ValidationTest::ValidationTest() {
dawnProcTable procs = dawn_native::GetProcs();
dawnDevice cDevice = dawn_native::null::CreateDevice();
mInstance = std::make_unique<dawn_native::Instance>();
mInstance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = mInstance->GetAdapters();
// Validation tests run against the null backend, find the corresponding adapter
bool foundNullAdapter = false;
dawn_native::Adapter nullAdapter;
for (auto adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Null) {
nullAdapter = adapter;
foundNullAdapter = true;
break;
}
}
ASSERT(foundNullAdapter);
device = dawn::Device::Acquire(nullAdapter.CreateDevice());
dawnProcTable procs = dawn_native::GetProcs();
dawnSetProcs(&procs);
device = dawn::Device::Acquire(cDevice);
device.SetErrorCallback(ValidationTest::OnDeviceError, static_cast<dawnCallbackUserdata>(reinterpret_cast<uintptr_t>(this)));
}

View File

@ -19,6 +19,10 @@
#include "dawn/dawncpp.h"
#include "dawn/dawncpp_traits.h"
namespace dawn_native {
class Instance;
};
#define ASSERT_DEVICE_ERROR(statement) \
StartExpectDeviceError(); \
statement; \
@ -66,6 +70,8 @@ class ValidationTest : public testing::Test {
dawn::Device device;
private:
std::unique_ptr<dawn_native::Instance> mInstance;
static void OnDeviceError(const char* message, dawnCallbackUserdata userdata);
std::string mDeviceErrorMessage;
bool mExpectError = false;