Bug 1855742 - Part 2. Allow CanvasDrawEventRecorder to be created on DOM workers. r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D189530
This commit is contained in:
Andrew Osmond 2024-02-21 03:08:03 +00:00
parent 39d3e5d31c
commit c743180764
8 changed files with 147 additions and 13 deletions

View File

@ -1987,7 +1987,7 @@ class DrawTarget : public external::AtomicRefCounted<DrawTarget> {
SurfaceFormat mFormat; SurfaceFormat mFormat;
}; };
class DrawEventRecorder : public RefCounted<DrawEventRecorder> { class DrawEventRecorder : public external::AtomicRefCounted<DrawEventRecorder> {
public: public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder) MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
virtual RecorderType GetRecorderType() const { return RecorderType::UNKNOWN; } virtual RecorderType GetRecorderType() const { return RecorderType::UNKNOWN; }

View File

@ -16,9 +16,7 @@ namespace gfx {
DrawEventRecorderPrivate::DrawEventRecorderPrivate() : mExternalFonts(false) {} DrawEventRecorderPrivate::DrawEventRecorderPrivate() : mExternalFonts(false) {}
DrawEventRecorderPrivate::~DrawEventRecorderPrivate() { DrawEventRecorderPrivate::~DrawEventRecorderPrivate() = default;
NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate);
}
void DrawEventRecorderPrivate::SetDrawTarget(ReferencePtr aDT) { void DrawEventRecorderPrivate::SetDrawTarget(ReferencePtr aDT) {
NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate);

View File

@ -41,7 +41,7 @@ class DrawEventRecorderPrivate : public DrawEventRecorder {
return true; return true;
} }
virtual void FlushItem(IntRect) {} virtual void FlushItem(IntRect) {}
void DetachResources() { virtual void DetachResources() {
NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate);
nsTHashSet<ScaledFont*> fonts = std::move(mStoredFonts); nsTHashSet<ScaledFont*> fonts = std::move(mStoredFonts);
@ -116,7 +116,7 @@ class DrawEventRecorderPrivate : public DrawEventRecorder {
return mStoredObjects.EnsureInserted(aObject); return mStoredObjects.EnsureInserted(aObject);
} }
void AddPendingDeletion(std::function<void()>&& aPendingDeletion) { virtual void AddPendingDeletion(std::function<void()>&& aPendingDeletion) {
auto lockedPendingDeletions = mPendingDeletions.Lock(); auto lockedPendingDeletions = mPendingDeletions.Lock();
lockedPendingDeletions->emplace_back(std::move(aPendingDeletion)); lockedPendingDeletions->emplace_back(std::move(aPendingDeletion));
} }

View File

@ -224,8 +224,9 @@ RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() {
} }
if (!mCanvasChild) { if (!mCanvasChild) {
mCanvasChild = MakeAndAddRef<layers::CanvasChild>(); mCanvasChild = MakeAndAddRef<layers::CanvasChild>(mWorkerRef);
if (!SendPCanvasConstructor(mCanvasChild)) { if (!SendPCanvasConstructor(mCanvasChild)) {
mCanvasChild->Destroy();
mCanvasChild = nullptr; mCanvasChild = nullptr;
} }
} }

View File

