Backed out 2 changesets (bug 1865921) for causing failures on test_device_lost.html...
[gecko.git] / dom / webgpu / ipc / WebGPUChild.cpp
blob58aa52bd29454461bad442e0c71cc4c906761781
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"
9 #include "js/String.h"
10 #include "js/TypeDecls.h"
11 #include "js/Value.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"
23 #include "Adapter.h"
24 #include "DeviceLostInfo.h"
25 #include "PipelineLayout.h"
26 #include "Sampler.h"
27 #include "CompilationInfo.h"
28 #include "mozilla/ipc/RawShmem.h"
29 #include "Utility.h"
31 #include <utility>
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);
40 if (aGlobal) {
41 dom::AutoJSAPI api;
42 if (api.Init(aGlobal)) {
43 JS::WarnUTF8(api.cx(), "%s", flatString.get());
45 } else {
46 printf_stderr("Validation error without device target: %s\n",
47 flatString.get());
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};
64 unsigned long count =
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)
73 ->Then(
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),
82 __func__)
83 : AdapterPromise::CreateAndReject(Nothing(), __func__);
85 [](const ipc::ResponseRejectReason& aReason) {
86 return AdapterPromise::CreateAndReject(Some(aReason), __func__);
87 });
90 Maybe<DeviceRequest> WebGPUChild::AdapterRequestDevice(
91 RawId aSelfId, const ffi::WGPUDeviceDescriptor& aDesc) {
92 RawId id = ffi::wgpu_client_make_device_id(mClient.get(), aSelfId);
94 ByteBuf bb;
95 ffi::wgpu_client_serialize_device_descriptor(&aDesc, ToFFI(&bb));
97 DeviceRequest request;
98 request.mId = id;
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();
112 ipc::ByteBuf bb;
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");
120 return id;
123 RawId WebGPUChild::RenderBundleEncoderFinishError(RawId aDeviceId,
124 const nsString& aLabel) {
125 webgpu::StringHelper label(aLabel);
127 ipc::ByteBuf bb;
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");
135 return id;
138 ipc::IPCResult WebGPUChild::RecvUncapturedError(const Maybe<RawId> aDeviceId,
139 const nsACString& aMessage) {
140 RefPtr<Device> device;
141 if (aDeviceId) {
142 const auto itr = mDeviceMap.find(*aDeviceId);
143 if (itr != mDeviceMap.end()) {
144 device = itr->second.get();
145 MOZ_ASSERT(device);
148 if (!device) {
149 JsWarning(nullptr, aMessage);
150 } else {
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);
163 return IPC_OK();
166 ipc::IPCResult WebGPUChild::RecvDropAction(const ipc::ByteBuf& aByteBuf) {
167 const auto* byteBuf = ToFFI(&aByteBuf);
168 ffi::wgpu_client_drop_action(mClient.get(), byteBuf);
169 return IPC_OK();
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();
179 MOZ_ASSERT(device);
182 if (device) {
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);
188 } else {
189 device->ResolveLost(Nothing(), message);
192 return IPC_OK();
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()`
223 // selection.
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);
234 if (IsOpen()) {
235 SendDeviceDrop(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);
249 mDeviceMap.clear();
251 for (const auto& targetIter : deviceMap) {
252 RefPtr<Device> device = targetIter.second.get();
253 if (!device) {
254 // The Device may have gotten freed when we resolved the Promise for
255 // another Device in the map.
256 continue;
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