summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/ipc/WebGPUChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/ipc/WebGPUChild.cpp')
-rw-r--r--dom/webgpu/ipc/WebGPUChild.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/dom/webgpu/ipc/WebGPUChild.cpp b/dom/webgpu/ipc/WebGPUChild.cpp
new file mode 100644
index 0000000000..663dd5cb89
--- /dev/null
+++ b/dom/webgpu/ipc/WebGPUChild.cpp
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGPUChild.h"
+
+#include "js/RootingAPI.h"
+#include "js/String.h"
+#include "js/TypeDecls.h"
+#include "js/Value.h"
+#include "js/Warnings.h" // JS::WarnUTF8
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/EnumTypeTraits.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/WebGPUBinding.h"
+#include "mozilla/dom/GPUUncapturedErrorEvent.h"
+#include "mozilla/webgpu/ValidationError.h"
+#include "mozilla/webgpu/WebGPUTypes.h"
+#include "mozilla/webgpu/ffi/wgpu.h"
+#include "Adapter.h"
+#include "DeviceLostInfo.h"
+#include "PipelineLayout.h"
+#include "Sampler.h"
+#include "CompilationInfo.h"
+#include "mozilla/ipc/RawShmem.h"
+#include "Utility.h"
+
+#include <utility>
+
+namespace mozilla::webgpu {
+
+NS_IMPL_CYCLE_COLLECTION(WebGPUChild)
+
+void WebGPUChild::JsWarning(nsIGlobalObject* aGlobal,
+ const nsACString& aMessage) {
+ const auto& flatString = PromiseFlatCString(aMessage);
+ if (aGlobal) {
+ dom::AutoJSAPI api;
+ if (api.Init(aGlobal)) {
+ JS::WarnUTF8(api.cx(), "%s", flatString.get());
+ }
+ } else {
+ printf_stderr("Validation error without device target: %s\n",
+ flatString.get());
+ }
+}
+
+static UniquePtr<ffi::WGPUClient> initialize() {
+ ffi::WGPUInfrastructure infra = ffi::wgpu_client_new();
+ return UniquePtr<ffi::WGPUClient>{infra.client};
+}
+
+WebGPUChild::WebGPUChild() : mClient(initialize()) {}
+
+WebGPUChild::~WebGPUChild() = default;
+
+RefPtr<AdapterPromise> WebGPUChild::InstanceRequestAdapter(
+ const dom::GPURequestAdapterOptions& aOptions) {
+ const int max_ids = 10;
+ RawId ids[max_ids] = {0};
+ unsigned long count =
+ ffi::wgpu_client_make_adapter_ids(mClient.get(), ids, max_ids);
+
+ nsTArray<RawId> sharedIds(count);
+ for (unsigned long i = 0; i != count; ++i) {
+ sharedIds.AppendElement(ids[i]);
+ }
+
+ return SendInstanceRequestAdapter(aOptions, sharedIds)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [](ipc::ByteBuf&& aInfoBuf) {
+ // Ideally, we'd just send an empty ByteBuf, but the IPC code
+ // complains if the capacity is zero...
+ // So for the case where an adapter wasn't found, we just
+ // transfer a single 0u64 in this buffer.
+ return aInfoBuf.mLen > sizeof(uint64_t)
+ ? AdapterPromise::CreateAndResolve(std::move(aInfoBuf),
+ __func__)
+ : AdapterPromise::CreateAndReject(Nothing(), __func__);
+ },
+ [](const ipc::ResponseRejectReason& aReason) {
+ return AdapterPromise::CreateAndReject(Some(aReason), __func__);
+ });
+}
+
+Maybe<DeviceRequest> WebGPUChild::AdapterRequestDevice(
+ RawId aSelfId, const ffi::WGPUDeviceDescriptor& aDesc) {
+ RawId id = ffi::wgpu_client_make_device_id(mClient.get(), aSelfId);
+
+ ByteBuf bb;
+ ffi::wgpu_client_serialize_device_descriptor(&aDesc, ToFFI(&bb));
+
+ DeviceRequest request;
+ request.mId = id;
+ request.mPromise = SendAdapterRequestDevice(aSelfId, std::move(bb), id);
+
+ return Some(std::move(request));
+}
+
+RawId WebGPUChild::RenderBundleEncoderFinish(
+ ffi::WGPURenderBundleEncoder& aEncoder, RawId aDeviceId,
+ const dom::GPURenderBundleDescriptor& aDesc) {
+ ffi::WGPURenderBundleDescriptor desc = {};
+
+ webgpu::StringHelper label(aDesc.mLabel);
+ desc.label = label.Get();
+
+ ipc::ByteBuf bb;
+ RawId id = ffi::wgpu_client_create_render_bundle(
+ mClient.get(), &aEncoder, aDeviceId, &desc, ToFFI(&bb));
+
+ SendDeviceAction(aDeviceId, std::move(bb));
+
+ return id;
+}
+
+RawId WebGPUChild::RenderBundleEncoderFinishError(RawId aDeviceId,
+ const nsString& aLabel) {
+ webgpu::StringHelper label(aLabel);
+
+ ipc::ByteBuf bb;
+ RawId id = ffi::wgpu_client_create_render_bundle_error(
+ mClient.get(), aDeviceId, label.Get(), ToFFI(&bb));
+
+ SendDeviceAction(aDeviceId, std::move(bb));
+
+ return id;
+}
+
+ipc::IPCResult WebGPUChild::RecvUncapturedError(const Maybe<RawId> aDeviceId,
+ const nsACString& aMessage) {
+ RefPtr<Device> device;
+ if (aDeviceId) {
+ const auto itr = mDeviceMap.find(*aDeviceId);
+ if (itr != mDeviceMap.end()) {
+ device = itr->second.get();
+ MOZ_ASSERT(device);
+ }
+ }
+ if (!device) {
+ JsWarning(nullptr, aMessage);
+ } else {
+ // We don't want to spam the errors to the console indefinitely
+ if (device->CheckNewWarning(aMessage)) {
+ JsWarning(device->GetOwnerGlobal(), aMessage);
+
+ dom::GPUUncapturedErrorEventInit init;
+ init.mError = new ValidationError(device->GetParentObject(), aMessage);
+ RefPtr<mozilla::dom::GPUUncapturedErrorEvent> event =
+ dom::GPUUncapturedErrorEvent::Constructor(
+ device, u"uncapturederror"_ns, init);
+ device->DispatchEvent(*event);
+ }
+ }
+ return IPC_OK();
+}
+
+ipc::IPCResult WebGPUChild::RecvDropAction(const ipc::ByteBuf& aByteBuf) {
+ const auto* byteBuf = ToFFI(&aByteBuf);
+ ffi::wgpu_client_drop_action(mClient.get(), byteBuf);
+ return IPC_OK();
+}
+
+ipc::IPCResult WebGPUChild::RecvDeviceLost(RawId aDeviceId,
+ Maybe<uint8_t> aReason,
+ const nsACString& aMessage) {
+ RefPtr<Device> device;
+ const auto itr = mDeviceMap.find(aDeviceId);
+ if (itr != mDeviceMap.end()) {
+ device = itr->second.get();
+ MOZ_ASSERT(device);
+ }
+
+ if (device) {
+ auto message = NS_ConvertUTF8toUTF16(aMessage);
+ if (aReason.isSome()) {
+ dom::GPUDeviceLostReason reason =
+ static_cast<dom::GPUDeviceLostReason>(*aReason);
+ device->ResolveLost(Some(reason), message);
+ } else {
+ device->ResolveLost(Nothing(), message);
+ }
+ }
+ return IPC_OK();
+}
+
+void WebGPUChild::DeviceCreateSwapChain(
+ RawId aSelfId, const RGBDescriptor& aRgbDesc, size_t maxBufferCount,
+ const layers::RemoteTextureOwnerId& aOwnerId,
+ bool aUseExternalTextureInSwapChain) {
+ RawId queueId = aSelfId; // TODO: multiple queues
+ nsTArray<RawId> bufferIds(maxBufferCount);
+ for (size_t i = 0; i < maxBufferCount; ++i) {
+ bufferIds.AppendElement(
+ ffi::wgpu_client_make_buffer_id(mClient.get(), aSelfId));
+ }
+ SendDeviceCreateSwapChain(aSelfId, queueId, aRgbDesc, bufferIds, aOwnerId,
+ aUseExternalTextureInSwapChain);
+}
+
+void WebGPUChild::QueueOnSubmittedWorkDone(
+ const RawId aSelfId, const RefPtr<dom::Promise>& aPromise) {
+ SendQueueOnSubmittedWorkDone(aSelfId)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aPromise]() { aPromise->MaybeResolveWithUndefined(); },
+ [aPromise](const ipc::ResponseRejectReason& aReason) {
+ aPromise->MaybeRejectWithNotSupportedError("IPC error");
+ });
+}
+
+void WebGPUChild::SwapChainPresent(RawId aTextureId,
+ const RemoteTextureId& aRemoteTextureId,
+ const RemoteTextureOwnerId& aOwnerId) {
+ // Hack: the function expects `DeviceId`, but it only uses it for `backend()`
+ // selection.
+ RawId encoderId = ffi::wgpu_client_make_encoder_id(mClient.get(), aTextureId);
+ SendSwapChainPresent(aTextureId, encoderId, aRemoteTextureId, aOwnerId);
+}
+
+void WebGPUChild::RegisterDevice(Device* const aDevice) {
+ mDeviceMap.insert({aDevice->mId, aDevice});
+}
+
+void WebGPUChild::UnregisterDevice(RawId aDeviceId) {
+ if (IsOpen()) {
+ SendDeviceDrop(aDeviceId);
+ }
+ mDeviceMap.erase(aDeviceId);
+}
+
+void WebGPUChild::FreeUnregisteredInParentDevice(RawId aId) {
+ ffi::wgpu_client_kill_device_id(mClient.get(), aId);
+ mDeviceMap.erase(aId);
+}
+
+void WebGPUChild::ActorDestroy(ActorDestroyReason) {
+ // Resolving the promise could cause us to update the original map if the
+ // callee frees the Device objects immediately. Since any remaining entries
+ // in the map are no longer valid, we can just move the map onto the stack.
+ const auto deviceMap = std::move(mDeviceMap);
+ mDeviceMap.clear();
+
+ for (const auto& targetIter : deviceMap) {
+ RefPtr<Device> device = targetIter.second.get();
+ if (!device) {
+ // The Device may have gotten freed when we resolved the Promise for
+ // another Device in the map.
+ continue;
+ }
+
+ device->ResolveLost(Nothing(), u"WebGPUChild destroyed"_ns);
+ }
+}
+
+void WebGPUChild::QueueSubmit(RawId aSelfId, RawId aDeviceId,
+ nsTArray<RawId>& aCommandBuffers) {
+ SendQueueSubmit(aSelfId, aDeviceId, aCommandBuffers,
+ mSwapChainTexturesWaitingForSubmit);
+ mSwapChainTexturesWaitingForSubmit.Clear();
+}
+
+void WebGPUChild::NotifyWaitForSubmit(RawId aTextureId) {
+ mSwapChainTexturesWaitingForSubmit.AppendElement(aTextureId);
+}
+
+} // namespace mozilla::webgpu