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 RefPtr
<WebGPUChild
> Device::GetBridge() { return mBridge
; }
48 Device::Device(Adapter
* const aParent
, RawId aId
,
49 const ffi::WGPULimits
& aRawLimits
)
50 : DOMEventTargetHelper(aParent
->GetParentObject()),
52 // features are filled in Adapter::RequestDevice
53 mFeatures(new SupportedFeatures(aParent
)),
54 mLimits(new SupportedLimits(aParent
, aRawLimits
)),
55 mBridge(aParent
->mBridge
),
56 mQueue(new class Queue(this, aParent
->mBridge
, aId
)) {
57 mBridge
->RegisterDevice(this);
60 Device::~Device() { Cleanup(); }
62 void Device::Cleanup() {
70 mBridge
->UnregisterDevice(mId
);
73 // Cycle collection may have disconnected the promise object.
74 if (mLostPromise
&& mLostPromise
->PromiseObj() != nullptr) {
75 auto info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(),
76 dom::GPUDeviceLostReason::Destroyed
,
77 u
"Device destroyed"_ns
);
78 mLostPromise
->MaybeResolve(info
);
82 void Device::CleanupUnregisteredInParent() {
84 mBridge
->FreeUnregisteredInParentDevice(mId
);
89 bool Device::IsLost() const { return !mBridge
|| !mBridge
->CanSend(); }
91 // Generate an error on the Device timeline for this device.
93 // aMessage is interpreted as UTF-8.
94 void Device::GenerateValidationError(const nsCString
& aMessage
) {
96 return; // Just drop it?
98 mBridge
->SendGenerateError(Some(mId
), dom::GPUErrorFilter::Validation
,
102 void Device::GetLabel(nsAString
& aValue
) const { aValue
= mLabel
; }
103 void Device::SetLabel(const nsAString
& aLabel
) { mLabel
= aLabel
; }
105 dom::Promise
* Device::GetLost(ErrorResult
& aRv
) {
107 mLostPromise
= dom::Promise::Create(GetParentObject(), aRv
);
108 if (mLostPromise
&& !mBridge
->CanSend()) {
109 auto info
= MakeRefPtr
<DeviceLostInfo
>(GetParentObject(),
110 u
"WebGPUChild destroyed"_ns
);
111 mLostPromise
->MaybeResolve(info
);
117 already_AddRefed
<Buffer
> Device::CreateBuffer(
118 const dom::GPUBufferDescriptor
& aDesc
, ErrorResult
& aRv
) {
119 return Buffer::Create(this, mId
, aDesc
, aRv
);
122 already_AddRefed
<Texture
> Device::CreateTexture(
123 const dom::GPUTextureDescriptor
& aDesc
) {
125 if (mBridge
->CanSend()) {
126 id
= mBridge
->DeviceCreateTexture(mId
, aDesc
);
128 RefPtr
<Texture
> texture
= new Texture(this, id
, aDesc
);
129 return texture
.forget();
132 already_AddRefed
<Sampler
> Device::CreateSampler(
133 const dom::GPUSamplerDescriptor
& aDesc
) {
135 if (mBridge
->CanSend()) {
136 id
= mBridge
->DeviceCreateSampler(mId
, aDesc
);
138 RefPtr
<Sampler
> sampler
= new Sampler(this, id
);
139 return sampler
.forget();
142 already_AddRefed
<CommandEncoder
> Device::CreateCommandEncoder(
143 const dom::GPUCommandEncoderDescriptor
& aDesc
) {
145 if (mBridge
->CanSend()) {
146 id
= mBridge
->DeviceCreateCommandEncoder(mId
, aDesc
);
148 RefPtr
<CommandEncoder
> encoder
= new CommandEncoder(this, mBridge
, id
);
149 return encoder
.forget();
152 already_AddRefed
<RenderBundleEncoder
> Device::CreateRenderBundleEncoder(
153 const dom::GPURenderBundleEncoderDescriptor
& aDesc
) {
154 RefPtr
<RenderBundleEncoder
> encoder
=
155 new RenderBundleEncoder(this, mBridge
, aDesc
);
156 return encoder
.forget();
159 already_AddRefed
<BindGroupLayout
> Device::CreateBindGroupLayout(
160 const dom::GPUBindGroupLayoutDescriptor
& aDesc
) {
162 if (mBridge
->CanSend()) {
163 id
= mBridge
->DeviceCreateBindGroupLayout(mId
, aDesc
);
165 RefPtr
<BindGroupLayout
> object
= new BindGroupLayout(this, id
, true);
166 return object
.forget();
168 already_AddRefed
<PipelineLayout
> Device::CreatePipelineLayout(
169 const dom::GPUPipelineLayoutDescriptor
& aDesc
) {
171 if (mBridge
->CanSend()) {
172 id
= mBridge
->DeviceCreatePipelineLayout(mId
, aDesc
);
174 RefPtr
<PipelineLayout
> object
= new PipelineLayout(this, id
);
175 return object
.forget();
177 already_AddRefed
<BindGroup
> Device::CreateBindGroup(
178 const dom::GPUBindGroupDescriptor
& aDesc
) {
180 if (mBridge
->CanSend()) {
181 id
= mBridge
->DeviceCreateBindGroup(mId
, aDesc
);
183 RefPtr
<BindGroup
> object
= new BindGroup(this, id
);
184 return object
.forget();
187 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION already_AddRefed
<ShaderModule
>
188 Device::CreateShaderModule(JSContext
* aCx
,
189 const dom::GPUShaderModuleDescriptor
& aDesc
) {
192 if (!mBridge
->CanSend()) {
197 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), err
);
198 if (NS_WARN_IF(err
.Failed())) {
202 return MOZ_KnownLive(mBridge
)->DeviceCreateShaderModule(this, aDesc
, promise
);
205 already_AddRefed
<ComputePipeline
> Device::CreateComputePipeline(
206 const dom::GPUComputePipelineDescriptor
& aDesc
) {
207 PipelineCreationContext context
= {mId
};
209 if (mBridge
->CanSend()) {
210 id
= mBridge
->DeviceCreateComputePipeline(&context
, aDesc
);
212 RefPtr
<ComputePipeline
> object
=
213 new ComputePipeline(this, id
, context
.mImplicitPipelineLayoutId
,
214 std::move(context
.mImplicitBindGroupLayoutIds
));
215 return object
.forget();
218 already_AddRefed
<RenderPipeline
> Device::CreateRenderPipeline(
219 const dom::GPURenderPipelineDescriptor
& aDesc
) {
220 PipelineCreationContext context
= {mId
};
222 if (mBridge
->CanSend()) {
223 id
= mBridge
->DeviceCreateRenderPipeline(&context
, aDesc
);
225 RefPtr
<RenderPipeline
> object
=
226 new RenderPipeline(this, id
, context
.mImplicitPipelineLayoutId
,
227 std::move(context
.mImplicitBindGroupLayoutIds
));
228 return object
.forget();
231 already_AddRefed
<dom::Promise
> Device::CreateComputePipelineAsync(
232 const dom::GPUComputePipelineDescriptor
& aDesc
, ErrorResult
& aRv
) {
233 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
234 if (NS_WARN_IF(aRv
.Failed())) {
238 if (!mBridge
->CanSend()) {
239 promise
->MaybeRejectWithOperationError("Internal communication error");
240 return promise
.forget();
243 std::shared_ptr
<PipelineCreationContext
> context(
244 new PipelineCreationContext());
245 context
->mParentId
= mId
;
246 mBridge
->DeviceCreateComputePipelineAsync(context
.get(), aDesc
)
248 GetCurrentSerialEventTarget(), __func__
,
249 [self
= RefPtr
{this}, context
, promise
](RawId aId
) {
250 RefPtr
<ComputePipeline
> object
= new ComputePipeline(
251 self
, aId
, context
->mImplicitPipelineLayoutId
,
252 std::move(context
->mImplicitBindGroupLayoutIds
));
253 promise
->MaybeResolve(object
);
255 [promise
](const ipc::ResponseRejectReason
&) {
256 promise
->MaybeRejectWithOperationError(
257 "Internal communication error");
260 return promise
.forget();
263 already_AddRefed
<dom::Promise
> Device::CreateRenderPipelineAsync(
264 const dom::GPURenderPipelineDescriptor
& aDesc
, ErrorResult
& aRv
) {
265 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
266 if (NS_WARN_IF(aRv
.Failed())) {
270 if (!mBridge
->CanSend()) {
271 promise
->MaybeRejectWithOperationError("Internal communication error");
272 return promise
.forget();
275 std::shared_ptr
<PipelineCreationContext
> context(
276 new PipelineCreationContext());
277 context
->mParentId
= mId
;
278 mBridge
->DeviceCreateRenderPipelineAsync(context
.get(), aDesc
)
280 GetCurrentSerialEventTarget(), __func__
,
281 [self
= RefPtr
{this}, context
, promise
](RawId aId
) {
282 RefPtr
<RenderPipeline
> object
= new RenderPipeline(
283 self
, aId
, context
->mImplicitPipelineLayoutId
,
284 std::move(context
->mImplicitBindGroupLayoutIds
));
285 promise
->MaybeResolve(object
);
287 [promise
](const ipc::ResponseRejectReason
&) {
288 promise
->MaybeRejectWithOperationError(
289 "Internal communication error");
292 return promise
.forget();
295 already_AddRefed
<Texture
> Device::InitSwapChain(
296 const dom::GPUCanvasConfiguration
& aDesc
,
297 const layers::RemoteTextureOwnerId aOwnerId
, gfx::SurfaceFormat aFormat
,
298 gfx::IntSize aCanvasSize
) {
299 if (!mBridge
->CanSend()) {
303 const layers::RGBDescriptor
rgbDesc(aCanvasSize
, aFormat
);
304 // buffer count doesn't matter much, will be created on demand
305 const size_t maxBufferCount
= 10;
306 mBridge
->DeviceCreateSwapChain(mId
, rgbDesc
, maxBufferCount
, aOwnerId
);
308 dom::GPUTextureDescriptor desc
;
309 desc
.mDimension
= dom::GPUTextureDimension::_2d
;
310 auto& sizeDict
= desc
.mSize
.SetAsGPUExtent3DDict();
311 sizeDict
.mWidth
= aCanvasSize
.width
;
312 sizeDict
.mHeight
= aCanvasSize
.height
;
313 sizeDict
.mDepthOrArrayLayers
= 1;
314 desc
.mFormat
= aDesc
.mFormat
;
315 desc
.mMipLevelCount
= 1;
316 desc
.mSampleCount
= 1;
317 desc
.mUsage
= aDesc
.mUsage
| dom::GPUTextureUsage_Binding::COPY_SRC
;
318 desc
.mViewFormats
= aDesc
.mViewFormats
;
319 return CreateTexture(desc
);
322 bool Device::CheckNewWarning(const nsACString
& aMessage
) {
323 return mKnownWarnings
.EnsureInserted(aMessage
);
326 void Device::Destroy() {
330 void Device::PushErrorScope(const dom::GPUErrorFilter
& aFilter
) {
334 mBridge
->SendDevicePushErrorScope(mId
, aFilter
);
337 already_AddRefed
<dom::Promise
> Device::PopErrorScope(ErrorResult
& aRv
) {
339 https://www.w3.org/TR/webgpu/#errors-and-debugging:
340 > After a device is lost (described below), errors are no longer surfaced.
341 > At this point, implementations do not need to run validation or error
342 tracking: > popErrorScope() and uncapturederror stop reporting errors, > and
343 the validity of objects on the device becomes unobservable.
345 RefPtr
<dom::Promise
> promise
= dom::Promise::Create(GetParentObject(), aRv
);
346 if (NS_WARN_IF(aRv
.Failed())) {
351 WebGPUChild::JsWarning(
353 "popErrorScope resolving to null because device is already lost."_ns
);
354 promise
->MaybeResolve(JS::NullHandleValue
);
355 return promise
.forget();
358 auto errorPromise
= mBridge
->SendDevicePopErrorScope(mId
);
361 GetCurrentSerialEventTarget(), __func__
,
362 [self
= RefPtr
{this}, promise
](const PopErrorScopeResult
& aResult
) {
365 switch (aResult
.resultType
) {
366 case PopErrorScopeResultType::NoError
:
367 promise
->MaybeResolve(JS::NullHandleValue
);
370 case PopErrorScopeResultType::DeviceLost
:
371 WebGPUChild::JsWarning(
372 self
->GetOwnerGlobal(),
373 "popErrorScope resolving to null because device was lost."_ns
);
374 promise
->MaybeResolve(JS::NullHandleValue
);
377 case PopErrorScopeResultType::ThrowOperationError
:
378 promise
->MaybeRejectWithOperationError(aResult
.message
);
381 case PopErrorScopeResultType::OutOfMemory
:
383 new OutOfMemoryError(self
->GetParentObject(), aResult
.message
);
386 case PopErrorScopeResultType::ValidationError
:
388 new ValidationError(self
->GetParentObject(), aResult
.message
);
391 case PopErrorScopeResultType::InternalError
:
392 error
= new InternalError(self
->GetParentObject(), aResult
.message
);
395 promise
->MaybeResolve(std::move(error
));
397 [self
= RefPtr
{this}, promise
](const ipc::ResponseRejectReason
&) {
399 WebGPUChild::JsWarning(
400 self
->GetOwnerGlobal(),
401 "popErrorScope resolving to null because device was just lost."_ns
);
402 promise
->MaybeResolve(JS::NullHandleValue
);
405 return promise
.forget();
408 } // namespace mozilla::webgpu