Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / webgpu / Adapter.cpp
blobfdfe2713536c9185e09f30479a2b68b17f5a6b3c
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 bool AdapterInfo::WrapObject(JSContext* const cx,
22 JS::Handle<JSObject*> givenProto,
23 JS::MutableHandle<JSObject*> reflector) {
24 return dom::GPUAdapterInfo_Binding::Wrap(cx, this, givenProto, reflector);
27 void AdapterInfo::GetWgpuName(nsString& s) const {
28 s = mAboutSupportInfo->name;
31 uint32_t AdapterInfo::WgpuVendor() const { return mAboutSupportInfo->vendor; }
33 uint32_t AdapterInfo::WgpuDevice() const { return mAboutSupportInfo->device; }
35 void AdapterInfo::GetWgpuDeviceType(nsString& s) const {
36 switch (mAboutSupportInfo->device_type) {
37 case ffi::WGPUDeviceType_Cpu:
38 s.AssignLiteral("Cpu");
39 return;
40 case ffi::WGPUDeviceType_DiscreteGpu:
41 s.AssignLiteral("DiscreteGpu");
42 return;
43 case ffi::WGPUDeviceType_IntegratedGpu:
44 s.AssignLiteral("IntegratedGpu");
45 return;
46 case ffi::WGPUDeviceType_VirtualGpu:
47 s.AssignLiteral("VirtualGpu");
48 return;
49 case ffi::WGPUDeviceType_Other:
50 s.AssignLiteral("Other");
51 return;
52 case ffi::WGPUDeviceType_Sentinel:
53 break;
55 MOZ_CRASH("Bad `ffi::WGPUDeviceType`");
58 void AdapterInfo::GetWgpuDriver(nsString& s) const {
59 s = mAboutSupportInfo->driver;
62 void AdapterInfo::GetWgpuDriverInfo(nsString& s) const {
63 s = mAboutSupportInfo->driver_info;
66 void AdapterInfo::GetWgpuBackend(nsString& s) const {
67 switch (mAboutSupportInfo->backend) {
68 case ffi::WGPUBackend_Empty:
69 s.AssignLiteral("Empty");
70 return;
71 case ffi::WGPUBackend_Vulkan:
72 s.AssignLiteral("Vulkan");
73 return;
74 case ffi::WGPUBackend_Metal:
75 s.AssignLiteral("Metal");
76 return;
77 case ffi::WGPUBackend_Dx12:
78 s.AssignLiteral("Dx12");
79 return;
80 case ffi::WGPUBackend_Dx11:
81 s.AssignLiteral("Dx11");
82 return;
83 case ffi::WGPUBackend_Gl:
84 s.AssignLiteral("Gl");
85 return;
86 case ffi::WGPUBackend_BrowserWebGpu: // This should never happen, because
87 // we _are_ the browser.
88 case ffi::WGPUBackend_Sentinel:
89 break;
91 MOZ_CRASH("Bad `ffi::WGPUBackend`");
94 // -
96 GPU_IMPL_CYCLE_COLLECTION(Adapter, mParent, mBridge, mFeatures, mLimits)
97 GPU_IMPL_JS_WRAP(Adapter)
99 static Maybe<ffi::WGPUFeatures> ToWGPUFeatures(
100 const dom::GPUFeatureName aFeature) {
101 switch (aFeature) {
102 case dom::GPUFeatureName::Depth_clip_control:
103 return Some(WGPUFeatures_DEPTH_CLIP_CONTROL);
105 case dom::GPUFeatureName::Depth32float_stencil8:
106 return Some(WGPUFeatures_DEPTH32FLOAT_STENCIL8);
108 case dom::GPUFeatureName::Texture_compression_bc:
109 return Some(WGPUFeatures_TEXTURE_COMPRESSION_BC);
111 case dom::GPUFeatureName::Texture_compression_etc2:
112 return Some(WGPUFeatures_TEXTURE_COMPRESSION_ETC2);
114 case dom::GPUFeatureName::Texture_compression_astc:
115 return Some(WGPUFeatures_TEXTURE_COMPRESSION_ASTC);
117 case dom::GPUFeatureName::Timestamp_query:
118 return Some(WGPUFeatures_TIMESTAMP_QUERY);
120 case dom::GPUFeatureName::Indirect_first_instance:
121 return Some(WGPUFeatures_INDIRECT_FIRST_INSTANCE);
123 case dom::GPUFeatureName::Shader_f16:
124 return Some(WGPUFeatures_SHADER_F16);
126 case dom::GPUFeatureName::Rg11b10ufloat_renderable:
127 return Some(WGPUFeatures_RG11B10UFLOAT_RENDERABLE);
129 case dom::GPUFeatureName::Bgra8unorm_storage:
130 #ifdef WGPUFeatures_BGRA8UNORM_STORAGE
131 # error fix todo
132 #endif
133 return Nothing(); // TODO
135 case dom::GPUFeatureName::Float32_filterable:
136 #ifdef WGPUFeatures_FLOAT32_FILTERABLE
137 # error fix todo
138 #endif
139 return Nothing(); // TODO
141 case dom::GPUFeatureName::EndGuard_:
142 break;
144 MOZ_CRASH("Bad GPUFeatureName.");
147 static Maybe<ffi::WGPUFeatures> MakeFeatureBits(
148 const dom::Sequence<dom::GPUFeatureName>& aFeatures) {
149 ffi::WGPUFeatures bits = 0;
150 for (const auto& feature : aFeatures) {
151 const auto bit = ToWGPUFeatures(feature);
152 if (!bit) {
153 const auto featureStr = dom::GPUFeatureNameValues::GetString(feature);
154 (void)featureStr;
155 NS_WARNING(
156 nsPrintfCString("Requested feature bit for '%s' is not implemented.",
157 featureStr.data())
158 .get());
159 return Nothing();
161 bits |= *bit;
163 return Some(bits);
166 Adapter::Adapter(Instance* const aParent, WebGPUChild* const aBridge,
167 const std::shared_ptr<ffi::WGPUAdapterInformation>& aInfo)
168 : ChildOf(aParent),
169 mBridge(aBridge),
170 mId(aInfo->id),
171 mFeatures(new SupportedFeatures(this)),
172 mLimits(new SupportedLimits(this, aInfo->limits)),
173 mInfo(aInfo) {
174 ErrorResult ignoredRv; // It's onerous to plumb this in from outside in this
175 // case, and we don't really need to.
177 static const auto FEATURE_BY_BIT = []() {
178 auto ret = std::unordered_map<ffi::WGPUFeatures, dom::GPUFeatureName>{};
180 for (const auto feature :
181 MakeEnumeratedRange(dom::GPUFeatureName::EndGuard_)) {
182 const auto bitForFeature = ToWGPUFeatures(feature);
183 if (!bitForFeature) {
184 // There are some features that don't have bits.
185 continue;
187 ret[*bitForFeature] = feature;
190 return ret;
191 }();
193 auto remainingFeatureBits = aInfo->features;
194 auto bitMask = decltype(remainingFeatureBits){0};
195 while (remainingFeatureBits) {
196 if (bitMask) {
197 bitMask <<= 1;
198 } else {
199 bitMask = 1;
201 const auto bit = remainingFeatureBits & bitMask;
202 remainingFeatureBits &= ~bitMask; // Clear bit.
203 if (!bit) {
204 continue;
207 const auto featureForBit = FEATURE_BY_BIT.find(bit);
208 if (featureForBit != FEATURE_BY_BIT.end()) {
209 mFeatures->Add(featureForBit->second, ignoredRv);
210 } else {
211 // We don't recognize that bit, but maybe it's a wpgu-native-only feature.
216 Adapter::~Adapter() { Cleanup(); }
218 void Adapter::Cleanup() {
219 if (mValid && mBridge && mBridge->CanSend()) {
220 mValid = false;
221 mBridge->SendAdapterDestroy(mId);
225 const RefPtr<SupportedFeatures>& Adapter::Features() const { return mFeatures; }
226 const RefPtr<SupportedLimits>& Adapter::Limits() const { return mLimits; }
227 bool Adapter::IsFallbackAdapter() const {
228 return mInfo->device_type == ffi::WGPUDeviceType::WGPUDeviceType_Cpu;
231 static std::string_view ToJsKey(const Limit limit) {
232 switch (limit) {
233 case Limit::MaxTextureDimension1D:
234 return "maxTextureDimension1D";
235 case Limit::MaxTextureDimension2D:
236 return "maxTextureDimension2D";
237 case Limit::MaxTextureDimension3D:
238 return "maxTextureDimension3D";
239 case Limit::MaxTextureArrayLayers:
240 return "maxTextureArrayLayers";
241 case Limit::MaxBindGroups:
242 return "maxBindGroups";
243 case Limit::MaxBindGroupsPlusVertexBuffers:
244 return "maxBindGroupsPlusVertexBuffers";
245 case Limit::MaxBindingsPerBindGroup:
246 return "maxBindingsPerBindGroup";
247 case Limit::MaxDynamicUniformBuffersPerPipelineLayout:
248 return "maxDynamicUniformBuffersPerPipelineLayout";
249 case Limit::MaxDynamicStorageBuffersPerPipelineLayout:
250 return "maxDynamicStorageBuffersPerPipelineLayout";
251 case Limit::MaxSampledTexturesPerShaderStage:
252 return "maxSampledTexturesPerShaderStage";
253 case Limit::MaxSamplersPerShaderStage:
254 return "maxSamplersPerShaderStage";
255 case Limit::MaxStorageBuffersPerShaderStage:
256 return "maxStorageBuffersPerShaderStage";
257 case Limit::MaxStorageTexturesPerShaderStage:
258 return "maxStorageTexturesPerShaderStage";
259 case Limit::MaxUniformBuffersPerShaderStage:
260 return "maxUniformBuffersPerShaderStage";
261 case Limit::MaxUniformBufferBindingSize:
262 return "maxUniformBufferBindingSize";
263 case Limit::MaxStorageBufferBindingSize:
264 return "maxStorageBufferBindingSize";
265 case Limit::MinUniformBufferOffsetAlignment:
266 return "minUniformBufferOffsetAlignment";
267 case Limit::MinStorageBufferOffsetAlignment:
268 return "minStorageBufferOffsetAlignment";
269 case Limit::MaxVertexBuffers:
270 return "maxVertexBuffers";
271 case Limit::MaxBufferSize:
272 return "maxBufferSize";
273 case Limit::MaxVertexAttributes:
274 return "maxVertexAttributes";
275 case Limit::MaxVertexBufferArrayStride:
276 return "maxVertexBufferArrayStride";
277 case Limit::MaxInterStageShaderComponents:
278 return "maxInterStageShaderComponents";
279 case Limit::MaxInterStageShaderVariables:
280 return "maxInterStageShaderVariables";
281 case Limit::MaxColorAttachments:
282 return "maxColorAttachments";
283 case Limit::MaxColorAttachmentBytesPerSample:
284 return "maxColorAttachmentBytesPerSample";
285 case Limit::MaxComputeWorkgroupStorageSize:
286 return "maxComputeWorkgroupStorageSize";
287 case Limit::MaxComputeInvocationsPerWorkgroup:
288 return "maxComputeInvocationsPerWorkgroup";
289 case Limit::MaxComputeWorkgroupSizeX:
290 return "maxComputeWorkgroupSizeX";
291 case Limit::MaxComputeWorkgroupSizeY:
292 return "maxComputeWorkgroupSizeY";
293 case Limit::MaxComputeWorkgroupSizeZ:
294 return "maxComputeWorkgroupSizeZ";
295 case Limit::MaxComputeWorkgroupsPerDimension:
296 return "maxComputeWorkgroupsPerDimension";
298 MOZ_CRASH("Bad Limit");
301 // -
302 // String helpers
304 static auto ToACString(const nsAString& s) { return NS_ConvertUTF16toUTF8(s); }
306 // -
307 // Adapter::RequestDevice
309 already_AddRefed<dom::Promise> Adapter::RequestDevice(
310 const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) {
311 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
312 if (NS_WARN_IF(aRv.Failed())) {
313 return nullptr;
316 ffi::WGPULimits deviceLimits = *mLimits->mFfi;
317 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
318 const auto defaultValue = [&]() -> double {
319 switch (limit) {
320 // clang-format off
321 case Limit::MaxTextureDimension1D: return 8192;
322 case Limit::MaxTextureDimension2D: return 8192;
323 case Limit::MaxTextureDimension3D: return 2048;
324 case Limit::MaxTextureArrayLayers: return 256;
325 case Limit::MaxBindGroups: return 4;
326 case Limit::MaxBindGroupsPlusVertexBuffers: return 24;
327 case Limit::MaxBindingsPerBindGroup: return 1000;
328 case Limit::MaxDynamicUniformBuffersPerPipelineLayout: return 8;
329 case Limit::MaxDynamicStorageBuffersPerPipelineLayout: return 4;
330 case Limit::MaxSampledTexturesPerShaderStage: return 16;
331 case Limit::MaxSamplersPerShaderStage: return 16;
332 case Limit::MaxStorageBuffersPerShaderStage: return 8;
333 case Limit::MaxStorageTexturesPerShaderStage: return 4;
334 case Limit::MaxUniformBuffersPerShaderStage: return 12;
335 case Limit::MaxUniformBufferBindingSize: return 65536;
336 case Limit::MaxStorageBufferBindingSize: return 134217728;
337 case Limit::MinUniformBufferOffsetAlignment: return 256;
338 case Limit::MinStorageBufferOffsetAlignment: return 256;
339 case Limit::MaxVertexBuffers: return 8;
340 case Limit::MaxBufferSize: return 268435456;
341 case Limit::MaxVertexAttributes: return 16;
342 case Limit::MaxVertexBufferArrayStride: return 2048;
343 case Limit::MaxInterStageShaderComponents: return 60;
344 case Limit::MaxInterStageShaderVariables: return 16;
345 case Limit::MaxColorAttachments: return 8;
346 case Limit::MaxColorAttachmentBytesPerSample: return 32;
347 case Limit::MaxComputeWorkgroupStorageSize: return 16384;
348 case Limit::MaxComputeInvocationsPerWorkgroup: return 256;
349 case Limit::MaxComputeWorkgroupSizeX: return 256;
350 case Limit::MaxComputeWorkgroupSizeY: return 256;
351 case Limit::MaxComputeWorkgroupSizeZ: return 64;
352 case Limit::MaxComputeWorkgroupsPerDimension: return 65535;
353 // clang-format on
355 MOZ_CRASH("Bad Limit");
356 }();
357 SetLimit(&deviceLimits, limit, defaultValue);
360 // -
362 [&]() { // So that we can `return;` instead of `return promise.forget();`.
363 if (!mBridge->CanSend()) {
364 promise->MaybeRejectWithInvalidStateError(
365 "WebGPUChild cannot send, must recreate Adapter");
366 return;
369 // -
370 // Validate Features
372 for (const auto requested : aDesc.mRequiredFeatures) {
373 const bool supported = mFeatures->Features().count(requested);
374 if (!supported) {
375 const auto fstr = dom::GPUFeatureNameValues::GetString(requested);
376 const auto astr = this->LabelOrId();
377 nsPrintfCString msg(
378 "requestDevice: Feature '%s' requested must be supported by "
379 "adapter %s",
380 fstr.data(), astr.get());
381 promise->MaybeRejectWithTypeError(msg);
382 return;
386 // -
387 // Validate Limits
389 if (aDesc.mRequiredLimits.WasPassed()) {
390 static const auto LIMIT_BY_JS_KEY = []() {
391 std::unordered_map<std::string_view, Limit> ret;
392 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) {
393 const auto jsKeyU8 = ToJsKey(limit);
394 ret[jsKeyU8] = limit;
396 return ret;
397 }();
399 for (const auto& entry : aDesc.mRequiredLimits.Value().Entries()) {
400 const auto& keyU16 = entry.mKey;
401 const nsCString keyU8 = ToACString(keyU16);
402 const auto itr = LIMIT_BY_JS_KEY.find(keyU8.get());
403 if (itr == LIMIT_BY_JS_KEY.end()) {
404 nsPrintfCString msg("requestDevice: Limit '%s' not recognized.",
405 keyU8.get());
406 promise->MaybeRejectWithOperationError(msg);
407 return;
410 const auto& limit = itr->second;
411 uint64_t requestedValue = entry.mValue;
412 const auto supportedValue = GetLimit(*mLimits->mFfi, limit);
413 if (StringBeginsWith(keyU8, "max"_ns)) {
414 if (requestedValue > supportedValue) {
415 nsPrintfCString msg(
416 "requestDevice: Request for limit '%s' must be <= supported "
417 "%s, was %s.",
418 keyU8.get(), std::to_string(supportedValue).c_str(),
419 std::to_string(requestedValue).c_str());
420 promise->MaybeRejectWithOperationError(msg);
421 return;
423 // Clamp to default if lower than default
424 requestedValue =
425 std::max(requestedValue, GetLimit(deviceLimits, limit));
426 } else {
427 MOZ_ASSERT(StringBeginsWith(keyU8, "min"_ns));
428 if (requestedValue < supportedValue) {
429 nsPrintfCString msg(
430 "requestDevice: Request for limit '%s' must be >= supported "
431 "%s, was %s.",
432 keyU8.get(), std::to_string(supportedValue).c_str(),
433 std::to_string(requestedValue).c_str());
434 promise->MaybeRejectWithOperationError(msg);
435 return;
437 if (StringEndsWith(keyU8, "Alignment"_ns)) {
438 if (!IsPowerOfTwo(requestedValue)) {
439 nsPrintfCString msg(
440 "requestDevice: Request for limit '%s' must be a power of "
441 "two, "
442 "was %s.",
443 keyU8.get(), std::to_string(requestedValue).c_str());
444 promise->MaybeRejectWithOperationError(msg);
445 return;
448 /// Clamp to default if higher than default
449 requestedValue =
450 std::min(requestedValue, GetLimit(deviceLimits, limit));
453 SetLimit(&deviceLimits, limit, requestedValue);
457 // -
459 ffi::WGPUDeviceDescriptor ffiDesc = {};
460 ffiDesc.features = *MakeFeatureBits(aDesc.mRequiredFeatures);
461 ffiDesc.limits = deviceLimits;
462 auto request = mBridge->AdapterRequestDevice(mId, ffiDesc);
463 if (!request) {
464 promise->MaybeRejectWithNotSupportedError(
465 "Unable to instantiate a Device");
466 return;
468 RefPtr<Device> device = new Device(this, request->mId, ffiDesc.limits);
469 for (const auto& feature : aDesc.mRequiredFeatures) {
470 device->mFeatures->Add(feature, aRv);
473 request->mPromise->Then(
474 GetCurrentSerialEventTarget(), __func__,
475 [promise, device](bool aSuccess) {
476 if (aSuccess) {
477 promise->MaybeResolve(device);
478 } else {
479 // In this path, request->mId has an error entry in the wgpu
480 // registry, so let Device::~Device clean things up on both the
481 // child and parent side.
482 promise->MaybeRejectWithInvalidStateError(
483 "Unable to fulfill requested features and limits");
486 [promise, device](const ipc::ResponseRejectReason& aReason) {
487 // We can't be sure how far along the WebGPUParent got in handling
488 // our AdapterRequestDevice message, but we can't communicate with it,
489 // so clear up our client state for this Device without trying to
490 // communicate with the parent about it.
491 device->CleanupUnregisteredInParent();
492 promise->MaybeRejectWithNotSupportedError("IPC error");
494 }();
496 return promise.forget();
499 // -
501 already_AddRefed<dom::Promise> Adapter::RequestAdapterInfo(
502 const dom::Sequence<nsString>& /*aUnmaskHints*/, ErrorResult& aRv) const {
503 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
504 if (!promise) return nullptr;
506 auto rai = UniquePtr<AdapterInfo>{new AdapterInfo(mInfo)};
507 promise->MaybeResolve(std::move(rai));
508 return promise.forget();
511 } // namespace mozilla::webgpu