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"
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"
16 #include "CommandEncoder.h"
17 #include "BindGroup.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"
28 #include "RenderBundleEncoder.h"
29 #include "RenderPipeline.h"
31 #include "SupportedFeatures.h"
32 #include "SupportedLimits.h"
34 #include "TextureView.h"
35 #include "ValidationError.h"
36 #include "ipc/WebGPUChild.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
) +
57 RefPtr
<WebGPUChild
> Device::GetBridge() { return mBridge
; }
59 Device::Device(Adapter
* const aParent
, RawId aId
,
60 const ffi::WGPULimits
& aRawLimits
)
61 : DOMEventTargetHelper(aParent
->GetParentObject()),
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() {
81 mBridge
->UnregisterDevice(mId
);
85 void Device::CleanupUnregisteredInParent() {
87 mBridge
->FreeUnregisteredInParentDevice(mId
);
92 bool Device::IsLost() const {
93 return !mBridge
|| !mBridge
->CanSend() ||
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
,
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
) {
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
);
131 void Device::ResolveLost(Maybe
<dom::GPUDeviceLostReason
> aReason
,
132 const nsAString
& aMessage
) {
133 IgnoredErrorResult rv
;
134 dom::Promise
* lostPromise
= GetLost(rv
);
136 // Promise doesn't exist? Maybe out of memory.
139 if (!lostPromise
->PromiseObj()) {
140 // The underlying JS object is gone.
143 if (lostPromise
->State() != dom::Promise::PromiseState::Pending
) {
144 // lostPromise was already resolved or rejected.
147 RefPtr
<DeviceLostInfo
> info
;
148 if (aReason
.isSome()) {
149 info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(), *aReason
, aMessage
);
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
) {
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
;
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
});
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
;
259 RawId id
= ffi::wgpu_client_create_sampler(mBridge
->GetClient(), mId
, &desc
,
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();
278 RawId id
= ffi::wgpu_client_create_command_encoder(mBridge
->GetClient(), mId
,
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
;
312 case dom::GPUTextureSampleType::Unfilterable_float
:
313 data
.type
= ffi::WGPURawTextureSampleType_UnfilterableFloat
;
315 case dom::GPUTextureSampleType::Uint
:
316 data
.type
= ffi::WGPURawTextureSampleType_Uint
;
318 case dom::GPUTextureSampleType::Sint
:
319 data
.type
= ffi::WGPURawTextureSampleType_Sint
;
321 case dom::GPUTextureSampleType::Depth
:
322 data
.type
= ffi::WGPURawTextureSampleType_Depth
;
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
;
345 case dom::GPUBufferBindingType::Storage
:
346 e
.ty
= ffi::WGPURawBindingType_StorageBuffer
;
348 case dom::GPUBufferBindingType::Read_only_storage
:
349 e
.ty
= ffi::WGPURawBindingType_ReadonlyStorageBuffer
;
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
;
366 case dom::GPUStorageTextureAccess::Read_only
: {
367 e
.ty
= ffi::WGPURawBindingType_ReadonlyStorageTexture
;
370 case dom::GPUStorageTextureAccess::Read_write
: {
371 e
.ty
= ffi::WGPURawBindingType_ReadWriteStorageTexture
;
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;
387 case dom::GPUSamplerBindingType::Non_filtering
:
389 case dom::GPUSamplerBindingType::Comparison
:
390 e
.sampler_compare
= true;
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();
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();
432 RawId id
= ffi::wgpu_client_create_pipeline_layout(mBridge
->GetClient(), mId
,
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.");
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
;
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.");
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();
480 RawId id
= ffi::wgpu_client_create_bind_group(mBridge
->GetClient(), mId
,
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();
496 if (!api
.Init(global
)) {
500 const auto& cx
= api
.cx();
503 RefPtr
<dom::Console
> console
=
504 nsGlobalWindowInner::Cast(global
->GetAsInnerWindow())->GetConsole(cx
, rv
);
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
)
517 JS::Rooted
<JSString
*> jsStr(
518 cx
, JS_NewUCStringCopyN(cx
, message
.Data(), message
.Length()));
522 JS::Rooted
<JS::Value
> val(cx
, JS::StringValue(jsStr
));
523 if (!args
->AppendElement(val
, fallible
)) {
529 aShaderModule
->GetLabel(label
);
530 auto appendNiceLabelIfPresent
= [&label
](nsString
* buf
) MOZ_CAN_RUN_SCRIPT
{
531 if (!label
.IsEmpty()) {
532 buf
->AppendLiteral(u
" \"");
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
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
:
558 case WebGPUCompilationMessageType::Warning
:
561 case WebGPUCompilationMessageType::Info
:
566 switch (highestSeveritySeen
) {
567 case WebGPUCompilationMessageType::Info
:
568 // shouldn't happen, but :shrug:
570 case WebGPUCompilationMessageType::Warning
: {
572 u
"Encountered one or more warnings while creating shader module");
573 appendNiceLabelIfPresent(&msg
);
574 SetSingleStrAsArgs(msg
, &args
);
575 console
->Warn(globalObj
, args
);
578 case WebGPUCompilationMessageType::Error
: {
580 u
"Encountered one or more errors while creating shader module");
581 appendNiceLabelIfPresent(&msg
);
582 SetSingleStrAsArgs(msg
, &args
);
583 console
->Error(globalObj
, args
);
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
);
607 case WebGPUCompilationMessageType::Warning
:
608 console
->Warn(globalObj
, args
);
610 case WebGPUCompilationMessageType::Info
:
611 console
->Info(globalObj
, args
);
615 console
->GroupEnd(globalObj
);
618 already_AddRefed
<ShaderModule
> Device::CreateShaderModule(
619 JSContext
* aCx
, const dom::GPUShaderModuleDescriptor
& aDesc
,
623 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
624 if (NS_WARN_IF(aRv
.Failed())) {
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()) {
639 ->SendDeviceCreateShaderModule(mId
, moduleId
, aDesc
.mLabel
, aDesc
.mCode
)
641 GetCurrentSerialEventTarget(), __func__
,
643 shaderModule
](nsTArray
<WebGPUCompilationMessage
>&& messages
)
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");
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()) {
676 } else if (aDesc
.mLayout
.IsGPUPipelineLayout()) {
677 desc
.layout
= aDesc
.mLayout
.GetAsGPUPipelineLayout()->mId
;
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();
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
) {
696 aContext
->mImplicitBindGroupLayoutIds
.AppendElement(cur
);
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()) {
723 } else if (aDesc
.mLayout
.IsGPUPipelineLayout()) {
724 desc
.layout
= aDesc
.mLayout
.GetAsGPUPipelineLayout()->mId
;
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();
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();
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
) {
835 aContext
->mImplicitBindGroupLayoutIds
.AppendElement(cur
);
841 already_AddRefed
<ComputePipeline
> Device::CreateComputePipeline(
842 const dom::GPUComputePipelineDescriptor
& aDesc
) {
843 PipelineCreationContext context
= {mId
};
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
};
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())) {
880 std::shared_ptr
<PipelineCreationContext
> context(
881 new PipelineCreationContext());
882 context
->mParentId
= mId
;
886 CreateComputePipelineImpl(context
.get(), mBridge
, aDesc
, &bb
);
888 if (mBridge
->CanSend()) {
889 mBridge
->SendDeviceActionWithAck(mId
, std::move(bb
))
891 GetCurrentSerialEventTarget(), __func__
,
892 [self
= RefPtr
{this}, context
, pipelineId
, promise
](bool 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");
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())) {
917 std::shared_ptr
<PipelineCreationContext
> context(
918 new PipelineCreationContext());
919 context
->mParentId
= mId
;
923 CreateRenderPipelineImpl(context
.get(), mBridge
, aDesc
, &bb
);
925 if (mBridge
->CanSend()) {
926 mBridge
->SendDeviceActionWithAck(mId
, std::move(bb
))
928 GetCurrentSerialEventTarget(), __func__
,
929 [self
= RefPtr
{this}, context
, promise
, pipelineId
](bool 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");
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
) {
954 if (!mBridge
->CanSend()) {
958 // Check that aCanvasSize and aFormat will generate a texture stride
960 const auto bufferStrideWithMask
= BufferStrideWithMask(aCanvasSize
, aFormat
);
961 if (!bufferStrideWithMask
.isValid()) {
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() {
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()) {
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())) {
1020 if (!IsBridgeAlive()) {
1021 WebGPUChild::JsWarning(
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
);
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
);
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
);
1047 case PopErrorScopeResultType::ThrowOperationError
:
1048 promise
->MaybeRejectWithOperationError(aResult
.message
);
1051 case PopErrorScopeResultType::OutOfMemory
:
1053 new OutOfMemoryError(self
->GetParentObject(), aResult
.message
);
1056 case PopErrorScopeResultType::ValidationError
:
1058 new ValidationError(self
->GetParentObject(), aResult
.message
);
1061 case PopErrorScopeResultType::InternalError
:
1062 error
= new InternalError(self
->GetParentObject(), aResult
.message
);
1065 promise
->MaybeResolve(std::move(error
));
1067 [self
= RefPtr
{this}, promise
](const ipc::ResponseRejectReason
&) {
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