no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / webgpu / Device.cpp
bloba47295c2f18a3b56fed8c0c28db842d141a818f3
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/Promise.h"
13 #include "mozilla/dom/WebGPUBinding.h"
14 #include "Device.h"
15 #include "CommandEncoder.h"
16 #include "BindGroup.h"
18 #include "Adapter.h"
19 #include "Buffer.h"
20 #include "ComputePipeline.h"
21 #include "DeviceLostInfo.h"
22 #include "InternalError.h"
23 #include "OutOfMemoryError.h"
24 #include "PipelineLayout.h"
25 #include "Queue.h"
26 #include "RenderBundleEncoder.h"
27 #include "RenderPipeline.h"
28 #include "Sampler.h"
29 #include "SupportedFeatures.h"
30 #include "SupportedLimits.h"
31 #include "Texture.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) +
50 kBufferAlignmentMask;
53 RefPtr<WebGPUChild> Device::GetBridge() { return mBridge; }
55 Device::Device(Adapter* const aParent, RawId aId,
56 const ffi::WGPULimits& aRawLimits)
57 : DOMEventTargetHelper(aParent->GetParentObject()),
58 mId(aId),
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() {
70 if (!mValid) {
71 return;
74 mValid = false;
76 if (mBridge) {
77 mBridge->UnregisterDevice(mId);
81 void Device::CleanupUnregisteredInParent() {
82 if (mBridge) {
83 mBridge->FreeUnregisteredInParentDevice(mId);
85 mValid = false;
88 bool Device::IsLost() const {
89 return !mBridge || !mBridge->CanSend() ||
90 (mLostPromise &&
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,
104 aMessage);
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) {
115 aRv = NS_OK;
116 if (!mLostPromise) {
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);
124 return mLostPromise;
127 void Device::ResolveLost(Maybe<dom::GPUDeviceLostReason> aReason,
128 const nsAString& aMessage) {
129 IgnoredErrorResult rv;
130 dom::Promise* lostPromise = GetLost(rv);
131 if (!lostPromise) {
132 // Promise doesn't exist? Maybe out of memory.
133 return;
135 if (lostPromise->State() != dom::Promise::PromiseState::Pending) {
136 // lostPromise was already resolved or rejected.
137 return;
139 if (!lostPromise->PromiseObj()) {
140 // The underlying JS object is gone.
141 return;
144 RefPtr<DeviceLostInfo> info;
145 if (aReason.isSome()) {
146 info = MakeRefPtr<DeviceLostInfo>(GetParentObject(), *aReason, aMessage);
147 } else {
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) {
161 MOZ_ASSERT(aConfig);
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) {
186 RawId id = 0;
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) {
196 RawId id = 0;
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) {
206 RawId id = 0;
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) {
223 RawId id = 0;
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) {
232 RawId id = 0;
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) {
241 RawId id = 0;
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,
251 ErrorResult& aRv) {
252 Unused << aCx;
254 if (!mBridge->CanSend()) {
255 aRv.ThrowInvalidStateError("Connection to GPU process has shut down");
256 return nullptr;
259 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), aRv);
260 if (NS_WARN_IF(aRv.Failed())) {
261 return nullptr;
264 return MOZ_KnownLive(mBridge)->DeviceCreateShaderModule(this, aDesc, promise);
267 already_AddRefed<ComputePipeline> Device::CreateComputePipeline(
268 const dom::GPUComputePipelineDescriptor& aDesc) {
269 PipelineCreationContext context = {mId};
270 RawId id = 0;
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};
283 RawId id = 0;
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())) {
297 return nullptr;
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)
309 ->Then(
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())) {
329 return nullptr;
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)
341 ->Then(
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) {
362 MOZ_ASSERT(aConfig);
364 if (!mBridge->CanSend()) {
365 return nullptr;
368 // Check that aCanvasSize and aFormat will generate a texture stride
369 // within limits.
370 const auto bufferStrideWithMask = BufferStrideWithMask(aCanvasSize, aFormat);
371 if (!bufferStrideWithMask.isValid()) {
372 return nullptr;
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() {
391 if (IsLost()) {
392 return;
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()) {
412 return;
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())) {
427 return nullptr;
430 if (!IsBridgeAlive()) {
431 WebGPUChild::JsWarning(
432 GetOwnerGlobal(),
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);
440 errorPromise->Then(
441 GetCurrentSerialEventTarget(), __func__,
442 [self = RefPtr{this}, promise](const PopErrorScopeResult& aResult) {
443 RefPtr<Error> error;
445 switch (aResult.resultType) {
446 case PopErrorScopeResultType::NoError:
447 promise->MaybeResolve(JS::NullHandleValue);
448 return;
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);
455 return;
457 case PopErrorScopeResultType::ThrowOperationError:
458 promise->MaybeRejectWithOperationError(aResult.message);
459 return;
461 case PopErrorScopeResultType::OutOfMemory:
462 error =
463 new OutOfMemoryError(self->GetParentObject(), aResult.message);
464 break;
466 case PopErrorScopeResultType::ValidationError:
467 error =
468 new ValidationError(self->GetParentObject(), aResult.message);
469 break;
471 case PopErrorScopeResultType::InternalError:
472 error = new InternalError(self->GetParentObject(), aResult.message);
473 break;
475 promise->MaybeResolve(std::move(error));
477 [self = RefPtr{this}, promise](const ipc::ResponseRejectReason&) {
478 // Device was lost.
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