Bug 1812353 - Update GPUSupportedLimits in webgpu.webidl. r=webgpu-reviewers,webidl...
[gecko.git] / dom / webgpu / Adapter.cpp
blobd7dc3672a98c69c21770b712c250e4c936aef2f4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/BindingDeclarations.h"
7 #include "mozilla/dom/WebGPUBinding.h"
8 #include "Adapter.h"
10 #include "Device.h"
11 #include "Instance.h"
12 #include "SupportedFeatures.h"
13 #include "SupportedLimits.h"
14 #include "ipc/WebGPUChild.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/webgpu/ffi/wgpu.h"
18 namespace mozilla::webgpu {
20 bool AdapterInfo::WrapObject(JSContext* const cx,
21 JS::Handle<JSObject*> givenProto,
22 JS::MutableHandle<JSObject*> reflector) {
23 return dom::GPUAdapterInfo_Binding::Wrap(cx, this, givenProto, reflector);
26 void AdapterInfo::GetWgpuName(nsString& s) const {
27 s = mAboutSupportInfo->name;
30 uint32_t AdapterInfo::WgpuVendor() const { return mAboutSupportInfo->vendor; }
32 uint32_t AdapterInfo::WgpuDevice() const { return mAboutSupportInfo->device; }
34 void AdapterInfo::GetWgpuDeviceType(nsString& s) const {
35 switch (mAboutSupportInfo->device_type) {
36 case ffi::WGPUDeviceType_Cpu:
37 s.AssignLiteral("Cpu");
38 return;
39 case ffi::WGPUDeviceType_DiscreteGpu:
40 s.AssignLiteral("DiscreteGpu");
41 return;
42 case ffi::WGPUDeviceType_IntegratedGpu:
43 s.AssignLiteral("IntegratedGpu");
44 return;
45 case ffi::WGPUDeviceType_VirtualGpu:
46 s.AssignLiteral("VirtualGpu");
47 return;
48 case ffi::WGPUDeviceType_Other:
49 s.AssignLiteral("Other");
50 return;
51 case ffi::WGPUDeviceType_Sentinel:
52 break;
54 MOZ_CRASH("Bad `ffi::WGPUDeviceType`");
57 void AdapterInfo::GetWgpuDriver(nsString& s) const {
58 s = mAboutSupportInfo->driver;
61 void AdapterInfo::GetWgpuDriverInfo(nsString& s) const {
62 s = mAboutSupportInfo->driver_info;
65 void AdapterInfo::GetWgpuBackend(nsString& s) const {
66 switch (mAboutSupportInfo->backend) {
67 case ffi::WGPUBackend_Empty:
68 s.AssignLiteral("Empty");
69 return;
70 case ffi::WGPUBackend_Vulkan:
71 s.AssignLiteral("Vulkan");
72 return;
73 case ffi::WGPUBackend_Metal:
74 s.AssignLiteral("Metal");
75 return;
76 case ffi::WGPUBackend_Dx12:
77 s.AssignLiteral("Dx12");
78 return;
79 case ffi::WGPUBackend_Dx11:
80 s.AssignLiteral("Dx11");
81 return;
82 case ffi::WGPUBackend_Gl:
83 s.AssignLiteral("Gl");
84 return;
85 case ffi::WGPUBackend_BrowserWebGpu: // This should never happen, because
86 // we _are_ the browser.
87 case ffi::WGPUBackend_Sentinel:
88 break;
90 MOZ_CRASH("Bad `ffi::WGPUBackend`");
93 // -
95 GPU_IMPL_CYCLE_COLLECTION(Adapter, mParent, mBridge, mFeatures, mLimits)
96 GPU_IMPL_JS_WRAP(Adapter)
98 static Maybe<ffi::WGPUFeatures> ToWGPUFeatures(
99 const dom::GPUFeatureName aFeature) {
100 switch (aFeature) {
101 case dom::GPUFeatureName::Depth_clip_control:
102 return Some(WGPUFeatures_DEPTH_CLIP_CONTROL);
104 case dom::GPUFeatureName::Depth32float_stencil8:
105 return Some(WGPUFeatures_DEPTH32FLOAT_STENCIL8);
107 case dom::GPUFeatureName::Texture_compression_bc:
108 return Some(WGPUFeatures_TEXTURE_COMPRESSION_BC);
110 case dom::GPUFeatureName::Texture_compression_etc2:
111 return Some(WGPUFeatures_TEXTURE_COMPRESSION_ETC2);
113 case dom::GPUFeatureName::Texture_compression_astc:
114 return Some(WGPUFeatures_TEXTURE_COMPRESSION_ASTC);
116 case dom::GPUFeatureName::Timestamp_query:
117 return Some(WGPUFeatures_TIMESTAMP_QUERY);
119 case dom::GPUFeatureName::Indirect_first_instance:
120 return Some(WGPUFeatures_INDIRECT_FIRST_INSTANCE);
122 case dom::GPUFeatureName::Shader_f16:
123 return Some(WGPUFeatures_SHADER_F16);
125 case dom::GPUFeatureName::Rg11b10ufloat_renderable:
126 return Some(WGPUFeatures_RG11B10UFLOAT_RENDERABLE);
128 case dom::GPUFeatureName::Bgra8unorm_storage:
129 #ifdef WGPUFeatures_BGRA8UNORM_STORAGE
130 # error fix todo
131 #endif
132 return Nothing(); // TODO
134 case dom::GPUFeatureName::Float32_filterable:
135 #ifdef WGPUFeatures_FLOAT32_FILTERABLE
136 # error fix todo
137 #endif
138 return Nothing(); // TODO
140 case dom::GPUFeatureName::EndGuard_:
141 break;
143 MOZ_CRASH("Bad GPUFeatureName.");
146 static Maybe<ffi::WGPUFeatures> MakeFeatureBits(
147 const dom::Sequence<dom::GPUFeatureName>& aFeatures) {
148 ffi::WGPUFeatures bits = 0;
149 for (const auto& feature : aFeatures) {
150 const auto bit = ToWGPUFeatures(feature);
151 if (!bit) {
152 const auto featureStr = dom::GPUFeatureNameValues::GetString(feature);
153 NS_WARNING(
154 nsPrintfCString("Requested feature bit for '%s' is not implemented.",
155 featureStr.data())
156 .get());
157 return Nothing();
159 bits |= *bit;
161 return Some(bits);
164 Adapter::Adapter(Instance* const aParent, WebGPUChild* const aBridge,
165 const std::shared_ptr<ffi::WGPUAdapterInformation>& aInfo)
166 : ChildOf(aParent),
167 mBridge(aBridge),
168 mId(aInfo->id),
169 mFeatures(new SupportedFeatures(this)),
170 mLimits(new SupportedLimits(this, aInfo->limits)),
171 mInfo(aInfo) {
172 ErrorResult ignoredRv; // It's onerous to plumb this in from outside in this
173 // case, and we don't really need to.
175 static const auto FEATURE_BY_BIT = []() {
176 auto ret = std::unordered_map<ffi::WGPUFeatures, dom::GPUFeatureName>{};
178 for (const auto feature :
179 MakeEnumeratedRange(dom::GPUFeatureName::EndGuard_)) {
180 const auto bitForFeature = ToWGPUFeatures(feature);
181 if (!bitForFeature) {
182 // There are some features that don't have bits.
183 continue;
185 ret[*bitForFeature] = feature;
188 return ret;
189 }();
191 auto remainingFeatureBits = aInfo->features;
192 auto bitMask = decltype(remainingFeatureBits){0};
193 while (remainingFeatureBits) {
194 if (bitMask) {
195 bitMask <<= 1;
196 } else {
197 bitMask = 1;
199 const auto bit = remainingFeatureBits & bitMask;
200 remainingFeatureBits &= ~bitMask; // Clear bit.
201 if (!bit) {
202 continue;
205 const auto featureForBit = FEATURE_BY_BIT.find(bit);
206 if (featureForBit != FEATURE_BY_BIT.end()) {
207 mFeatures->Add(featureForBit->second, ignoredRv);
208 } else {
209 // We don't recognize that bit, but maybe it's a wpgu-native-only feature.
214 Adapter::~Adapter() { Cleanup(); }
216 void Adapter::Cleanup() {
217 if (mValid && mBridge && mBridge->CanSend()) {
218 mValid = false;
219 mBridge->SendAdapterDestroy(mId);
223 const RefPtr<SupportedFeatures>& Adapter::Features() const { return mFeatures; }
224 const RefPtr<SupportedLimits>& Adapter::Limits() const { return mLimits; }
225 bool Adapter::IsFallbackAdapter() const {
226 return mInfo->device_type == ffi::WGPUDeviceType::WGPUDeviceType_Cpu;
229 static std::string_view ToJsKey(const Limit limit) {
230 switch (limit) {
231 case Limit::MaxTextureDimension1D:
232 return "maxTextureDimension1D";
233 case Limit::MaxTextureDimension2D:
234 return "maxTextureDimension2D";
235 case Limit::MaxTextureDimension3D:
236 return "maxTextureDimension3D";
237 case Limit::MaxTextureArrayLayers:
238 return "maxTextureArrayLayers";
239 case Limit::MaxBindGroups:
240 return "maxBindGroups";
241 case Limit::MaxBindGroupsPlusVertexBuffers:
242 return "maxBindGroupsPlusVertexBuffers";
243 case Limit::MaxBindingsPerBindGroup:
244 return "maxBindingsPerBindGroup";
245 case Limit::MaxDynamicUniformBuffersPerPipelineLayout:
246 return "maxDynamicUniformBuffersPerPipelineLayout";
247 case Limit::MaxDynamicStorageBuffersPerPipelineLayout:
248 return "maxDynamicStorageBuffersPerPipelineLayout";
249 case Limit::MaxSampledTexturesPerShaderStage:
250 return "maxSampledTexturesPerShaderStage";
251 case Limit::MaxSamplersPerShaderStage:
252 return "maxSamplersPerShaderStage";
253 case Limit::MaxStorageBuffersPerShaderStage:
254 return "maxStorageBuffersPerShaderStage";
255 case Limit::MaxStorageTexturesPerShaderStage:
256 return "maxStorageTexturesPerShaderStage";
257 case Limit::MaxUniformBuffersPerShaderStage:
258 return "maxUniformBuffersPerShaderStage";
259 case Limit::MaxUniformBufferBindingSize:
260 return "maxUniformBufferBindingSize";
261 case Limit::MaxStorageBufferBindingSize:
262 return "maxStorageBufferBindingSize";
263 case Limit::MinUniformBufferOffsetAlignment:
264 return "minUniformBufferOffsetAlignment";
265 case Limit::MinStorageBufferOffsetAlignment:
266 return "minStorageBufferOffsetAlignment";
267 case Limit::MaxVertexBuffers:
268 return "maxVertexBuffers";
269 case Limit::MaxBufferSize:
270 return "maxBufferSize";
271 case Limit::MaxVertexAttributes:
272 return "maxVertexAttributes";
273 case Limit::MaxVertexBufferArrayStride:
274 return "maxVertexBufferArrayStride";
275 case Limit::MaxInterStageShaderComponents:
276 return "maxInterStageShaderComponents";
277 case Limit::MaxInterStageShaderVariables:
278 return "maxInterStageShaderVariables";
279 case Limit::MaxColorAttachments:
280 return "maxColorAttachments";
281 case Limit::MaxColorAttachmentBytesPerSample:
282 return "maxColorAttachmentBytesPerSample";
283 case Limit::MaxComputeWorkgroupStorageSize:
284 return "maxComputeWorkgroupStorageSize";
285 case Limit::MaxComputeInvocationsPerWorkgroup:
286 return "maxComputeInvocationsPerWorkgroup";
287 case Limit::MaxComputeWorkgroupSizeX:
288 return "maxComputeWorkgroupSizeX";
289 case Limit::MaxComputeWorkgroupSizeY:
290 return "maxComputeWorkgroupSizeY";
291 case Limit::MaxComputeWorkgroupSizeZ:
292 return "maxComputeWorkgroupSizeZ";
293 case Limit::MaxComputeWorkgroupsPerDimension:
294 return "maxComputeWorkgroupsPerDimension";
296 MOZ_CRASH("Bad Limit");
299 // -
300 // String helpers
302 static auto ToACString(const nsAString& s) { return NS_ConvertUTF16toUTF8(s); }
304 // -
305 // Adapter::RequestDevice
307 already_AddRefed<dom::Promise> Adapter::RequestDevice(
308 const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) {
309 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
310 if (NS_WARN_IF(aRv.Failed())) {
311 return nullptr;
314 ffi::WGPULimits deviceLimits = *mLimits->mFfi;
315 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
316 const auto defaultValue = [&]() -> double {
317 switch (limit) {
318 // clang-format off
319 case Limit::MaxTextureDimension1D: return 8192;
320 case Limit::MaxTextureDimension2D: return 8192;
321 case Limit::MaxTextureDimension3D: return 2048;
322 case Limit::MaxTextureArrayLayers: return 256;
323 case Limit::MaxBindGroups: return 4;
324 case Limit::MaxBindGroupsPlusVertexBuffers: return 24;
325 case Limit::MaxBindingsPerBindGroup: return 1000;
326 case Limit::MaxDynamicUniformBuffersPerPipelineLayout: return 8;
327 case Limit::MaxDynamicStorageBuffersPerPipelineLayout: return 4;
328 case Limit::MaxSampledTexturesPerShaderStage: return 16;
329 case Limit::MaxSamplersPerShaderStage: return 16;
330 case Limit::MaxStorageBuffersPerShaderStage: return 8;
331 case Limit::MaxStorageTexturesPerShaderStage: return 4;
332 case Limit::MaxUniformBuffersPerShaderStage: return 12;
333 case Limit::MaxUniformBufferBindingSize: return 65536;
334 case Limit::MaxStorageBufferBindingSize: return 134217728;
335 case Limit::MinUniformBufferOffsetAlignment: return 256;
336 case Limit::MinStorageBufferOffsetAlignment: return 256;
337 case Limit::MaxVertexBuffers: return 8;
338 case Limit::MaxBufferSize: return 268435456;
339 case Limit::MaxVertexAttributes: return 16;
340 case Limit::MaxVertexBufferArrayStride: return 2048;
341 case Limit::MaxInterStageShaderComponents: return 60;
342 case Limit::MaxInterStageShaderVariables: return 16;
343 case Limit::MaxColorAttachments: return 8;
344 case Limit::MaxColorAttachmentBytesPerSample: return 32;
345 case Limit::MaxComputeWorkgroupStorageSize: return 16384;
346 case Limit::MaxComputeInvocationsPerWorkgroup: return 256;
347 case Limit::MaxComputeWorkgroupSizeX: return 256;
348 case Limit::MaxComputeWorkgroupSizeY: return 256;
349 case Limit::MaxComputeWorkgroupSizeZ: return 64;
350 case Limit::MaxComputeWorkgroupsPerDimension: return 65535;
351 // clang-format on
353 MOZ_CRASH("Bad Limit");
354 }();
355 SetLimit(&deviceLimits, limit, defaultValue);
358 // -
360 [&]() { // So that we can `return;` instead of `return promise.forget();`.
361 if (!mBridge->CanSend()) {
362 promise->MaybeRejectWithInvalidStateError(
363 "WebGPUChild cannot send, must recreate Adapter");
364 return;
367 // -
368 // Validate Features
370 for (const auto requested : aDesc.mRequiredFeatures) {
371 const bool supported = mFeatures->Features().count(requested);
372 if (!supported) {
373 const auto fstr = dom::GPUFeatureNameValues::GetString(requested);
374 const auto astr = this->LabelOrId();
375 nsPrintfCString msg(
376 "requestDevice: Feature '%s' requested must be supported by "
377 "adapter %s",
378 fstr.data(), astr.get());
379 promise->MaybeRejectWithTypeError(msg);
380 return;
384 // -
385 // Validate Limits
387 if (aDesc.mRequiredLimits.WasPassed()) {
388 static const auto LIMIT_BY_JS_KEY = []() {
389 std::unordered_map<std::string_view, Limit> ret;
390 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
391 const auto jsKeyU8 = ToJsKey(limit);
392 ret[jsKeyU8] = limit;
394 return ret;
395 }();
397 for (const auto& entry : aDesc.mRequiredLimits.Value().Entries()) {
398 const auto& keyU16 = entry.mKey;
399 const nsCString keyU8 = ToACString(keyU16);
400 const auto itr = LIMIT_BY_JS_KEY.find(keyU8.get());
401 if (itr == LIMIT_BY_JS_KEY.end()) {
402 nsPrintfCString msg("requestDevice: Limit '%s' not recognized.",
403 keyU8.get());
404 promise->MaybeRejectWithOperationError(msg);
405 return;
408 const auto& limit = itr->second;
409 const auto& requestedValue = entry.mValue;
410 const auto supportedValueF64 = GetLimit(*mLimits->mFfi, limit);
411 const auto supportedValue = static_cast<uint64_t>(supportedValueF64);
412 if (StringBeginsWith(keyU8, "max"_ns)) {
413 if (requestedValue > supportedValue) {
414 nsPrintfCString msg(
415 "requestDevice: Request for limit '%s' must be <= supported "
416 "%s, was %s.",
417 keyU8.get(), std::to_string(supportedValue).c_str(),
418 std::to_string(requestedValue).c_str());
419 promise->MaybeRejectWithOperationError(msg);
420 return;
422 } else {
423 MOZ_ASSERT(StringBeginsWith(keyU8, "min"_ns));
424 if (requestedValue < supportedValue) {
425 nsPrintfCString msg(
426 "requestDevice: Request for limit '%s' must be >= supported "
427 "%s, was %s.",
428 keyU8.get(), std::to_string(supportedValue).c_str(),
429 std::to_string(requestedValue).c_str());
430 promise->MaybeRejectWithOperationError(msg);
431 return;
434 if (StringEndsWith(keyU8, "Alignment"_ns)) {
435 if (!IsPowerOfTwo(requestedValue)) {
436 nsPrintfCString msg(
437 "requestDevice: Request for limit '%s' must be a power of two, "
438 "was %s.",
439 keyU8.get(), std::to_string(requestedValue).c_str());
440 promise->MaybeRejectWithOperationError(msg);
441 return;
445 SetLimit(&deviceLimits, limit, requestedValue);
449 // -
451 ffi::WGPUDeviceDescriptor ffiDesc = {};
452 ffiDesc.features = *MakeFeatureBits(aDesc.mRequiredFeatures);
453 ffiDesc.limits = deviceLimits;
454 auto request = mBridge->AdapterRequestDevice(mId, ffiDesc);
455 if (!request) {
456 promise->MaybeRejectWithNotSupportedError(
457 "Unable to instantiate a Device");
458 return;
460 RefPtr<Device> device = new Device(this, request->mId, ffiDesc.limits);
461 for (const auto& feature : aDesc.mRequiredFeatures) {
462 device->mFeatures->Add(feature, aRv);
465 request->mPromise->Then(
466 GetCurrentSerialEventTarget(), __func__,
467 [promise, device](bool aSuccess) {
468 if (aSuccess) {
469 promise->MaybeResolve(device);
470 } else {
471 // In this path, request->mId has an error entry in the wgpu
472 // registry, so let Device::~Device clean things up on both the
473 // child and parent side.
474 promise->MaybeRejectWithInvalidStateError(
475 "Unable to fulfill requested features and limits");
478 [promise, device](const ipc::ResponseRejectReason& aReason) {
479 // We can't be sure how far along the WebGPUParent got in handling
480 // our AdapterRequestDevice message, but we can't communicate with it,
481 // so clear up our client state for this Device without trying to
482 // communicate with the parent about it.
483 device->CleanupUnregisteredInParent();
484 promise->MaybeRejectWithNotSupportedError("IPC error");
486 }();
488 return promise.forget();
491 // -
493 already_AddRefed<dom::Promise> Adapter::RequestAdapterInfo(
494 const dom::Sequence<nsString>& /*aUnmaskHints*/, ErrorResult& aRv) const {
495 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
496 if (!promise) return nullptr;
498 auto rai = UniquePtr<AdapterInfo>{new AdapterInfo(mInfo)};
499 promise->MaybeResolve(std::move(rai));
500 return promise.forget();
503 } // namespace mozilla::webgpu