1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "src/dawn_node/binding/GPUAdapter.h" 16 17 #include <unordered_set> 18 19 #include "src/dawn_node/binding/Flags.h" 20 #include "src/dawn_node/binding/GPUDevice.h" 21 #include "src/dawn_node/binding/GPUSupportedLimits.h" 22 23 namespace { 24 // TODO(amaiorano): Move to utility header Split(const std::string & s,char delim)25 std::vector<std::string> Split(const std::string& s, char delim) { 26 if (s.empty()) 27 return {}; 28 29 std::vector<std::string> result; 30 const size_t lastIndex = s.length() - 1; 31 size_t startIndex = 0; 32 size_t i = startIndex; 33 34 while (i <= lastIndex) { 35 if (s[i] == delim) { 36 auto token = s.substr(startIndex, i - startIndex); 37 if (!token.empty()) // Discard empty tokens 38 result.push_back(token); 39 startIndex = i + 1; 40 } else if (i == lastIndex) { 41 auto token = s.substr(startIndex, i - startIndex + 1); 42 if (!token.empty()) // Discard empty tokens 43 result.push_back(token); 44 } 45 ++i; 46 } 47 return result; 48 } 49 } // namespace 50 51 namespace wgpu { namespace binding { 52 53 namespace { 54 55 //////////////////////////////////////////////////////////////////////////////// 56 // wgpu::binding::<anon>::Features 57 // Implements interop::GPUSupportedFeatures 58 //////////////////////////////////////////////////////////////////////////////// 59 class Features : public interop::GPUSupportedFeatures { 60 public: Features(WGPUDeviceProperties properties)61 Features(WGPUDeviceProperties properties) { 62 if (properties.depthClamping) { 63 enabled_.emplace(interop::GPUFeatureName::kDepthClamping); 64 } 65 if (properties.pipelineStatisticsQuery) { 66 enabled_.emplace(interop::GPUFeatureName::kPipelineStatisticsQuery); 67 } 68 if (properties.textureCompressionBC) { 69 enabled_.emplace(interop::GPUFeatureName::kTextureCompressionBc); 70 } 71 if (properties.timestampQuery) { 72 enabled_.emplace(interop::GPUFeatureName::kTimestampQuery); 73 } 74 75 // TODO(crbug.com/dawn/1130) 76 // interop::GPUFeatureName::kDepth24UnormStencil8: 77 // interop::GPUFeatureName::kDepth32FloatStencil8: 78 } 79 has(interop::GPUFeatureName feature)80 bool has(interop::GPUFeatureName feature) { 81 return enabled_.count(feature) != 0; 82 } 83 84 // interop::GPUSupportedFeatures compliance has(Napi::Env,std::string name)85 bool has(Napi::Env, std::string name) override { 86 interop::GPUFeatureName feature; 87 if (interop::Converter<interop::GPUFeatureName>::FromString(name, feature)) { 88 return has(feature); 89 } 90 return false; 91 } keys(Napi::Env)92 std::vector<std::string> keys(Napi::Env) override { 93 std::vector<std::string> out; 94 out.reserve(enabled_.size()); 95 for (auto feature : enabled_) { 96 out.push_back(interop::Converter<interop::GPUFeatureName>::ToString(feature)); 97 } 98 return out; 99 } 100 101 private: 102 std::unordered_set<interop::GPUFeatureName> enabled_; 103 }; 104 105 } // namespace 106 107 //////////////////////////////////////////////////////////////////////////////// 108 // wgpu::bindings::GPUAdapter 109 // TODO(crbug.com/dawn/1133): This is a stub implementation. Properly implement. 110 //////////////////////////////////////////////////////////////////////////////// GPUAdapter(dawn_native::Adapter a,const Flags & flags)111 GPUAdapter::GPUAdapter(dawn_native::Adapter a, const Flags& flags) 112 : adapter_(a), flags_(flags) { 113 } 114 getName(Napi::Env)115 std::string GPUAdapter::getName(Napi::Env) { 116 return "dawn-adapter"; 117 } 118 getFeatures(Napi::Env env)119 interop::Interface<interop::GPUSupportedFeatures> GPUAdapter::getFeatures(Napi::Env env) { 120 return interop::GPUSupportedFeatures::Create<Features>(env, 121 adapter_.GetAdapterProperties()); 122 } 123 getLimits(Napi::Env env)124 interop::Interface<interop::GPUSupportedLimits> GPUAdapter::getLimits(Napi::Env env) { 125 WGPUSupportedLimits limits{}; 126 if (!adapter_.GetLimits(&limits)) { 127 Napi::Error::New(env, "failed to get adapter limits").ThrowAsJavaScriptException(); 128 } 129 130 wgpu::SupportedLimits wgpuLimits{}; 131 132 #define COPY_LIMIT(LIMIT) wgpuLimits.limits.LIMIT = limits.limits.LIMIT 133 COPY_LIMIT(maxTextureDimension1D); 134 COPY_LIMIT(maxTextureDimension2D); 135 COPY_LIMIT(maxTextureDimension3D); 136 COPY_LIMIT(maxTextureArrayLayers); 137 COPY_LIMIT(maxBindGroups); 138 COPY_LIMIT(maxDynamicUniformBuffersPerPipelineLayout); 139 COPY_LIMIT(maxDynamicStorageBuffersPerPipelineLayout); 140 COPY_LIMIT(maxSampledTexturesPerShaderStage); 141 COPY_LIMIT(maxSamplersPerShaderStage); 142 COPY_LIMIT(maxStorageBuffersPerShaderStage); 143 COPY_LIMIT(maxStorageTexturesPerShaderStage); 144 COPY_LIMIT(maxUniformBuffersPerShaderStage); 145 COPY_LIMIT(maxUniformBufferBindingSize); 146 COPY_LIMIT(maxStorageBufferBindingSize); 147 COPY_LIMIT(minUniformBufferOffsetAlignment); 148 COPY_LIMIT(minStorageBufferOffsetAlignment); 149 COPY_LIMIT(maxVertexBuffers); 150 COPY_LIMIT(maxVertexAttributes); 151 COPY_LIMIT(maxVertexBufferArrayStride); 152 COPY_LIMIT(maxInterStageShaderComponents); 153 COPY_LIMIT(maxComputeWorkgroupStorageSize); 154 COPY_LIMIT(maxComputeInvocationsPerWorkgroup); 155 COPY_LIMIT(maxComputeWorkgroupSizeX); 156 COPY_LIMIT(maxComputeWorkgroupSizeY); 157 COPY_LIMIT(maxComputeWorkgroupSizeZ); 158 COPY_LIMIT(maxComputeWorkgroupsPerDimension); 159 #undef COPY_LIMIT 160 161 return interop::GPUSupportedLimits::Create<GPUSupportedLimits>(env, wgpuLimits); 162 } 163 getIsFallbackAdapter(Napi::Env)164 bool GPUAdapter::getIsFallbackAdapter(Napi::Env) { 165 UNIMPLEMENTED(); 166 } 167 requestDevice(Napi::Env env,interop::GPUDeviceDescriptor descriptor)168 interop::Promise<interop::Interface<interop::GPUDevice>> GPUAdapter::requestDevice( 169 Napi::Env env, 170 interop::GPUDeviceDescriptor descriptor) { 171 dawn_native::DawnDeviceDescriptor desc{}; // TODO(crbug.com/dawn/1133): Fill in. 172 interop::Promise<interop::Interface<interop::GPUDevice>> promise(env, PROMISE_INFO); 173 174 // See src/dawn_native/Features.cpp for enum <-> string mappings. 175 for (auto required : descriptor.requiredFeatures) { 176 switch (required) { 177 case interop::GPUFeatureName::kDepthClamping: 178 desc.requiredFeatures.emplace_back("depth-clamping"); 179 continue; 180 case interop::GPUFeatureName::kPipelineStatisticsQuery: 181 desc.requiredFeatures.emplace_back("pipeline-statistics-query"); 182 continue; 183 case interop::GPUFeatureName::kTextureCompressionBc: 184 desc.requiredFeatures.emplace_back("texture-compression-bc"); 185 continue; 186 case interop::GPUFeatureName::kTimestampQuery: 187 desc.requiredFeatures.emplace_back("timestamp-query"); 188 continue; 189 case interop::GPUFeatureName::kDepth24UnormStencil8: 190 case interop::GPUFeatureName::kDepth32FloatStencil8: 191 continue; // TODO(crbug.com/dawn/1130) 192 } 193 UNIMPLEMENTED("required: ", required); 194 } 195 196 // Propogate enabled/disabled dawn features 197 // Note: DawnDeviceDescriptor::forceEnabledToggles and forceDisabledToggles are vectors of 198 // 'const char*', so we make sure the parsed strings survive the CreateDevice() call by 199 // storing them on the stack. 200 std::vector<std::string> enabledToggles; 201 std::vector<std::string> disabledToggles; 202 if (auto values = flags_.Get("enable-dawn-features")) { 203 enabledToggles = Split(*values, ','); 204 for (auto& t : enabledToggles) { 205 desc.forceEnabledToggles.emplace_back(t.c_str()); 206 } 207 } 208 if (auto values = flags_.Get("disable-dawn-features")) { 209 disabledToggles = Split(*values, ','); 210 for (auto& t : disabledToggles) { 211 desc.forceDisabledToggles.emplace_back(t.c_str()); 212 } 213 } 214 215 auto wgpu_device = adapter_.CreateDevice(&desc); 216 if (wgpu_device) { 217 promise.Resolve(interop::GPUDevice::Create<GPUDevice>(env, env, wgpu_device)); 218 } else { 219 Napi::Error::New(env, "failed to create device").ThrowAsJavaScriptException(); 220 } 221 return promise; 222 } 223 }} // namespace wgpu::binding 224