diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/webgpu/ipc/WebGPUChild.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/webgpu/ipc/WebGPUChild.cpp')
-rw-r--r-- | dom/webgpu/ipc/WebGPUChild.cpp | 270 |
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 |