1 /* -*- Mode: C++; tab-width: 20; 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 "WebGPUChild.h"
8 #include "js/RootingAPI.h"
10 #include "js/TypeDecls.h"
12 #include "js/Warnings.h" // JS::WarnUTF8
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/EnumTypeTraits.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/dom/ScriptSettings.h"
18 #include "mozilla/dom/WebGPUBinding.h"
19 #include "mozilla/dom/GPUUncapturedErrorEvent.h"
20 #include "mozilla/webgpu/ValidationError.h"
21 #include "mozilla/webgpu/WebGPUTypes.h"
22 #include "mozilla/webgpu/ffi/wgpu.h"
24 #include "DeviceLostInfo.h"
25 #include "PipelineLayout.h"
27 #include "CompilationInfo.h"
28 #include "mozilla/ipc/RawShmem.h"
33 namespace mozilla::webgpu
{
35 NS_IMPL_CYCLE_COLLECTION(WebGPUChild
)
37 void WebGPUChild::JsWarning(nsIGlobalObject
* aGlobal
,
38 const nsACString
& aMessage
) {
39 const auto& flatString
= PromiseFlatCString(aMessage
);
42 if (api
.Init(aGlobal
)) {
43 JS::WarnUTF8(api
.cx(), "%s", flatString
.get());
46 printf_stderr("Validation error without device target: %s\n",
51 static UniquePtr
<ffi::WGPUClient
> initialize() {
52 ffi::WGPUInfrastructure infra
= ffi::wgpu_client_new();
53 return UniquePtr
<ffi::WGPUClient
>{infra
.client
};
56 WebGPUChild::WebGPUChild() : mClient(initialize()) {}
58 WebGPUChild::~WebGPUChild() = default;
60 RefPtr
<AdapterPromise
> WebGPUChild::InstanceRequestAdapter(
61 const dom::GPURequestAdapterOptions
& aOptions
) {
62 const int max_ids
= 10;
63 RawId ids
[max_ids
] = {0};
65 ffi::wgpu_client_make_adapter_ids(mClient
.get(), ids
, max_ids
);
67 nsTArray
<RawId
> sharedIds(count
);
68 for (unsigned long i
= 0; i
!= count
; ++i
) {
69 sharedIds
.AppendElement(ids
[i
]);
72 return SendInstanceRequestAdapter(aOptions
, sharedIds
)
74 GetCurrentSerialEventTarget(), __func__
,
75 [](ipc::ByteBuf
&& aInfoBuf
) {
76 // Ideally, we'd just send an empty ByteBuf, but the IPC code
77 // complains if the capacity is zero...
78 // So for the case where an adapter wasn't found, we just
79 // transfer a single 0u64 in this buffer.
80 return aInfoBuf
.mLen
> sizeof(uint64_t)
81 ? AdapterPromise::CreateAndResolve(std::move(aInfoBuf
),
83 : AdapterPromise::CreateAndReject(Nothing(), __func__
);
85 [](const ipc::ResponseRejectReason
& aReason
) {
86 return AdapterPromise::CreateAndReject(Some(aReason
), __func__
);
90 Maybe
<DeviceRequest
> WebGPUChild::AdapterRequestDevice(
91 RawId aSelfId
, const ffi::WGPUDeviceDescriptor
& aDesc
) {
92 RawId id
= ffi::wgpu_client_make_device_id(mClient
.get(), aSelfId
);
95 ffi::wgpu_client_serialize_device_descriptor(&aDesc
, ToFFI(&bb
));
97 DeviceRequest request
;
99 request
.mPromise
= SendAdapterRequestDevice(aSelfId
, std::move(bb
), id
);
101 return Some(std::move(request
));
104 RawId
WebGPUChild::RenderBundleEncoderFinish(
105 ffi::WGPURenderBundleEncoder
& aEncoder
, RawId aDeviceId
,
106 const dom::GPURenderBundleDescriptor
& aDesc
) {
107 ffi::WGPURenderBundleDescriptor desc
= {};
109 webgpu::StringHelper
label(aDesc
.mLabel
);
110 desc
.label
= label
.Get();
113 RawId id
= ffi::wgpu_client_create_render_bundle(
114 mClient
.get(), &aEncoder
, aDeviceId
, &desc
, ToFFI(&bb
));
116 if (!SendDeviceAction(aDeviceId
, std::move(bb
))) {
117 MOZ_CRASH("IPC failure");
123 RawId
WebGPUChild::RenderBundleEncoderFinishError(RawId aDeviceId
,
124 const nsString
& aLabel
) {
125 webgpu::StringHelper
label(aLabel
);
128 RawId id
= ffi::wgpu_client_create_render_bundle_error(
129 mClient
.get(), aDeviceId
, label
.Get(), ToFFI(&bb
));
131 if (!SendDeviceAction(aDeviceId
, std::move(bb
))) {
132 MOZ_CRASH("IPC failure");
138 ipc::IPCResult
WebGPUChild::RecvUncapturedError(const Maybe
<RawId
> aDeviceId
,
139 const nsACString
& aMessage
) {
140 RefPtr
<Device
> device
;
142 const auto itr
= mDeviceMap
.find(*aDeviceId
);
143 if (itr
!= mDeviceMap
.end()) {
144 device
= itr
->second
.get();
149 JsWarning(nullptr, aMessage
);
151 // We don't want to spam the errors to the console indefinitely
152 if (device
->CheckNewWarning(aMessage
)) {
153 JsWarning(device
->GetOwnerGlobal(), aMessage
);
155 dom::GPUUncapturedErrorEventInit init
;
156 init
.mError
= new ValidationError(device
->GetParentObject(), aMessage
);
157 RefPtr
<mozilla::dom::GPUUncapturedErrorEvent
> event
=
158 dom::GPUUncapturedErrorEvent::Constructor(
159 device
, u
"uncapturederror"_ns
, init
);
160 device
->DispatchEvent(*event
);
166 ipc::IPCResult
WebGPUChild::RecvDropAction(const ipc::ByteBuf
& aByteBuf
) {
167 const auto* byteBuf
= ToFFI(&aByteBuf
);
168 ffi::wgpu_client_drop_action(mClient
.get(), byteBuf
);
172 ipc::IPCResult
WebGPUChild::RecvDeviceLost(RawId aDeviceId
,
173 Maybe
<uint8_t> aReason
,
174 const nsACString
& aMessage
) {
175 RefPtr
<Device
> device
;
176 const auto itr
= mDeviceMap
.find(aDeviceId
);
177 if (itr
!= mDeviceMap
.end()) {
178 device
= itr
->second
.get();
183 auto message
= NS_ConvertUTF8toUTF16(aMessage
);
184 if (aReason
.isSome()) {
185 dom::GPUDeviceLostReason reason
=
186 static_cast<dom::GPUDeviceLostReason
>(*aReason
);
187 device
->ResolveLost(Some(reason
), message
);
189 device
->ResolveLost(Nothing(), message
);
195 void WebGPUChild::DeviceCreateSwapChain(
196 RawId aSelfId
, const RGBDescriptor
& aRgbDesc
, size_t maxBufferCount
,
197 const layers::RemoteTextureOwnerId
& aOwnerId
,
198 bool aUseExternalTextureInSwapChain
) {
199 RawId queueId
= aSelfId
; // TODO: multiple queues
200 nsTArray
<RawId
> bufferIds(maxBufferCount
);
201 for (size_t i
= 0; i
< maxBufferCount
; ++i
) {
202 bufferIds
.AppendElement(
203 ffi::wgpu_client_make_buffer_id(mClient
.get(), aSelfId
));
205 SendDeviceCreateSwapChain(aSelfId
, queueId
, aRgbDesc
, bufferIds
, aOwnerId
,
206 aUseExternalTextureInSwapChain
);
209 void WebGPUChild::QueueOnSubmittedWorkDone(
210 const RawId aSelfId
, const RefPtr
<dom::Promise
>& aPromise
) {
211 SendQueueOnSubmittedWorkDone(aSelfId
)->Then(
212 GetCurrentSerialEventTarget(), __func__
,
213 [aPromise
]() { aPromise
->MaybeResolveWithUndefined(); },
214 [aPromise
](const ipc::ResponseRejectReason
& aReason
) {
215 aPromise
->MaybeRejectWithNotSupportedError("IPC error");
219 void WebGPUChild::SwapChainPresent(RawId aTextureId
,
220 const RemoteTextureId
& aRemoteTextureId
,
221 const RemoteTextureOwnerId
& aOwnerId
) {
222 // Hack: the function expects `DeviceId`, but it only uses it for `backend()`
224 RawId encoderId
= ffi::wgpu_client_make_encoder_id(mClient
.get(), aTextureId
);
225 SendSwapChainPresent(aTextureId
, encoderId
, aRemoteTextureId
, aOwnerId
);
228 void WebGPUChild::RegisterDevice(Device
* const aDevice
) {
229 mDeviceMap
.insert({aDevice
->mId
, aDevice
});
232 void WebGPUChild::UnregisterDevice(RawId aId
) {
233 mDeviceMap
.erase(aId
);
239 void WebGPUChild::FreeUnregisteredInParentDevice(RawId aId
) {
240 ffi::wgpu_client_kill_device_id(mClient
.get(), aId
);
241 mDeviceMap
.erase(aId
);
244 void WebGPUChild::ActorDestroy(ActorDestroyReason
) {
245 // Resolving the promise could cause us to update the original map if the
246 // callee frees the Device objects immediately. Since any remaining entries
247 // in the map are no longer valid, we can just move the map onto the stack.
248 const auto deviceMap
= std::move(mDeviceMap
);
251 for (const auto& targetIter
: deviceMap
) {
252 RefPtr
<Device
> device
= targetIter
.second
.get();
254 // The Device may have gotten freed when we resolved the Promise for
255 // another Device in the map.
259 device
->ResolveLost(Nothing(), u
"WebGPUChild destroyed"_ns
);
263 void WebGPUChild::QueueSubmit(RawId aSelfId
, RawId aDeviceId
,
264 nsTArray
<RawId
>& aCommandBuffers
) {
265 SendQueueSubmit(aSelfId
, aDeviceId
, aCommandBuffers
,
266 mSwapChainTexturesWaitingForSubmit
);
267 mSwapChainTexturesWaitingForSubmit
.Clear();
270 void WebGPUChild::NotifyWaitForSubmit(RawId aTextureId
) {
271 mSwapChainTexturesWaitingForSubmit
.AppendElement(aTextureId
);
274 } // namespace mozilla::webgpu