Bug 1883853 [wpt PR 44937] - [wdspec] fix test_set_permission_origin_unknown, a=testonly
[gecko.git] / dom / webgpu / Device.cpp
bloba9fd5ee44c12e35d631e9ca487f52b557fa74cc7
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 "js/ArrayBuffer.h"
7 #include "js/Value.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/ErrorResult.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/dom/Console.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/WebGPUBinding.h"
15 #include "Device.h"
16 #include "CommandEncoder.h"
17 #include "BindGroup.h"
19 #include "Adapter.h"
20 #include "Buffer.h"
21 #include "CompilationInfo.h"
22 #include "ComputePipeline.h"
23 #include "DeviceLostInfo.h"
24 #include "InternalError.h"
25 #include "OutOfMemoryError.h"
26 #include "PipelineLayout.h"
27 #include "Queue.h"
28 #include "RenderBundleEncoder.h"
29 #include "RenderPipeline.h"
30 #include "Sampler.h"
31 #include "SupportedFeatures.h"
32 #include "SupportedLimits.h"
33 #include "Texture.h"
34 #include "TextureView.h"
35 #include "ValidationError.h"
36 #include "ipc/WebGPUChild.h"
37 #include "Utility.h"
38 #include "nsGlobalWindowInner.h"
40 namespace mozilla::webgpu {
42 mozilla::LazyLogModule gWebGPULog("WebGPU");
44 GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(Device, DOMEventTargetHelper,
45 mBridge, mQueue, mFeatures,
46 mLimits, mLostPromise);
47 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device, DOMEventTargetHelper)
48 GPU_IMPL_JS_WRAP(Device)
50 /* static */ CheckedInt<uint32_t> Device::BufferStrideWithMask(
51 const gfx::IntSize& aSize, const gfx::SurfaceFormat& aFormat) {
52 constexpr uint32_t kBufferAlignmentMask = 0xff;
53 return CheckedInt<uint32_t>(aSize.width) * gfx::BytesPerPixel(aFormat) +
54 kBufferAlignmentMask;
57 RefPtr<WebGPUChild> Device::GetBridge() { return mBridge; }
59 Device::Device(Adapter* const aParent, RawId aId,
60 const ffi::WGPULimits& aRawLimits)
61 : DOMEventTargetHelper(aParent->GetParentObject()),
62 mId(aId),
63 // features are filled in Adapter::RequestDevice
64 mFeatures(new SupportedFeatures(aParent)),
65 mLimits(new SupportedLimits(aParent, aRawLimits)),
66 mBridge(aParent->mBridge),
67 mQueue(new class Queue(this, aParent->mBridge, aId)) {
68 mBridge->RegisterDevice(this);
71 Device::~Device() { Cleanup(); }
73 void Device::Cleanup() {
74 if (!mValid) {
75 return;
78 mValid = false;
80 if (mBridge) {
81 mBridge->UnregisterDevice(mId);
85 void Device::CleanupUnregisteredInParent() {
86 if (mBridge) {
87 mBridge->FreeUnregisteredInParentDevice(mId);
89 mValid = false;
92 bool Device::IsLost() const {
93 return !mBridge || !mBridge->CanSend() ||
94 (mLostPromise &&
95 (mLostPromise->State() != dom::Promise::PromiseState::Pending));
98 bool Device::IsBridgeAlive() const { return mBridge && mBridge->CanSend(); }
100 // Generate an error on the Device timeline for this device.
102 // aMessage is interpreted as UTF-8.
103 void Device::GenerateValidationError(const nsCString& aMessage) {
104 if (!IsBridgeAlive()) {
105 return; // Just drop it?
107 mBridge->SendGenerateError(Some(mId), dom::GPUErrorFilter::Validation,
108 aMessage);
111 void Device::TrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Insert(aBuffer); }
113 void Device::UntrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Remove(aBuffer); }
115 void Device::GetLabel(nsAString& aValue) const { aValue = mLabel; }
116 void Device::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
118 dom::Promise* Device::GetLost(ErrorResult& aRv) {
119 aRv = NS_OK;
120 if (!mLostPromise) {
121 mLostPromise = dom::Promise::Create(GetParentObject(), aRv);
122 if (mLostPromise && !mBridge->CanSend()) {
123 auto info = MakeRefPtr<DeviceLostInfo>(GetParentObject(),
124 u"WebGPUChild destroyed"_ns);
125 mLostPromise->MaybeResolve(info);
128 return mLostPromise;
131 void Device::ResolveLost(Maybe<dom::GPUDeviceLostReason> aReason,
132 const nsAString& aMessage) {
133 IgnoredErrorResult rv;
134 dom::Promise* lostPromise = GetLost(rv);
135 if (!lostPromise) {
136 // Promise doesn't exist? Maybe out of memory.
137 return;
139 if (!lostPromise->PromiseObj()) {
140 // The underlying JS object is gone.
141 return;
143 if (lostPromise->State() != dom::Promise::PromiseState::Pending) {
144 // lostPromise was already resolved or rejected.
145 return;
147 RefPtr<DeviceLostInfo> info;
148 if (aReason.isSome()) {
149 info = MakeRefPtr<DeviceLostInfo>(GetParentObject(), *aReason, aMessage);
150 } else {
151 info = MakeRefPtr<DeviceLostInfo>(GetParentObject(), aMessage);
153 lostPromise->MaybeResolve(info);
156 already_AddRefed<Buffer> Device::CreateBuffer(
157 const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
158 return Buffer::Create(this, mId, aDesc, aRv);
161 already_AddRefed<Texture> Device::CreateTextureForSwapChain(
162 const dom::GPUCanvasConfiguration* const aConfig,
163 const gfx::IntSize& aCanvasSize, layers::RemoteTextureOwnerId aOwnerId) {
164 MOZ_ASSERT(aConfig);
166 dom::GPUTextureDescriptor desc;
167 desc.mDimension = dom::GPUTextureDimension::_2d;
168 auto& sizeDict = desc.mSize.SetAsGPUExtent3DDict();
169 sizeDict.mWidth = aCanvasSize.width;
170 sizeDict.mHeight = aCanvasSize.height;
171 sizeDict.mDepthOrArrayLayers = 1;
172 desc.mFormat = aConfig->mFormat;
173 desc.mMipLevelCount = 1;
174 desc.mSampleCount = 1;
175 desc.mUsage = aConfig->mUsage | dom::GPUTextureUsage_Binding::COPY_SRC;
176 desc.mViewFormats = aConfig->mViewFormats;
178 return CreateTexture(desc, Some(aOwnerId));
181 already_AddRefed<Texture> Device::CreateTexture(
182 const dom::GPUTextureDescriptor& aDesc) {
183 return CreateTexture(aDesc, /* aOwnerId */ Nothing());
186 already_AddRefed<Texture> Device::CreateTexture(
187 const dom::GPUTextureDescriptor& aDesc,
188 Maybe<layers::RemoteTextureOwnerId> aOwnerId) {
189 ffi::WGPUTextureDescriptor desc = {};
191 webgpu::StringHelper label(aDesc.mLabel);
192 desc.label = label.Get();
194 if (aDesc.mSize.IsRangeEnforcedUnsignedLongSequence()) {
195 const auto& seq = aDesc.mSize.GetAsRangeEnforcedUnsignedLongSequence();
196 desc.size.width = seq.Length() > 0 ? seq[0] : 1;
197 desc.size.height = seq.Length() > 1 ? seq[1] : 1;
198 desc.size.depth_or_array_layers = seq.Length() > 2 ? seq[2] : 1;
199 } else if (aDesc.mSize.IsGPUExtent3DDict()) {
200 const auto& dict = aDesc.mSize.GetAsGPUExtent3DDict();
201 desc.size.width = dict.mWidth;
202 desc.size.height = dict.mHeight;
203 desc.size.depth_or_array_layers = dict.mDepthOrArrayLayers;
204 } else {
205 MOZ_CRASH("Unexpected union");
207 desc.mip_level_count = aDesc.mMipLevelCount;
208 desc.sample_count = aDesc.mSampleCount;
209 desc.dimension = ffi::WGPUTextureDimension(aDesc.mDimension);
210 desc.format = ConvertTextureFormat(aDesc.mFormat);
211 desc.usage = aDesc.mUsage;
213 AutoTArray<ffi::WGPUTextureFormat, 8> viewFormats;
214 for (auto format : aDesc.mViewFormats) {
215 viewFormats.AppendElement(ConvertTextureFormat(format));
217 desc.view_formats = {viewFormats.Elements(), viewFormats.Length()};
219 Maybe<ffi::WGPUSwapChainId> ownerId;
220 if (aOwnerId.isSome()) {
221 ownerId = Some(ffi::WGPUSwapChainId{aOwnerId->mId});
224 ipc::ByteBuf bb;
225 RawId id = ffi::wgpu_client_create_texture(
226 mBridge->GetClient(), mId, &desc, ownerId.ptrOr(nullptr), ToFFI(&bb));
228 if (mBridge->CanSend()) {
229 mBridge->SendDeviceAction(mId, std::move(bb));
232 RefPtr<Texture> texture = new Texture(this, id, aDesc);
233 return texture.forget();
236 already_AddRefed<Sampler> Device::CreateSampler(
237 const dom::GPUSamplerDescriptor& aDesc) {
238 ffi::WGPUSamplerDescriptor desc = {};
239 webgpu::StringHelper label(aDesc.mLabel);
241 desc.label = label.Get();
242 desc.address_modes[0] = ffi::WGPUAddressMode(aDesc.mAddressModeU);
243 desc.address_modes[1] = ffi::WGPUAddressMode(aDesc.mAddressModeV);
244 desc.address_modes[2] = ffi::WGPUAddressMode(aDesc.mAddressModeW);
245 desc.mag_filter = ffi::WGPUFilterMode(aDesc.mMagFilter);
246 desc.min_filter = ffi::WGPUFilterMode(aDesc.mMinFilter);
247 desc.mipmap_filter = ffi::WGPUFilterMode(aDesc.mMipmapFilter);
248 desc.lod_min_clamp = aDesc.mLodMinClamp;
249 desc.lod_max_clamp = aDesc.mLodMaxClamp;
250 desc.max_anisotropy = aDesc.mMaxAnisotropy;
252 ffi::WGPUCompareFunction comparison = ffi::WGPUCompareFunction_Sentinel;
253 if (aDesc.mCompare.WasPassed()) {
254 comparison = ConvertCompareFunction(aDesc.mCompare.Value());
255 desc.compare = &comparison;
258 ipc::ByteBuf bb;
259 RawId id = ffi::wgpu_client_create_sampler(mBridge->GetClient(), mId, &desc,
260 ToFFI(&bb));
262 if (mBridge->CanSend()) {
263 mBridge->SendDeviceAction(mId, std::move(bb));
266 RefPtr<Sampler> sampler = new Sampler(this, id);
267 return sampler.forget();
270 already_AddRefed<CommandEncoder> Device::CreateCommandEncoder(
271 const dom::GPUCommandEncoderDescriptor& aDesc) {
272 ffi::WGPUCommandEncoderDescriptor desc = {};
274 webgpu::StringHelper label(aDesc.mLabel);
275 desc.label = label.Get();
277 ipc::ByteBuf bb;
278 RawId id = ffi::wgpu_client_create_command_encoder(mBridge->GetClient(), mId,
279 &desc, ToFFI(&bb));
280 if (mBridge->CanSend()) {
281 mBridge->SendDeviceAction(mId, std::move(bb));
284 RefPtr<CommandEncoder> encoder = new CommandEncoder(this, mBridge, id);
285 return encoder.forget();
288 already_AddRefed<RenderBundleEncoder> Device::CreateRenderBundleEncoder(
289 const dom::GPURenderBundleEncoderDescriptor& aDesc) {
290 RefPtr<RenderBundleEncoder> encoder =
291 new RenderBundleEncoder(this, mBridge, aDesc);
292 return encoder.forget();
295 already_AddRefed<BindGroupLayout> Device::CreateBindGroupLayout(
296 const dom::GPUBindGroupLayoutDescriptor& aDesc) {
297 struct OptionalData {
298 ffi::WGPUTextureViewDimension dim;
299 ffi::WGPURawTextureSampleType type;
300 ffi::WGPUTextureFormat format;
302 nsTArray<OptionalData> optional(aDesc.mEntries.Length());
303 for (const auto& entry : aDesc.mEntries) {
304 OptionalData data = {};
305 if (entry.mTexture.WasPassed()) {
306 const auto& texture = entry.mTexture.Value();
307 data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
308 switch (texture.mSampleType) {
309 case dom::GPUTextureSampleType::Float:
310 data.type = ffi::WGPURawTextureSampleType_Float;
311 break;
312 case dom::GPUTextureSampleType::Unfilterable_float:
313 data.type = ffi::WGPURawTextureSampleType_UnfilterableFloat;
314 break;
315 case dom::GPUTextureSampleType::Uint:
316 data.type = ffi::WGPURawTextureSampleType_Uint;
317 break;
318 case dom::GPUTextureSampleType::Sint:
319 data.type = ffi::WGPURawTextureSampleType_Sint;
320 break;
321 case dom::GPUTextureSampleType::Depth:
322 data.type = ffi::WGPURawTextureSampleType_Depth;
323 break;
326 if (entry.mStorageTexture.WasPassed()) {
327 const auto& texture = entry.mStorageTexture.Value();
328 data.dim = ffi::WGPUTextureViewDimension(texture.mViewDimension);
329 data.format = ConvertTextureFormat(texture.mFormat);
331 optional.AppendElement(data);
334 nsTArray<ffi::WGPUBindGroupLayoutEntry> entries(aDesc.mEntries.Length());
335 for (size_t i = 0; i < aDesc.mEntries.Length(); ++i) {
336 const auto& entry = aDesc.mEntries[i];
337 ffi::WGPUBindGroupLayoutEntry e = {};
338 e.binding = entry.mBinding;
339 e.visibility = entry.mVisibility;
340 if (entry.mBuffer.WasPassed()) {
341 switch (entry.mBuffer.Value().mType) {
342 case dom::GPUBufferBindingType::Uniform:
343 e.ty = ffi::WGPURawBindingType_UniformBuffer;
344 break;
345 case dom::GPUBufferBindingType::Storage:
346 e.ty = ffi::WGPURawBindingType_StorageBuffer;
347 break;
348 case dom::GPUBufferBindingType::Read_only_storage:
349 e.ty = ffi::WGPURawBindingType_ReadonlyStorageBuffer;
350 break;
352 e.has_dynamic_offset = entry.mBuffer.Value().mHasDynamicOffset;
354 if (entry.mTexture.WasPassed()) {
355 e.ty = ffi::WGPURawBindingType_SampledTexture;
356 e.view_dimension = &optional[i].dim;
357 e.texture_sample_type = &optional[i].type;
358 e.multisampled = entry.mTexture.Value().mMultisampled;
360 if (entry.mStorageTexture.WasPassed()) {
361 switch (entry.mStorageTexture.Value().mAccess) {
362 case dom::GPUStorageTextureAccess::Write_only: {
363 e.ty = ffi::WGPURawBindingType_WriteonlyStorageTexture;
364 break;
366 case dom::GPUStorageTextureAccess::Read_only: {
367 e.ty = ffi::WGPURawBindingType_ReadonlyStorageTexture;
368 break;
370 case dom::GPUStorageTextureAccess::Read_write: {
371 e.ty = ffi::WGPURawBindingType_ReadWriteStorageTexture;
372 break;
374 default: {
375 MOZ_ASSERT_UNREACHABLE();
378 e.view_dimension = &optional[i].dim;
379 e.storage_texture_format = &optional[i].format;
381 if (entry.mSampler.WasPassed()) {
382 e.ty = ffi::WGPURawBindingType_Sampler;
383 switch (entry.mSampler.Value().mType) {
384 case dom::GPUSamplerBindingType::Filtering:
385 e.sampler_filter = true;
386 break;
387 case dom::GPUSamplerBindingType::Non_filtering:
388 break;
389 case dom::GPUSamplerBindingType::Comparison:
390 e.sampler_compare = true;
391 break;
394 entries.AppendElement(e);
397 ffi::WGPUBindGroupLayoutDescriptor desc = {};
399 webgpu::StringHelper label(aDesc.mLabel);
400 desc.label = label.Get();
401 desc.entries = entries.Elements();
402 desc.entries_length = entries.Length();
404 ipc::ByteBuf bb;
405 RawId id = ffi::wgpu_client_create_bind_group_layout(mBridge->GetClient(),
406 mId, &desc, ToFFI(&bb));
407 if (mBridge->CanSend()) {
408 mBridge->SendDeviceAction(mId, std::move(bb));
411 RefPtr<BindGroupLayout> object = new BindGroupLayout(this, id, true);
412 return object.forget();
415 already_AddRefed<PipelineLayout> Device::CreatePipelineLayout(
416 const dom::GPUPipelineLayoutDescriptor& aDesc) {
417 nsTArray<ffi::WGPUBindGroupLayoutId> bindGroupLayouts(
418 aDesc.mBindGroupLayouts.Length());
420 for (const auto& layout : aDesc.mBindGroupLayouts) {
421 bindGroupLayouts.AppendElement(layout->mId);
424 ffi::WGPUPipelineLayoutDescriptor desc = {};
426 webgpu::StringHelper label(aDesc.mLabel);
427 desc.label = label.Get();
428 desc.bind_group_layouts = bindGroupLayouts.Elements();
429 desc.bind_group_layouts_length = bindGroupLayouts.Length();
431 ipc::ByteBuf bb;
432 RawId id = ffi::wgpu_client_create_pipeline_layout(mBridge->GetClient(), mId,
433 &desc, ToFFI(&bb));
434 if (mBridge->CanSend()) {
435 mBridge->SendDeviceAction(mId, std::move(bb));
438 RefPtr<PipelineLayout> object = new PipelineLayout(this, id);
439 return object.forget();
442 already_AddRefed<BindGroup> Device::CreateBindGroup(
443 const dom::GPUBindGroupDescriptor& aDesc) {
444 nsTArray<ffi::WGPUBindGroupEntry> entries(aDesc.mEntries.Length());
445 for (const auto& entry : aDesc.mEntries) {
446 ffi::WGPUBindGroupEntry e = {};
447 e.binding = entry.mBinding;
448 if (entry.mResource.IsGPUBufferBinding()) {
449 const auto& bufBinding = entry.mResource.GetAsGPUBufferBinding();
450 if (!bufBinding.mBuffer->mId) {
451 NS_WARNING("Buffer binding has no id -- ignoring.");
452 continue;
454 e.buffer = bufBinding.mBuffer->mId;
455 e.offset = bufBinding.mOffset;
456 e.size = bufBinding.mSize.WasPassed() ? bufBinding.mSize.Value() : 0;
457 } else if (entry.mResource.IsGPUTextureView()) {
458 e.texture_view = entry.mResource.GetAsGPUTextureView()->mId;
459 } else if (entry.mResource.IsGPUSampler()) {
460 e.sampler = entry.mResource.GetAsGPUSampler()->mId;
461 } else {
462 // Not a buffer, nor a texture view, nor a sampler. If we pass
463 // this to wgpu_client, it'll panic. Log a warning instead and
464 // ignore this entry.
465 NS_WARNING("Bind group entry has unknown type.");
466 continue;
468 entries.AppendElement(e);
471 ffi::WGPUBindGroupDescriptor desc = {};
473 webgpu::StringHelper label(aDesc.mLabel);
474 desc.label = label.Get();
475 desc.layout = aDesc.mLayout->mId;
476 desc.entries = entries.Elements();
477 desc.entries_length = entries.Length();
479 ipc::ByteBuf bb;
480 RawId id = ffi::wgpu_client_create_bind_group(mBridge->GetClient(), mId,
481 &desc, ToFFI(&bb));
482 if (mBridge->CanSend()) {
483 mBridge->SendDeviceAction(mId, std::move(bb));
486 RefPtr<BindGroup> object = new BindGroup(this, id);
487 return object.forget();
490 MOZ_CAN_RUN_SCRIPT void reportCompilationMessagesToConsole(
491 const RefPtr<ShaderModule>& aShaderModule,
492 const nsTArray<WebGPUCompilationMessage>& aMessages) {
493 auto* global = aShaderModule->GetParentObject();
495 dom::AutoJSAPI api;
496 if (!api.Init(global)) {
497 return;
500 const auto& cx = api.cx();
502 ErrorResult rv;
503 RefPtr<dom::Console> console =
504 nsGlobalWindowInner::Cast(global->GetAsInnerWindow())->GetConsole(cx, rv);
505 if (rv.Failed()) {
506 return;
509 dom::GlobalObject globalObj(cx, global->GetGlobalJSObject());
511 dom::Sequence<JS::Value> args;
512 dom::SequenceRooter<JS::Value> msgArgsRooter(cx, &args);
513 auto SetSingleStrAsArgs =
514 [&](const nsString& message, dom::Sequence<JS::Value>* args)
515 MOZ_CAN_RUN_SCRIPT {
516 args->Clear();
517 JS::Rooted<JSString*> jsStr(
518 cx, JS_NewUCStringCopyN(cx, message.Data(), message.Length()));
519 if (!jsStr) {
520 return;
522 JS::Rooted<JS::Value> val(cx, JS::StringValue(jsStr));
523 if (!args->AppendElement(val, fallible)) {
524 return;
528 nsString label;
529 aShaderModule->GetLabel(label);
530 auto appendNiceLabelIfPresent = [&label](nsString* buf) MOZ_CAN_RUN_SCRIPT {
531 if (!label.IsEmpty()) {
532 buf->AppendLiteral(u" \"");
533 buf->Append(label);
534 buf->AppendLiteral(u"\"");
538 // We haven't actually inspected a message for severity, but
539 // it doesn't actually matter, since we don't do anything at
540 // this level.
541 auto highestSeveritySeen = WebGPUCompilationMessageType::Info;
542 uint64_t errorCount = 0;
543 uint64_t warningCount = 0;
544 uint64_t infoCount = 0;
545 for (const auto& message : aMessages) {
546 bool higherThanSeen =
547 static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>(
548 message.messageType) <
549 static_cast<std::underlying_type_t<WebGPUCompilationMessageType>>(
550 highestSeveritySeen);
551 if (higherThanSeen) {
552 highestSeveritySeen = message.messageType;
554 switch (message.messageType) {
555 case WebGPUCompilationMessageType::Error:
556 errorCount += 1;
557 break;
558 case WebGPUCompilationMessageType::Warning:
559 warningCount += 1;
560 break;
561 case WebGPUCompilationMessageType::Info:
562 infoCount += 1;
563 break;
566 switch (highestSeveritySeen) {
567 case WebGPUCompilationMessageType::Info:
568 // shouldn't happen, but :shrug:
569 break;
570 case WebGPUCompilationMessageType::Warning: {
571 nsString msg(
572 u"Encountered one or more warnings while creating shader module");
573 appendNiceLabelIfPresent(&msg);
574 SetSingleStrAsArgs(msg, &args);
575 console->Warn(globalObj, args);
576 break;
578 case WebGPUCompilationMessageType::Error: {
579 nsString msg(
580 u"Encountered one or more errors while creating shader module");
581 appendNiceLabelIfPresent(&msg);
582 SetSingleStrAsArgs(msg, &args);
583 console->Error(globalObj, args);
584 break;
588 nsString header;
589 header.AppendLiteral(u"WebGPU compilation info for shader module");
590 appendNiceLabelIfPresent(&header);
591 header.AppendLiteral(u" (");
592 header.AppendInt(errorCount);
593 header.AppendLiteral(u" error(s), ");
594 header.AppendInt(warningCount);
595 header.AppendLiteral(u" warning(s), ");
596 header.AppendInt(infoCount);
597 header.AppendLiteral(u" info)");
598 SetSingleStrAsArgs(header, &args);
599 console->GroupCollapsed(globalObj, args);
601 for (const auto& message : aMessages) {
602 SetSingleStrAsArgs(message.message, &args);
603 switch (message.messageType) {
604 case WebGPUCompilationMessageType::Error:
605 console->Error(globalObj, args);
606 break;
607 case WebGPUCompilationMessageType::Warning:
608 console->Warn(globalObj, args);
609 break;
610 case WebGPUCompilationMessageType::Info:
611 console->Info(globalObj, args);
612 break;
615 console->GroupEnd(globalObj);
618 already_AddRefed<ShaderModule> Device::CreateShaderModule(
619 JSContext* aCx, const dom::GPUShaderModuleDescriptor& aDesc,
620 ErrorResult& aRv) {
621 Unused << aCx;
623 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
624 if (NS_WARN_IF(aRv.Failed())) {
625 return nullptr;
628 RawId moduleId =
629 ffi::wgpu_client_make_shader_module_id(mBridge->GetClient(), mId);
631 RefPtr<ShaderModule> shaderModule = new ShaderModule(this, moduleId, promise);
633 shaderModule->SetLabel(aDesc.mLabel);
635 RefPtr<Device> device = this;
637 if (mBridge->CanSend()) {
638 mBridge
639 ->SendDeviceCreateShaderModule(mId, moduleId, aDesc.mLabel, aDesc.mCode)
640 ->Then(
641 GetCurrentSerialEventTarget(), __func__,
642 [promise, device,
643 shaderModule](nsTArray<WebGPUCompilationMessage>&& messages)
644 MOZ_CAN_RUN_SCRIPT {
645 if (!messages.IsEmpty()) {
646 reportCompilationMessagesToConsole(shaderModule,
647 std::cref(messages));
649 RefPtr<CompilationInfo> infoObject(
650 new CompilationInfo(device));
651 infoObject->SetMessages(messages);
652 promise->MaybeResolve(infoObject);
654 [promise](const ipc::ResponseRejectReason& aReason) {
655 promise->MaybeRejectWithNotSupportedError("IPC error");
657 } else {
658 promise->MaybeRejectWithNotSupportedError("IPC error");
661 return shaderModule.forget();
664 RawId CreateComputePipelineImpl(PipelineCreationContext* const aContext,
665 WebGPUChild* aBridge,
666 const dom::GPUComputePipelineDescriptor& aDesc,
667 ipc::ByteBuf* const aByteBuf) {
668 ffi::WGPUComputePipelineDescriptor desc = {};
669 nsCString entryPoint;
671 webgpu::StringHelper label(aDesc.mLabel);
672 desc.label = label.Get();
674 if (aDesc.mLayout.IsGPUAutoLayoutMode()) {
675 desc.layout = 0;
676 } else if (aDesc.mLayout.IsGPUPipelineLayout()) {
677 desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->mId;
678 } else {
679 MOZ_ASSERT_UNREACHABLE();
681 desc.stage.module = aDesc.mCompute.mModule->mId;
682 if (aDesc.mCompute.mEntryPoint.WasPassed()) {
683 CopyUTF16toUTF8(aDesc.mCompute.mEntryPoint.Value(), entryPoint);
684 desc.stage.entry_point = entryPoint.get();
685 } else {
686 desc.stage.entry_point = nullptr;
689 RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {};
690 RawId id = ffi::wgpu_client_create_compute_pipeline(
691 aBridge->GetClient(), aContext->mParentId, &desc, ToFFI(aByteBuf),
692 &aContext->mImplicitPipelineLayoutId, implicit_bgl_ids);
694 for (const auto& cur : implicit_bgl_ids) {
695 if (!cur) break;
696 aContext->mImplicitBindGroupLayoutIds.AppendElement(cur);
699 return id;
702 RawId CreateRenderPipelineImpl(PipelineCreationContext* const aContext,
703 WebGPUChild* aBridge,
704 const dom::GPURenderPipelineDescriptor& aDesc,
705 ipc::ByteBuf* const aByteBuf) {
706 // A bunch of stack locals that we can have pointers into
707 nsTArray<ffi::WGPUVertexBufferLayout> vertexBuffers;
708 nsTArray<ffi::WGPUVertexAttribute> vertexAttributes;
709 ffi::WGPURenderPipelineDescriptor desc = {};
710 nsCString vsEntry, fsEntry;
711 ffi::WGPUIndexFormat stripIndexFormat = ffi::WGPUIndexFormat_Uint16;
712 ffi::WGPUFace cullFace = ffi::WGPUFace_Front;
713 ffi::WGPUVertexState vertexState = {};
714 ffi::WGPUFragmentState fragmentState = {};
715 nsTArray<ffi::WGPUColorTargetState> colorStates;
716 nsTArray<ffi::WGPUBlendState> blendStates;
718 webgpu::StringHelper label(aDesc.mLabel);
719 desc.label = label.Get();
721 if (aDesc.mLayout.IsGPUAutoLayoutMode()) {
722 desc.layout = 0;
723 } else if (aDesc.mLayout.IsGPUPipelineLayout()) {
724 desc.layout = aDesc.mLayout.GetAsGPUPipelineLayout()->mId;
725 } else {
726 MOZ_ASSERT_UNREACHABLE();
730 const auto& stage = aDesc.mVertex;
731 vertexState.stage.module = stage.mModule->mId;
732 if (stage.mEntryPoint.WasPassed()) {
733 CopyUTF16toUTF8(stage.mEntryPoint.Value(), vsEntry);
734 vertexState.stage.entry_point = vsEntry.get();
735 } else {
736 vertexState.stage.entry_point = nullptr;
739 for (const auto& vertex_desc : stage.mBuffers) {
740 ffi::WGPUVertexBufferLayout vb_desc = {};
741 if (!vertex_desc.IsNull()) {
742 const auto& vd = vertex_desc.Value();
743 vb_desc.array_stride = vd.mArrayStride;
744 vb_desc.step_mode = ffi::WGPUVertexStepMode(vd.mStepMode);
745 // Note: we are setting the length but not the pointer
746 vb_desc.attributes_length = vd.mAttributes.Length();
747 for (const auto& vat : vd.mAttributes) {
748 ffi::WGPUVertexAttribute ad = {};
749 ad.offset = vat.mOffset;
750 ad.format = ffi::WGPUVertexFormat(vat.mFormat);
751 ad.shader_location = vat.mShaderLocation;
752 vertexAttributes.AppendElement(ad);
755 vertexBuffers.AppendElement(vb_desc);
757 // Now patch up all the pointers to attribute lists.
758 size_t numAttributes = 0;
759 for (auto& vb_desc : vertexBuffers) {
760 vb_desc.attributes = vertexAttributes.Elements() + numAttributes;
761 numAttributes += vb_desc.attributes_length;
764 vertexState.buffers = vertexBuffers.Elements();
765 vertexState.buffers_length = vertexBuffers.Length();
766 desc.vertex = &vertexState;
769 if (aDesc.mFragment.WasPassed()) {
770 const auto& stage = aDesc.mFragment.Value();
771 fragmentState.stage.module = stage.mModule->mId;
772 if (stage.mEntryPoint.WasPassed()) {
773 CopyUTF16toUTF8(stage.mEntryPoint.Value(), fsEntry);
774 fragmentState.stage.entry_point = fsEntry.get();
775 } else {
776 fragmentState.stage.entry_point = nullptr;
779 // Note: we pre-collect the blend states into a different array
780 // so that we can have non-stale pointers into it.
781 for (const auto& colorState : stage.mTargets) {
782 ffi::WGPUColorTargetState desc = {};
783 desc.format = ConvertTextureFormat(colorState.mFormat);
784 desc.write_mask = colorState.mWriteMask;
785 colorStates.AppendElement(desc);
786 ffi::WGPUBlendState bs = {};
787 if (colorState.mBlend.WasPassed()) {
788 const auto& blend = colorState.mBlend.Value();
789 bs.alpha = ConvertBlendComponent(blend.mAlpha);
790 bs.color = ConvertBlendComponent(blend.mColor);
792 blendStates.AppendElement(bs);
794 for (size_t i = 0; i < colorStates.Length(); ++i) {
795 if (stage.mTargets[i].mBlend.WasPassed()) {
796 colorStates[i].blend = &blendStates[i];
800 fragmentState.targets = colorStates.Elements();
801 fragmentState.targets_length = colorStates.Length();
802 desc.fragment = &fragmentState;
806 const auto& prim = aDesc.mPrimitive;
807 desc.primitive.topology = ffi::WGPUPrimitiveTopology(prim.mTopology);
808 if (prim.mStripIndexFormat.WasPassed()) {
809 stripIndexFormat = ffi::WGPUIndexFormat(prim.mStripIndexFormat.Value());
810 desc.primitive.strip_index_format = &stripIndexFormat;
812 desc.primitive.front_face = ffi::WGPUFrontFace(prim.mFrontFace);
813 if (prim.mCullMode != dom::GPUCullMode::None) {
814 cullFace = prim.mCullMode == dom::GPUCullMode::Front ? ffi::WGPUFace_Front
815 : ffi::WGPUFace_Back;
816 desc.primitive.cull_mode = &cullFace;
818 desc.primitive.unclipped_depth = prim.mUnclippedDepth;
820 desc.multisample = ConvertMultisampleState(aDesc.mMultisample);
822 ffi::WGPUDepthStencilState depthStencilState = {};
823 if (aDesc.mDepthStencil.WasPassed()) {
824 depthStencilState = ConvertDepthStencilState(aDesc.mDepthStencil.Value());
825 desc.depth_stencil = &depthStencilState;
828 RawId implicit_bgl_ids[WGPUMAX_BIND_GROUPS] = {};
829 RawId id = ffi::wgpu_client_create_render_pipeline(
830 aBridge->GetClient(), aContext->mParentId, &desc, ToFFI(aByteBuf),
831 &aContext->mImplicitPipelineLayoutId, implicit_bgl_ids);
833 for (const auto& cur : implicit_bgl_ids) {
834 if (!cur) break;
835 aContext->mImplicitBindGroupLayoutIds.AppendElement(cur);
838 return id;
841 already_AddRefed<ComputePipeline> Device::CreateComputePipeline(
842 const dom::GPUComputePipelineDescriptor& aDesc) {
843 PipelineCreationContext context = {mId};
844 ipc::ByteBuf bb;
845 RawId id = CreateComputePipelineImpl(&context, mBridge, aDesc, &bb);
847 if (mBridge->CanSend()) {
848 mBridge->SendDeviceAction(mId, std::move(bb));
851 RefPtr<ComputePipeline> object =
852 new ComputePipeline(this, id, context.mImplicitPipelineLayoutId,
853 std::move(context.mImplicitBindGroupLayoutIds));
854 return object.forget();
857 already_AddRefed<RenderPipeline> Device::CreateRenderPipeline(
858 const dom::GPURenderPipelineDescriptor& aDesc) {
859 PipelineCreationContext context = {mId};
860 ipc::ByteBuf bb;
861 RawId id = CreateRenderPipelineImpl(&context, mBridge, aDesc, &bb);
863 if (mBridge->CanSend()) {
864 mBridge->SendDeviceAction(mId, std::move(bb));
867 RefPtr<RenderPipeline> object =
868 new RenderPipeline(this, id, context.mImplicitPipelineLayoutId,
869 std::move(context.mImplicitBindGroupLayoutIds));
870 return object.forget();
873 already_AddRefed<dom::Promise> Device::CreateComputePipelineAsync(
874 const dom::GPUComputePipelineDescriptor& aDesc, ErrorResult& aRv) {
875 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
876 if (NS_WARN_IF(aRv.Failed())) {
877 return nullptr;
880 std::shared_ptr<PipelineCreationContext> context(
881 new PipelineCreationContext());
882 context->mParentId = mId;
884 ipc::ByteBuf bb;
885 RawId pipelineId =
886 CreateComputePipelineImpl(context.get(), mBridge, aDesc, &bb);
888 if (mBridge->CanSend()) {
889 mBridge->SendDeviceActionWithAck(mId, std::move(bb))
890 ->Then(
891 GetCurrentSerialEventTarget(), __func__,
892 [self = RefPtr{this}, context, pipelineId, promise](bool aDummy) {
893 Unused << aDummy;
894 RefPtr<ComputePipeline> object = new ComputePipeline(
895 self, pipelineId, context->mImplicitPipelineLayoutId,
896 std::move(context->mImplicitBindGroupLayoutIds));
897 promise->MaybeResolve(object);
899 [promise](const ipc::ResponseRejectReason&) {
900 promise->MaybeRejectWithOperationError(
901 "Internal communication error");
903 } else {
904 promise->MaybeRejectWithOperationError("Internal communication error");
907 return promise.forget();
910 already_AddRefed<dom::Promise> Device::CreateRenderPipelineAsync(
911 const dom::GPURenderPipelineDescriptor& aDesc, ErrorResult& aRv) {
912 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
913 if (NS_WARN_IF(aRv.Failed())) {
914 return nullptr;
917 std::shared_ptr<PipelineCreationContext> context(
918 new PipelineCreationContext());
919 context->mParentId = mId;
921 ipc::ByteBuf bb;
922 RawId pipelineId =
923 CreateRenderPipelineImpl(context.get(), mBridge, aDesc, &bb);
925 if (mBridge->CanSend()) {
926 mBridge->SendDeviceActionWithAck(mId, std::move(bb))
927 ->Then(
928 GetCurrentSerialEventTarget(), __func__,
929 [self = RefPtr{this}, context, promise, pipelineId](bool aDummy) {
930 Unused << aDummy;
931 RefPtr<RenderPipeline> object = new RenderPipeline(
932 self, pipelineId, context->mImplicitPipelineLayoutId,
933 std::move(context->mImplicitBindGroupLayoutIds));
934 promise->MaybeResolve(object);
936 [promise](const ipc::ResponseRejectReason&) {
937 promise->MaybeRejectWithOperationError(
938 "Internal communication error");
940 } else {
941 promise->MaybeRejectWithOperationError("Internal communication error");
944 return promise.forget();
947 already_AddRefed<Texture> Device::InitSwapChain(
948 const dom::GPUCanvasConfiguration* const aConfig,
949 const layers::RemoteTextureOwnerId aOwnerId,
950 bool aUseExternalTextureInSwapChain, gfx::SurfaceFormat aFormat,
951 gfx::IntSize aCanvasSize) {
952 MOZ_ASSERT(aConfig);
954 if (!mBridge->CanSend()) {
955 return nullptr;
958 // Check that aCanvasSize and aFormat will generate a texture stride
959 // within limits.
960 const auto bufferStrideWithMask = BufferStrideWithMask(aCanvasSize, aFormat);
961 if (!bufferStrideWithMask.isValid()) {
962 return nullptr;
965 const layers::RGBDescriptor rgbDesc(aCanvasSize, aFormat);
966 // buffer count doesn't matter much, will be created on demand
967 const size_t maxBufferCount = 10;
968 mBridge->DeviceCreateSwapChain(mId, rgbDesc, maxBufferCount, aOwnerId,
969 aUseExternalTextureInSwapChain);
971 // TODO: `mColorSpace`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846608>
972 // TODO: `mAlphaMode`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846605>
973 return CreateTextureForSwapChain(aConfig, aCanvasSize, aOwnerId);
976 bool Device::CheckNewWarning(const nsACString& aMessage) {
977 return mKnownWarnings.EnsureInserted(aMessage);
980 void Device::Destroy() {
981 if (IsLost()) {
982 return;
985 // Unmap all buffers from this device, as specified by
986 // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
987 dom::AutoJSAPI jsapi;
988 if (jsapi.Init(GetOwnerGlobal())) {
989 IgnoredErrorResult rv;
990 for (const auto& buffer : mTrackedBuffers) {
991 buffer->Unmap(jsapi.cx(), rv);
994 mTrackedBuffers.Clear();
997 mBridge->SendDeviceDestroy(mId);
1000 void Device::PushErrorScope(const dom::GPUErrorFilter& aFilter) {
1001 if (!IsBridgeAlive()) {
1002 return;
1004 mBridge->SendDevicePushErrorScope(mId, aFilter);
1007 already_AddRefed<dom::Promise> Device::PopErrorScope(ErrorResult& aRv) {
1009 https://www.w3.org/TR/webgpu/#errors-and-debugging:
1010 > After a device is lost (described below), errors are no longer surfaced.
1011 > At this point, implementations do not need to run validation or error
1012 tracking: > popErrorScope() and uncapturederror stop reporting errors, > and
1013 the validity of objects on the device becomes unobservable.
1015 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
1016 if (NS_WARN_IF(aRv.Failed())) {
1017 return nullptr;
1020 if (!IsBridgeAlive()) {
1021 WebGPUChild::JsWarning(
1022 GetOwnerGlobal(),
1023 "popErrorScope resolving to null because device is already lost."_ns);
1024 promise->MaybeResolve(JS::NullHandleValue);
1025 return promise.forget();
1028 auto errorPromise = mBridge->SendDevicePopErrorScope(mId);
1030 errorPromise->Then(
1031 GetCurrentSerialEventTarget(), __func__,
1032 [self = RefPtr{this}, promise](const PopErrorScopeResult& aResult) {
1033 RefPtr<Error> error;
1035 switch (aResult.resultType) {
1036 case PopErrorScopeResultType::NoError:
1037 promise->MaybeResolve(JS::NullHandleValue);
1038 return;
1040 case PopErrorScopeResultType::DeviceLost:
1041 WebGPUChild::JsWarning(
1042 self->GetOwnerGlobal(),
1043 "popErrorScope resolving to null because device was lost."_ns);
1044 promise->MaybeResolve(JS::NullHandleValue);
1045 return;
1047 case PopErrorScopeResultType::ThrowOperationError:
1048 promise->MaybeRejectWithOperationError(aResult.message);
1049 return;
1051 case PopErrorScopeResultType::OutOfMemory:
1052 error =
1053 new OutOfMemoryError(self->GetParentObject(), aResult.message);
1054 break;
1056 case PopErrorScopeResultType::ValidationError:
1057 error =
1058 new ValidationError(self->GetParentObject(), aResult.message);
1059 break;
1061 case PopErrorScopeResultType::InternalError:
1062 error = new InternalError(self->GetParentObject(), aResult.message);
1063 break;
1065 promise->MaybeResolve(std::move(error));
1067 [self = RefPtr{this}, promise](const ipc::ResponseRejectReason&) {
1068 // Device was lost.
1069 WebGPUChild::JsWarning(
1070 self->GetOwnerGlobal(),
1071 "popErrorScope resolving to null because device was just lost."_ns);
1072 promise->MaybeResolve(JS::NullHandleValue);
1075 return promise.forget();
1078 } // namespace mozilla::webgpu