Bug 1869346 - Rework how canvas accesses shared surfaces in the compositor process. r=gfx-reviewers,lsalzman

This patch adds plumbing to allow sharing ExternalImageId via
SurfaceDescriptor. This will be used in a future patch by WebGL and
WebGPU to avoid extra copies. It also refactors how CanvasTranslator
gets a SourceSurfaceSharedDataWrapper pointer by ensuring the surfaces
are kept alive long enough even if the CompositorManagerParent actor is
destroyed before CanvasManagerParent and its children. Now we keep the
surfaces around until all of these dependent objects have been freed.

Differential Revision: https://phabricator.services.mozilla.com/D196067
This commit is contained in:
Andrew Osmond 2023-12-11 20:44:44 +00:00
parent 58a3a1839f
commit e96409bd27
13 changed files with 114 additions and 15 deletions

View file

@ -14,6 +14,7 @@
#include "mozilla/layers/CanvasTranslator.h" #include "mozilla/layers/CanvasTranslator.h"
#include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "mozilla/webgpu/WebGPUParent.h" #include "mozilla/webgpu/WebGPUParent.h"
#include "nsIThread.h" #include "nsIThread.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
@ -29,10 +30,12 @@ bool CanvasManagerParent::sReplayTexturesEnabled(true);
/* static */ void CanvasManagerParent::Init( /* static */ void CanvasManagerParent::Init(
Endpoint<PCanvasManagerParent>&& aEndpoint, Endpoint<PCanvasManagerParent>&& aEndpoint,
layers::SharedSurfacesHolder* aSharedSurfacesHolder,
const dom::ContentParentId& aContentId) { const dom::ContentParentId& aContentId) {
MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
auto manager = MakeRefPtr<CanvasManagerParent>(aContentId); auto manager =
MakeRefPtr<CanvasManagerParent>(aSharedSurfacesHolder, aContentId);
nsCOMPtr<nsIThread> owningThread = nsCOMPtr<nsIThread> owningThread =
gfx::CanvasRenderThread::GetCanvasRenderThread(); gfx::CanvasRenderThread::GetCanvasRenderThread();
@ -220,8 +223,10 @@ CanvasManagerParent::WaitForReplayTexture(layers::HostIPCAllocator* aAllocator,
return desc; return desc;
} }
CanvasManagerParent::CanvasManagerParent(const dom::ContentParentId& aContentId) CanvasManagerParent::CanvasManagerParent(
: mContentId(aContentId) {} layers::SharedSurfacesHolder* aSharedSurfacesHolder,
const dom::ContentParentId& aContentId)
: mSharedSurfacesHolder(aSharedSurfacesHolder), mContentId(aContentId) {}
CanvasManagerParent::~CanvasManagerParent() = default; CanvasManagerParent::~CanvasManagerParent() = default;
@ -273,7 +278,8 @@ mozilla::ipc::IPCResult CanvasManagerParent::RecvInitialize(
already_AddRefed<layers::PCanvasParent> already_AddRefed<layers::PCanvasParent>
CanvasManagerParent::AllocPCanvasParent() { CanvasManagerParent::AllocPCanvasParent() {
MOZ_RELEASE_ASSERT(mId != 0); MOZ_RELEASE_ASSERT(mId != 0);
return MakeAndAddRef<layers::CanvasTranslator>(mContentId, mId); return MakeAndAddRef<layers::CanvasTranslator>(mSharedSurfacesHolder,
mContentId, mId);
} }
mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot( mozilla::ipc::IPCResult CanvasManagerParent::RecvGetSnapshot(

View file

@ -17,6 +17,7 @@ namespace mozilla {
namespace layers { namespace layers {
class CanvasTranslator; class CanvasTranslator;
class HostIPCAllocator; class HostIPCAllocator;
class SharedSurfacesHolder;
class SurfaceDescriptor; class SurfaceDescriptor;
} // namespace layers } // namespace layers
@ -27,6 +28,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerParent, override); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerParent, override);
static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint, static void Init(Endpoint<PCanvasManagerParent>&& aEndpoint,
layers::SharedSurfacesHolder* aSharedSurfacesHolder,
const dom::ContentParentId& aContentId); const dom::ContentParentId& aContentId);
static void Shutdown(); static void Shutdown();
@ -45,7 +47,8 @@ class CanvasManagerParent final : public PCanvasManagerParent {
static UniquePtr<layers::SurfaceDescriptor> WaitForReplayTexture( static UniquePtr<layers::SurfaceDescriptor> WaitForReplayTexture(
layers::HostIPCAllocator* aAllocator, int64_t aTextureId); layers::HostIPCAllocator* aAllocator, int64_t aTextureId);
explicit CanvasManagerParent(const dom::ContentParentId& aContentId); CanvasManagerParent(layers::SharedSurfacesHolder* aSharedSurfacesHolder,
const dom::ContentParentId& aContentId);
void Bind(Endpoint<PCanvasManagerParent>&& aEndpoint); void Bind(Endpoint<PCanvasManagerParent>&& aEndpoint);
void ActorDestroy(ActorDestroyReason aWhy) override; void ActorDestroy(ActorDestroyReason aWhy) override;
@ -70,6 +73,7 @@ class CanvasManagerParent final : public PCanvasManagerParent {
~CanvasManagerParent() override; ~CanvasManagerParent() override;
RefPtr<layers::SharedSurfacesHolder> mSharedSurfacesHolder;
const dom::ContentParentId mContentId; const dom::ContentParentId mContentId;
uint32_t mId = 0; uint32_t mId = 0;

View file

@ -50,9 +50,11 @@ TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
return textureData; return textureData;
} }
CanvasTranslator::CanvasTranslator(const dom::ContentParentId& aContentId, CanvasTranslator::CanvasTranslator(
uint32_t aManagerId) layers::SharedSurfacesHolder* aSharedSurfacesHolder,
: mMaxSpinCount(StaticPrefs::gfx_canvas_remote_max_spin_count()), const dom::ContentParentId& aContentId, uint32_t aManagerId)
: mSharedSurfacesHolder(aSharedSurfacesHolder),
mMaxSpinCount(StaticPrefs::gfx_canvas_remote_max_spin_count()),
mContentId(aContentId), mContentId(aContentId),
mManagerId(aManagerId) { mManagerId(aManagerId) {
mNextEventTimeout = TimeDuration::FromMilliseconds( mNextEventTimeout = TimeDuration::FromMilliseconds(
@ -640,7 +642,7 @@ TextureData* CanvasTranslator::LookupTextureData(int64_t aTextureId) {
already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface( already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
uint64_t aKey) { uint64_t aKey) {
return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey)); return mSharedSurfacesHolder->Get(wr::ToExternalImageId(aKey));
} }
void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); } void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }

View file

@ -28,6 +28,7 @@ class TaskQueue;
namespace layers { namespace layers {
class SharedSurfacesHolder;
class TextureData; class TextureData;
class CanvasTranslator final : public gfx::InlineTranslator, class CanvasTranslator final : public gfx::InlineTranslator,
@ -37,7 +38,8 @@ class CanvasTranslator final : public gfx::InlineTranslator,
friend class PProtocolParent; friend class PProtocolParent;
CanvasTranslator(const dom::ContentParentId& aContentId, uint32_t aManagerId); CanvasTranslator(layers::SharedSurfacesHolder* aSharedSurfacesHolder,
const dom::ContentParentId& aContentId, uint32_t aManagerId);
const dom::ContentParentId& GetContentId() const { return mContentId; } const dom::ContentParentId& GetContentId() const { return mContentId; }
@ -292,6 +294,7 @@ class CanvasTranslator final : public gfx::InlineTranslator,
void NotifyDeviceChanged(); void NotifyDeviceChanged();
RefPtr<TaskQueue> mTranslationTaskQueue; RefPtr<TaskQueue> mTranslationTaskQueue;
RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
#if defined(XP_WIN) #if defined(XP_WIN)
RefPtr<ID3D11Device> mDevice; RefPtr<ID3D11Device> mDevice;
#endif #endif

View file

@ -117,6 +117,7 @@ CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
CompositorManagerParent::CompositorManagerParent( CompositorManagerParent::CompositorManagerParent(
dom::ContentParentId aContentId, uint32_t aNamespace) dom::ContentParentId aContentId, uint32_t aNamespace)
: mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()), : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton()),
mSharedSurfacesHolder(MakeRefPtr<SharedSurfacesHolder>(aNamespace)),
mContentId(aContentId), mContentId(aContentId),
mNamespace(aNamespace) {} mNamespace(aNamespace) {}
@ -146,8 +147,6 @@ void CompositorManagerParent::BindComplete(bool aIsRoot) {
} }
void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) { void CompositorManagerParent::ActorDestroy(ActorDestroyReason aReason) {
SharedSurfacesParent::RemoveAll(mNamespace);
GetCurrentSerialEventTarget()->Dispatch( GetCurrentSerialEventTarget()->Dispatch(
NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy", NewRunnableMethod("layers::CompositorManagerParent::DeferredDestroy",
this, &CompositorManagerParent::DeferredDestroy)); this, &CompositorManagerParent::DeferredDestroy));
@ -349,7 +348,8 @@ mozilla::ipc::IPCResult CompositorManagerParent::RecvReportMemory(
mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager( mozilla::ipc::IPCResult CompositorManagerParent::RecvInitCanvasManager(
Endpoint<PCanvasManagerParent>&& aEndpoint) { Endpoint<PCanvasManagerParent>&& aEndpoint) {
gfx::CanvasManagerParent::Init(std::move(aEndpoint), mContentId); gfx::CanvasManagerParent::Init(std::move(aEndpoint), mSharedSurfacesHolder,
mContentId);
return IPC_OK(); return IPC_OK();
} }

View file

@ -22,6 +22,7 @@ namespace layers {
class CompositorBridgeParent; class CompositorBridgeParent;
class CompositorThreadHolder; class CompositorThreadHolder;
class SharedSurfacesHolder;
class CompositorManagerParent final : public PCompositorManagerParent { class CompositorManagerParent final : public PCompositorManagerParent {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent, final) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent, final)
@ -90,6 +91,7 @@ class CompositorManagerParent final : public PCompositorManagerParent {
void DeferredDestroy(); void DeferredDestroy();
RefPtr<CompositorThreadHolder> mCompositorThreadHolder; RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges; AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges;
const dom::ContentParentId mContentId; const dom::ContentParentId mContentId;
const uint32_t mNamespace; const uint32_t mNamespace;

View file

@ -5,6 +5,7 @@
include "gfxipc/ShadowLayerUtils.h"; include "gfxipc/ShadowLayerUtils.h";
include "mozilla/GfxMessageUtils.h"; include "mozilla/GfxMessageUtils.h";
include "mozilla/layers/LayersMessageUtils.h"; include "mozilla/layers/LayersMessageUtils.h";
include "mozilla/layers/WebRenderMessageUtils.h";
using gfxPoint from "gfxPoint.h"; using gfxPoint from "gfxPoint.h";
using nsIntRegion from "nsRegion.h"; using nsIntRegion from "nsRegion.h";
@ -27,6 +28,8 @@ using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUt
using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h"; using mozilla::layers::RemoteTextureId from "mozilla/layers/LayersTypes.h";
using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h"; using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h";
using mozilla::layers::GpuProcessTextureId from "mozilla/layers/LayersTypes.h"; using mozilla::layers::GpuProcessTextureId from "mozilla/layers/LayersTypes.h";
using mozilla::wr::ExternalImageSource from "mozilla/webrender/WebRenderTypes.h";
using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
namespace mozilla { namespace mozilla {
namespace layers { namespace layers {
@ -178,6 +181,12 @@ namespace layers {
Handle handle; Handle handle;
}; };
[Comparable] struct SurfaceDescriptorExternalImage
{
ExternalImageSource source;
ExternalImageId id;
};
[Comparable] struct SurfaceDescriptorRecorded { [Comparable] struct SurfaceDescriptorRecorded {
int64_t textureId; int64_t textureId;
}; };
@ -201,6 +210,7 @@ namespace layers {
SurfaceDescriptorRecorded; SurfaceDescriptorRecorded;
SurfaceDescriptorRemoteTexture; SurfaceDescriptorRemoteTexture;
SurfaceDescriptorDcompSurface; SurfaceDescriptorDcompSurface;
SurfaceDescriptorExternalImage;
null_t; null_t;
}; };

View file

@ -350,6 +350,30 @@ nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
return rv; return rv;
} }
/* static */ nsresult SharedSurfacesChild::Share(
gfx::SourceSurface* aSurface, Maybe<SurfaceDescriptor>& aDesc) {
if (!aSurface) {
return NS_ERROR_INVALID_ARG;
}
// TODO(aosmond): With a refactor of how we store the external image ID, we
// could probably make it safe to access off the main thread. This would be
// useful for OffscreenCanvas on DOM workers.
if (!NS_IsMainThread()) {
return NS_ERROR_UNEXPECTED;
}
wr::ExternalImageId extId{};
nsresult rv = Share(aSurface, extId);
if (NS_FAILED(rv)) {
return rv;
}
aDesc = Some(SurfaceDescriptorExternalImage(
wr::ExternalImageSource::SharedSurfaces, extId));
return NS_OK;
}
/* static */ /* static */
void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId, void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
bool aReleaseId, bool aReleaseId,

View file

@ -13,6 +13,7 @@
#include "mozilla/RefPtr.h" // for already_AddRefed #include "mozilla/RefPtr.h" // for already_AddRefed
#include "mozilla/StaticPtr.h" // for StaticRefPtr #include "mozilla/StaticPtr.h" // for StaticRefPtr
#include "mozilla/gfx/UserData.h" // for UserDataKey #include "mozilla/gfx/UserData.h" // for UserDataKey
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey #include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey
#include "nsTArray.h" // for AutoTArray #include "nsTArray.h" // for AutoTArray
#include "nsThreadUtils.h" // for Runnable #include "nsThreadUtils.h" // for Runnable
@ -64,6 +65,14 @@ class SharedSurfacesChild {
*/ */
static nsresult Share(gfx::SourceSurface* aSurface, wr::ExternalImageId& aId); static nsresult Share(gfx::SourceSurface* aSurface, wr::ExternalImageId& aId);
/**
* Request that the surface be mapped into the compositor thread's memory
* space, and a valid ExternalImageId be generated for it for use with
* WebRender. This must be called from the main thread.
*/
static nsresult Share(gfx::SourceSurface* aSurface,
Maybe<SurfaceDescriptor>& aDesc);
/** /**
* Request that the surface be mapped into the compositor thread's memory * Request that the surface be mapped into the compositor thread's memory
* space, and a valid ImageKey be generated for it for use with WebRender. * space, and a valid ImageKey be generated for it for use with WebRender.

View file

@ -206,6 +206,8 @@ void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) {
return; return;
} }
auto* renderThread = wr::RenderThread::Get();
// Note that the destruction of a parent may not be cheap if it still has a // Note that the destruction of a parent may not be cheap if it still has a
// lot of surfaces still bound that require unmapping. // lot of surfaces still bound that require unmapping.
for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) { for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) {
@ -217,8 +219,9 @@ void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) {
if (surface->HasCreatorRef() && if (surface->HasCreatorRef() &&
surface->RemoveConsumer(/* aForCreator */ true)) { surface->RemoveConsumer(/* aForCreator */ true)) {
RemoveTrackingLocked(surface, lock); RemoveTrackingLocked(surface, lock);
wr::RenderThread::Get()->UnregisterExternalImage( if (renderThread) {
wr::ToExternalImageId(i.Key())); renderThread->UnregisterExternalImage(wr::ToExternalImageId(i.Key()));
}
i.Remove(); i.Remove();
} }
} }

