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/Promise.h"
13 #include "mozilla/dom/WebGPUBinding.h"
15 #include "CommandEncoder.h"
16 #include "BindGroup.h"
20 #include "ComputePipeline.h"
21 #include "DeviceLostInfo.h"
22 #include "InternalError.h"
23 #include "OutOfMemoryError.h"
24 #include "PipelineLayout.h"
26 #include "RenderBundleEncoder.h"
27 #include "RenderPipeline.h"
29 #include "SupportedFeatures.h"
30 #include "SupportedLimits.h"
32 #include "TextureView.h"
33 #include "ValidationError.h"
34 #include "ipc/WebGPUChild.h"
36 namespace mozilla::webgpu
{
38 mozilla::LazyLogModule
gWebGPULog("WebGPU");
40 GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(Device
, DOMEventTargetHelper
,
41 mBridge
, mQueue
, mFeatures
,
42 mLimits
, mLostPromise
);
43 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device
, DOMEventTargetHelper
)
44 GPU_IMPL_JS_WRAP(Device
)
46 /* static */ CheckedInt
<uint32_t> Device::BufferStrideWithMask(
47 const gfx::IntSize
& aSize
, const gfx::SurfaceFormat
& aFormat
) {
48 constexpr uint32_t kBufferAlignmentMask
= 0xff;
49 return CheckedInt
<uint32_t>(aSize
.width
) * gfx::BytesPerPixel(aFormat
) +
53 RefPtr
<WebGPUChild
> Device::GetBridge() { return mBridge
; }
55 Device::Device(Adapter
* const aParent
, RawId aId
,
56 const ffi::WGPULimits
& aRawLimits
)
57 : DOMEventTargetHelper(aParent
->GetParentObject()),
59 // features are filled in Adapter::RequestDevice
60 mFeatures(new SupportedFeatures(aParent
)),
61 mLimits(new SupportedLimits(aParent
, aRawLimits
)),
62 mBridge(aParent
->mBridge
),
63 mQueue(new class Queue(this, aParent
->mBridge
, aId
)) {
64 mBridge
->RegisterDevice(this);
67 Device::~Device() { Cleanup(); }
69 void Device::Cleanup() {
77 mBridge
->UnregisterDevice(mId
);
81 void Device::CleanupUnregisteredInParent() {
83 mBridge
->FreeUnregisteredInParentDevice(mId
);
88 bool Device::IsLost() const {
89 return !mBridge
|| !mBridge
->CanSend() ||
91 (mLostPromise
->State() != dom::Promise::PromiseState::Pending
));
94 bool Device::IsBridgeAlive() const { return mBridge
&& mBridge
->CanSend(); }
96 // Generate an error on the Device timeline for this device.
98 // aMessage is interpreted as UTF-8.
99 void Device::GenerateValidationError(const nsCString
& aMessage
) {
100 if (!IsBridgeAlive()) {
101 return; // Just drop it?
103 mBridge
->SendGenerateError(Some(mId
), dom::GPUErrorFilter::Validation
,
107 void Device::TrackBuffer(Buffer
* aBuffer
) { mTrackedBuffers
.Insert(aBuffer
); }
109 void Device::UntrackBuffer(Buffer
* aBuffer
) { mTrackedBuffers
.Remove(aBuffer
); }
111 void Device::GetLabel(nsAString
& aValue
) const { aValue
= mLabel
; }
112 void Device::SetLabel(const nsAString
& aLabel
) { mLabel
= aLabel
; }
114 dom::Promise
* Device::GetLost(ErrorResult
& aRv
) {
117 mLostPromise
= dom::Promise::Create(GetParentObject(), aRv
);
118 if (mLostPromise
&& !mBridge
->CanSend()) {
119 auto info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(),
120 u
"WebGPUChild destroyed"_ns
);
121 mLostPromise
->MaybeResolve(info
);
127 void Device::ResolveLost(Maybe
<dom::GPUDeviceLostReason
> aReason
,
128 const nsAString
& aMessage
) {
129 IgnoredErrorResult rv
;
130 dom::Promise
* lostPromise
= GetLost(rv
);
132 // Promise doesn't exist? Maybe out of memory.
135 if (lostPromise
->State() != dom::Promise::PromiseState::Pending
) {
136 // lostPromise was already resolved or rejected.
139 if (!lostPromise
->PromiseObj()) {
140 // The underlying JS object is gone.
144 RefPtr
<DeviceLostInfo
> info
;
145 if (aReason
.isSome()) {
146 info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(), *aReason
, aMessage
);
148 info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(), aMessage
);
150 lostPromise
->MaybeResolve(info
);
153 already_AddRefed
<Buffer
> Device::CreateBuffer(
154 const dom::GPUBufferDescriptor
& aDesc
, ErrorResult
& aRv
) {
155 return Buffer::Create(this, mId
, aDesc
, aRv
);
158 already_AddRefed
<Texture
> Device::CreateTextureForSwapChain(
159 const dom::GPUCanvasConfiguration
* const aConfig
,
160 const gfx::IntSize
& aCanvasSize
, layers::RemoteTextureOwnerId aOwnerId
) {
163 dom::GPUTextureDescriptor desc
;
164 desc
.mDimension
= dom::GPUTextureDimension::_2d
;
165 auto& sizeDict
= desc
.mSize
.SetAsGPUExtent3DDict();
166 sizeDict
.mWidth
= aCanvasSize
.width
;
167 sizeDict
.mHeight
= aCanvasSize
.height
;
168 sizeDict
.mDepthOrArrayLayers
= 1;
169 desc
.mFormat
= aConfig
->mFormat
;
170 desc
.mMipLevelCount
= 1;
171 desc
.mSampleCount
= 1;
172 desc
.mUsage
= aConfig
->mUsage
| dom::GPUTextureUsage_Binding::COPY_SRC
;
173 desc
.mViewFormats
= aConfig
->mViewFormats
;
175 return CreateTexture(desc
, Some(aOwnerId
));
178 already_AddRefed
<Texture
> Device::CreateTexture(
179 const dom::GPUTextureDescriptor
& aDesc
) {
180 return CreateTexture(aDesc
, /* aOwnerId */ Nothing());
183 already_AddRefed
<Texture
> Device::CreateTexture(
184 const dom::GPUTextureDescriptor
& aDesc
,
185 Maybe
<layers::RemoteTextureOwnerId
> aOwnerId
) {
187 if (mBridge
->CanSend()) {
188 id
= mBridge
->DeviceCreateTexture(mId
, aDesc
, aOwnerId
);
190 RefPtr
<Texture
> texture
= new Texture(this, id
, aDesc
);
191 return texture
.forget();
194 already_AddRefed
<Sampler
> Device::CreateSampler(
195 const dom::GPUSamplerDescriptor
& aDesc
) {
197 if (mBridge
->CanSend()) {
198 id
= mBridge
->DeviceCreateSampler(mId
, aDesc
);
200 RefPtr
<Sampler
> sampler
= new Sampler(this, id
);
201 return sampler
.forget();
204 already_AddRefed
<CommandEncoder
> Device::CreateCommandEncoder(
205 const dom::GPUCommandEncoderDescriptor
& aDesc
) {
207 if (mBridge
->CanSend()) {
208 id
= mBridge
->DeviceCreateCommandEncoder(mId
, aDesc
);
210 RefPtr
<CommandEncoder
> encoder
= new CommandEncoder(this, mBridge
, id
);
211 return encoder
.forget();
214 already_AddRefed
<RenderBundleEncoder
> Device::CreateRenderBundleEncoder(
215 const dom::GPURenderBundleEncoderDescriptor
& aDesc
) {
216 RefPtr
<RenderBundleEncoder
> encoder
=
217 new RenderBundleEncoder(this, mBridge
, aDesc
);
218 return encoder
.forget();
221 already_AddRefed
<BindGroupLayout
> Device::CreateBindGroupLayout(
222 const dom::GPUBindGroupLayoutDescriptor
& aDesc
) {
224 if (mBridge
->CanSend()) {
225 id
= mBridge
->DeviceCreateBindGroupLayout(mId
, aDesc
);
227 RefPtr
<BindGroupLayout
> object
= new BindGroupLayout(this, id
, true);
228 return object
.forget();
230 already_AddRefed
<PipelineLayout
> Device::CreatePipelineLayout(
231 const dom::GPUPipelineLayoutDescriptor
& aDesc
) {
233 if (mBridge
->CanSend()) {
234 id
= mBridge
->DeviceCreatePipelineLayout(mId
, aDesc
);
236 RefPtr
<PipelineLayout
> object
= new PipelineLayout(this, id
);
237 return object
.forget();
239 already_AddRefed
<BindGroup
> Device::CreateBindGroup(
240 const dom::GPUBindGroupDescriptor
& aDesc
) {
242 if (mBridge
->CanSend()) {
243 id
= mBridge
->DeviceCreateBindGroup(mId
, aDesc
);
245 RefPtr
<BindGroup
> object
= new BindGroup(this, id
);
246 return object
.forget();
249 already_AddRefed
<ShaderModule
> Device::CreateShaderModule(
250 JSContext
* aCx
, const dom::GPUShaderModuleDescriptor
& aDesc
,
254 if (!mBridge
->CanSend()) {
255 aRv
.ThrowInvalidStateError("Connection to GPU process has shut down");
259 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
260 if (NS_WARN_IF(aRv
.Failed())) {
264 return MOZ_KnownLive(mBridge
)->DeviceCreateShaderModule(this, aDesc
, promise
);
267 already_AddRefed
<ComputePipeline
> Device::CreateComputePipeline(
268 const dom::GPUComputePipelineDescriptor
& aDesc
) {
269 PipelineCreationContext context
= {mId
};
271 if (mBridge
->CanSend()) {
272 id
= mBridge
->DeviceCreateComputePipeline(&context
, aDesc
);
274 RefPtr
<ComputePipeline
> object
=
275 new ComputePipeline(this, id
, context
.mImplicitPipelineLayoutId
,
276 std::move(context
.mImplicitBindGroupLayoutIds
));
277 return object
.forget();
280 already_AddRefed
<RenderPipeline
> Device::CreateRenderPipeline(
281 const dom::GPURenderPipelineDescriptor
& aDesc
) {
282 PipelineCreationContext context
= {mId
};
284 if (mBridge
->CanSend()) {
285 id
= mBridge
->DeviceCreateRenderPipeline(&context
, aDesc
);
287 RefPtr
<RenderPipeline
> object
=
288 new RenderPipeline(this, id
, context
.mImplicitPipelineLayoutId
,
289 std::move(context
.mImplicitBindGroupLayoutIds
));
290 return object
.forget();
293 already_AddRefed
<dom::Promise
> Device::CreateComputePipelineAsync(
294 const dom::GPUComputePipelineDescriptor
& aDesc
, ErrorResult
& aRv
) {
295 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
296 if (NS_WARN_IF(aRv
.Failed())) {
300 if (!mBridge
->CanSend()) {
301 promise
->MaybeRejectWithOperationError("Internal communication error");
302 return promise
.forget();
305 std::shared_ptr
<PipelineCreationContext
> context(
306 new PipelineCreationContext());
307 context
->mParentId
= mId
;
308 mBridge
->DeviceCreateComputePipelineAsync(context
.get(), aDesc
)
310 GetCurrentSerialEventTarget(), __func__
,
311 [self
= RefPtr
{this}, context
, promise
](RawId aId
) {
312 RefPtr
<ComputePipeline
> object
= new ComputePipeline(
313 self
, aId
, context
->mImplicitPipelineLayoutId
,
314 std::move(context
->mImplicitBindGroupLayoutIds
));
315 promise
->MaybeResolve(object
);
317 [promise
](const ipc::ResponseRejectReason
&) {
318 promise
->MaybeRejectWithOperationError(
319 "Internal communication error");
322 return promise
.forget();
325 already_AddRefed
<dom::Promise
> Device::CreateRenderPipelineAsync(
326 const dom::GPURenderPipelineDescriptor
& aDesc
, ErrorResult
& aRv
) {
327 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
328 if (NS_WARN_IF(aRv
.Failed())) {
332 if (!mBridge
->CanSend()) {
333 promise
->MaybeRejectWithOperationError("Internal communication error");
334 return promise
.forget();
337 std::shared_ptr
<PipelineCreationContext
> context(
338 new PipelineCreationContext());
339 context
->mParentId
= mId
;
340 mBridge
->DeviceCreateRenderPipelineAsync(context
.get(), aDesc
)
342 GetCurrentSerialEventTarget(), __func__
,
343 [self
= RefPtr
{this}, context
, promise
](RawId aId
) {
344 RefPtr
<RenderPipeline
> object
= new RenderPipeline(
345 self
, aId
, context
->mImplicitPipelineLayoutId
,
346 std::move(context
->mImplicitBindGroupLayoutIds
));
347 promise
->MaybeResolve(object
);
349 [promise
](const ipc::ResponseRejectReason
&) {
350 promise
->MaybeRejectWithOperationError(
351 "Internal communication error");
354 return promise
.forget();
357 already_AddRefed
<Texture
> Device::InitSwapChain(
358 const dom::GPUCanvasConfiguration
* const aConfig
,
359 const layers::RemoteTextureOwnerId aOwnerId
,
360 bool aUseExternalTextureInSwapChain
, gfx::SurfaceFormat aFormat
,
361 gfx::IntSize aCanvasSize
) {
364 if (!mBridge
->CanSend()) {
368 // Check that aCanvasSize and aFormat will generate a texture stride
370 const auto bufferStrideWithMask
= BufferStrideWithMask(aCanvasSize
, aFormat
);
371 if (!bufferStrideWithMask
.isValid()) {
375 const layers::RGBDescriptor
rgbDesc(aCanvasSize
, aFormat
);
376 // buffer count doesn't matter much, will be created on demand
377 const size_t maxBufferCount
= 10;
378 mBridge
->DeviceCreateSwapChain(mId
, rgbDesc
, maxBufferCount
, aOwnerId
,
379 aUseExternalTextureInSwapChain
);
381 // TODO: `mColorSpace`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846608>
382 // TODO: `mAlphaMode`: <https://bugzilla.mozilla.org/show_bug.cgi?id=1846605>
383 return CreateTextureForSwapChain(aConfig
, aCanvasSize
, aOwnerId
);
386 bool Device::CheckNewWarning(const nsACString
& aMessage
) {
387 return mKnownWarnings
.EnsureInserted(aMessage
);
390 void Device::Destroy() {
395 // Unmap all buffers from this device, as specified by
396 // https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
397 dom::AutoJSAPI jsapi
;
398 if (jsapi
.Init(GetOwnerGlobal())) {
399 IgnoredErrorResult rv
;
400 for (const auto& buffer
: mTrackedBuffers
) {
401 buffer
->Unmap(jsapi
.cx(), rv
);
404 mTrackedBuffers
.Clear();
407 mBridge
->SendDeviceDestroy(mId
);
410 void Device::PushErrorScope(const dom::GPUErrorFilter
& aFilter
) {
411 if (!IsBridgeAlive()) {
414 mBridge
->SendDevicePushErrorScope(mId
, aFilter
);
417 already_AddRefed
<dom::Promise
> Device::PopErrorScope(ErrorResult
& aRv
) {
419 https://www.w3.org/TR/webgpu/#errors-and-debugging:
420 > After a device is lost (described below), errors are no longer surfaced.
421 > At this point, implementations do not need to run validation or error
422 tracking: > popErrorScope() and uncapturederror stop reporting errors, > and
423 the validity of objects on the device becomes unobservable.
425 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
426 if (NS_WARN_IF(aRv
.Failed())) {
430 if (!IsBridgeAlive()) {
431 WebGPUChild::JsWarning(
433 "popErrorScope resolving to null because device is already lost."_ns
);
434 promise
->MaybeResolve(JS::NullHandleValue
);
435 return promise
.forget();
438 auto errorPromise
= mBridge
->SendDevicePopErrorScope(mId
);
441 GetCurrentSerialEventTarget(), __func__
,
442 [self
= RefPtr
{this}, promise
](const PopErrorScopeResult
& aResult
) {
445 switch (aResult
.resultType
) {
446 case PopErrorScopeResultType::NoError
:
447 promise
->MaybeResolve(JS::NullHandleValue
);
450 case PopErrorScopeResultType::DeviceLost
:
451 WebGPUChild::JsWarning(
452 self
->GetOwnerGlobal(),
453 "popErrorScope resolving to null because device was lost."_ns
);
454 promise
->MaybeResolve(JS::NullHandleValue
);
457 case PopErrorScopeResultType::ThrowOperationError
:
458 promise
->MaybeRejectWithOperationError(aResult
.message
);
461 case PopErrorScopeResultType::OutOfMemory
:
463 new OutOfMemoryError(self
->GetParentObject(), aResult
.message
);
466 case PopErrorScopeResultType::ValidationError
:
468 new ValidationError(self
->GetParentObject(), aResult
.message
);
471 case PopErrorScopeResultType::InternalError
:
472 error
= new InternalError(self
->GetParentObject(), aResult
.message
);
475 promise
->MaybeResolve(std::move(error
));
477 [self
= RefPtr
{this}, promise
](const ipc::ResponseRejectReason
&) {
479 WebGPUChild::JsWarning(
480 self
->GetOwnerGlobal(),
481 "popErrorScope resolving to null because device was just lost."_ns
);
482 promise
->MaybeResolve(JS::NullHandleValue
);
485 return promise
.forget();
488 } // namespace mozilla::webgpu