@ -8,6 +8,11 @@
#include <string.h> #include <string.h>
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/layers/TextureRecorded.h"
#include "mozilla/layers/SharedSurfacesChild.h" #include "mozilla/layers/SharedSurfacesChild.h"
#include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_gfx.h"
#include "RecordedCanvasEventImpl.h" #include "RecordedCanvasEventImpl.h"
@ -34,7 +39,9 @@ static Maybe<ShmemAndHandle> CreateAndMapShmem(size_t aSize) {
return Some(ShmemAndHandle{shmem.forget(), std::move(shmemHandle)}); return Some(ShmemAndHandle{shmem.forget(), std::move(shmemHandle)});
} }
CanvasDrawEventRecorder::CanvasDrawEventRecorder() { CanvasDrawEventRecorder::CanvasDrawEventRecorder(
dom::ThreadSafeWorkerRef* aWorkerRef)
: mWorkerRef(aWorkerRef), mIsOnWorker(!!aWorkerRef) {
mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize( mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize(
StaticPrefs::gfx_canvas_remote_default_buffer_size()); StaticPrefs::gfx_canvas_remote_default_buffer_size());
mMaxDefaultBuffers = StaticPrefs::gfx_canvas_remote_max_default_buffers(); mMaxDefaultBuffers = StaticPrefs::gfx_canvas_remote_max_default_buffers();
@ -43,6 +50,8 @@ CanvasDrawEventRecorder::CanvasDrawEventRecorder() {
mDropBufferOnZero = mDropBufferLimit; mDropBufferOnZero = mDropBufferLimit;
} }
CanvasDrawEventRecorder::~CanvasDrawEventRecorder() { MOZ_ASSERT(!mWorkerRef); }
bool CanvasDrawEventRecorder::Init(TextureType aTextureType, bool CanvasDrawEventRecorder::Init(TextureType aTextureType,
gfx::BackendType aBackendType, gfx::BackendType aBackendType,
UniquePtr<Helpers> aHelpers) { UniquePtr<Helpers> aHelpers) {
@ -317,6 +326,106 @@ void CanvasDrawEventRecorder::CheckAndSignalReader() {
} while (true); } while (true);
} }
void CanvasDrawEventRecorder::DetachResources() {
NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder);
DrawEventRecorderPrivate::DetachResources();
{
auto lockedPendingDeletions = mPendingDeletions.Lock();
mWorkerRef = nullptr;
}
}
void CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked(
RefPtr<CanvasDrawEventRecorder>&& aRecorder) {
if (!mWorkerRef) {
MOZ_RELEASE_ASSERT(
!mIsOnWorker,
"QueueProcessPendingDeletionsLocked called after worker shutdown!");
NS_DispatchToMainThread(NS_NewRunnableFunction(
"CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked",
[self = std::move(aRecorder)]() { self->ProcessPendingDeletions(); }));
return;
}
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked",
[self = std::move(aRecorder)]() mutable {
self->QueueProcessPendingDeletions(std::move(self));
}));
return;
}
class ProcessPendingRunnable final : public dom::WorkerRunnable {
public:
ProcessPendingRunnable(dom::WorkerPrivate* aWorkerPrivate,
RefPtr<CanvasDrawEventRecorder>&& aRecorder)
: dom::WorkerRunnable(aWorkerPrivate),
mRecorder(std::move(aRecorder)) {}
bool WorkerRun(JSContext*, dom::WorkerPrivate*) override {
RefPtr<CanvasDrawEventRecorder> recorder = std::move(mRecorder);
recorder->ProcessPendingDeletions();
return true;
}
private:
RefPtr<CanvasDrawEventRecorder> mRecorder;
};
auto task = MakeRefPtr<ProcessPendingRunnable>(mWorkerRef->Private(),
std::move(aRecorder));
if (NS_WARN_IF(!task->Dispatch())) {
MOZ_CRASH("ProcessPendingRunnable leaked!");
}
}
void CanvasDrawEventRecorder::QueueProcessPendingDeletions(
RefPtr<CanvasDrawEventRecorder>&& aRecorder) {
auto lockedPendingDeletions = mPendingDeletions.Lock();
if (lockedPendingDeletions->empty()) {
// We raced to handle the deletions, and something got there first.
return;
}
QueueProcessPendingDeletionsLocked(std::move(aRecorder));
}
void CanvasDrawEventRecorder::AddPendingDeletion(
std::function<void()>&& aPendingDeletion) {
PendingDeletionsVector pendingDeletions;
{
auto lockedPendingDeletions = mPendingDeletions.Lock();
bool wasEmpty = lockedPendingDeletions->empty();
lockedPendingDeletions->emplace_back(std::move(aPendingDeletion));
MOZ_RELEASE_ASSERT(!mIsOnWorker || mWorkerRef,
"AddPendingDeletion called after worker shutdown!");
// If we are not on the owning thread, we must queue an event to run the
// deletions, if we transitioned from empty to non-empty.
if ((mWorkerRef && !mWorkerRef->Private()->IsOnCurrentThread()) ||
(!mWorkerRef && !NS_IsMainThread())) {
if (wasEmpty) {
RefPtr<CanvasDrawEventRecorder> self(this);
QueueProcessPendingDeletionsLocked(std::move(self));
}
return;
}
// Otherwise, we can just run all of them right now.
pendingDeletions.swap(*lockedPendingDeletions);
}
for (const auto& pendingDeletion : pendingDeletions) {
pendingDeletion();
}
}
void CanvasDrawEventRecorder::StoreSourceSurfaceRecording( void CanvasDrawEventRecorder::StoreSourceSurfaceRecording(
gfx::SourceSurface* aSurface, const char* aReason) { gfx::SourceSurface* aSurface, const char* aReason) {
NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder); NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder);

