Bug 1941128 - Turn off network.dns.native_https_query on Mac again
[gecko.git] / dom / webgpu / Adapter.cpp
blobcc9e9fa180b49235ccb2be0d51e1cf69e88d5d1b
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 <algorithm>
11 #include "Device.h"
12 #include "Instance.h"
13 #include "SupportedFeatures.h"
14 #include "SupportedLimits.h"
15 #include "ipc/WebGPUChild.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/webgpu/ffi/wgpu.h"
19 namespace mozilla::webgpu {
21 GPU_IMPL_CYCLE_COLLECTION(AdapterInfo, mParent)
22 GPU_IMPL_JS_WRAP(AdapterInfo)
24 void AdapterInfo::GetWgpuName(nsString& s) const {
25 s = mAboutSupportInfo->name;
28 uint32_t AdapterInfo::WgpuVendor() const { return mAboutSupportInfo->vendor; }
30 uint32_t AdapterInfo::WgpuDevice() const { return mAboutSupportInfo->device; }
32 void AdapterInfo::GetWgpuDeviceType(nsString& s) const {
33 switch (mAboutSupportInfo->device_type) {
34 case ffi::WGPUDeviceType_Cpu:
35 s.AssignLiteral("Cpu");
36 return;
37 case ffi::WGPUDeviceType_DiscreteGpu:
38 s.AssignLiteral("DiscreteGpu");
39 return;
40 case ffi::WGPUDeviceType_IntegratedGpu:
41 s.AssignLiteral("IntegratedGpu");
42 return;
43 case ffi::WGPUDeviceType_VirtualGpu:
44 s.AssignLiteral("VirtualGpu");
45 return;
46 case ffi::WGPUDeviceType_Other:
47 s.AssignLiteral("Other");
48 return;
49 case ffi::WGPUDeviceType_Sentinel:
50 break;
52 MOZ_CRASH("Bad `ffi::WGPUDeviceType`");
55 void AdapterInfo::GetWgpuDriver(nsString& s) const {
56 s = mAboutSupportInfo->driver;
59 void AdapterInfo::GetWgpuDriverInfo(nsString& s) const {
60 s = mAboutSupportInfo->driver_info;
63 void AdapterInfo::GetWgpuBackend(nsString& s) const {
64 switch (mAboutSupportInfo->backend) {
65 case ffi::WGPUBackend_Empty:
66 s.AssignLiteral("Empty");
67 return;
68 case ffi::WGPUBackend_Vulkan:
69 s.AssignLiteral("Vulkan");
70 return;
71 case ffi::WGPUBackend_Metal:
72 s.AssignLiteral("Metal");
73 return;
74 case ffi::WGPUBackend_Dx12:
75 s.AssignLiteral("Dx12");
76 return;
77 case ffi::WGPUBackend_Gl:
78 s.AssignLiteral("Gl");
79 return;
80 case ffi::WGPUBackend_BrowserWebGpu: // This should never happen, because
81 // we _are_ the browser.
82 case ffi::WGPUBackend_Sentinel:
83 break;
85 MOZ_CRASH("Bad `ffi::WGPUBackend`");
88 // -
90 GPU_IMPL_CYCLE_COLLECTION(Adapter, mParent, mBridge, mFeatures, mLimits, mInfo)
91 GPU_IMPL_JS_WRAP(Adapter)
93 enum class FeatureImplementationStatusTag {
94 Implemented,
95 NotImplemented,
98 struct FeatureImplementationStatus {
99 FeatureImplementationStatusTag tag =
100 FeatureImplementationStatusTag::NotImplemented;
101 union {
102 struct {
103 ffi::WGPUFeatures wgpuBit;
104 } implemented;
105 struct {
106 const char* bugzillaUrlAscii;
107 } unimplemented;
108 } value = {
109 .unimplemented = {
110 .bugzillaUrlAscii =
111 "https://bugzilla.mozilla.org/"
112 "enter_bug.cgi?product=Core&component=Graphics%3A+WebGPU"}};
114 static FeatureImplementationStatus fromDomFeature(
115 const dom::GPUFeatureName aFeature) {
116 auto implemented = [](const ffi::WGPUFeatures aBit) {
117 FeatureImplementationStatus feat;
118 feat.tag = FeatureImplementationStatusTag::Implemented;
119 feat.value.implemented.wgpuBit = aBit;
120 return feat;
122 auto unimplemented = [](const char* aBugzillaUrl) {
123 FeatureImplementationStatus feat;
124 feat.tag = FeatureImplementationStatusTag::NotImplemented;
125 feat.value.unimplemented.bugzillaUrlAscii = aBugzillaUrl;
126 return feat;
128 switch (aFeature) {
129 case dom::GPUFeatureName::Depth_clip_control:
130 return implemented(WGPUFeatures_DEPTH_CLIP_CONTROL);
132 case dom::GPUFeatureName::Depth32float_stencil8:
133 return implemented(WGPUFeatures_DEPTH32FLOAT_STENCIL8);
135 case dom::GPUFeatureName::Texture_compression_bc:
136 return implemented(WGPUFeatures_TEXTURE_COMPRESSION_BC);
138 case dom::GPUFeatureName::Texture_compression_etc2:
139 return implemented(WGPUFeatures_TEXTURE_COMPRESSION_ETC2);
141 case dom::GPUFeatureName::Texture_compression_astc:
142 return implemented(WGPUFeatures_TEXTURE_COMPRESSION_ASTC);
144 case dom::GPUFeatureName::Timestamp_query:
145 return implemented(WGPUFeatures_TIMESTAMP_QUERY);
147 case dom::GPUFeatureName::Indirect_first_instance:
148 return implemented(WGPUFeatures_INDIRECT_FIRST_INSTANCE);
150 case dom::GPUFeatureName::Shader_f16:
151 // return implemented(WGPUFeatures_SHADER_F16);
152 return unimplemented(
153 "https://bugzilla.mozilla.org/show_bug.cgi?id=1891593");
155 case dom::GPUFeatureName::Rg11b10ufloat_renderable:
156 return implemented(WGPUFeatures_RG11B10UFLOAT_RENDERABLE);
158 case dom::GPUFeatureName::Bgra8unorm_storage:
159 return implemented(WGPUFeatures_BGRA8UNORM_STORAGE);
161 case dom::GPUFeatureName::Float32_filterable:
162 return implemented(WGPUFeatures_FLOAT32_FILTERABLE);
164 case dom::GPUFeatureName::Float32_blendable:
165 return unimplemented(
166 "https://bugzilla.mozilla.org/show_bug.cgi?id=1931630");
168 case dom::GPUFeatureName::Clip_distances:
169 return unimplemented(
170 "https://bugzilla.mozilla.org/show_bug.cgi?id=1931629");
172 case dom::GPUFeatureName::Dual_source_blending:
173 // return implemented(WGPUFeatures_DUAL_SOURCE_BLENDING);
174 return unimplemented(
175 "https://bugzilla.mozilla.org/show_bug.cgi?id=1924328");
177 MOZ_CRASH("Bad GPUFeatureName.");
181 Adapter::Adapter(Instance* const aParent, WebGPUChild* const aBridge,
182 const std::shared_ptr<ffi::WGPUAdapterInformation>& aInfo)
183 : ChildOf(aParent),
184 mBridge(aBridge),
185 mId(aInfo->id),
186 mFeatures(new SupportedFeatures(this)),
187 mLimits(new SupportedLimits(this, aInfo->limits)),
188 mInfo(new AdapterInfo(this, aInfo)),
189 mInfoInner(aInfo) {
190 ErrorResult ignoredRv; // It's onerous to plumb this in from outside in this
191 // case, and we don't really need to.
193 static const auto FEATURE_BY_BIT = []() {
194 auto ret = std::unordered_map<ffi::WGPUFeatures, dom::GPUFeatureName>{};
196 for (const auto feature :
197 dom::MakeWebIDLEnumeratedRange<dom::GPUFeatureName>()) {
198 const auto status = FeatureImplementationStatus::fromDomFeature(feature);
199 switch (status.tag) {
200 case FeatureImplementationStatusTag::Implemented:
201 ret[status.value.implemented.wgpuBit] = feature;
202 break;
203 case FeatureImplementationStatusTag::NotImplemented:
204 break;
208 return ret;
209 }();
211 auto remainingFeatureBits = aInfo->features;
212 auto bitMask = decltype(remainingFeatureBits){0};
213 while (remainingFeatureBits) {
214 if (bitMask) {
215 bitMask <<= 1;
216 } else {
217 bitMask = 1;
219 const auto bit = remainingFeatureBits & bitMask;
220 remainingFeatureBits &= ~bitMask; // Clear bit.
221 if (!bit) {
222 continue;
225 const auto featureForBit = FEATURE_BY_BIT.find(bit);
226 if (featureForBit != FEATURE_BY_BIT.end()) {
227 mFeatures->Add(featureForBit->second, ignoredRv);
228 } else {
229 // One of two cases:
231 // 1. WGPU claims to implement this, but we've explicitly marked this as
232 // not implemented.
233 // 2. We don't recognize that bit, but maybe it's a wpgu-native-only
234 // feature.
239 Adapter::~Adapter() { Cleanup(); }
241 void Adapter::Cleanup() {
242 if (mValid && mBridge && mBridge->CanSend()) {
243 mValid = false;
244 mBridge->SendAdapterDrop(mId);
248 const RefPtr<SupportedFeatures>& Adapter::Features() const { return mFeatures; }
249 const RefPtr<SupportedLimits>& Adapter::Limits() const { return mLimits; }
250 const RefPtr<AdapterInfo>& Adapter::Info() const { return mInfo; }
252 bool Adapter::IsFallbackAdapter() const {
253 return mInfoInner->device_type == ffi::WGPUDeviceType::WGPUDeviceType_Cpu;
256 bool Adapter::SupportExternalTextureInSwapChain() const {
257 return mInfoInner->support_use_external_texture_in_swap_chain;
260 static std::string_view ToJsKey(const Limit limit) {
261 switch (limit) {
262 case Limit::MaxTextureDimension1D:
263 return "maxTextureDimension1D";
264 case Limit::MaxTextureDimension2D:
265 return "maxTextureDimension2D";
266 case Limit::MaxTextureDimension3D:
267 return "maxTextureDimension3D";
268 case Limit::MaxTextureArrayLayers:
269 return "maxTextureArrayLayers";
270 case Limit::MaxBindGroups:
271 return "maxBindGroups";
272 case Limit::MaxBindGroupsPlusVertexBuffers:
273 return "maxBindGroupsPlusVertexBuffers";
274 case Limit::MaxBindingsPerBindGroup:
275 return "maxBindingsPerBindGroup";
276 case Limit::MaxDynamicUniformBuffersPerPipelineLayout:
277 return "maxDynamicUniformBuffersPerPipelineLayout";
278 case Limit::MaxDynamicStorageBuffersPerPipelineLayout:
279 return "maxDynamicStorageBuffersPerPipelineLayout";
280 case Limit::MaxSampledTexturesPerShaderStage:
281 return "maxSampledTexturesPerShaderStage";
282 case Limit::MaxSamplersPerShaderStage:
283 return "maxSamplersPerShaderStage";
284 case Limit::MaxStorageBuffersPerShaderStage:
285 return "maxStorageBuffersPerShaderStage";
286 case Limit::MaxStorageTexturesPerShaderStage:
287 return "maxStorageTexturesPerShaderStage";
288 case Limit::MaxUniformBuffersPerShaderStage:
289 return "maxUniformBuffersPerShaderStage";
290 case Limit::MaxUniformBufferBindingSize:
291 return "maxUniformBufferBindingSize";
292 case Limit::MaxStorageBufferBindingSize:
293 return "maxStorageBufferBindingSize";
294 case Limit::MinUniformBufferOffsetAlignment:
295 return "minUniformBufferOffsetAlignment";
296 case Limit::MinStorageBufferOffsetAlignment:
297 return "minStorageBufferOffsetAlignment";
298 case Limit::MaxVertexBuffers:
299 return "maxVertexBuffers";
300 case Limit::MaxBufferSize:
301 return "maxBufferSize";
302 case Limit::MaxVertexAttributes:
303 return "maxVertexAttributes";
304 case Limit::MaxVertexBufferArrayStride:
305 return "maxVertexBufferArrayStride";
306 case Limit::MaxInterStageShaderVariables:
307 return "maxInterStageShaderVariables";
308 case Limit::MaxColorAttachments:
309 return "maxColorAttachments";
310 case Limit::MaxColorAttachmentBytesPerSample:
311 return "maxColorAttachmentBytesPerSample";
312 case Limit::MaxComputeWorkgroupStorageSize:
313 return "maxComputeWorkgroupStorageSize";
314 case Limit::MaxComputeInvocationsPerWorkgroup:
315 return "maxComputeInvocationsPerWorkgroup";
316 case Limit::MaxComputeWorkgroupSizeX:
317 return "maxComputeWorkgroupSizeX";
318 case Limit::MaxComputeWorkgroupSizeY:
319 return "maxComputeWorkgroupSizeY";
320 case Limit::MaxComputeWorkgroupSizeZ:
321 return "maxComputeWorkgroupSizeZ";
322 case Limit::MaxComputeWorkgroupsPerDimension:
323 return "maxComputeWorkgroupsPerDimension";
325 MOZ_CRASH("Bad Limit");
328 // -
329 // String helpers
331 static auto ToACString(const nsAString& s) { return NS_ConvertUTF16toUTF8(s); }
333 // -
334 // Adapter::RequestDevice
336 already_AddRefed<dom::Promise> Adapter::RequestDevice(
337 const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) {
338 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
339 if (NS_WARN_IF(aRv.Failed())) {
340 return nullptr;
343 ffi::WGPULimits deviceLimits = *mLimits->mFfi;
344 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
345 const auto defaultValue = [&]() -> double {
346 switch (limit) {
347 // clang-format off
348 case Limit::MaxTextureDimension1D: return 8192;
349 case Limit::MaxTextureDimension2D: return 8192;
350 case Limit::MaxTextureDimension3D: return 2048;
351 case Limit::MaxTextureArrayLayers: return 256;
352 case Limit::MaxBindGroups: return 4;
353 case Limit::MaxBindGroupsPlusVertexBuffers: return 24;
354 case Limit::MaxBindingsPerBindGroup: return 1000;
355 case Limit::MaxDynamicUniformBuffersPerPipelineLayout: return 8;
356 case Limit::MaxDynamicStorageBuffersPerPipelineLayout: return 4;
357 case Limit::MaxSampledTexturesPerShaderStage: return 16;
358 case Limit::MaxSamplersPerShaderStage: return 16;
359 case Limit::MaxStorageBuffersPerShaderStage: return 8;
360 case Limit::MaxStorageTexturesPerShaderStage: return 4;
361 case Limit::MaxUniformBuffersPerShaderStage: return 12;
362 case Limit::MaxUniformBufferBindingSize: return 65536;
363 case Limit::MaxStorageBufferBindingSize: return 134217728;
364 case Limit::MinUniformBufferOffsetAlignment: return 256;
365 case Limit::MinStorageBufferOffsetAlignment: return 256;
366 case Limit::MaxVertexBuffers: return 8;
367 case Limit::MaxBufferSize: return 268435456;
368 case Limit::MaxVertexAttributes: return 16;
369 case Limit::MaxVertexBufferArrayStride: return 2048;
370 case Limit::MaxInterStageShaderVariables: return 16;
371 case Limit::MaxColorAttachments: return 8;
372 case Limit::MaxColorAttachmentBytesPerSample: return 32;
373 case Limit::MaxComputeWorkgroupStorageSize: return 16384;
374 case Limit::MaxComputeInvocationsPerWorkgroup: return 256;
375 case Limit::MaxComputeWorkgroupSizeX: return 256;
376 case Limit::MaxComputeWorkgroupSizeY: return 256;
377 case Limit::MaxComputeWorkgroupSizeZ: return 64;
378 case Limit::MaxComputeWorkgroupsPerDimension: return 65535;
379 // clang-format on
381 MOZ_CRASH("Bad Limit");
382 }();
383 SetLimit(&deviceLimits, limit, defaultValue);
386 // -
388 [&]() { // So that we can `return;` instead of `return promise.forget();`.
389 if (!mBridge->CanSend()) {
390 promise->MaybeRejectWithInvalidStateError(
391 "WebGPUChild cannot send, must recreate Adapter");
392 return;
395 // -
396 // Validate Features
398 ffi::WGPUFeatures featureBits = 0;
399 for (const auto requested : aDesc.mRequiredFeatures) {
400 auto status = FeatureImplementationStatus::fromDomFeature(requested);
401 switch (status.tag) {
402 case FeatureImplementationStatusTag::Implemented:
403 featureBits |= status.value.implemented.wgpuBit;
404 break;
405 case FeatureImplementationStatusTag::NotImplemented: {
406 const auto featureStr = dom::GetEnumString(requested);
407 (void)featureStr;
408 nsPrintfCString msg(
409 "`GPUAdapter.requestDevice`: '%s' was requested in "
410 "`requiredFeatures`, but it is not supported by Firefox."
411 "Follow <%s> for updates.",
412 featureStr.get(), status.value.unimplemented.bugzillaUrlAscii);
413 promise->MaybeRejectWithTypeError(msg);
414 return;
418 const bool supportedByAdapter = mFeatures->Features().count(requested);
419 if (!supportedByAdapter) {
420 const auto fstr = dom::GetEnumString(requested);
421 const auto astr = this->LabelOrId();
422 nsPrintfCString msg(
423 "`GPUAdapter.requestDevice`: '%s' was requested in "
424 "`requiredFeatures`, but it is not supported by adapter %s.",
425 fstr.get(), astr.get());
426 promise->MaybeRejectWithTypeError(msg);
427 return;
431 // -
432 // Validate Limits
434 if (aDesc.mRequiredLimits.WasPassed()) {
435 static const auto LIMIT_BY_JS_KEY = []() {
436 std::unordered_map<std::string_view, Limit> ret;
437 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
438 const auto jsKeyU8 = ToJsKey(limit);
439 ret[jsKeyU8] = limit;
441 return ret;
442 }();
444 for (const auto& entry : aDesc.mRequiredLimits.Value().Entries()) {
445 const auto& keyU16 = entry.mKey;
446 const nsCString keyU8 = ToACString(keyU16);
447 const auto itr = LIMIT_BY_JS_KEY.find(keyU8.get());
448 if (itr == LIMIT_BY_JS_KEY.end()) {
449 nsPrintfCString msg("requestDevice: Limit '%s' not recognized.",
450 keyU8.get());
451 promise->MaybeRejectWithOperationError(msg);
452 return;
455 const auto& limit = itr->second;
456 uint64_t requestedValue = entry.mValue;
457 const auto supportedValue = GetLimit(*mLimits->mFfi, limit);
458 if (StringBeginsWith(keyU8, "max"_ns)) {
459 if (requestedValue > supportedValue) {
460 nsPrintfCString msg(
461 "requestDevice: Request for limit '%s' must be <= supported "
462 "%s, was %s.",
463 keyU8.get(), std::to_string(supportedValue).c_str(),
464 std::to_string(requestedValue).c_str());
465 promise->MaybeRejectWithOperationError(msg);
466 return;
468 // Clamp to default if lower than default
469 requestedValue =
470 std::max(requestedValue, GetLimit(deviceLimits, limit));
471 } else {
472 MOZ_ASSERT(StringBeginsWith(keyU8, "min"_ns));
473 if (requestedValue < supportedValue) {
474 nsPrintfCString msg(
475 "requestDevice: Request for limit '%s' must be >= supported "
476 "%s, was %s.",
477 keyU8.get(), std::to_string(supportedValue).c_str(),
478 std::to_string(requestedValue).c_str());
479 promise->MaybeRejectWithOperationError(msg);
480 return;
482 if (StringEndsWith(keyU8, "Alignment"_ns)) {
483 if (!IsPowerOfTwo(requestedValue)) {
484 nsPrintfCString msg(
485 "requestDevice: Request for limit '%s' must be a power of "
486 "two, "
487 "was %s.",
488 keyU8.get(), std::to_string(requestedValue).c_str());
489 promise->MaybeRejectWithOperationError(msg);
490 return;
493 /// Clamp to default if higher than default
494 requestedValue =
495 std::min(requestedValue, GetLimit(deviceLimits, limit));
498 SetLimit(&deviceLimits, limit, requestedValue);
502 // -
504 ffi::WGPUFfiDeviceDescriptor ffiDesc = {};
505 ffiDesc.required_features = featureBits;
506 ffiDesc.required_limits = deviceLimits;
507 auto request = mBridge->AdapterRequestDevice(mId, ffiDesc);
508 if (!request) {
509 promise->MaybeRejectWithNotSupportedError(
510 "Unable to instantiate a Device");
511 return;
513 RefPtr<Device> device = new Device(
514 this, request->mDeviceId, request->mQueueId, ffiDesc.required_limits);
515 for (const auto& feature : aDesc.mRequiredFeatures) {
516 device->mFeatures->Add(feature, aRv);
519 request->mPromise->Then(
520 GetCurrentSerialEventTarget(), __func__,
521 [promise, device](bool aSuccess) {
522 if (aSuccess) {
523 promise->MaybeResolve(device);
524 } else {
525 device->CleanupUnregisteredInParent();
526 promise->MaybeRejectWithInvalidStateError(
527 "Unable to fulfill requested features and limits");
530 [promise, device](const ipc::ResponseRejectReason& aReason) {
531 // We can't be sure how far along the WebGPUParent got in handling
532 // our AdapterRequestDevice message, but we can't communicate with it,
533 // so clear up our client state for this Device without trying to
534 // communicate with the parent about it.
535 device->CleanupUnregisteredInParent();
536 promise->MaybeRejectWithNotSupportedError("IPC error");
538 }();
540 return promise.forget();
543 } // namespace mozilla::webgpu