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"
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");
40 case ffi::WGPUDeviceType_DiscreteGpu
:
41 s
.AssignLiteral("DiscreteGpu");
43 case ffi::WGPUDeviceType_IntegratedGpu
:
44 s
.AssignLiteral("IntegratedGpu");
46 case ffi::WGPUDeviceType_VirtualGpu
:
47 s
.AssignLiteral("VirtualGpu");
49 case ffi::WGPUDeviceType_Other
:
50 s
.AssignLiteral("Other");
52 case ffi::WGPUDeviceType_Sentinel
:
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");
71 case ffi::WGPUBackend_Vulkan
:
72 s
.AssignLiteral("Vulkan");
74 case ffi::WGPUBackend_Metal
:
75 s
.AssignLiteral("Metal");
77 case ffi::WGPUBackend_Dx12
:
78 s
.AssignLiteral("Dx12");
80 case ffi::WGPUBackend_Dx11
:
81 s
.AssignLiteral("Dx11");
83 case ffi::WGPUBackend_Gl
:
84 s
.AssignLiteral("Gl");
86 case ffi::WGPUBackend_BrowserWebGpu
: // This should never happen, because
87 // we _are_ the browser.
88 case ffi::WGPUBackend_Sentinel
:
91 MOZ_CRASH("Bad `ffi::WGPUBackend`");
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
) {
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
133 return Nothing(); // TODO
135 case dom::GPUFeatureName::Float32_filterable
:
136 #ifdef WGPUFeatures_FLOAT32_FILTERABLE
139 return Nothing(); // TODO
141 case dom::GPUFeatureName::EndGuard_
:
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
);
153 const auto featureStr
= dom::GPUFeatureNameValues::GetString(feature
);
156 nsPrintfCString("Requested feature bit for '%s' is not implemented.",
166 Adapter::Adapter(Instance
* const aParent
, WebGPUChild
* const aBridge
,
167 const std::shared_ptr
<ffi::WGPUAdapterInformation
>& aInfo
)
171 mFeatures(new SupportedFeatures(this)),
172 mLimits(new SupportedLimits(this, aInfo
->limits
)),
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.
187 ret
[*bitForFeature
] = feature
;
193 auto remainingFeatureBits
= aInfo
->features
;
194 auto bitMask
= decltype(remainingFeatureBits
){0};
195 while (remainingFeatureBits
) {
201 const auto bit
= remainingFeatureBits
& bitMask
;
202 remainingFeatureBits
&= ~bitMask
; // Clear bit.
207 const auto featureForBit
= FEATURE_BY_BIT
.find(bit
);
208 if (featureForBit
!= FEATURE_BY_BIT
.end()) {
209 mFeatures
->Add(featureForBit
->second
, ignoredRv
);
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()) {
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
) {
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");
304 static auto ToACString(const nsAString
& s
) { return NS_ConvertUTF16toUTF8(s
); }
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())) {
316 ffi::WGPULimits deviceLimits
= *mLimits
->mFfi
;
317 for (const auto limit
: MakeInclusiveEnumeratedRange(Limit::_LAST
)) {
318 const auto defaultValue
= [&]() -> double {
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;
355 MOZ_CRASH("Bad Limit");
357 SetLimit(&deviceLimits
, limit
, defaultValue
);
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");
372 for (const auto requested
: aDesc
.mRequiredFeatures
) {
373 const bool supported
= mFeatures
->Features().count(requested
);
375 const auto fstr
= dom::GPUFeatureNameValues::GetString(requested
);
376 const auto astr
= this->LabelOrId();
378 "requestDevice: Feature '%s' requested must be supported by "
380 fstr
.data(), astr
.get());
381 promise
->MaybeRejectWithTypeError(msg
);
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
;
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.",
406 promise
->MaybeRejectWithOperationError(msg
);
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
) {
416 "requestDevice: Request for limit '%s' must be <= supported "
418 keyU8
.get(), std::to_string(supportedValue
).c_str(),
419 std::to_string(requestedValue
).c_str());
420 promise
->MaybeRejectWithOperationError(msg
);
423 // Clamp to default if lower than default
425 std::max(requestedValue
, GetLimit(deviceLimits
, limit
));
427 MOZ_ASSERT(StringBeginsWith(keyU8
, "min"_ns
));
428 if (requestedValue
< supportedValue
) {
430 "requestDevice: Request for limit '%s' must be >= supported "
432 keyU8
.get(), std::to_string(supportedValue
).c_str(),
433 std::to_string(requestedValue
).c_str());
434 promise
->MaybeRejectWithOperationError(msg
);
437 if (StringEndsWith(keyU8
, "Alignment"_ns
)) {
438 if (!IsPowerOfTwo(requestedValue
)) {
440 "requestDevice: Request for limit '%s' must be a power of "
443 keyU8
.get(), std::to_string(requestedValue
).c_str());
444 promise
->MaybeRejectWithOperationError(msg
);
448 /// Clamp to default if higher than default
450 std::min(requestedValue
, GetLimit(deviceLimits
, limit
));
453 SetLimit(&deviceLimits
, limit
, requestedValue
);
459 ffi::WGPUDeviceDescriptor ffiDesc
= {};
460 ffiDesc
.features
= *MakeFeatureBits(aDesc
.mRequiredFeatures
);
461 ffiDesc
.limits
= deviceLimits
;
462 auto request
= mBridge
->AdapterRequestDevice(mId
, ffiDesc
);
464 promise
->MaybeRejectWithNotSupportedError(
465 "Unable to instantiate a Device");
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
) {
477 promise
->MaybeResolve(device
);
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");
496 return promise
.forget();
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