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;
};
class DrawEventRecorder : public RefCounted<DrawEventRecorder> {
class DrawEventRecorder : public external::AtomicRefCounted<DrawEventRecorder> {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
virtual RecorderType GetRecorderType() const { return RecorderType::UNKNOWN; }

View File

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

View File

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

View File

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

View File

@ -8,6 +8,11 @@
#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/StaticPrefs_gfx.h"
#include "RecordedCanvasEventImpl.h"
@ -34,7 +39,9 @@ static Maybe<ShmemAndHandle> CreateAndMapShmem(size_t aSize) {
return Some(ShmemAndHandle{shmem.forget(), std::move(shmemHandle)});
}
CanvasDrawEventRecorder::CanvasDrawEventRecorder() {
CanvasDrawEventRecorder::CanvasDrawEventRecorder(
dom::ThreadSafeWorkerRef* aWorkerRef)
: mWorkerRef(aWorkerRef), mIsOnWorker(!!aWorkerRef) {
mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize(
StaticPrefs::gfx_canvas_remote_default_buffer_size());
mMaxDefaultBuffers = StaticPrefs::gfx_canvas_remote_max_default_buffers();
@ -43,6 +50,8 @@ CanvasDrawEventRecorder::CanvasDrawEventRecorder() {
mDropBufferOnZero = mDropBufferLimit;
}
CanvasDrawEventRecorder::~CanvasDrawEventRecorder() { MOZ_ASSERT(!mWorkerRef); }
bool CanvasDrawEventRecorder::Init(TextureType aTextureType,
gfx::BackendType aBackendType,
UniquePtr<Helpers> aHelpers) {
@ -317,6 +326,106 @@ void CanvasDrawEventRecorder::CheckAndSignalReader() {
} 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(
gfx::SourceSurface* aSurface, const char* aReason) {
NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder);

View File

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

View File

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

View File

@ -15,6 +15,10 @@
namespace mozilla {
namespace dom {
class ThreadSafeWorkerRef;
}
namespace gfx {
class DrawTargetRecording;
class SourceSurface;
@ -28,7 +32,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
public:
NS_INLINE_DECL_REFCOUNTING(CanvasChild)
CanvasChild();
explicit CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef);
/**
* @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;
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
RefPtr<CanvasDrawEventRecorder> mRecorder;
RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;