From 0abafd6d24b1dc900bfa18fe015be564104325ef Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Sat, 15 Jan 2022 05:17:21 +0000 Subject: [PATCH] Bug 1741767: Use shared memory surfaces in Canvas 2D recording. r=lsalzman This also changes SharedSurfacesParent to use a StaticMonitor, so that we can wait for the surface to be added. Differential Revision: https://phabricator.services.mozilla.com/D132703 --- gfx/layers/CanvasDrawEventRecorder.cpp | 13 ++++++ gfx/layers/CanvasDrawEventRecorder.h | 3 ++ gfx/layers/ipc/CanvasChild.cpp | 7 ++++ gfx/layers/ipc/CanvasChild.h | 1 + gfx/layers/ipc/CanvasTranslator.cpp | 6 +++ gfx/layers/ipc/CanvasTranslator.h | 3 ++ gfx/layers/ipc/SharedSurfacesParent.cpp | 56 +++++++++++++++---------- gfx/layers/ipc/SharedSurfacesParent.h | 24 +++++------ 8 files changed, 79 insertions(+), 34 deletions(-) diff --git a/gfx/layers/CanvasDrawEventRecorder.cpp b/gfx/layers/CanvasDrawEventRecorder.cpp index d68c85d660bf..2842b420dbbf 100644 --- a/gfx/layers/CanvasDrawEventRecorder.cpp +++ b/gfx/layers/CanvasDrawEventRecorder.cpp @@ -8,6 +8,7 @@ #include +#include "mozilla/layers/SharedSurfacesChild.h" #include "nsThreadUtils.h" namespace mozilla { @@ -501,6 +502,18 @@ void CanvasEventRingBuffer::ReturnRead(char* aOut, size_t aSize) { mWrite->returnCount = readCount; } +void CanvasDrawEventRecorder::StoreSourceSurfaceRecording( + gfx::SourceSurface* aSurface, const char* aReason) { + wr::ExternalImageId extId{}; + nsresult rv = layers::SharedSurfacesChild::Share(aSurface, extId); + if (NS_FAILED(rv)) { + DrawEventRecorderPrivate::StoreSourceSurfaceRecording(aSurface, aReason); + return; + } + + StoreExternalSurfaceRecording(aSurface, wr::AsUint64(extId)); +} + void CanvasDrawEventRecorder::RecordSourceSurfaceDestruction(void* aSurface) { // We must only record things on the main thread and surfaces that have been // recorded can sometimes be destroyed off the main thread. diff --git a/gfx/layers/CanvasDrawEventRecorder.h b/gfx/layers/CanvasDrawEventRecorder.h index a88e74cd2e14..d67ce4fdd443 100644 --- a/gfx/layers/CanvasDrawEventRecorder.h +++ b/gfx/layers/CanvasDrawEventRecorder.h @@ -253,6 +253,9 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate { aEvent.RecordToStream(mOutputStream); } + void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface, + const char* aReason) final; + void RecordSourceSurfaceDestruction(void* aSurface) final; void Flush() final {} diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp index b6f2e3800246..c4715cdafef5 100644 --- a/gfx/layers/ipc/CanvasChild.cpp +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -200,6 +200,13 @@ void CanvasChild::OnTextureForwarded() { mHasOutstandingWriteLock = false; } + + // We hold onto the last transaction's external surfaces until we have waited + // for the write locks in this transaction. This means we know that the + // surfaces have been picked up in the canvas threads and there is no race + // with them being removed from SharedSurfacesParent. Note this releases the + // current contents of mLastTransactionExternalSurfaces. + mRecorder->TakeExternalSurfaces(mLastTransactionExternalSurfaces); } void CanvasChild::EnsureBeginTransaction() { diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h index 68ac863b3537..ca5b8640088b 100644 --- a/gfx/layers/ipc/CanvasChild.h +++ b/gfx/layers/ipc/CanvasChild.h @@ -141,6 +141,7 @@ class CanvasChild final : public PCanvasChild { uint32_t mLastWriteLockCheckpoint = 0; uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold; TimeStamp mLastNonEmptyTransaction = TimeStamp::NowLoRes(); + std::vector> mLastTransactionExternalSurfaces; bool mIsInTransaction = false; bool mHasOutstandingWriteLock = false; }; diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp index 94a2703c27b2..712b5afe2bca 100644 --- a/gfx/layers/ipc/CanvasTranslator.cpp +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -11,6 +11,7 @@ #include "mozilla/gfx/GPUParent.h" #include "mozilla/gfx/Logging.h" #include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/SharedSurfacesParent.h" #include "mozilla/layers/TextureClient.h" #include "mozilla/SyncRunnable.h" #include "mozilla/Telemetry.h" @@ -502,6 +503,11 @@ UniquePtr CanvasTranslator::WaitForSurfaceDescriptor( return descriptor; } +already_AddRefed CanvasTranslator::LookupExternalSurface( + uint64_t aKey) { + return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey)); +} + already_AddRefed CanvasTranslator::GetOrCreateGradientStops( gfx::GradientStop* aRawStops, uint32_t aNumStops, gfx::ExtendMode aExtendMode) { diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h index 99b7f7236f12..5033221363a1 100644 --- a/gfx/layers/ipc/CanvasTranslator.h +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -175,6 +175,9 @@ class CanvasTranslator final : public gfx::InlineTranslator, InlineTranslator::RemoveSourceSurface(aRefPtr); } + already_AddRefed LookupExternalSurface( + uint64_t aKey) final; + /** * Gets the cached DataSourceSurface, if it exists, associated with a * SourceSurface from another process. diff --git a/gfx/layers/ipc/SharedSurfacesParent.cpp b/gfx/layers/ipc/SharedSurfacesParent.cpp index b7e04d3c5f50..05771254e05a 100644 --- a/gfx/layers/ipc/SharedSurfacesParent.cpp +++ b/gfx/layers/ipc/SharedSurfacesParent.cpp @@ -22,26 +22,30 @@ namespace layers { using namespace mozilla::gfx; -StaticMutex SharedSurfacesParent::sMutex; +StaticMonitor SharedSurfacesParent::sMonitor; StaticAutoPtr SharedSurfacesParent::sInstance; +// Short wait to allow for a surface to be added, where the consumer has a +// different thread route. +static const TimeDuration kGetTimeout = TimeDuration::FromMilliseconds(50); + void SharedSurfacesParent::MappingTracker::NotifyExpiredLocked( SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock) { + const StaticMonitorAutoLock& aAutoLock) { RemoveObjectLocked(aSurface, aAutoLock); mExpired.AppendElement(aSurface); } void SharedSurfacesParent::MappingTracker::TakeExpired( nsTArray>& aExpired, - const StaticMutexAutoLock& aAutoLock) { + const StaticMonitorAutoLock& aAutoLock) { aExpired = std::move(mExpired); } void SharedSurfacesParent::MappingTracker::NotifyHandlerEnd() { nsTArray> expired; { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); TakeExpired(expired, lock); } @@ -56,7 +60,7 @@ SharedSurfacesParent::SharedSurfacesParent() /* static */ void SharedSurfacesParent::Initialize() { MOZ_ASSERT(NS_IsMainThread()); - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { sInstance = new SharedSurfacesParent(); } @@ -67,7 +71,7 @@ void SharedSurfacesParent::ShutdownRenderThread() { // The main thread should blocked on waiting for the render thread to // complete so this should be safe to release off the main thread. MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); MOZ_ASSERT(sInstance); for (const auto& key : sInstance->mSurfaces.Keys()) { @@ -85,28 +89,34 @@ void SharedSurfacesParent::Shutdown() { // thread that could use it. The expiration tracker needs to be freed on the // main thread. MOZ_ASSERT(NS_IsMainThread()); - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); sInstance = nullptr; } /* static */ already_AddRefed SharedSurfacesParent::Get( const wr::ExternalImageId& aId) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { gfxCriticalNote << "SSP:Get " << wr::AsUint64(aId) << " shtd"; return nullptr; } RefPtr surface; - sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface)); + while ( + !sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface))) { + CVStatus status = lock.Wait(kGetTimeout); + if (status == CVStatus::Timeout) { + return nullptr; + } + } return surface.forget(); } /* static */ already_AddRefed SharedSurfacesParent::Acquire( const wr::ExternalImageId& aId) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { gfxCriticalNote << "SSP:Acq " << wr::AsUint64(aId) << " shtd"; return nullptr; @@ -125,7 +135,7 @@ already_AddRefed SharedSurfacesParent::Acquire( /* static */ bool SharedSurfacesParent::Release(const wr::ExternalImageId& aId, bool aForCreator) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return false; } @@ -151,7 +161,7 @@ void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId, SourceSurfaceSharedData* aSurface) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { gfxCriticalNote << "SSP:Ads " << wr::AsUint64(aId) << " shtd"; return; @@ -174,11 +184,12 @@ void SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId, surface->AddConsumer(); sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); + lock.NotifyAll(); } /* static */ void SharedSurfacesParent::DestroyProcess(base::ProcessId aPid) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return; } @@ -217,7 +228,7 @@ void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), std::move(aDesc.handle()), aPid); - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd"; return; @@ -231,6 +242,7 @@ void SharedSurfacesParent::Add(const wr::ExternalImageId& aId, surface->AddConsumer(); sInstance->mSurfaces.InsertOrUpdate(id, std::move(surface)); + lock.NotifyAll(); } /* static */ @@ -242,7 +254,7 @@ void SharedSurfacesParent::Remove(const wr::ExternalImageId& aId) { /* static */ void SharedSurfacesParent::AddTrackingLocked( SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock) { + const StaticMonitorAutoLock& aAutoLock) { MOZ_ASSERT(!aSurface->GetExpirationState()->IsTracked()); sInstance->mTracker.AddObjectLocked(aSurface, aAutoLock); } @@ -250,7 +262,7 @@ void SharedSurfacesParent::AddTrackingLocked( /* static */ void SharedSurfacesParent::AddTracking( SourceSurfaceSharedDataWrapper* aSurface) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return; } @@ -261,7 +273,7 @@ void SharedSurfacesParent::AddTracking( /* static */ void SharedSurfacesParent::RemoveTrackingLocked( SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock) { + const StaticMonitorAutoLock& aAutoLock) { if (!aSurface->GetExpirationState()->IsTracked()) { return; } @@ -272,7 +284,7 @@ void SharedSurfacesParent::RemoveTrackingLocked( /* static */ void SharedSurfacesParent::RemoveTracking( SourceSurfaceSharedDataWrapper* aSurface) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return; } @@ -283,7 +295,7 @@ void SharedSurfacesParent::RemoveTracking( /* static */ bool SharedSurfacesParent::AgeOneGenerationLocked( nsTArray>& aExpired, - const StaticMutexAutoLock& aAutoLock) { + const StaticMonitorAutoLock& aAutoLock) { if (sInstance->mTracker.IsEmptyLocked(aAutoLock)) { return false; } @@ -296,7 +308,7 @@ bool SharedSurfacesParent::AgeOneGenerationLocked( /* static */ bool SharedSurfacesParent::AgeOneGeneration( nsTArray>& aExpired) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return false; } @@ -323,7 +335,7 @@ void SharedSurfacesParent::ExpireMap( /* static */ void SharedSurfacesParent::AccumulateMemoryReport( base::ProcessId aPid, SharedSurfacesMemoryReport& aReport) { - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return; } @@ -352,7 +364,7 @@ bool SharedSurfacesParent::AccumulateMemoryReport( return false; } - StaticMutexAutoLock lock(sMutex); + StaticMonitorAutoLock lock(sMonitor); if (!sInstance) { return true; } diff --git a/gfx/layers/ipc/SharedSurfacesParent.h b/gfx/layers/ipc/SharedSurfacesParent.h index a42588d717a2..a4196690bc13 100644 --- a/gfx/layers/ipc/SharedSurfacesParent.h +++ b/gfx/layers/ipc/SharedSurfacesParent.h @@ -9,7 +9,7 @@ #include // for uint32_t #include "mozilla/Attributes.h" // for override -#include "mozilla/StaticMutex.h" // for StaticMutex +#include "mozilla/StaticMonitor.h" // for StaticMutex #include "mozilla/StaticPtr.h" // for StaticAutoPtr #include "mozilla/RefPtr.h" // for already_AddRefed #include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc @@ -78,20 +78,20 @@ class SharedSurfacesParent final { gfx::SourceSurfaceSharedData* aSurface); static void AddTrackingLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock); + const StaticMonitorAutoLock& aAutoLock); static void RemoveTrackingLocked( gfx::SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock); + const StaticMonitorAutoLock& aAutoLock); static bool AgeOneGenerationLocked( nsTArray>& aExpired, - const StaticMutexAutoLock& aAutoLock); + const StaticMonitorAutoLock& aAutoLock); static void ExpireMap( nsTArray>& aExpired); - static StaticMutex sMutex; + static StaticMonitor sMonitor; static StaticAutoPtr sInstance; @@ -100,28 +100,28 @@ class SharedSurfacesParent final { class MappingTracker final : public ExpirationTrackerImpl { + StaticMonitor, StaticMonitorAutoLock> { public: explicit MappingTracker(uint32_t aExpirationTimeoutMS, nsIEventTarget* aEventTarget) : ExpirationTrackerImpl( + StaticMonitor, StaticMonitorAutoLock>( aExpirationTimeoutMS, "SharedMappingTracker", aEventTarget) {} void TakeExpired( nsTArray>& aExpired, - const StaticMutexAutoLock& aAutoLock); + const StaticMonitorAutoLock& aAutoLock); protected: void NotifyExpiredLocked(gfx::SourceSurfaceSharedDataWrapper* aSurface, - const StaticMutexAutoLock& aAutoLock) override; + const StaticMonitorAutoLock& aAutoLock) override; - void NotifyHandlerEndLocked(const StaticMutexAutoLock& aAutoLock) override { - } + void NotifyHandlerEndLocked( + const StaticMonitorAutoLock& aAutoLock) override {} void NotifyHandlerEnd() override; - StaticMutex& GetMutex() override { return sMutex; } + StaticMonitor& GetMutex() override { return sMonitor; } nsTArray> mExpired; };