View file

@ -129,6 +129,33 @@ class SharedSurfacesParent final {
MappingTracker mTracker; MappingTracker mTracker;
}; };
/**
* Helper class that is used to keep SourceSurfaceSharedDataWrapper objects
* around as long as one of the dependent IPDL actors is still alive and may
* reference them for a given PCompositorManager namespace.
*/
class SharedSurfacesHolder final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSurfacesHolder)
public:
explicit SharedSurfacesHolder(uint32_t aNamespace) : mNamespace(aNamespace) {}
already_AddRefed<gfx::DataSourceSurface> Get(const wr::ExternalImageId& aId) {
uint32_t extNamespace = static_cast<uint32_t>(wr::AsUint64(aId) >> 32);
if (NS_WARN_IF(extNamespace != mNamespace)) {
MOZ_ASSERT_UNREACHABLE("Wrong namespace?");
return nullptr;
}
return SharedSurfacesParent::Get(aId);
}
private:
~SharedSurfacesHolder() { SharedSurfacesParent::RemoveAll(mNamespace); }
uint32_t mNamespace;
};
} // namespace layers } // namespace layers
} // namespace mozilla } // namespace mozilla

View file

@ -218,6 +218,13 @@ template <>
struct ParamTraits<mozilla::wr::RenderReasons> struct ParamTraits<mozilla::wr::RenderReasons>
: public PlainOldDataSerializer<mozilla::wr::RenderReasons> {}; : public PlainOldDataSerializer<mozilla::wr::RenderReasons> {};
template <>
struct ParamTraits<mozilla::wr::ExternalImageSource>
: public ContiguousEnumSerializer<mozilla::wr::ExternalImageSource,
mozilla::wr::ExternalImageSource::Unknown,
mozilla::wr::ExternalImageSource::Last> {
};
} // namespace IPC } // namespace IPC
#endif // GFX_WEBRENDERMESSAGEUTILS_H #endif // GFX_WEBRENDERMESSAGEUTILS_H

View file

@ -903,6 +903,8 @@ static inline wr::HasScrollLinkedEffect ToWrHasScrollLinkedEffect(
: wr::HasScrollLinkedEffect::No; : wr::HasScrollLinkedEffect::No;
} }
enum class ExternalImageSource : uint8_t { Unknown = 0, SharedSurfaces, Last };
} // namespace wr } // namespace wr
} // namespace mozilla } // namespace mozilla