Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / webgpu / Device.cpp
blobda3ea829ab1226685469281fb375edda438f2674
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 RefPtr<WebGPUChild> Device::GetBridge() { return mBridge; }
48 Device::Device(Adapter* const aParent, RawId aId,
49 const ffi::WGPULimits& aRawLimits)
50 : DOMEventTargetHelper(aParent->GetParentObject()),
51 mId(aId),
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() {
63 if (!mValid) {
64 return;
67 mValid = false;
69 if (mBridge) {
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() {
83 if (mBridge) {
84 mBridge->FreeUnregisteredInParentDevice(mId);
86 mValid = false;
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) {
95 if (IsLost()) {
96 return; // Just drop it?
98 mBridge->SendGenerateError(Some(mId), dom::GPUErrorFilter::Validation,
99 aMessage);
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) {
106 if (!mLostPromise) {
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);
114 return mLostPromise;
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) {
124 RawId id = 0;
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) {
134 RawId id = 0;
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) {
144 RawId id = 0;
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) {
161 RawId id = 0;
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) {
170 RawId id = 0;
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) {
179 RawId id = 0;
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) {
190 Unused << aCx;
192 if (!mBridge->CanSend()) {
193 return nullptr;
196 ErrorResult err;
197 RefPtr<dom::Promise> promise = dom::Promise::Create(GetParentObject(), err);
198 if (NS_WARN_IF(err.Failed())) {
199 return nullptr;
202 return MOZ_KnownLive(mBridge)->DeviceCreateShaderModule(this, aDesc, promise);
205 already_AddRefed<ComputePipeline> Device::CreateComputePipeline(
206 const dom::GPUComputePipelineDescriptor& aDesc) {
207 PipelineCreationContext context = {mId};
208 RawId id = 0;
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};
221 RawId id = 0;
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())) {
235 return nullptr;
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)
247 ->Then(
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())) {
267 return nullptr;
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)
279 ->Then(
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()) {
300 return nullptr;
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() {
327 // TODO
330 void Device::PushErrorScope(const dom::GPUErrorFilter& aFilter) {
331 if (IsLost()) {
332 return;
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())) {
347 return nullptr;
350 if (IsLost()) {
351 WebGPUChild::JsWarning(
352 GetOwnerGlobal(),
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);
360 errorPromise->Then(
361 GetCurrentSerialEventTarget(), __func__,
362 [self = RefPtr{this}, promise](const PopErrorScopeResult& aResult) {
363 RefPtr<Error> error;
365 switch (aResult.resultType) {
366 case PopErrorScopeResultType::NoError:
367 promise->MaybeResolve(JS::NullHandleValue);
368 return;
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);
375 return;
377 case PopErrorScopeResultType::ThrowOperationError:
378 promise->MaybeRejectWithOperationError(aResult.message);
379 return;
381 case PopErrorScopeResultType::OutOfMemory:
382 error =
383 new OutOfMemoryError(self->GetParentObject(), aResult.message);
384 break;
386 case PopErrorScopeResultType::ValidationError:
387 error =
388 new ValidationError(self->GetParentObject(), aResult.message);
389 break;
391 case PopErrorScopeResultType::InternalError:
392 error = new InternalError(self->GetParentObject(), aResult.message);
393 break;
395 promise->MaybeResolve(std::move(error));
397 [self = RefPtr{this}, promise](const ipc::ResponseRejectReason&) {
398 // Device was lost.
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