250 lines
7.5 KiB
C++
250 lines
7.5 KiB
C++
// 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 "src/dawn/node/binding/GPU.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "src/dawn/node/binding/GPUAdapter.h"
|
|
|
|
#if defined(_WIN32)
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
namespace {
|
|
std::string GetEnvVar(const char* varName) {
|
|
#if defined(_WIN32)
|
|
// Use _dupenv_s to avoid unsafe warnings about std::getenv
|
|
char* value = nullptr;
|
|
_dupenv_s(&value, nullptr, varName);
|
|
if (value) {
|
|
std::string result = value;
|
|
free(value);
|
|
return result;
|
|
}
|
|
return "";
|
|
#else
|
|
if (auto* val = std::getenv(varName)) {
|
|
return val;
|
|
}
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
void SetDllDir(const char* dir) {
|
|
(void)dir;
|
|
#if defined(_WIN32)
|
|
::SetDllDirectory(dir);
|
|
#endif
|
|
}
|
|
|
|
struct BackendInfo {
|
|
const char* const name;
|
|
const char* const alias; // may be nullptr
|
|
wgpu::BackendType const backend;
|
|
};
|
|
|
|
constexpr BackendInfo kBackends[] = {
|
|
{"null", nullptr, wgpu::BackendType::Null}, //
|
|
{"webgpu", nullptr, wgpu::BackendType::WebGPU}, //
|
|
{"d3d11", nullptr, wgpu::BackendType::D3D11}, //
|
|
{"d3d12", "d3d", wgpu::BackendType::D3D12}, //
|
|
{"metal", nullptr, wgpu::BackendType::Metal}, //
|
|
{"vulkan", "vk", wgpu::BackendType::Vulkan}, //
|
|
{"opengl", "gl", wgpu::BackendType::OpenGL}, //
|
|
{"opengles", "gles", wgpu::BackendType::OpenGLES}, //
|
|
};
|
|
|
|
std::optional<wgpu::BackendType> ParseBackend(std::string_view name) {
|
|
for (auto& info : kBackends) {
|
|
if (info.name == name || (info.alias && info.alias == name)) {
|
|
return info.backend;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
const char* BackendName(wgpu::BackendType backend) {
|
|
for (auto& info : kBackends) {
|
|
if (info.backend == backend) {
|
|
return info.name;
|
|
}
|
|
}
|
|
return "<unknown>";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace wgpu::binding {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// wgpu::bindings::GPU
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
GPU::GPU(Flags flags) : flags_(std::move(flags)) {
|
|
if (auto validate = flags_.Get("validate"); validate == "1" || validate == "true") {
|
|
instance_.EnableBackendValidation(true);
|
|
instance_.SetBackendValidationLevel(dawn::native::BackendValidationLevel::Full);
|
|
}
|
|
|
|
// Setting the DllDir changes where we load adapter DLLs from (e.g. d3dcompiler_47.dll)
|
|
if (auto dir = flags_.Get("dlldir")) {
|
|
SetDllDir(dir->c_str());
|
|
}
|
|
instance_.DiscoverDefaultPhysicalDevices();
|
|
}
|
|
|
|
interop::Promise<std::optional<interop::Interface<interop::GPUAdapter>>> GPU::requestAdapter(
|
|
Napi::Env env,
|
|
interop::GPURequestAdapterOptions options) {
|
|
auto promise =
|
|
interop::Promise<std::optional<interop::Interface<interop::GPUAdapter>>>(env, PROMISE_INFO);
|
|
|
|
if (options.forceFallbackAdapter) {
|
|
// Software adapters are not currently supported.
|
|
promise.Resolve({});
|
|
return promise;
|
|
}
|
|
|
|
auto adapters = instance_.GetAdapters();
|
|
if (adapters.empty()) {
|
|
promise.Resolve({});
|
|
return promise;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
constexpr auto defaultBackendType = wgpu::BackendType::D3D12;
|
|
#elif defined(__linux__)
|
|
constexpr auto defaultBackendType = wgpu::BackendType::Vulkan;
|
|
#elif defined(__APPLE__)
|
|
constexpr auto defaultBackendType = wgpu::BackendType::Metal;
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
|
|
// Check for backend override from env var / flag
|
|
std::string forceBackend;
|
|
if (auto f = flags_.Get("backend")) {
|
|
forceBackend = *f;
|
|
} else if (std::string envVar = GetEnvVar("DAWNNODE_BACKEND"); !envVar.empty()) {
|
|
forceBackend = envVar;
|
|
}
|
|
|
|
// Check for specific adapter name
|
|
std::string adapterName;
|
|
if (auto f = flags_.Get("adapter")) {
|
|
adapterName = *f;
|
|
}
|
|
|
|
std::transform(forceBackend.begin(), forceBackend.end(), forceBackend.begin(),
|
|
[](char c) { return std::tolower(c); });
|
|
|
|
auto targetBackendType = defaultBackendType;
|
|
if (!forceBackend.empty()) {
|
|
if (auto parsed = ParseBackend(forceBackend)) {
|
|
targetBackendType = parsed.value();
|
|
} else {
|
|
std::stringstream msg;
|
|
msg << "unrecognised backend '" + forceBackend + "'" << std::endl
|
|
<< "Possible backends: ";
|
|
for (auto& info : kBackends) {
|
|
if (&info != &kBackends[0]) {
|
|
msg << ", ";
|
|
}
|
|
msg << "'" << info.name << "'";
|
|
}
|
|
promise.Reject(msg.str());
|
|
return promise;
|
|
}
|
|
}
|
|
|
|
dawn::native::Adapter* adapter = nullptr;
|
|
for (auto& a : adapters) {
|
|
wgpu::AdapterProperties props;
|
|
a.GetProperties(&props);
|
|
if (props.backendType != targetBackendType) {
|
|
continue;
|
|
}
|
|
if (!adapterName.empty() && props.name &&
|
|
std::string(props.name).find(adapterName) == std::string::npos) {
|
|
continue;
|
|
}
|
|
adapter = &a;
|
|
break;
|
|
}
|
|
|
|
if (!adapter) {
|
|
std::stringstream msg;
|
|
if (!forceBackend.empty() || adapterName.empty()) {
|
|
msg << "no adapter ";
|
|
if (!forceBackend.empty()) {
|
|
msg << "with backend '" << forceBackend << "'";
|
|
if (!adapterName.empty()) {
|
|
msg << " and name '" << adapterName << "'";
|
|
}
|
|
} else {
|
|
msg << " with name '" << adapterName << "'";
|
|
}
|
|
msg << " found";
|
|
} else {
|
|
msg << "no suitable backends found";
|
|
}
|
|
msg << std::endl << "Available adapters:";
|
|
for (auto& a : adapters) {
|
|
wgpu::AdapterProperties props;
|
|
a.GetProperties(&props);
|
|
msg << std::endl
|
|
<< " * backend: '" << BackendName(props.backendType) << "', name: '" << props.name
|
|
<< "'";
|
|
}
|
|
promise.Reject(msg.str());
|
|
return promise;
|
|
}
|
|
|
|
if (flags_.Get("verbose")) {
|
|
wgpu::AdapterProperties props;
|
|
adapter->GetProperties(&props);
|
|
printf("using GPU adapter: %s\n", props.name);
|
|
}
|
|
|
|
auto gpuAdapter = GPUAdapter::Create<GPUAdapter>(env, *adapter, flags_);
|
|
promise.Resolve(std::optional<interop::Interface<interop::GPUAdapter>>(gpuAdapter));
|
|
return promise;
|
|
}
|
|
|
|
interop::GPUTextureFormat GPU::getPreferredCanvasFormat(Napi::Env) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
interop::Interface<interop::WGSLLanguageFeatures> GPU::getWgslLanguageFeatures(Napi::Env env) {
|
|
// TODO(crbug.com/dawn/1777)
|
|
struct Features : public interop::WGSLLanguageFeatures {
|
|
~Features() = default;
|
|
bool has(Napi::Env, std::string) {
|
|
UNIMPLEMENTED();
|
|
return false;
|
|
}
|
|
std::vector<std::string> keys(Napi::Env) {
|
|
UNIMPLEMENTED();
|
|
return {};
|
|
}
|
|
};
|
|
return interop::WGSLLanguageFeatures::Create<Features>(env);
|
|
}
|
|
|
|
} // namespace wgpu::binding
|