Bug 1868397 - Make WebGL use VideoBridge to avoid copies with GPUVideoImage. r=jgilbert

This patch makes it so that WebGL that runs in the compositor process
can take advantage of the fact that GPUVideoImage should have an already
created TextureHost between the compositor process and the producing
utility process. For software decoders, this will allow us to avoid a
copy into a shmem from the utility process, and another copy into a
shmem to the compositor process.

Differential Revision: https://phabricator.services.mozilla.com/D195559
This commit is contained in:
Andrew Osmond 2023-12-06 19:31:32 +00:00
parent da74a43f47
commit ec51df1bde
9 changed files with 134 additions and 39 deletions

View File

@ -4414,27 +4414,53 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
return Some(ToString(msg));
}
if (sdType == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer) {
const auto& sdb = sd.get_SurfaceDescriptorBuffer();
const auto& data = sdb.data();
if (data.type() == layers::MemoryOrShmem::TShmem) {
pShmem = &data.get_Shmem();
} else {
return Some(
std::string{"SurfaceDescriptorBuffer data is not Shmem."});
}
}
switch (sdType) {
default:
break;
case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: {
const auto& sdb = sd.get_SurfaceDescriptorBuffer();
const auto& data = sdb.data();
if (data.type() == layers::MemoryOrShmem::TShmem) {
pShmem = &data.get_Shmem();
} else {
return Some(
std::string{"SurfaceDescriptorBuffer data is not Shmem."});
}
} break;
case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10: {
const auto& sdD3D = sd.get_SurfaceDescriptorD3D10();
const auto& inProcess = mNotLost->inProcess;
MOZ_ASSERT(desc->image);
keepAliveImage = desc->image;
if (sdType == layers::SurfaceDescriptor::TSurfaceDescriptorD3D10) {
const auto& sdD3D = sd.get_SurfaceDescriptorD3D10();
const auto& inProcess = mNotLost->inProcess;
MOZ_ASSERT(desc->image);
keepAliveImage = desc->image;
if (sdD3D.gpuProcessTextureId().isSome() && inProcess) {
return Some(
std::string{"gpuProcessTextureId works only in GPU process."});
}
if (sdD3D.gpuProcessTextureId().isSome() && inProcess) {
return Some(
std::string{"gpuProcessTextureId works only in GPU process."});
}
} break;
case layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
const auto& inProcess = mNotLost->inProcess;
MOZ_ASSERT(desc->image);
keepAliveImage = desc->image;
if (inProcess) {
return Some(std::string{
"SurfaceDescriptorGPUVideo works only in GPU process."});
}
const auto& sdv = sd.get_SurfaceDescriptorGPUVideo();
if (sdv.type() != layers::SurfaceDescriptorGPUVideo::
TSurfaceDescriptorRemoteDecoder) {
return Some(std::string{
"SurfaceDescriptorGPUVideo does not contain RemoteDecoder."});
}
const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder();
const auto& subdesc = sdrd.subdesc();
if (subdesc.type() !=
layers::RemoteDecoderVideoSubDescriptor::Tnull_t) {
return Some(
std::string{"SurfaceDescriptorGPUVideo does not contain "
"RemoteDecoder null subdesc."});
}
} break;
}
switch (respecFormat) {

View File

@ -11,6 +11,8 @@
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/TextureHost.h"
#include "mozilla/layers/VideoBridgeParent.h"
#include "mozilla/RefPtr.h"
#include "nsLayoutUtils.h"
#include "WebGLBuffer.h"
@ -299,6 +301,17 @@ static bool SDIsRGBBuffer(const layers::SurfaceDescriptor& sd) {
layers::BufferDescriptor::TRGBDescriptor;
}
// Check if the surface descriptor describes a GPUVideo texture for which we
// only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
// derive the actual texture from.
static bool SDIsNullRemoteDecoder(const layers::SurfaceDescriptor& sd) {
return sd.type() == layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo &&
sd.get_SurfaceDescriptorGPUVideo()
.get_SurfaceDescriptorRemoteDecoder()
.subdesc()
.type() == layers::RemoteDecoderVideoSubDescriptor::Tnull_t;
}
// static
std::unique_ptr<TexUnpackBlob> TexUnpackBlob::Create(
const TexUnpackBlobDesc& desc) {
@ -324,7 +337,9 @@ std::unique_ptr<TexUnpackBlob> TexUnpackBlob::Create(
// Otherwise, TexUnpackImage will try to blit the surface descriptor as
// if it can be mapped as a framebuffer, whereas the Shmem is still CPU
// data.
if (SDIsRGBBuffer(*desc.sd)) return new TexUnpackSurface(desc);
if (SDIsRGBBuffer(*desc.sd) || SDIsNullRemoteDecoder(*desc.sd)) {
return new TexUnpackSurface(desc);
}
return new TexUnpackImage(desc);
}
if (desc.dataSurf) {
@ -874,15 +889,34 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
if (mDesc.sd) {
// If we get here, we assume the SD describes an RGBA Shmem.
const auto& sd = *(mDesc.sd);
MOZ_ASSERT(SDIsRGBBuffer(sd));
const auto& sdb = sd.get_SurfaceDescriptorBuffer();
const auto& rgb = sdb.desc().get_RGBDescriptor();
const auto& data = sdb.data();
MOZ_ASSERT(data.type() == layers::MemoryOrShmem::TShmem);
const auto& shmem = data.get_Shmem();
surf = gfx::Factory::CreateWrappingDataSourceSurface(
shmem.get<uint8_t>(), layers::ImageDataSerializer::GetRGBStride(rgb),
rgb.size(), rgb.format());
if (SDIsRGBBuffer(sd)) {
const auto& sdb = sd.get_SurfaceDescriptorBuffer();
const auto& rgb = sdb.desc().get_RGBDescriptor();
const auto& data = sdb.data();
MOZ_ASSERT(data.type() == layers::MemoryOrShmem::TShmem);
const auto& shmem = data.get_Shmem();
surf = gfx::Factory::CreateWrappingDataSourceSurface(
shmem.get<uint8_t>(), layers::ImageDataSerializer::GetRGBStride(rgb),
rgb.size(), rgb.format());
} else if (SDIsNullRemoteDecoder(sd)) {
const auto& sdrd = sd.get_SurfaceDescriptorGPUVideo()
.get_SurfaceDescriptorRemoteDecoder();
RefPtr<layers::VideoBridgeParent> parent =
layers::VideoBridgeParent::GetSingleton(sdrd.source());
if (!parent) {
gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent";
return false;
}
RefPtr<layers::TextureHost> texture =
parent->LookupTexture(webgl->GetContentId(), sdrd.handle());
if (!texture) {
gfxCriticalNote << "TexUnpackSurface failed to get TextureHost";
return false;
}
surf = texture->GetAsSurface();
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected surface descriptor!");
}
if (!surf) {
gfxCriticalError() << "TexUnpackSurface failed to create wrapping "
"DataSourceSurface for Shmem.";

View File

@ -27,6 +27,7 @@
#include "ImageEncoder.h"
#include "LayerUserData.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLVideoElement.h"
@ -619,6 +620,15 @@ RefPtr<WebGLContext> WebGLContext::Create(HostWebGLContext& host,
const auto UploadableSdTypes = [&]() {
webgl::EnumMask<layers::SurfaceDescriptor::Type> types;
types[layers::SurfaceDescriptor::TSurfaceDescriptorBuffer] = true;
// This is conditional on not using the Compositor thread because we may
// need to synchronize with the RDD process over the PVideoBridge protocol
// to wait for the texture to be available in the compositor process. We
// cannot block on the Compositor thread, so in that configuration, we would
// prefer to do the readback from the RDD which is guaranteed to work, and
// only block the owning thread for WebGL.
types[layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo] =
gfx::gfxVars::UseCanvasRenderThread() ||
!gfx::gfxVars::SupportsThreadsafeGL();
if (webgl->gl->IsANGLE()) {
types[layers::SurfaceDescriptor::TSurfaceDescriptorD3D10] = true;
types[layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr] = true;
@ -1386,6 +1396,17 @@ void WebGLContext::DummyReadFramebufferOperation() {
}
}
dom::ContentParentId WebGLContext::GetContentId() const {
const auto* outOfProcess = mHost ? mHost->mOwnerData.outOfProcess : nullptr;
if (outOfProcess) {
return outOfProcess->mContentId;
}
if (XRE_IsContentProcess()) {
return dom::ContentChild::GetSingleton()->GetID();
}
return dom::ContentParentId();
}
bool WebGLContext::Has64BitTimestamps() const {
// 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
// GLES3+.

View File

@ -17,6 +17,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/Atomics.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/Nullable.h"
@ -461,6 +462,8 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
void DummyReadFramebufferOperation();
dom::ContentParentId GetContentId() const;
WebGLTexture* GetActiveTex(const GLenum texTarget) const;
gl::GLContext* GL() const { return gl; }

View File

@ -24,7 +24,9 @@ mozilla::ipc::IPCResult WebGLParent::RecvInitialize(
return IPC_OK();
}
WebGLParent::WebGLParent() = default;
WebGLParent::WebGLParent(const dom::ContentParentId& aContentId)
: mContentId(aContentId) {}
WebGLParent::~WebGLParent() = default;
// -

View File

@ -7,6 +7,7 @@
#define WEBGLPARENT_H_
#include "mozilla/GfxMessageUtils.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/PWebGLParent.h"
#include "mozilla/WeakPtr.h"
@ -31,7 +32,7 @@ class WebGLParent : public PWebGLParent, public SupportsWeakPtr {
mozilla::ipc::IPCResult RecvInitialize(const webgl::InitContextDesc&,
webgl::InitContextResult* out);
WebGLParent(); // For IPDL
explicit WebGLParent(const dom::ContentParentId& aContentId); // For IPDL
using IPCResult = mozilla::ipc::IPCResult;
@ -108,6 +109,8 @@ class WebGLParent : public PWebGLParent, public SupportsWeakPtr {
// -
const dom::ContentParentId mContentId;
private:
~WebGLParent();

View File

@ -27,10 +27,11 @@ nsTArray<CanvasManagerParent::ReplayTexture>
bool CanvasManagerParent::sReplayTexturesEnabled(true);
/* static */ void CanvasManagerParent::Init(
Endpoint<PCanvasManagerParent>&& aEndpoint) {
Endpoint<PCanvasManagerParent>&& aEndpoint,
const dom::ContentParentId& aContentId) {
MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
auto manager = MakeRefPtr<CanvasManagerParent>();
auto manager = MakeRefPtr<CanvasManagerParent>(aContentId);
nsCOMPtr<nsIThread> owningThread =
gfx::CanvasRenderThread::GetCanvasRenderThread();
@ -203,7 +204,9 @@ CanvasManagerParent::WaitForReplayTexture(base::ProcessId aOtherPid,
return desc;
}
CanvasManagerParent::CanvasManagerParent() = default;
CanvasManagerParent::CanvasManagerParent(const dom::ContentParentId& aContentId)
: mContentId(aContentId) {}
CanvasManagerParent::~CanvasManagerParent() = default;
void CanvasManagerParent::Bind(Endpoint<PCanvasManagerParent>&& aEndpoint) {
@ -220,7 +223,7 @@ void CanvasManagerParent::ActorDestroy(ActorDestroyReason aWhy) {
}
already_AddRefed<dom::PWebGLParent> CanvasManagerParent::AllocPWebGLParent() {
return MakeAndAddRef<dom::WebGLParent>();
return MakeAndAddRef<dom::WebGLParent>(mContentId);
}
already_AddRefed<webgpu::PWebGPUParent>

View File

@ -7,6 +7,7 @@
#define _include_gfx_ipc_CanvasManagerParent_h__
#include "mozilla/gfx/PCanvasManagerParent.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/StaticMonitor.h"
#include "mozilla/UniquePtr.h"
#include "nsHashtablesFwd.h"
@ -24,7 +25,8 @@ class CanvasManagerParent final : public PCanvasManagerParent {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerParent, override);
static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint);
static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint,
const dom::ContentParentId& aContentId);
static void Shutdown();
@ -42,7 +44,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
static UniquePtr<layers::SurfaceDescriptor> WaitForReplayTexture(
base::ProcessId aOtherPid, int64_t aTextureId);
CanvasManagerParent();
explicit CanvasManagerParent(const dom::ContentParentId& aContentId);
void Bind(Endpoint<PCanvasManagerParent>&& aEndpoint);
void ActorDestroy(ActorDestroyReason aWhy) override;
@ -67,6 +69,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
~CanvasManagerParent() override;
const dom::ContentParentId mContentId;
uint32_t mId = 0;
using ManagerSet = nsTHashSet<RefPtr<CanvasManagerParent>>;

View File

@ -319,7 +319,7 @@ mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager(
Endpoint<PCanvasManagerParent>&& aEndpoint) {
gfx::CanvasManagerParent::Init(std::move(aEndpoint));
gfx::CanvasManagerParent::Init(std::move(aEndpoint), mContentId);
return IPC_OK();
}