Backed out changeset 8d9053f1c203 (bug 1942129) Backed out changeset 393e3c507c27 (bug 1942129) Backed out changeset 8240d353d224 (bug 1942129) Backed out changeset 8c4cd026b720 (bug 1942129) Backed out changeset 742634b0d6e9 (bug 1942129) Backed out changeset d16857f9812f (bug 1942129) Backed out changeset 7ff7af041ee7 (bug 1942129) Backed out changeset ef41d9e4c7de (bug 1942129)
275 lines
9.4 KiB
C++
275 lines
9.4 KiB
C++
/* -*- 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(), "Uncaptured WebGPU error: %s", flatString.get());
|
|
}
|
|
} else {
|
|
printf_stderr("Uncaptured WebGPU 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) {
|
|
RawId id = ffi::wgpu_client_make_adapter_id(mClient.get());
|
|
|
|
return SendInstanceRequestAdapter(aOptions, id)
|
|
->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::WGPUFfiDeviceDescriptor& aDesc) {
|
|
ffi::WGPUDeviceQueueId ids =
|
|
ffi::wgpu_client_make_device_queue_id(mClient.get());
|
|
|
|
ByteBuf bb;
|
|
ffi::wgpu_client_serialize_device_descriptor(&aDesc, ToFFI(&bb));
|
|
|
|
DeviceRequest request;
|
|
request.mDeviceId = ids.device;
|
|
request.mQueueId = ids.queue;
|
|
request.mPromise =
|
|
SendAdapterRequestDevice(aSelfId, std::move(bb), ids.device, ids.queue);
|
|
|
|
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,
|
|
&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(), 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();
|
|
}
|
|
|
|
bool WebGPUChild::ResolveLostForDeviceId(RawId aDeviceId,
|
|
Maybe<uint8_t> aReason,
|
|
const nsAString& aMessage) {
|
|
RefPtr<Device> device;
|
|
const auto itr = mDeviceMap.find(aDeviceId);
|
|
if (itr != mDeviceMap.end()) {
|
|
device = itr->second.get();
|
|
MOZ_ASSERT(device);
|
|
}
|
|
if (!device) {
|
|
// We must have unregistered the device already.
|
|
return false;
|
|
}
|
|
|
|
if (aReason.isSome()) {
|
|
dom::GPUDeviceLostReason reason =
|
|
static_cast<dom::GPUDeviceLostReason>(*aReason);
|
|
MOZ_ASSERT(reason == dom::GPUDeviceLostReason::Destroyed,
|
|
"There is only one valid GPUDeviceLostReason value.");
|
|
device->ResolveLost(Some(reason), aMessage);
|
|
} else {
|
|
device->ResolveLost(Nothing(), aMessage);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ipc::IPCResult WebGPUChild::RecvDeviceLost(RawId aDeviceId,
|
|
Maybe<uint8_t> aReason,
|
|
const nsACString& aMessage) {
|
|
auto message = NS_ConvertUTF8toUTF16(aMessage);
|
|
ResolveLostForDeviceId(aDeviceId, aReason, 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()));
|
|
}
|
|
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.
|
|
// The parent side needs to create a command encoder which will be submitted
|
|
// and dropped right away so we create and release an encoder ID here.
|
|
RawId encoderId = ffi::wgpu_client_make_encoder_id(mClient.get());
|
|
SendSwapChainPresent(aTextureId, encoderId, aRemoteTextureId, aOwnerId);
|
|
ffi::wgpu_client_free_command_encoder_id(mClient.get(), encoderId);
|
|
}
|
|
|
|
void WebGPUChild::RegisterDevice(Device* const aDevice) {
|
|
mDeviceMap.insert({aDevice->mId, aDevice});
|
|
}
|
|
|
|
void WebGPUChild::UnregisterDevice(RawId aDeviceId) {
|
|
if (CanSend()) {
|
|
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();
|
|
MOZ_ASSERT(device);
|
|
ResolveLostForDeviceId(device->mId, 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
|