blob: e858213f77be2f1519a088cff80833a53d70d2ad [file] [log] [blame]
// 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