View File

@ -21,6 +21,10 @@ namespace mozilla {
using EventType = gfx::RecordedEvent::EventType; using EventType = gfx::RecordedEvent::EventType;
namespace dom {
class ThreadSafeWorkerRef;
}
namespace layers { namespace layers {
typedef mozilla::ipc::SharedMemoryBasic::Handle Handle; typedef mozilla::ipc::SharedMemoryBasic::Handle Handle;
@ -31,7 +35,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
public: public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final) MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final)
CanvasDrawEventRecorder(); explicit CanvasDrawEventRecorder(dom::ThreadSafeWorkerRef* aWorkerRef);
~CanvasDrawEventRecorder() override;
enum class State : uint32_t { enum class State : uint32_t {
Processing, Processing,
@ -98,6 +103,10 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
*/ */
void RecordEvent(const gfx::RecordedEvent& aEvent) final; void RecordEvent(const gfx::RecordedEvent& aEvent) final;
void DetachResources() final;
void AddPendingDeletion(std::function<void()>&& aPendingDeletion) override;
void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface, void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
const char* aReason) final; const char* aReason) final;
@ -134,6 +143,11 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
void CheckAndSignalReader(); void CheckAndSignalReader();
void QueueProcessPendingDeletions(
RefPtr<CanvasDrawEventRecorder>&& aRecorder);
void QueueProcessPendingDeletionsLocked(
RefPtr<CanvasDrawEventRecorder>&& aRecorder);
size_t mDefaultBufferSize; size_t mDefaultBufferSize;
size_t mMaxDefaultBuffers; size_t mMaxDefaultBuffers;
uint32_t mMaxSpinCount; uint32_t mMaxSpinCount;
@ -173,6 +187,9 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
UniquePtr<CrossProcessSemaphore> mWriterSemaphore; UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
UniquePtr<CrossProcessSemaphore> mReaderSemaphore; UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
bool mIsOnWorker = false;
}; };
} // namespace layers } // namespace layers

View File

@ -7,6 +7,7 @@
#include "CanvasChild.h" #include "CanvasChild.h"
#include "MainThreadUtils.h" #include "MainThreadUtils.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/gfx/CanvasManagerChild.h" #include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/DrawTargetRecording.h" #include "mozilla/gfx/DrawTargetRecording.h"
#include "mozilla/gfx/Tools.h" #include "mozilla/gfx/Tools.h"
@ -165,9 +166,10 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
bool mDetached = false; bool mDetached = false;
}; };
CanvasChild::CanvasChild() = default; CanvasChild::CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef)
: mWorkerRef(aWorkerRef) {}
CanvasChild::~CanvasChild() = default; CanvasChild::~CanvasChild() { MOZ_ASSERT(!mWorkerRef); }
static void NotifyCanvasDeviceReset() { static void NotifyCanvasDeviceReset() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@ -213,7 +215,7 @@ void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
if (!mRecorder) { if (!mRecorder) {
gfx::BackendType backendType = gfx::BackendType backendType =
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(); auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(mWorkerRef);
if (!recorder->Init(aTextureType, backendType, if (!recorder->Init(aTextureType, backendType,
MakeUnique<RecorderHelpers>(this))) { MakeUnique<RecorderHelpers>(this))) {
return; return;
@ -242,6 +244,8 @@ void CanvasChild::Destroy() {
if (CanSend()) { if (CanSend()) {
Send__delete__(this); Send__delete__(this);
} }
mWorkerRef = nullptr;
} }
bool CanvasChild::EnsureBeginTransaction() { bool CanvasChild::EnsureBeginTransaction() {

View File

@ -15,6 +15,10 @@
namespace mozilla { namespace mozilla {
namespace dom {
class ThreadSafeWorkerRef;
}
namespace gfx { namespace gfx {
class DrawTargetRecording; class DrawTargetRecording;
class SourceSurface; class SourceSurface;
@ -28,7 +32,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
public: public:
NS_INLINE_DECL_REFCOUNTING(CanvasChild) NS_INLINE_DECL_REFCOUNTING(CanvasChild)
CanvasChild(); explicit CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef);
/** /**
* @returns true if remote canvas has been deactivated due to failure. * @returns true if remote canvas has been deactivated due to failure.
@ -173,6 +177,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
static bool mDeactivated; static bool mDeactivated;
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
RefPtr<CanvasDrawEventRecorder> mRecorder; RefPtr<CanvasDrawEventRecorder> mRecorder;
RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem; RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;