Adapter.cpp (24842B)
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/. */ 5 6 #include "Adapter.h" 7 8 #include <algorithm> 9 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/BindingDeclarations.h" 16 #include "mozilla/dom/Promise.h" 17 #include "mozilla/dom/WebGPUBinding.h" 18 #include "mozilla/webgpu/ffi/wgpu.h" 19 20 namespace mozilla::webgpu { 21 22 GPU_IMPL_CYCLE_COLLECTION(AdapterInfo, mParent) 23 GPU_IMPL_JS_WRAP(AdapterInfo) 24 25 uint32_t AdapterInfo::SubgroupMinSize() const { 26 // From the spec. at 27 // <https://www.w3.org/TR/2025/CRD-webgpu-20250319/#dom-gpuadapterinfo-subgroupminsize>: 28 // 29 // > If `["subgroups"](https://www.w3.org/TR/webgpu/#subgroups)` is supported, 30 // > set `subgroupMinSize` to the smallest supported subgroup size. Otherwise, 31 // > set this value to 4. 32 // > 33 // > Note: To preserve privacy, the user agent may choose to not support some 34 // > features or provide values for the property which do not distinguish 35 // > different devices, but are still usable (e.g. use the default value of 36 // > 4 for all devices). 37 38 if (GetParentObject()->ShouldResistFingerprinting( 39 RFPTarget::WebGPUSubgroupSizes)) { 40 return 4; 41 } 42 43 // TODO: When we support `subgroups`, use the supported amount instead: 44 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1955417> 45 return 4; 46 } 47 48 uint32_t AdapterInfo::SubgroupMaxSize() const { 49 // From the spec. at 50 // <https://www.w3.org/TR/2025/CRD-webgpu-20250319/#dom-gpuadapterinfo-subgroupmaxsize>: 51 // 52 // > If `["subgroups"](https://www.w3.org/TR/webgpu/#subgroups)` is supported, 53 // > set `subgroupMaxSize` to the largest supported subgroup size. Otherwise, 54 // > set this value to 128. 55 // > 56 // > Note: To preserve privacy, the user agent may choose to not support some 57 // > features or provide values for the property which do not distinguish 58 // > different devices, but are still usable (e.g. use the default value of 59 // > 128 for all devices). 60 61 if (GetParentObject()->ShouldResistFingerprinting( 62 RFPTarget::WebGPUSubgroupSizes)) { 63 return 128; 64 } 65 66 // TODO: When we support `subgroups`, use the supported amount instead: 67 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1955417> 68 return 128; 69 } 70 71 bool AdapterInfo::IsFallbackAdapter() const { 72 if (GetParentObject()->ShouldResistFingerprinting( 73 RFPTarget::WebGPUIsFallbackAdapter)) { 74 // Always report hardware support for WebGPU. 75 // This behaviour matches with media capabilities API. 76 return false; 77 } 78 79 return mAboutSupportInfo->device_type == 80 ffi::WGPUDeviceType::WGPUDeviceType_Cpu; 81 } 82 83 void AdapterInfo::GetWgpuName(nsString& s) const { 84 s = mAboutSupportInfo->name; 85 } 86 87 uint32_t AdapterInfo::WgpuVendor() const { return mAboutSupportInfo->vendor; } 88 89 uint32_t AdapterInfo::WgpuDevice() const { return mAboutSupportInfo->device; } 90 91 void AdapterInfo::GetWgpuDeviceType(nsString& s) const { 92 switch (mAboutSupportInfo->device_type) { 93 case ffi::WGPUDeviceType_Cpu: 94 s.AssignLiteral("Cpu"); 95 return; 96 case ffi::WGPUDeviceType_DiscreteGpu: 97 s.AssignLiteral("DiscreteGpu"); 98 return; 99 case ffi::WGPUDeviceType_IntegratedGpu: 100 s.AssignLiteral("IntegratedGpu"); 101 return; 102 case ffi::WGPUDeviceType_VirtualGpu: 103 s.AssignLiteral("VirtualGpu"); 104 return; 105 case ffi::WGPUDeviceType_Other: 106 s.AssignLiteral("Other"); 107 return; 108 case ffi::WGPUDeviceType_Sentinel: 109 break; 110 } 111 MOZ_CRASH("Bad `ffi::WGPUDeviceType`"); 112 } 113 114 void AdapterInfo::GetWgpuDriver(nsString& s) const { 115 s = mAboutSupportInfo->driver; 116 } 117 118 void AdapterInfo::GetWgpuDriverInfo(nsString& s) const { 119 s = mAboutSupportInfo->driver_info; 120 } 121 122 void AdapterInfo::GetWgpuBackend(nsString& s) const { 123 switch (mAboutSupportInfo->backend) { 124 case ffi::WGPUBackend_Noop: 125 s.AssignLiteral("No-op"); 126 return; 127 case ffi::WGPUBackend_Vulkan: 128 s.AssignLiteral("Vulkan"); 129 return; 130 case ffi::WGPUBackend_Metal: 131 s.AssignLiteral("Metal"); 132 return; 133 case ffi::WGPUBackend_Dx12: 134 s.AssignLiteral("Dx12"); 135 return; 136 case ffi::WGPUBackend_Gl: 137 s.AssignLiteral("Gl"); 138 return; 139 case ffi::WGPUBackend_BrowserWebGpu: // This should never happen, because 140 // we _are_ the browser. 141 case ffi::WGPUBackend_Sentinel: 142 break; 143 } 144 MOZ_CRASH("Bad `ffi::WGPUBackend`"); 145 } 146 147 // - 148 149 GPU_IMPL_CYCLE_COLLECTION(Adapter, mParent, mFeatures, mLimits, mInfo) 150 GPU_IMPL_JS_WRAP(Adapter) 151 152 enum class FeatureImplementationStatusTag { 153 Implemented, 154 NotImplemented, 155 }; 156 157 struct FeatureImplementationStatus { 158 FeatureImplementationStatusTag tag = 159 FeatureImplementationStatusTag::NotImplemented; 160 union { 161 struct { 162 ffi::WGPUFeaturesWebGPU wgpuBit; 163 } implemented; 164 struct { 165 const char* bugzillaUrlAscii; 166 } unimplemented; 167 } value = { 168 .unimplemented = { 169 .bugzillaUrlAscii = 170 "https://bugzilla.mozilla.org/" 171 "enter_bug.cgi?product=Core&component=Graphics%3A+WebGPU"}}; 172 173 static FeatureImplementationStatus fromDomFeature( 174 const dom::GPUFeatureName aFeature) { 175 auto implemented = [](const ffi::WGPUFeaturesWebGPU aBit) { 176 FeatureImplementationStatus feat; 177 feat.tag = FeatureImplementationStatusTag::Implemented; 178 feat.value.implemented.wgpuBit = aBit; 179 return feat; 180 }; 181 auto unimplemented = [](const char* aBugzillaUrl) { 182 FeatureImplementationStatus feat; 183 feat.tag = FeatureImplementationStatusTag::NotImplemented; 184 feat.value.unimplemented.bugzillaUrlAscii = aBugzillaUrl; 185 return feat; 186 }; 187 switch (aFeature) { 188 case dom::GPUFeatureName::Depth_clip_control: 189 return implemented(WGPUWEBGPU_FEATURE_DEPTH_CLIP_CONTROL); 190 191 case dom::GPUFeatureName::Depth32float_stencil8: 192 return implemented(WGPUWEBGPU_FEATURE_DEPTH32FLOAT_STENCIL8); 193 194 case dom::GPUFeatureName::Texture_compression_bc: 195 return implemented(WGPUWEBGPU_FEATURE_TEXTURE_COMPRESSION_BC); 196 197 case dom::GPUFeatureName::Texture_compression_bc_sliced_3d: 198 return implemented(WGPUWEBGPU_FEATURE_TEXTURE_COMPRESSION_BC_SLICED_3D); 199 200 case dom::GPUFeatureName::Texture_compression_etc2: 201 return implemented(WGPUWEBGPU_FEATURE_TEXTURE_COMPRESSION_ETC2); 202 203 case dom::GPUFeatureName::Texture_compression_astc: 204 return implemented(WGPUWEBGPU_FEATURE_TEXTURE_COMPRESSION_ASTC); 205 206 case dom::GPUFeatureName::Texture_compression_astc_sliced_3d: 207 return implemented( 208 WGPUWEBGPU_FEATURE_TEXTURE_COMPRESSION_ASTC_SLICED_3D); 209 210 case dom::GPUFeatureName::Timestamp_query: 211 return implemented(WGPUWEBGPU_FEATURE_TIMESTAMP_QUERY); 212 213 case dom::GPUFeatureName::Indirect_first_instance: 214 return implemented(WGPUWEBGPU_FEATURE_INDIRECT_FIRST_INSTANCE); 215 216 case dom::GPUFeatureName::Shader_f16: 217 return implemented(WGPUWEBGPU_FEATURE_SHADER_F16); 218 219 case dom::GPUFeatureName::Rg11b10ufloat_renderable: 220 return implemented(WGPUWEBGPU_FEATURE_RG11B10UFLOAT_RENDERABLE); 221 222 case dom::GPUFeatureName::Bgra8unorm_storage: 223 return implemented(WGPUWEBGPU_FEATURE_BGRA8UNORM_STORAGE); 224 225 case dom::GPUFeatureName::Float32_filterable: 226 return implemented(WGPUWEBGPU_FEATURE_FLOAT32_FILTERABLE); 227 228 case dom::GPUFeatureName::Float32_blendable: 229 return unimplemented( 230 "https://bugzilla.mozilla.org/show_bug.cgi?id=1931630"); 231 232 case dom::GPUFeatureName::Clip_distances: 233 return unimplemented( 234 "https://bugzilla.mozilla.org/show_bug.cgi?id=1931629"); 235 236 case dom::GPUFeatureName::Dual_source_blending: 237 // return implemented(WGPUWEBGPU_FEATURE_DUAL_SOURCE_BLENDING); 238 return unimplemented( 239 "https://bugzilla.mozilla.org/show_bug.cgi?id=1924328"); 240 241 case dom::GPUFeatureName::Subgroups: 242 // return implemented(WGPUWEBGPU_FEATURE_SUBGROUPS); 243 return unimplemented( 244 "https://bugzilla.mozilla.org/show_bug.cgi?id=1955417"); 245 246 case dom::GPUFeatureName::Primitive_index: 247 // return implemented(WGPUWEBGPU_FEATURE_PRIMITIVE_INDEX); 248 return unimplemented( 249 "https://bugzilla.mozilla.org/show_bug.cgi?id=1989116"); 250 251 case dom::GPUFeatureName::Core_features_and_limits: 252 // NOTE: `0` means that no bits are set in calling code, but this is on 253 // purpose. We currently _always_ return this feature elsewhere. If this 254 // actually corresponds to a value in the future, remove the 255 // unconditional setting of this feature! 256 return implemented(0); 257 } 258 MOZ_CRASH("Bad GPUFeatureName."); 259 } 260 }; 261 262 double GetLimitDefault(Limit aLimit) { 263 switch (aLimit) { 264 // clang-format off 265 case Limit::MaxTextureDimension1D: return 8192; 266 case Limit::MaxTextureDimension2D: return 8192; 267 case Limit::MaxTextureDimension3D: return 2048; 268 case Limit::MaxTextureArrayLayers: return 256; 269 case Limit::MaxBindGroups: return 4; 270 case Limit::MaxBindGroupsPlusVertexBuffers: return 24; 271 case Limit::MaxBindingsPerBindGroup: return 1000; 272 case Limit::MaxDynamicUniformBuffersPerPipelineLayout: return 8; 273 case Limit::MaxDynamicStorageBuffersPerPipelineLayout: return 4; 274 case Limit::MaxSampledTexturesPerShaderStage: return 16; 275 case Limit::MaxSamplersPerShaderStage: return 16; 276 case Limit::MaxStorageBuffersPerShaderStage: return 8; 277 case Limit::MaxStorageTexturesPerShaderStage: return 4; 278 case Limit::MaxUniformBuffersPerShaderStage: return 12; 279 case Limit::MaxUniformBufferBindingSize: return 65536; 280 case Limit::MaxStorageBufferBindingSize: return 134217728; 281 case Limit::MinUniformBufferOffsetAlignment: return 256; 282 case Limit::MinStorageBufferOffsetAlignment: return 256; 283 case Limit::MaxVertexBuffers: return 8; 284 case Limit::MaxBufferSize: return 268435456; 285 case Limit::MaxVertexAttributes: return 16; 286 case Limit::MaxVertexBufferArrayStride: return 2048; 287 case Limit::MaxInterStageShaderVariables: return 16; 288 case Limit::MaxColorAttachments: return 8; 289 case Limit::MaxColorAttachmentBytesPerSample: return 32; 290 case Limit::MaxComputeWorkgroupStorageSize: return 16384; 291 case Limit::MaxComputeInvocationsPerWorkgroup: return 256; 292 case Limit::MaxComputeWorkgroupSizeX: return 256; 293 case Limit::MaxComputeWorkgroupSizeY: return 256; 294 case Limit::MaxComputeWorkgroupSizeZ: return 64; 295 case Limit::MaxComputeWorkgroupsPerDimension: return 65535; 296 // clang-format on 297 } 298 MOZ_CRASH("Bad Limit"); 299 } 300 301 Adapter::Adapter(Instance* const aParent, WebGPUChild* const aChild, 302 const std::shared_ptr<ffi::WGPUAdapterInformation>& aInfo) 303 : ObjectBase(aChild, aInfo->id, ffi::wgpu_client_drop_adapter), 304 ChildOf(aParent), 305 mFeatures(new SupportedFeatures(this)), 306 mLimits(new SupportedLimits(this, aInfo->limits)), 307 mInfo(new AdapterInfo(this, aInfo)), 308 mInfoInner(aInfo) { 309 ErrorResult ignoredRv; // It's onerous to plumb this in from outside in this 310 // case, and we don't really need to. 311 312 static const auto FEATURE_BY_BIT = []() { 313 auto ret = 314 std::unordered_map<ffi::WGPUFeaturesWebGPU, dom::GPUFeatureName>{}; 315 316 for (const auto feature : 317 dom::MakeWebIDLEnumeratedRange<dom::GPUFeatureName>()) { 318 const auto status = FeatureImplementationStatus::fromDomFeature(feature); 319 switch (status.tag) { 320 case FeatureImplementationStatusTag::Implemented: 321 ret[status.value.implemented.wgpuBit] = feature; 322 break; 323 case FeatureImplementationStatusTag::NotImplemented: 324 break; 325 } 326 } 327 328 return ret; 329 }(); 330 331 auto remainingFeatureBits = aInfo->features; 332 auto bitMask = decltype(remainingFeatureBits){0}; 333 while (remainingFeatureBits) { 334 if (bitMask) { 335 bitMask <<= 1; 336 } else { 337 bitMask = 1; 338 } 339 const auto bit = remainingFeatureBits & bitMask; 340 remainingFeatureBits &= ~bitMask; // Clear bit. 341 if (!bit) { 342 continue; 343 } 344 345 const auto featureForBit = FEATURE_BY_BIT.find(bit); 346 if (featureForBit != FEATURE_BY_BIT.end()) { 347 mFeatures->Add(featureForBit->second, ignoredRv); 348 } else { 349 // One of two cases: 350 // 351 // 1. WGPU claims to implement this, but we've explicitly marked this as 352 // not implemented. 353 // 2. We don't recognize that bit, but maybe it's a wpgu-native-only 354 // feature. 355 } 356 } 357 // TODO: Once we implement compat mode (see 358 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1905951>), do not report this 359 // unconditionally. 360 // 361 // Meanwhile, the current spec. proposal's `Initialization` section (see 362 // <https://github.com/gpuweb/gpuweb/blob/main/proposals/compatibility-mode.md#initialization>) 363 // says: 364 // 365 // > Core-defaulting adapters *always* support the 366 // > `"core-features-and-limits"` feature. It is *automatically enabled* on 367 // > devices created from such adapters. 368 mFeatures->Add(dom::GPUFeatureName::Core_features_and_limits, ignoredRv); 369 370 // We clamp limits to defaults when requestDevice is called, but 371 // we return the actual limits when only requestAdapter is called. 372 // So, we should clamp the limits here too if we should RFP. 373 if (GetParentObject()->ShouldResistFingerprinting(RFPTarget::WebGPULimits)) { 374 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) { 375 SetLimit(mLimits->mFfi.get(), limit, GetLimitDefault(limit)); 376 } 377 } 378 } 379 380 Adapter::~Adapter() = default; 381 382 const RefPtr<SupportedFeatures>& Adapter::Features() const { return mFeatures; } 383 const RefPtr<SupportedLimits>& Adapter::Limits() const { return mLimits; } 384 const RefPtr<AdapterInfo>& Adapter::Info() const { return mInfo; } 385 386 bool Adapter::SupportSharedTextureInSwapChain() const { 387 return mInfoInner->support_use_shared_texture_in_swap_chain; 388 } 389 390 static std::string_view ToJsKey(const Limit limit) { 391 switch (limit) { 392 case Limit::MaxTextureDimension1D: 393 return "maxTextureDimension1D"; 394 case Limit::MaxTextureDimension2D: 395 return "maxTextureDimension2D"; 396 case Limit::MaxTextureDimension3D: 397 return "maxTextureDimension3D"; 398 case Limit::MaxTextureArrayLayers: 399 return "maxTextureArrayLayers"; 400 case Limit::MaxBindGroups: 401 return "maxBindGroups"; 402 case Limit::MaxBindGroupsPlusVertexBuffers: 403 return "maxBindGroupsPlusVertexBuffers"; 404 case Limit::MaxBindingsPerBindGroup: 405 return "maxBindingsPerBindGroup"; 406 case Limit::MaxDynamicUniformBuffersPerPipelineLayout: 407 return "maxDynamicUniformBuffersPerPipelineLayout"; 408 case Limit::MaxDynamicStorageBuffersPerPipelineLayout: 409 return "maxDynamicStorageBuffersPerPipelineLayout"; 410 case Limit::MaxSampledTexturesPerShaderStage: 411 return "maxSampledTexturesPerShaderStage"; 412 case Limit::MaxSamplersPerShaderStage: 413 return "maxSamplersPerShaderStage"; 414 case Limit::MaxStorageBuffersPerShaderStage: 415 return "maxStorageBuffersPerShaderStage"; 416 case Limit::MaxStorageTexturesPerShaderStage: 417 return "maxStorageTexturesPerShaderStage"; 418 case Limit::MaxUniformBuffersPerShaderStage: 419 return "maxUniformBuffersPerShaderStage"; 420 case Limit::MaxUniformBufferBindingSize: 421 return "maxUniformBufferBindingSize"; 422 case Limit::MaxStorageBufferBindingSize: 423 return "maxStorageBufferBindingSize"; 424 case Limit::MinUniformBufferOffsetAlignment: 425 return "minUniformBufferOffsetAlignment"; 426 case Limit::MinStorageBufferOffsetAlignment: 427 return "minStorageBufferOffsetAlignment"; 428 case Limit::MaxVertexBuffers: 429 return "maxVertexBuffers"; 430 case Limit::MaxBufferSize: 431 return "maxBufferSize"; 432 case Limit::MaxVertexAttributes: 433 return "maxVertexAttributes"; 434 case Limit::MaxVertexBufferArrayStride: 435 return "maxVertexBufferArrayStride"; 436 case Limit::MaxInterStageShaderVariables: 437 return "maxInterStageShaderVariables"; 438 case Limit::MaxColorAttachments: 439 return "maxColorAttachments"; 440 case Limit::MaxColorAttachmentBytesPerSample: 441 return "maxColorAttachmentBytesPerSample"; 442 case Limit::MaxComputeWorkgroupStorageSize: 443 return "maxComputeWorkgroupStorageSize"; 444 case Limit::MaxComputeInvocationsPerWorkgroup: 445 return "maxComputeInvocationsPerWorkgroup"; 446 case Limit::MaxComputeWorkgroupSizeX: 447 return "maxComputeWorkgroupSizeX"; 448 case Limit::MaxComputeWorkgroupSizeY: 449 return "maxComputeWorkgroupSizeY"; 450 case Limit::MaxComputeWorkgroupSizeZ: 451 return "maxComputeWorkgroupSizeZ"; 452 case Limit::MaxComputeWorkgroupsPerDimension: 453 return "maxComputeWorkgroupsPerDimension"; 454 } 455 MOZ_CRASH("Bad Limit"); 456 } 457 458 uint64_t Adapter::MissingFeatures() const { 459 uint64_t missingFeatures = 0; 460 461 // Turn on all implemented features. 462 for (const auto feature : 463 dom::MakeWebIDLEnumeratedRange<dom::GPUFeatureName>()) { 464 const auto status = FeatureImplementationStatus::fromDomFeature(feature); 465 switch (status.tag) { 466 case FeatureImplementationStatusTag::Implemented: 467 missingFeatures |= status.value.implemented.wgpuBit; 468 break; 469 case FeatureImplementationStatusTag::NotImplemented: 470 break; 471 } 472 } 473 474 // Turn off features that are supported by the adapter. 475 for (auto feature : mFeatures->Features()) { 476 const auto status = FeatureImplementationStatus::fromDomFeature(feature); 477 switch (status.tag) { 478 case FeatureImplementationStatusTag::Implemented: 479 missingFeatures &= ~status.value.implemented.wgpuBit; 480 break; 481 case FeatureImplementationStatusTag::NotImplemented: 482 break; 483 } 484 } 485 486 return missingFeatures; 487 } 488 489 // - 490 // String helpers 491 492 static auto ToACString(const nsAString& s) { return NS_ConvertUTF16toUTF8(s); } 493 494 // - 495 // Adapter::RequestDevice 496 497 already_AddRefed<dom::Promise> Adapter::RequestDevice( 498 const dom::GPUDeviceDescriptor& aDesc, ErrorResult& aRv) { 499 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv); 500 if (NS_WARN_IF(aRv.Failed())) { 501 return nullptr; 502 } 503 RefPtr<dom::Promise> lost_promise = 504 dom::Promise::Create(GetParentObject(), aRv); 505 if (NS_WARN_IF(aRv.Failed())) { 506 return nullptr; 507 } 508 509 ffi::WGPULimits deviceLimits = *mLimits->mFfi; 510 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) { 511 SetLimit(&deviceLimits, limit, GetLimitDefault(limit)); 512 } 513 514 // - 515 516 [&]() { // So that we can `return;` instead of `return promise.forget();`. 517 // - 518 // Validate Features 519 520 ffi::WGPUFeaturesWebGPU featureBits = 0; 521 for (const auto requested : aDesc.mRequiredFeatures) { 522 auto status = FeatureImplementationStatus::fromDomFeature(requested); 523 switch (status.tag) { 524 case FeatureImplementationStatusTag::Implemented: 525 featureBits |= status.value.implemented.wgpuBit; 526 break; 527 case FeatureImplementationStatusTag::NotImplemented: { 528 const auto featureStr = dom::GetEnumString(requested); 529 (void)featureStr; 530 nsPrintfCString msg( 531 "`GPUAdapter.requestDevice`: '%s' was requested in " 532 "`requiredFeatures`, but it is not supported by Firefox. " 533 "Follow <%s> for updates.", 534 featureStr.get(), status.value.unimplemented.bugzillaUrlAscii); 535 promise->MaybeRejectWithTypeError(msg); 536 return; 537 } 538 } 539 540 const bool supportedByAdapter = mFeatures->Features().count(requested); 541 if (!supportedByAdapter) { 542 const auto fstr = dom::GetEnumString(requested); 543 const auto astr = this->LabelOrId(); 544 nsPrintfCString msg( 545 "`GPUAdapter.requestDevice`: '%s' was requested in " 546 "`requiredFeatures`, but it is not supported by adapter %s.", 547 fstr.get(), astr.get()); 548 promise->MaybeRejectWithTypeError(msg); 549 return; 550 } 551 } 552 553 // - 554 // Validate Limits 555 556 if (aDesc.mRequiredLimits.WasPassed()) { 557 static const auto LIMIT_BY_JS_KEY = []() { 558 std::unordered_map<std::string_view, Limit> ret; 559 for (const auto limit : MakeInclusiveEnumeratedRange(Limit::_LAST)) { 560 const auto jsKeyU8 = ToJsKey(limit); 561 ret[jsKeyU8] = limit; 562 } 563 return ret; 564 }(); 565 566 for (const auto& entry : aDesc.mRequiredLimits.Value().Entries()) { 567 const auto& keyU16 = entry.mKey; 568 const nsCString keyU8 = ToACString(keyU16); 569 const auto itr = LIMIT_BY_JS_KEY.find(keyU8.get()); 570 if (itr == LIMIT_BY_JS_KEY.end()) { 571 nsPrintfCString msg("requestDevice: Limit '%s' not recognized.", 572 keyU8.get()); 573 promise->MaybeRejectWithOperationError(msg); 574 return; 575 } 576 577 const auto& limit = itr->second; 578 uint64_t requestedValue = entry.mValue; 579 const auto supportedValue = GetLimit(*mLimits->mFfi, limit); 580 if (StringBeginsWith(keyU8, "max"_ns)) { 581 if (requestedValue > supportedValue) { 582 nsPrintfCString msg( 583 "requestDevice: Request for limit '%s' must be <= supported " 584 "%s, was %s.", 585 keyU8.get(), std::to_string(supportedValue).c_str(), 586 std::to_string(requestedValue).c_str()); 587 promise->MaybeRejectWithOperationError(msg); 588 return; 589 } 590 // Clamp to default if lower than default 591 requestedValue = 592 std::max(requestedValue, GetLimit(deviceLimits, limit)); 593 } else { 594 MOZ_ASSERT(StringBeginsWith(keyU8, "min"_ns)); 595 if (requestedValue < supportedValue) { 596 nsPrintfCString msg( 597 "requestDevice: Request for limit '%s' must be >= supported " 598 "%s, was %s.", 599 keyU8.get(), std::to_string(supportedValue).c_str(), 600 std::to_string(requestedValue).c_str()); 601 promise->MaybeRejectWithOperationError(msg); 602 return; 603 } 604 if (StringEndsWith(keyU8, "Alignment"_ns)) { 605 if (!IsPowerOfTwo(requestedValue)) { 606 nsPrintfCString msg( 607 "requestDevice: Request for limit '%s' must be a power of " 608 "two, " 609 "was %s.", 610 keyU8.get(), std::to_string(requestedValue).c_str()); 611 promise->MaybeRejectWithOperationError(msg); 612 return; 613 } 614 } 615 /// Clamp to default if higher than default 616 /// Changing implementation in a way that increases fingerprinting 617 /// surface? Please create a bug in [Core::Privacy: Anti 618 /// Tracking](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Privacy%3A%20Anti-Tracking) 619 requestedValue = 620 std::min(requestedValue, GetLimit(deviceLimits, limit)); 621 } 622 623 SetLimit(&deviceLimits, limit, requestedValue); 624 } 625 } 626 627 // - 628 629 RefPtr<SupportedFeatures> features = new SupportedFeatures(this); 630 for (const auto& feature : aDesc.mRequiredFeatures) { 631 features->Add(feature, aRv); 632 } 633 // TODO: Once we implement compat mode (see 634 // <https://bugzilla.mozilla.org/show_bug.cgi?id=1905951>), do not report 635 // this unconditionally. 636 // 637 // Meanwhile, the current spec. proposal's `Initialization` section (see 638 // <https://github.com/gpuweb/gpuweb/blob/main/proposals/compatibility-mode.md#initialization>) 639 // says: 640 // 641 // > Core-defaulting adapters *always* support the 642 // > `"core-features-and-limits"` feature. It is *automatically enabled* on 643 // > devices created from such adapters. 644 features->Add(dom::GPUFeatureName::Core_features_and_limits, aRv); 645 646 RefPtr<SupportedLimits> limits = new SupportedLimits(this, deviceLimits); 647 648 ffi::WGPUFfiDeviceDescriptor ffiDesc = {}; 649 ffiDesc.required_features = featureBits; 650 ffiDesc.required_limits = deviceLimits; 651 652 ffi::WGPUDeviceQueueId ids = 653 ffi::wgpu_client_request_device(GetClient(), GetId(), &ffiDesc); 654 655 auto pending_promise = WebGPUChild::PendingRequestDevicePromise{ 656 RefPtr(promise), ids.device, ids.queue, aDesc.mLabel, RefPtr(this), 657 features, limits, mInfo, lost_promise}; 658 GetChild()->mPendingRequestDevicePromises.push_back( 659 std::move(pending_promise)); 660 661 }(); 662 663 return promise.forget(); 664 } 665 666 } // namespace mozilla::webgpu