// 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/GPUAdapter.h" #include #include #include "src/dawn/node/binding/Errors.h" #include "src/dawn/node/binding/Flags.h" #include "src/dawn/node/binding/GPUDevice.h" #include "src/dawn/node/binding/GPUSupportedLimits.h" namespace { // TODO(amaiorano): Move to utility header std::vector Split(const std::string& s, char delim) { if (s.empty()) { return {}; } std::vector result; const size_t lastIndex = s.length() - 1; size_t startIndex = 0; size_t i = startIndex; while (i <= lastIndex) { if (s[i] == delim) { auto token = s.substr(startIndex, i - startIndex); if (!token.empty()) { // Discard empty tokens result.push_back(token); } startIndex = i + 1; } else if (i == lastIndex) { auto token = s.substr(startIndex, i - startIndex + 1); if (!token.empty()) { // Discard empty tokens result.push_back(token); } } ++i; } return result; } } // namespace #define FOR_EACH_LIMIT(X) \ X(maxTextureDimension1D) \ X(maxTextureDimension2D) \ X(maxTextureDimension3D) \ X(maxTextureArrayLayers) \ X(maxBindGroups) \ X(maxDynamicUniformBuffersPerPipelineLayout) \ X(maxDynamicStorageBuffersPerPipelineLayout) \ X(maxSampledTexturesPerShaderStage) \ X(maxSamplersPerShaderStage) \ X(maxStorageBuffersPerShaderStage) \ X(maxStorageTexturesPerShaderStage) \ X(maxUniformBuffersPerShaderStage) \ X(maxUniformBufferBindingSize) \ X(maxStorageBufferBindingSize) \ X(minUniformBufferOffsetAlignment) \ X(minStorageBufferOffsetAlignment) \ X(maxVertexBuffers) \ X(maxVertexAttributes) \ X(maxVertexBufferArrayStride) \ X(maxInterStageShaderComponents) \ X(maxComputeWorkgroupStorageSize) \ X(maxComputeInvocationsPerWorkgroup) \ X(maxComputeWorkgroupSizeX) \ X(maxComputeWorkgroupSizeY) \ X(maxComputeWorkgroupSizeZ) \ X(maxComputeWorkgroupsPerDimension) namespace wgpu::binding { namespace { //////////////////////////////////////////////////////////////////////////////// // wgpu::binding::::Features // Implements interop::GPUSupportedFeatures //////////////////////////////////////////////////////////////////////////////// class Features : public interop::GPUSupportedFeatures { public: explicit Features(WGPUDeviceProperties properties) { if (properties.depth24UnormStencil8) { enabled_.emplace(interop::GPUFeatureName::kDepth24UnormStencil8); } if (properties.depth32FloatStencil8) { enabled_.emplace(interop::GPUFeatureName::kDepth32FloatStencil8); } if (properties.timestampQuery) { enabled_.emplace(interop::GPUFeatureName::kTimestampQuery); } if (properties.textureCompressionBC) { enabled_.emplace(interop::GPUFeatureName::kTextureCompressionBc); } if (properties.textureCompressionETC2) { enabled_.emplace(interop::GPUFeatureName::kTextureCompressionEtc2); } if (properties.textureCompressionASTC) { enabled_.emplace(interop::GPUFeatureName::kTextureCompressionAstc); } if (properties.timestampQuery) { enabled_.emplace(interop::GPUFeatureName::kTimestampQuery); } // TODO(dawn:1123) add support for these extensions when possible. // wgpu::interop::GPUFeatureName::kIndirectFirstInstance // wgpu::interop::GPUFeatureName::kDepthClipControl } bool has(interop::GPUFeatureName feature) { return enabled_.count(feature) != 0; } // interop::GPUSupportedFeatures compliance bool has(Napi::Env, std::string name) override { interop::GPUFeatureName feature; if (interop::Converter::FromString(name, feature)) { return has(feature); } return false; } std::vector keys(Napi::Env) override { std::vector out; out.reserve(enabled_.size()); for (auto feature : enabled_) { out.push_back(interop::Converter::ToString(feature)); } return out; } private: std::unordered_set enabled_; }; } // namespace //////////////////////////////////////////////////////////////////////////////// // wgpu::bindings::GPUAdapter // TODO(crbug.com/dawn/1133): This is a stub implementation. Properly implement. //////////////////////////////////////////////////////////////////////////////// GPUAdapter::GPUAdapter(dawn::native::Adapter a, const Flags& flags) : adapter_(a), flags_(flags) { wgpu::AdapterProperties props; adapter_.GetProperties(&props); name_ = props.name; } // TODO(dawn:1133): Avoid the extra copy by making the generator make a virtual method with const // std::string& std::string GPUAdapter::getName(Napi::Env) { return name_; } interop::Interface GPUAdapter::getFeatures(Napi::Env env) { return interop::GPUSupportedFeatures::Create(env, adapter_.GetAdapterProperties()); } interop::Interface GPUAdapter::getLimits(Napi::Env env) { WGPUSupportedLimits limits{}; if (!adapter_.GetLimits(&limits)) { Napi::Error::New(env, "failed to get adapter limits").ThrowAsJavaScriptException(); } wgpu::SupportedLimits wgpuLimits{}; #define COPY_LIMIT(LIMIT) wgpuLimits.limits.LIMIT = limits.limits.LIMIT; FOR_EACH_LIMIT(COPY_LIMIT) #undef COPY_LIMIT return interop::GPUSupportedLimits::Create(env, wgpuLimits); } bool GPUAdapter::getIsFallbackAdapter(Napi::Env) { UNIMPLEMENTED(); } interop::Promise> GPUAdapter::requestDevice( Napi::Env env, interop::GPUDeviceDescriptor descriptor) { wgpu::DeviceDescriptor desc{}; // TODO(crbug.com/dawn/1133): Fill in. interop::Promise> promise(env, PROMISE_INFO); std::vector requiredFeatures; // See src/dawn/native/Features.cpp for enum <-> string mappings. for (auto required : descriptor.requiredFeatures) { switch (required) { case interop::GPUFeatureName::kTextureCompressionBc: requiredFeatures.emplace_back(wgpu::FeatureName::TextureCompressionBC); continue; case interop::GPUFeatureName::kTextureCompressionEtc2: requiredFeatures.emplace_back(wgpu::FeatureName::TextureCompressionETC2); continue; case interop::GPUFeatureName::kTextureCompressionAstc: requiredFeatures.emplace_back(wgpu::FeatureName::TextureCompressionASTC); continue; case interop::GPUFeatureName::kTimestampQuery: requiredFeatures.emplace_back(wgpu::FeatureName::TimestampQuery); continue; case interop::GPUFeatureName::kDepth24UnormStencil8: requiredFeatures.emplace_back(wgpu::FeatureName::Depth24UnormStencil8); continue; case interop::GPUFeatureName::kDepth32FloatStencil8: requiredFeatures.emplace_back(wgpu::FeatureName::Depth32FloatStencil8); continue; case interop::GPUFeatureName::kDepthClipControl: case interop::GPUFeatureName::kIndirectFirstInstance: case interop::GPUFeatureName::kShaderF16: // TODO(dawn:1123) Add support for these extensions when possible. continue; } UNIMPLEMENTED("required: ", required); } wgpu::RequiredLimits limits; #define COPY_LIMIT(LIMIT) \ if (descriptor.requiredLimits.count(#LIMIT)) { \ limits.limits.LIMIT = descriptor.requiredLimits[#LIMIT]; \ descriptor.requiredLimits.erase(#LIMIT); \ } FOR_EACH_LIMIT(COPY_LIMIT) #undef COPY_LIMIT for (auto [key, _] : descriptor.requiredLimits) { promise.Reject(binding::Errors::OperationError(env, "Unknown limit \"" + key + "\"")); return promise; } // Propogate enabled/disabled dawn features // Note: DawnDeviceTogglesDescriptor::forceEnabledToggles and forceDisabledToggles are // vectors of 'const char*', so we make sure the parsed strings survive the CreateDevice() // call by storing them on the stack. std::vector enabledToggles; std::vector disabledToggles; std::vector forceEnabledToggles; std::vector forceDisabledToggles; if (auto values = flags_.Get("enable-dawn-features")) { enabledToggles = Split(*values, ','); for (auto& t : enabledToggles) { forceEnabledToggles.emplace_back(t.c_str()); } } if (auto values = flags_.Get("disable-dawn-features")) { disabledToggles = Split(*values, ','); for (auto& t : disabledToggles) { forceDisabledToggles.emplace_back(t.c_str()); } } desc.requiredFeaturesCount = requiredFeatures.size(); desc.requiredFeatures = requiredFeatures.data(); desc.requiredLimits = &limits; DawnTogglesDeviceDescriptor togglesDesc = {}; desc.nextInChain = &togglesDesc; togglesDesc.forceEnabledTogglesCount = forceEnabledToggles.size(); togglesDesc.forceEnabledToggles = forceEnabledToggles.data(); togglesDesc.forceDisabledTogglesCount = forceDisabledToggles.size(); togglesDesc.forceDisabledToggles = forceDisabledToggles.data(); auto wgpu_device = adapter_.CreateDevice(&desc); if (wgpu_device) { promise.Resolve(interop::GPUDevice::Create(env, env, wgpu_device)); } else { promise.Reject(binding::Errors::OperationError(env, "failed to create device")); } return promise; } } // namespace wgpu::binding