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

View file

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

View file

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

View file

@ -28,6 +28,7 @@ class TaskQueue;
namespace layers {
class SharedSurfacesHolder;
class TextureData;
class CanvasTranslator final : public gfx::InlineTranslator,
@ -37,7 +38,8 @@ class CanvasTranslator final : public gfx::InlineTranslator,
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; }
@ -292,6 +294,7 @@ class CanvasTranslator final : public gfx::InlineTranslator,
void NotifyDeviceChanged();
RefPtr<TaskQueue> mTranslationTaskQueue;
RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
#if defined(XP_WIN)
RefPtr<ID3D11Device> mDevice;
#endif

View file

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

View file

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

View file

@ -5,6 +5,7 @@
include "gfxipc/ShadowLayerUtils.h";
include "mozilla/GfxMessageUtils.h";
include "mozilla/layers/LayersMessageUtils.h";
include "mozilla/layers/WebRenderMessageUtils.h";
using gfxPoint from "gfxPoint.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::RemoteTextureOwnerId 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 layers {
@ -178,6 +181,12 @@ namespace layers {
Handle handle;
};
[Comparable] struct SurfaceDescriptorExternalImage
{
ExternalImageSource source;
ExternalImageId id;
};
[Comparable] struct SurfaceDescriptorRecorded {
int64_t textureId;
};
@ -201,6 +210,7 @@ namespace layers {
SurfaceDescriptorRecorded;
SurfaceDescriptorRemoteTexture;
SurfaceDescriptorDcompSurface;
SurfaceDescriptorExternalImage;
null_t;
};

View file

@ -350,6 +350,30 @@ nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
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 */
void SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
bool aReleaseId,

View file

@ -13,6 +13,7 @@
#include "mozilla/RefPtr.h" // for already_AddRefed
#include "mozilla/StaticPtr.h" // for StaticRefPtr
#include "mozilla/gfx/UserData.h" // for UserDataKey
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/webrender/WebRenderTypes.h" // for wr::ImageKey
#include "nsTArray.h" // for AutoTArray
#include "nsThreadUtils.h" // for Runnable
@ -64,6 +65,14 @@ class SharedSurfacesChild {
*/
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
* 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;
}
auto* renderThread = wr::RenderThread::Get();
// Note that the destruction of a parent may not be cheap if it still has a
// lot of surfaces still bound that require unmapping.
for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) {
@ -217,8 +219,9 @@ void SharedSurfacesParent::RemoveAll(uint32_t aNamespace) {
if (surface->HasCreatorRef() &&
surface->RemoveConsumer(/* aForCreator */ true)) {
RemoveTrackingLocked(surface, lock);
wr::RenderThread::Get()->UnregisterExternalImage(
wr::ToExternalImageId(i.Key()));
if (renderThread) {
renderThread->UnregisterExternalImage(wr::ToExternalImageId(i.Key()));
}
i.Remove();
}
}

View file

@ -129,6 +129,33 @@ class SharedSurfacesParent final {
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 mozilla

View file

@ -218,6 +218,13 @@ template <>
struct ParamTraits<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
#endif // GFX_WEBRENDERMESSAGEUTILS_H

View file

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