diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 5ca36390e6b7..cd7ede5772fc 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -64,7 +64,6 @@ #include "nsIMemoryReporter.h" #include "nsStyleUtil.h" #include "CanvasImageCache.h" -#include "DrawTargetWebgl.h" #include @@ -1357,6 +1356,7 @@ bool CanvasRenderingContext2D::CopyBufferProvider( } aTarget.CopySurface(snapshot, aCopyRect, IntPoint()); + aOld.ReturnSnapshot(snapshot.forget()); return true; } @@ -1651,13 +1651,22 @@ bool CanvasRenderingContext2D::TryAcceleratedTarget( if (!mAllowAcceleration || GetEffectiveWillReadFrequently()) { return false; } - aOutDT = DrawTargetWebgl::Create(GetSize(), GetSurfaceFormat()); - if (!aOutDT) { + + if (!mCanvasElement) { return false; } - - aOutProvider = new PersistentBufferProviderAccelerated(aOutDT); - return true; + WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement); + if (!renderer) { + return false; + } + aOutProvider = PersistentBufferProviderAccelerated::Create( + GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor()); + if (!aOutProvider) { + return false; + } + aOutDT = aOutProvider->BorrowDrawTarget(IntRect()); + MOZ_ASSERT(aOutDT); + return !!aOutDT; } bool CanvasRenderingContext2D::TrySharedTarget( diff --git a/dom/canvas/test/reftest/reftest.list b/dom/canvas/test/reftest/reftest.list index 887ceda2c39d..0eef0b3daf82 100644 --- a/dom/canvas/test/reftest/reftest.list +++ b/dom/canvas/test/reftest/reftest.list @@ -238,7 +238,7 @@ fuzzy(0-1,0-2) skip-if(cocoaWidget||winWidget||gtkWidget) needs-focus == drawFoc # Check that captureStream() displays in a local video element == capturestream.html wrapper.html?green.png -fuzzy-if(azureSkia,0-16,0-20) fuzzy-if(Android,0-3,0-40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-1,0-1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html +fuzzy(0-235,0-3104) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html # Bug 1366027 == clipped-dash-stroke-rect.html clipped-dash-stroke-rect-ref.html diff --git a/gfx/ipc/CanvasManagerChild.cpp b/gfx/ipc/CanvasManagerChild.cpp index c26319bc552c..5528a13f884a 100644 --- a/gfx/ipc/CanvasManagerChild.cpp +++ b/gfx/ipc/CanvasManagerChild.cpp @@ -165,7 +165,13 @@ void CanvasManagerChild::DeactivateCanvas() { } } +void CanvasManagerChild::BlockCanvas() { mBlocked = true; } + RefPtr CanvasManagerChild::GetCanvasChild() { + if (mBlocked) { + return nullptr; + } + if (!mActive) { MOZ_ASSERT(!mCanvasChild); return nullptr; diff --git a/gfx/ipc/CanvasManagerChild.h b/gfx/ipc/CanvasManagerChild.h index 7e6f2dcfb50d..54480753c0a4 100644 --- a/gfx/ipc/CanvasManagerChild.h +++ b/gfx/ipc/CanvasManagerChild.h @@ -49,6 +49,7 @@ class CanvasManagerChild final : public PCanvasManagerChild { void EndCanvasTransaction(); void ClearCachedResources(); void DeactivateCanvas(); + void BlockCanvas(); RefPtr GetCanvasChild(); @@ -63,6 +64,7 @@ class CanvasManagerChild final : public PCanvasManagerChild { RefPtr mWebGPUChild; const uint32_t mId; bool mActive = true; + bool mBlocked = false; static MOZ_THREAD_LOCAL(CanvasManagerChild*) sLocalManager; static Atomic sNextId; diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp index c85081036b80..00b12f0f73a6 100644 --- a/gfx/ipc/GPUParent.cpp +++ b/gfx/ipc/GPUParent.cpp @@ -360,14 +360,15 @@ mozilla::ipc::IPCResult GPUParent::RecvInit( // here that would normally be initialized there. SkGraphics::Init(); - if (gfxVars::RemoteCanvasEnabled()) { + bool useRemoteCanvas = + gfxVars::RemoteCanvasEnabled() || gfxVars::UseAcceleratedCanvas2D(); + if (useRemoteCanvas) { gfxGradientCache::Init(); } #if defined(XP_WIN) if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { - if (DeviceManagerDx::Get()->CreateCompositorDevices() && - gfxVars::RemoteCanvasEnabled()) { + if (DeviceManagerDx::Get()->CreateCompositorDevices() && useRemoteCanvas) { if (DeviceManagerDx::Get()->CreateCanvasDevice()) { gfxDWriteFont::InitDWriteSupport(); } else { diff --git a/gfx/layers/CanvasDrawEventRecorder.cpp b/gfx/layers/CanvasDrawEventRecorder.cpp index 2c682c3f44a5..a461637478a4 100644 --- a/gfx/layers/CanvasDrawEventRecorder.cpp +++ b/gfx/layers/CanvasDrawEventRecorder.cpp @@ -43,6 +43,7 @@ CanvasDrawEventRecorder::CanvasDrawEventRecorder() { } bool CanvasDrawEventRecorder::Init(TextureType aTextureType, + gfx::BackendType aBackendType, UniquePtr aHelpers) { mHelpers = std::move(aHelpers); @@ -91,7 +92,8 @@ bool CanvasDrawEventRecorder::Init(TextureType aTextureType, return false; } - if (!mHelpers->InitTranslator(aTextureType, std::move(header->handle), + if (!mHelpers->InitTranslator(aTextureType, aBackendType, + std::move(header->handle), std::move(bufferHandles), mDefaultBufferSize, std::move(readerSem), std::move(writerSem), /* aUseIPDLThread */ false)) { diff --git a/gfx/layers/CanvasDrawEventRecorder.h b/gfx/layers/CanvasDrawEventRecorder.h index 0ad0a0ef176f..3223bf0051ce 100644 --- a/gfx/layers/CanvasDrawEventRecorder.h +++ b/gfx/layers/CanvasDrawEventRecorder.h @@ -67,16 +67,13 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, public: virtual ~Helpers() = default; - virtual bool InitTranslator(const TextureType& aTextureType, - Handle&& aReadHandle, - nsTArray&& aBufferHandles, - const uint64_t& aBufferSize, - CrossProcessSemaphoreHandle&& aReaderSem, - CrossProcessSemaphoreHandle&& aWriterSem, - const bool& aUseIPDLThread) = 0; + virtual bool InitTranslator( + TextureType aTextureType, gfx::BackendType aBackendType, + Handle&& aReadHandle, nsTArray&& aBufferHandles, + uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, + CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) = 0; - virtual bool AddBuffer(Handle&& aBufferHandle, - const uint64_t& aBufferSize) = 0; + virtual bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) = 0; /** * @returns true if the reader of the CanvasEventRingBuffer has permanently @@ -90,7 +87,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate, virtual bool RestartReader() = 0; }; - bool Init(TextureType aTextureType, UniquePtr aHelpers); + bool Init(TextureType aTextureType, gfx::BackendType aBackendType, + UniquePtr aHelpers); /** * Record an event for processing by the CanvasParent's CanvasTranslator. diff --git a/gfx/layers/PersistentBufferProvider.cpp b/gfx/layers/PersistentBufferProvider.cpp index 4ce356bea991..117f9bc82eae 100644 --- a/gfx/layers/PersistentBufferProvider.cpp +++ b/gfx/layers/PersistentBufferProvider.cpp @@ -6,6 +6,7 @@ #include "PersistentBufferProvider.h" +#include "mozilla/layers/RemoteTextureMap.h" #include "mozilla/layers/TextureClient.h" #include "mozilla/layers/TextureForwarder.h" #include "mozilla/gfx/gfxVars.h" @@ -96,65 +97,149 @@ PersistentBufferProviderBasic::Create(gfx::IntSize aSize, return provider.forget(); } +static already_AddRefed CreateTexture( + KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, bool aWillReadFrequently = false, + Maybe aRemoteTextureOwnerId = {}) { + TextureAllocationFlags flags = ALLOC_DEFAULT; + if (aWillReadFrequently) { + flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE); + } + if (aRemoteTextureOwnerId) { + flags = TextureAllocationFlags(flags | ALLOC_FORCE_REMOTE); + } + RefPtr tc = TextureClient::CreateForDrawing( + aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas, + TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags); + if (tc && aRemoteTextureOwnerId) { + if (TextureData* td = tc->GetInternalData()) { + td->SetRemoteTextureOwnerId(*aRemoteTextureOwnerId); + } + } + return tc.forget(); +} + +// static +already_AddRefed +PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + KnowsCompositor* aKnowsCompositor) { + if (!DrawTargetWebgl::CanCreate(aSize, aFormat)) { + return nullptr; + } + + if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() || + !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) { + return nullptr; + } + + auto remoteTextureOwnerId = RemoteTextureOwnerId::GetNext(); + + RefPtr texture = CreateTexture( + aKnowsCompositor, aFormat, aSize, false, Some(remoteTextureOwnerId)); + if (!texture) { + return nullptr; + } + + RefPtr provider = + new PersistentBufferProviderAccelerated(texture); + return provider.forget(); +} + PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated( - DrawTarget* aDt) - : PersistentBufferProviderBasic(aDt) { + const RefPtr& aTexture) + : mTexture(aTexture) { MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated); - MOZ_ASSERT(aDt->GetBackendType() == BackendType::WEBGL); } PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() { MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated); + Destroy(); } -inline gfx::DrawTargetWebgl* -PersistentBufferProviderAccelerated::GetDrawTargetWebgl() const { - return static_cast(mDrawTarget.get()); -} +void PersistentBufferProviderAccelerated::Destroy() { + mSnapshot = nullptr; + mDrawTarget = nullptr; -Maybe -PersistentBufferProviderAccelerated::GetFrontBuffer() { - return GetDrawTargetWebgl()->GetFrontBuffer(); + if (mTexture) { + if (mTexture->IsLocked()) { + MOZ_ASSERT(false); + mTexture->Unlock(); + } + mTexture = nullptr; + } } already_AddRefed PersistentBufferProviderAccelerated::BorrowDrawTarget( const gfx::IntRect& aPersistedRect) { - GetDrawTargetWebgl()->BeginFrame(aPersistedRect); - return PersistentBufferProviderBasic::BorrowDrawTarget(aPersistedRect); + if (!mDrawTarget) { + if (!mTexture->Lock(OpenMode::OPEN_READ_WRITE)) { + return nullptr; + } + mDrawTarget = mTexture->BorrowDrawTarget(); + if (mDrawTarget) { + mDrawTarget->ClearRect(Rect(0, 0, 0, 0)); + if (!mDrawTarget->IsValid()) { + mDrawTarget = nullptr; + } + } + } + return do_AddRef(mDrawTarget); } bool PersistentBufferProviderAccelerated::ReturnDrawTarget( already_AddRefed aDT) { - bool result = PersistentBufferProviderBasic::ReturnDrawTarget(std::move(aDT)); - GetDrawTargetWebgl()->EndFrame(); - return result; + { + RefPtr dt(aDT); + MOZ_ASSERT(mDrawTarget == dt); + if (!mDrawTarget) { + return false; + } + mDrawTarget = nullptr; + } + mTexture->Unlock(); + return true; } already_AddRefed PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) { - mSnapshot = GetDrawTargetWebgl()->GetOptimizedSnapshot(aTarget); + if (mDrawTarget) { + MOZ_ASSERT(mTexture->IsLocked()); + } else { + if (mTexture->IsLocked()) { + MOZ_ASSERT(false); + return nullptr; + } + if (!mTexture->Lock(OpenMode::OPEN_READ)) { + return nullptr; + } + } + mSnapshot = mTexture->BorrowSnapshot(); return do_AddRef(mSnapshot); } -bool PersistentBufferProviderAccelerated::RequiresRefresh() const { - return GetDrawTargetWebgl()->RequiresRefresh(); -} - -void PersistentBufferProviderAccelerated::OnMemoryPressure() { - GetDrawTargetWebgl()->OnMemoryPressure(); -} - -static already_AddRefed CreateTexture( - KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat, - gfx::IntSize aSize, bool aWillReadFrequently) { - TextureAllocationFlags flags = ALLOC_DEFAULT; - if (aWillReadFrequently) { - flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE); +void PersistentBufferProviderAccelerated::ReturnSnapshot( + already_AddRefed aSnapshot) { + RefPtr snapshot = aSnapshot; + MOZ_ASSERT(!snapshot || snapshot == mSnapshot); + snapshot = nullptr; + mTexture->ReturnSnapshot(mSnapshot.forget()); + if (!mDrawTarget) { + mTexture->Unlock(); } - return TextureClient::CreateForDrawing( - aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas, - TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags); +} + +Maybe PersistentBufferProviderAccelerated::GetFrontBuffer() { + SurfaceDescriptor desc; + if (mTexture->GetInternalData()->Serialize(desc)) { + return Some(desc); + } + return Nothing(); +} + +bool PersistentBufferProviderAccelerated::RequiresRefresh() const { + return mTexture->GetInternalData()->RequiresRefresh(); } // static @@ -198,7 +283,6 @@ PersistentBufferProviderShared::PersistentBufferProviderShared( gfx::IntSize aSize, gfx::SurfaceFormat aFormat, KnowsCompositor* aKnowsCompositor, RefPtr& aTexture, bool aWillReadFrequently) - : mSize(aSize), mFormat(aFormat), mKnowsCompositor(aKnowsCompositor), diff --git a/gfx/layers/PersistentBufferProvider.h b/gfx/layers/PersistentBufferProvider.h index 25abcc8fcb12..e857cf7c1ab2 100644 --- a/gfx/layers/PersistentBufferProvider.h +++ b/gfx/layers/PersistentBufferProvider.h @@ -24,11 +24,11 @@ class ClientWebGLContext; namespace gfx { class SourceSurface; class DrawTarget; -class DrawTargetWebgl; } // namespace gfx namespace layers { +struct RemoteTextureOwnerId; class TextureClient; /** @@ -104,12 +104,7 @@ class PersistentBufferProvider : public RefCounted, */ virtual bool RequiresRefresh() const { return false; } - /** - * Provide a WebGL front buffer for compositing, if available. - */ - virtual Maybe GetFrontBuffer() { - return Nothing(); - } + virtual Maybe GetFrontBuffer() { return Nothing(); } }; class PersistentBufferProviderBasic : public PersistentBufferProvider { @@ -146,18 +141,17 @@ class PersistentBufferProviderBasic : public PersistentBufferProvider { RefPtr mSnapshot; }; -class PersistentBufferProviderAccelerated - : public PersistentBufferProviderBasic { +class PersistentBufferProviderAccelerated : public PersistentBufferProvider { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderAccelerated, override) - explicit PersistentBufferProviderAccelerated(gfx::DrawTarget* aTarget); + static already_AddRefed Create( + gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + KnowsCompositor* aKnowsCompositor); bool IsAccelerated() const override { return true; } - Maybe GetFrontBuffer() override; - already_AddRefed BorrowDrawTarget( const gfx::IntRect& aPersistedRect) override; @@ -166,14 +160,27 @@ class PersistentBufferProviderAccelerated already_AddRefed BorrowSnapshot( gfx::DrawTarget* aTarget) override; + void ReturnSnapshot(already_AddRefed aSnapshot) override; + + bool PreservesDrawingState() const override { return true; } + + void OnShutdown() override { Destroy(); } + + Maybe GetFrontBuffer() override; + bool RequiresRefresh() const override; - void OnMemoryPressure() override; - protected: + explicit PersistentBufferProviderAccelerated( + const RefPtr& aTexture); ~PersistentBufferProviderAccelerated() override; - gfx::DrawTargetWebgl* GetDrawTargetWebgl() const; + void Destroy(); + + RefPtr mTexture; + + RefPtr mDrawTarget; + RefPtr mSnapshot; }; /** diff --git a/gfx/layers/RecordedCanvasEventImpl.h b/gfx/layers/RecordedCanvasEventImpl.h index 43385d57245b..7ac1e8154619 100644 --- a/gfx/layers/RecordedCanvasEventImpl.h +++ b/gfx/layers/RecordedCanvasEventImpl.h @@ -44,7 +44,8 @@ const EventType CHECKPOINT = EventType(EventType::LAST + 13); const EventType PAUSE_TRANSLATION = EventType(EventType::LAST + 14); const EventType RECYCLE_BUFFER = EventType(EventType::LAST + 15); const EventType DROP_BUFFER = EventType(EventType::LAST + 16); -const EventType LAST_CANVAS_EVENT_TYPE = DROP_BUFFER; +const EventType PREPARE_SHMEM = EventType(EventType::LAST + 17); +const EventType LAST_CANVAS_EVENT_TYPE = PREPARE_SHMEM; class RecordedCanvasBeginTransaction final : public RecordedEventDerived { @@ -138,10 +139,12 @@ RecordedCanvasFlush::RecordedCanvasFlush(S& aStream) class RecordedTextureLock final : public RecordedEventDerived { public: - RecordedTextureLock(int64_t aTextureId, const OpenMode aMode) + RecordedTextureLock(int64_t aTextureId, const OpenMode aMode, + RemoteTextureId aId) : RecordedEventDerived(TEXTURE_LOCK), mTextureId(aTextureId), - mMode(aMode) {} + mMode(aMode), + mLastRemoteTextureId(aId) {} template MOZ_IMPLICIT RecordedTextureLock(S& aStream); @@ -156,16 +159,14 @@ class RecordedTextureLock final private: int64_t mTextureId; OpenMode mMode; + RemoteTextureId mLastRemoteTextureId; }; inline bool RecordedTextureLock::PlayCanvasEvent( CanvasTranslator* aTranslator) const { - TextureData* textureData = aTranslator->LookupTextureData(mTextureId); - if (!textureData) { + if (!aTranslator->LockTexture(mTextureId, mMode, mLastRemoteTextureId)) { return false; } - - textureData->Lock(mMode); return true; } @@ -173,6 +174,7 @@ template void RecordedTextureLock::Record(S& aStream) const { WriteElement(aStream, mTextureId); WriteElement(aStream, mMode); + WriteElement(aStream, mLastRemoteTextureId.mId); } template @@ -181,13 +183,16 @@ RecordedTextureLock::RecordedTextureLock(S& aStream) ReadElement(aStream, mTextureId); ReadElementConstrained(aStream, mMode, OpenMode::OPEN_NONE, OpenMode::OPEN_READ_WRITE_ASYNC); + ReadElement(aStream, mLastRemoteTextureId.mId); } class RecordedTextureUnlock final : public RecordedEventDerived { public: - explicit RecordedTextureUnlock(int64_t aTextureId) - : RecordedEventDerived(TEXTURE_UNLOCK), mTextureId(aTextureId) {} + explicit RecordedTextureUnlock(int64_t aTextureId, RemoteTextureId aId) + : RecordedEventDerived(TEXTURE_UNLOCK), + mTextureId(aTextureId), + mLastRemoteTextureId(aId) {} template MOZ_IMPLICIT RecordedTextureUnlock(S& aStream); @@ -201,28 +206,28 @@ class RecordedTextureUnlock final private: int64_t mTextureId; + RemoteTextureId mLastRemoteTextureId; }; inline bool RecordedTextureUnlock::PlayCanvasEvent( CanvasTranslator* aTranslator) const { - TextureData* textureData = aTranslator->LookupTextureData(mTextureId); - if (!textureData) { + if (!aTranslator->UnlockTexture(mTextureId, mLastRemoteTextureId)) { return false; } - - textureData->Unlock(); return true; } template void RecordedTextureUnlock::Record(S& aStream) const { WriteElement(aStream, mTextureId); + WriteElement(aStream, mLastRemoteTextureId.mId); } template RecordedTextureUnlock::RecordedTextureUnlock(S& aStream) : RecordedEventDerived(TEXTURE_UNLOCK) { ReadElement(aStream, mTextureId); + ReadElement(aStream, mLastRemoteTextureId.mId); } class RecordedCacheDataSurface final @@ -486,8 +491,11 @@ RecordedDeviceChangeAcknowledged::RecordedDeviceChangeAcknowledged(S& aStream) class RecordedNextTextureId final : public RecordedEventDerived { public: - explicit RecordedNextTextureId(int64_t aNextTextureId) - : RecordedEventDerived(NEXT_TEXTURE_ID), mNextTextureId(aNextTextureId) {} + RecordedNextTextureId(int64_t aNextTextureId, + RemoteTextureOwnerId aRemoteTextureOwnerId) + : RecordedEventDerived(NEXT_TEXTURE_ID), + mNextTextureId(aNextTextureId), + mRemoteTextureOwnerId(aRemoteTextureOwnerId) {} template MOZ_IMPLICIT RecordedNextTextureId(S& aStream); @@ -500,24 +508,28 @@ class RecordedNextTextureId final std::string GetName() const final { return "RecordedNextTextureId"; } private: - int64_t mNextTextureId; + int64_t mNextTextureId = 0; + RemoteTextureOwnerId mRemoteTextureOwnerId; + RemoteTextureId mRemoteTextureId; }; inline bool RecordedNextTextureId::PlayCanvasEvent( CanvasTranslator* aTranslator) const { - aTranslator->SetNextTextureId(mNextTextureId); + aTranslator->SetNextTextureId(mNextTextureId, mRemoteTextureOwnerId); return true; } template void RecordedNextTextureId::Record(S& aStream) const { WriteElement(aStream, mNextTextureId); + WriteElement(aStream, mRemoteTextureOwnerId.mId); } template RecordedNextTextureId::RecordedNextTextureId(S& aStream) : RecordedEventDerived(NEXT_TEXTURE_ID) { ReadElement(aStream, mNextTextureId); + ReadElement(aStream, mRemoteTextureOwnerId.mId); } class RecordedTextureDestruction final @@ -537,7 +549,7 @@ class RecordedTextureDestruction final std::string GetName() const final { return "RecordedTextureDestruction"; } private: - int64_t mTextureId; + int64_t mTextureId = 0; }; inline bool RecordedTextureDestruction::PlayCanvasEvent( @@ -638,6 +650,43 @@ class RecordedDropBuffer final std::string GetName() const final { return "RecordedDropAndMoveNextBuffer"; } }; +class RecordedPrepareShmem final + : public RecordedEventDerived { + public: + explicit RecordedPrepareShmem(int64_t aTextureId) + : RecordedEventDerived(PREPARE_SHMEM), mTextureId(aTextureId) {} + + template + MOZ_IMPLICIT RecordedPrepareShmem(S& aStream); + + bool PlayCanvasEvent(CanvasTranslator* aTranslator) const; + + template + void Record(S& aStream) const; + + std::string GetName() const final { return "RecordedPrepareShmem"; } + + private: + int64_t mTextureId = 0; +}; + +inline bool RecordedPrepareShmem::PlayCanvasEvent( + CanvasTranslator* aTranslator) const { + aTranslator->PrepareShmem(mTextureId); + return true; +} + +template +void RecordedPrepareShmem::Record(S& aStream) const { + WriteElement(aStream, mTextureId); +} + +template +RecordedPrepareShmem::RecordedPrepareShmem(S& aStream) + : RecordedEventDerived(PREPARE_SHMEM) { + ReadElement(aStream, mTextureId); +} + #define FOR_EACH_CANVAS_EVENT(f) \ f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \ f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \ @@ -655,7 +704,8 @@ class RecordedDropBuffer final f(CHECKPOINT, RecordedCheckpoint); \ f(PAUSE_TRANSLATION, RecordedPauseTranslation); \ f(RECYCLE_BUFFER, RecordedRecycleBuffer); \ - f(DROP_BUFFER, RecordedDropBuffer); + f(DROP_BUFFER, RecordedDropBuffer); \ + f(PREPARE_SHMEM, RecordedPrepareShmem); } // namespace layers } // namespace mozilla diff --git a/gfx/layers/RemoteTextureMap.cpp b/gfx/layers/RemoteTextureMap.cpp index 48864b7e5c8e..3f8dc47779f1 100644 --- a/gfx/layers/RemoteTextureMap.cpp +++ b/gfx/layers/RemoteTextureMap.cpp @@ -217,7 +217,7 @@ void RemoteTextureMap::Shutdown() { } } -RemoteTextureMap::RemoteTextureMap() : mMonitor("D3D11TextureMap::mMonitor") {} +RemoteTextureMap::RemoteTextureMap() : mMonitor("RemoteTextureMap::mMonitor") {} RemoteTextureMap::~RemoteTextureMap() = default; @@ -314,17 +314,7 @@ void RemoteTextureMap::PushTexture( // used by WebRender anymore. if (front->mTextureHost && front->mTextureHost->NumCompositableRefs() == 0) { - // Recycle SharedTexture - if (front->mResourceWrapper) { - owner->mRecycledSharedTextures.push( - std::move(front->mResourceWrapper)); - } - // Recycle BufferTextureData - if (!(front->mTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE) && - (front->mTextureData && - front->mTextureData->AsBufferTextureData())) { - owner->mRecycledTextures.push(std::move(front->mTextureData)); - } + owner->mReleasingTextureDataHolders.push_back(std::move(front)); owner->mUsingTextureDataHolders.pop_front(); } else if (front->mTextureHost && front->mTextureHost->NumCompositableRefs() >= 0) { @@ -335,6 +325,19 @@ void RemoteTextureMap::PushTexture( owner->mUsingTextureDataHolders.pop_front(); } } + while (!owner->mReleasingTextureDataHolders.empty()) { + auto& front = owner->mReleasingTextureDataHolders.front(); + // Recycle SharedTexture + if (front->mResourceWrapper) { + owner->mRecycledSharedTextures.push(std::move(front->mResourceWrapper)); + } + // Recycle BufferTextureData + if (!(front->mTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE) && + (front->mTextureData && front->mTextureData->AsBufferTextureData())) { + owner->mRecycledTextures.push(std::move(front->mTextureData)); + } + owner->mReleasingTextureDataHolders.pop_front(); + } } const auto info = RemoteTextureInfo(aTextureId, aOwnerId, aForPid); @@ -445,8 +448,7 @@ void RemoteTextureMap::KeepTextureDataAliveForTextureHostIfNecessary( // SharedResourceWrapper/TextureData alive while the TextureHost is alive. if (holder->mTextureHost && holder->mTextureHost->NumCompositableRefs() >= 0) { - RefPtr eventTarget = - MessageLoop::current()->SerialEventTarget(); + RefPtr eventTarget = GetCurrentSerialEventTarget(); RefPtr runnable = NS_NewRunnableFunction( "RemoteTextureMap::UnregisterTextureOwner::Runnable", [data = std::move(holder->mTextureData), @@ -511,6 +513,9 @@ void RemoteTextureMap::UnregisterTextureOwner( KeepTextureDataAliveForTextureHostIfNecessary( lock, it->second->mUsingTextureDataHolders); + KeepTextureDataAliveForTextureHostIfNecessary( + lock, it->second->mReleasingTextureDataHolders); + releasingOwner = std::move(it->second); mTextureOwners.erase(it); @@ -577,6 +582,9 @@ void RemoteTextureMap::UnregisterTextureOwners( KeepTextureDataAliveForTextureHostIfNecessary( lock, it->second->mUsingTextureDataHolders); + KeepTextureDataAliveForTextureHostIfNecessary( + lock, it->second->mReleasingTextureDataHolders); + releasingOwners.push_back(std::move(it->second)); mTextureOwners.erase(it); } @@ -657,6 +665,19 @@ void RemoteTextureMap::UpdateTexture(const MonitorAutoLock& aProofOfLock, UniquePtr holder = std::move(front); aOwner->mWaitingTextureDataHolders.pop_front(); + // If there are textures not being used by the compositor that will be + // obsoleted by this new texture, then queue them for removal later on + // the creating thread. + while (!aOwner->mUsingTextureDataHolders.empty()) { + auto& back = aOwner->mUsingTextureDataHolders.back(); + if (back->mTextureHost && + back->mTextureHost->NumCompositableRefs() == 0) { + aOwner->mReleasingTextureDataHolders.push_back(std::move(back)); + aOwner->mUsingTextureDataHolders.pop_back(); + continue; + } + break; + } aOwner->mUsingTextureDataHolders.push_back(std::move(holder)); } } @@ -696,7 +717,8 @@ RemoteTextureMap::GetAllRenderingReadyCallbacks( bool RemoteTextureMap::GetRemoteTextureForDisplayList( RemoteTextureHostWrapper* aTextureHostWrapper, - std::function&& aReadyCallback) { + std::function&& aReadyCallback, + bool aWaitForRemoteTextureOwner) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aTextureHostWrapper); @@ -714,8 +736,23 @@ bool RemoteTextureMap::GetRemoteTextureForDisplayList( MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, ownerId, forPid); - if (!owner) { - return false; + // If there is no texture owner yet, then we might need to wait for one to + // be created, if allowed. If so, we must also wait for an initial texture + // host to be created so we can use it. + if (!owner || (aWaitForRemoteTextureOwner && !owner->mLatestTextureHost && + owner->mWaitingTextureDataHolders.empty())) { + if (!aWaitForRemoteTextureOwner) { + return false; + } + const TimeDuration timeout = TimeDuration::FromMilliseconds(10000); + while (!owner || (!owner->mLatestTextureHost && + owner->mWaitingTextureDataHolders.empty())) { + CVStatus status = mMonitor.Wait(timeout); + if (status == CVStatus::Timeout) { + return false; + } + owner = GetTextureOwner(lock, ownerId, forPid); + } } UpdateTexture(lock, owner, textureId); diff --git a/gfx/layers/RemoteTextureMap.h b/gfx/layers/RemoteTextureMap.h index 0ea2477a77f5..36f0871da757 100644 --- a/gfx/layers/RemoteTextureMap.h +++ b/gfx/layers/RemoteTextureMap.h @@ -213,7 +213,8 @@ class RemoteTextureMap { // return true when aReadyCallback will be called. bool GetRemoteTextureForDisplayList( RemoteTextureHostWrapper* aTextureHostWrapper, - std::function&& aReadyCallback); + std::function&& aReadyCallback, + bool aWaitForRemoteTextureOwner = false); // Get ExternalImageId of remote texture for WebRender rendering. wr::MaybeExternalImageId GetExternalImageIdOfRemoteTexture( @@ -297,6 +298,7 @@ class RemoteTextureMap { std::deque> mWaitingTextureDataHolders; // Holds TextureDataHolders that are used for building wr display list. std::deque> mUsingTextureDataHolders; + std::deque> mReleasingTextureDataHolders; // Holds async RemoteTexture ready callbacks. std::deque> mRenderingReadyCallbackHolders; diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index c6448667c022..841300980376 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -318,6 +318,36 @@ static bool ShouldRemoteTextureType(TextureType aTextureType, } } +/* static */ +TextureData* TextureData::Create(TextureType aTextureType, + gfx::SurfaceFormat aFormat, + const gfx::IntSize& aSize, + TextureAllocationFlags aAllocFlags, + gfx::BackendType aBackendType) { + switch (aTextureType) { +#ifdef XP_WIN + case TextureType::D3D11: + return D3D11TextureData::Create(aSize, aFormat, aAllocFlags); +#endif + +#ifdef MOZ_WIDGET_GTK + case TextureType::DMABUF: + return DMABUFTextureData::Create(aSize, aFormat, aBackendType); +#endif + +#ifdef XP_MACOSX + case TextureType::MacIOSurface: + return MacIOSurfaceTextureData::Create(aSize, aFormat, aBackendType); +#endif +#ifdef MOZ_WIDGET_ANDROID + case TextureType::AndroidNativeWindow: + return AndroidNativeWindowTextureData::Create(aSize, aFormat); +#endif + default: + return nullptr; + } +} + /* static */ TextureData* TextureData::Create(TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, @@ -328,45 +358,33 @@ TextureData* TextureData::Create(TextureForwarder* aAllocator, TextureType textureType = GetTextureType(aFormat, aSize, aKnowsCompositor, aSelector, aAllocFlags); - if (ShouldRemoteTextureType(textureType, aSelector)) { + if ((aAllocFlags & ALLOC_FORCE_REMOTE) || + ShouldRemoteTextureType(textureType, aSelector)) { RefPtr canvasChild = aAllocator->GetCanvasChild(); if (canvasChild) { return new RecordedTextureData(canvasChild.forget(), aSize, aFormat, textureType); } + if (aAllocFlags & ALLOC_FORCE_REMOTE) { + // If we must be remote, but there is no canvas child, then falling back + // is not possible. + return nullptr; + } // We don't have a CanvasChild, but are supposed to be remote. // Fall back to software. textureType = TextureType::Unknown; } + gfx::BackendType moz2DBackend = gfx::BackendType::NONE; + #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) - gfx::BackendType moz2DBackend = BackendTypeForBackendSelector( + moz2DBackend = BackendTypeForBackendSelector( aKnowsCompositor->GetCompositorBackendType(), aSelector); #endif - switch (textureType) { -#ifdef XP_WIN - case TextureType::D3D11: - return D3D11TextureData::Create(aSize, aFormat, aAllocFlags); -#endif - -#ifdef MOZ_WIDGET_GTK - case TextureType::DMABUF: - return DMABUFTextureData::Create(aSize, aFormat, moz2DBackend); -#endif - -#ifdef XP_MACOSX - case TextureType::MacIOSurface: - return MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend); -#endif -#ifdef MOZ_WIDGET_ANDROID - case TextureType::AndroidNativeWindow: - return AndroidNativeWindowTextureData::Create(aSize, aFormat); -#endif - default: - return nullptr; - } + return TextureData::Create(textureType, aFormat, aSize, aAllocFlags, + moz2DBackend); } /* static */ @@ -515,6 +533,7 @@ void TextureClient::Destroy() { } mBorrowedDrawTarget = nullptr; + mBorrowedSnapshot = false; mReadLock = nullptr; RefPtr actor = mActor; @@ -670,6 +689,7 @@ void TextureClient::Unlock() { mBorrowedDrawTarget = nullptr; } + mBorrowedSnapshot = false; if (mOpenMode & OpenMode::OPEN_WRITE) { mUpdated = true; @@ -806,6 +826,7 @@ void TextureClient::EndDraw() { MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs); mBorrowedDrawTarget = nullptr; + mBorrowedSnapshot = false; mData->EndDraw(); } @@ -813,7 +834,9 @@ already_AddRefed TextureClient::BorrowSnapshot() { MOZ_ASSERT(mIsLocked); RefPtr surface = mData->BorrowSnapshot(); - if (!surface) { + if (surface) { + mBorrowedSnapshot = true; + } else { RefPtr drawTarget = BorrowDrawTarget(); if (!drawTarget) { return nullptr; @@ -824,6 +847,15 @@ already_AddRefed TextureClient::BorrowSnapshot() { return surface.forget(); } +void TextureClient::ReturnSnapshot( + already_AddRefed aSnapshot) { + RefPtr snapshot = aSnapshot; + if (mBorrowedSnapshot) { + mData->ReturnSnapshot(snapshot.forget()); + mBorrowedSnapshot = false; + } +} + bool TextureClient::BorrowMappedData(MappedTextureData& aMap) { MOZ_ASSERT(IsValid()); @@ -1160,6 +1192,10 @@ already_AddRefed TextureClient::CreateForDrawing( if (data) { return MakeAndAddRef(data, aTextureFlags, aAllocator); } + if (aAllocFlags & ALLOC_FORCE_REMOTE) { + // If we must be remote, but allocation failed, then don't fall back. + return nullptr; + } // Can't do any better than a buffer texture client. return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize, diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index f799e34bbed5..e87ac08ca82a 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -70,6 +70,7 @@ class SharedSurfaceTextureData; class TextureClientPool; #endif class TextureForwarder; +struct RemoteTextureOwnerId; /** * TextureClient is the abstraction that allows us to share data between the @@ -99,6 +100,9 @@ enum TextureAllocationFlags { // Do not use an accelerated texture type. ALLOC_DO_NOT_ACCELERATE = 1 << 8, + + // Force allocation of remote/recorded texture, or fail if not possible. + ALLOC_FORCE_REMOTE = 1 << 9, }; enum class BackendSelector { Content, Canvas }; @@ -223,6 +227,10 @@ class TextureData { canConcurrentlyReadLock(true) {} }; + static TextureData* Create( + TextureType aTextureType, gfx::SurfaceFormat aFormat, + const gfx::IntSize& aSize, TextureAllocationFlags aAllocFlags, + gfx::BackendType aBackendType = gfx::BackendType::NONE); static TextureData* Create(TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize, KnowsCompositor* aKnowsCompositor, @@ -255,6 +263,8 @@ class TextureData { return nullptr; } + virtual void ReturnSnapshot(already_AddRefed aSnapshot) {} + virtual bool BorrowMappedData(MappedTextureData&) { return false; } virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; } @@ -312,6 +322,10 @@ class TextureData { return mozilla::ipc::FileDescriptor(); } + virtual void SetRemoteTextureOwnerId(RemoteTextureOwnerId) {} + + virtual bool RequiresRefresh() const { return false; } + protected: MOZ_COUNTED_DEFAULT_CTOR(TextureData) }; @@ -447,6 +461,8 @@ class TextureClient : public AtomicRefCountedWithFinalize { already_AddRefed BorrowSnapshot(); + void ReturnSnapshot(already_AddRefed aSnapshot); + /** * Similar to BorrowDrawTarget but provides direct access to the texture's * bits instead of a DrawTarget. @@ -703,6 +719,7 @@ class TextureClient : public AtomicRefCountedWithFinalize { TextureData* mData; RefPtr mBorrowedDrawTarget; + bool mBorrowedSnapshot = false; TextureFlags mFlags; diff --git a/gfx/layers/client/TextureRecorded.cpp b/gfx/layers/client/TextureRecorded.cpp index f2f1d086032e..dd79e02ff2ea 100644 --- a/gfx/layers/client/TextureRecorded.cpp +++ b/gfx/layers/client/TextureRecorded.cpp @@ -30,6 +30,7 @@ RecordedTextureData::~RecordedTextureData() { // We need the translator to drop its reference for the DrawTarget first, // because the TextureData might need to destroy its DrawTarget within a lock. mDT = nullptr; + mCanvasChild->CleanupTexture(mTextureId); mCanvasChild->RecordEvent(RecordedTextureDestruction(mTextureId)); } @@ -40,15 +41,25 @@ void RecordedTextureData::FillInfo(TextureData::Info& aInfo) const { aInfo.hasSynchronization = true; } +void RecordedTextureData::SetRemoteTextureOwnerId( + RemoteTextureOwnerId aRemoteTextureOwnerId) { + mRemoteTextureOwnerId = aRemoteTextureOwnerId; +} + bool RecordedTextureData::Lock(OpenMode aMode) { if (!mCanvasChild->EnsureBeginTransaction()) { return false; } + if (mRemoteTextureOwnerId.IsValid()) { + mLastRemoteTextureId = RemoteTextureId::GetNext(); + } + if (!mDT) { mTextureId = sNextRecordedTextureId++; - mCanvasChild->RecordEvent(RecordedNextTextureId(mTextureId)); - mDT = mCanvasChild->CreateDrawTarget(mSize, mFormat); + mCanvasChild->RecordEvent( + RecordedNextTextureId(mTextureId, mRemoteTextureOwnerId)); + mDT = mCanvasChild->CreateDrawTarget(mTextureId, mSize, mFormat); if (!mDT) { return false; } @@ -59,7 +70,8 @@ bool RecordedTextureData::Lock(OpenMode aMode) { return true; } - mCanvasChild->RecordEvent(RecordedTextureLock(mTextureId, aMode)); + mCanvasChild->RecordEvent( + RecordedTextureLock(mTextureId, aMode, mLastRemoteTextureId)); if (aMode & OpenMode::OPEN_WRITE) { mCanvasChild->OnTextureWriteLock(); } @@ -75,12 +87,18 @@ void RecordedTextureData::Unlock() { mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get())); } - mCanvasChild->RecordEvent(RecordedTextureUnlock(mTextureId)); + mCanvasChild->RecordEvent( + RecordedTextureUnlock(mTextureId, mLastRemoteTextureId)); + mLockedMode = OpenMode::OPEN_NONE; } already_AddRefed RecordedTextureData::BorrowDrawTarget() { mSnapshot = nullptr; + if (RefPtr wrapper = do_AddRef(mSnapshotWrapper)) { + mCanvasChild->DetachSurface(wrapper); + mSnapshotWrapper = nullptr; + } return do_AddRef(mDT); } @@ -95,23 +113,39 @@ void RecordedTextureData::EndDraw() { } already_AddRefed RecordedTextureData::BorrowSnapshot() { + if (RefPtr wrapper = do_AddRef(mSnapshotWrapper)) { + return wrapper.forget(); + } + // There are some failure scenarios where we have no DrawTarget and // BorrowSnapshot is called in an attempt to copy to a new texture. if (!mDT) { return nullptr; } - if (mSnapshot) { - return mCanvasChild->WrapSurface(mSnapshot); - } + RefPtr wrapper = mCanvasChild->WrapSurface( + mSnapshot ? mSnapshot : mDT->Snapshot(), mTextureId); + mSnapshotWrapper = wrapper; + return wrapper.forget(); +} - return mCanvasChild->WrapSurface(mDT->Snapshot()); +void RecordedTextureData::ReturnSnapshot( + already_AddRefed aSnapshot) { + RefPtr snapshot = aSnapshot; + if (RefPtr wrapper = do_AddRef(mSnapshotWrapper)) { + mCanvasChild->DetachSurface(wrapper); + } } void RecordedTextureData::Deallocate(LayersIPCChannel* aAllocator) {} bool RecordedTextureData::Serialize(SurfaceDescriptor& aDescriptor) { - aDescriptor = SurfaceDescriptorRecorded(mTextureId); + if (mRemoteTextureOwnerId.IsValid()) { + aDescriptor = SurfaceDescriptorRemoteTexture(mLastRemoteTextureId, + mRemoteTextureOwnerId); + } else { + aDescriptor = SurfaceDescriptorRecorded(mTextureId); + } return true; } @@ -125,5 +159,9 @@ TextureFlags RecordedTextureData::GetTextureFlags() const { return TextureFlags::WAIT_HOST_USAGE_END; } +bool RecordedTextureData::RequiresRefresh() const { + return mCanvasChild->RequiresRefresh(mTextureId); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/client/TextureRecorded.h b/gfx/layers/client/TextureRecorded.h index d393d9082190..c75c2e93bb38 100644 --- a/gfx/layers/client/TextureRecorded.h +++ b/gfx/layers/client/TextureRecorded.h @@ -32,6 +32,8 @@ class RecordedTextureData final : public TextureData { already_AddRefed BorrowSnapshot() final; + void ReturnSnapshot(already_AddRefed aSnapshot) final; + void Deallocate(LayersIPCChannel* aAllocator) final; bool Serialize(SurfaceDescriptor& aDescriptor) final; @@ -40,6 +42,11 @@ class RecordedTextureData final : public TextureData { TextureFlags GetTextureFlags() const final; + void SetRemoteTextureOwnerId( + RemoteTextureOwnerId aRemoteTextureOwnerId) final; + + bool RequiresRefresh() const final; + private: DISALLOW_COPY_AND_ASSIGN(RecordedTextureData); @@ -51,7 +58,10 @@ class RecordedTextureData final : public TextureData { gfx::SurfaceFormat mFormat; RefPtr mDT; RefPtr mSnapshot; + ThreadSafeWeakPtr mSnapshotWrapper; OpenMode mLockedMode; + layers::RemoteTextureId mLastRemoteTextureId; + layers::RemoteTextureOwnerId mRemoteTextureOwnerId; }; } // namespace layers diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp index 6966fb4684d6..d104a621a47b 100644 --- a/gfx/layers/ipc/CanvasChild.cpp +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -15,6 +15,7 @@ #include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/ProcessChild.h" #include "mozilla/layers/CanvasDrawEventRecorder.h" +#include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/SourceSurfaceSharedData.h" #include "mozilla/Maybe.h" #include "nsIObserverService.h" @@ -30,22 +31,22 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers { ~RecorderHelpers() override = default; - bool InitTranslator(const TextureType& aTextureType, Handle&& aReadHandle, - nsTArray&& aBufferHandles, - const uint64_t& aBufferSize, + bool InitTranslator(TextureType aTextureType, gfx::BackendType aBackendType, + Handle&& aReadHandle, nsTArray&& aBufferHandles, + uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, CrossProcessSemaphoreHandle&& aWriterSem, - const bool& aUseIPDLThread) override { + bool aUseIPDLThread) override { if (!mCanvasChild) { return false; } return mCanvasChild->SendInitTranslator( - aTextureType, std::move(aReadHandle), std::move(aBufferHandles), - aBufferSize, std::move(aReaderSem), std::move(aWriterSem), - aUseIPDLThread); + aTextureType, aBackendType, std::move(aReadHandle), + std::move(aBufferHandles), aBufferSize, std::move(aReaderSem), + std::move(aWriterSem), aUseIPDLThread); } - bool AddBuffer(Handle&& aBufferHandle, const uint64_t& aBufferSize) override { + bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) override { if (!mCanvasChild) { return false; } @@ -75,10 +76,11 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface { MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final) SourceSurfaceCanvasRecording( - const RefPtr& aRecordedSuface, + int64_t aTextureId, const RefPtr& aRecordedSuface, CanvasChild* aCanvasChild, const RefPtr& aRecorder) - : mRecordedSurface(aRecordedSuface), + : mTextureId(aTextureId), + mRecordedSurface(aRecordedSuface), mCanvasChild(aCanvasChild), mRecorder(aRecorder) { // It's important that AddStoredObject is called first because that will @@ -119,11 +121,14 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface { return do_AddRef(mDataSourceSurface); } + void DrawTargetWillChange() { mDetached = true; } + private: void EnsureDataSurfaceOnMainThread() { // The data can only be retrieved on the main thread. if (!mDataSourceSurface && NS_IsMainThread()) { - mDataSourceSurface = mCanvasChild->GetDataSurface(mRecordedSurface); + mDataSourceSurface = + mCanvasChild->GetDataSurface(mTextureId, mRecordedSurface, mDetached); } } @@ -141,10 +146,12 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface { aRecorder = nullptr; } + int64_t mTextureId; RefPtr mRecordedSurface; RefPtr mCanvasChild; RefPtr mRecorder; RefPtr mDataSourceSurface; + bool mDetached = false; }; CanvasChild::CanvasChild() = default; @@ -176,11 +183,21 @@ ipc::IPCResult CanvasChild::RecvDeactivate() { return IPC_OK(); } +ipc::IPCResult CanvasChild::RecvBlockCanvas() { + if (auto* cm = gfx::CanvasManagerChild::Get()) { + cm->BlockCanvas(); + } + return IPC_OK(); +} + void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureType aTextureType) { if (!mRecorder) { + gfx::BackendType backendType = + gfxPlatform::GetPlatform()->GetPreferredCanvasBackend(); auto recorder = MakeRefPtr(); - if (!recorder->Init(aTextureType, MakeUnique(this))) { + if (!recorder->Init(aTextureType, backendType, + MakeUnique(this))) { return; } @@ -276,19 +293,26 @@ bool CanvasChild::ShouldBeCleanedUp() const { } already_AddRefed CanvasChild::CreateDrawTarget( - gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { + int64_t aTextureId, gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { RefPtr dummyDt = gfx::Factory::CreateDrawTarget( gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat); RefPtr dt = MakeAndAddRef( mRecorder, dummyDt, gfx::IntRect(gfx::IntPoint(0, 0), aSize)); + + mTextureInfo.insert({aTextureId, {}}); + return dt.forget(); } bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { - size_t dataFormatWidth = aSize.width * BytesPerPixel(aFormat); size_t sizeRequired = - ipc::SharedMemory::PageAlignedSize(dataFormatWidth * aSize.height); + ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat); + if (!sizeRequired) { + return false; + } + sizeRequired = ipc::SharedMemory::PageAlignedSize(sizeRequired); + if (!mDataSurfaceShmemAvailable || mDataSurfaceShmem->Size() < sizeRequired) { RecordEvent(RecordedPauseTranslation()); auto dataSurfaceShmem = MakeRefPtr(); @@ -328,7 +352,7 @@ int64_t CanvasChild::CreateCheckpoint() { } already_AddRefed CanvasChild::GetDataSurface( - const gfx::SourceSurface* aSurface) { + int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached) { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aSurface); @@ -344,6 +368,26 @@ already_AddRefed CanvasChild::GetDataSurface( return nullptr; } + // Shmem is only valid if the surface is the latest snapshot (not detached). + if (!aDetached) { + // If there is a shmem associated with this snapshot id, then we want to try + // use that directly without having to allocate a new shmem for retrieval. + auto it = mTextureInfo.find(aTextureId); + if (it != mTextureInfo.end() && it->second.mSnapshotShmem.IsReadable()) { + ipc::Shmem& shmem = it->second.mSnapshotShmem; + mRecorder->RecordEvent(RecordedPrepareShmem(aTextureId)); + uint32_t checkpoint = mRecorder->CreateCheckpoint(); + mRecorder->WaitForCheckpoint(checkpoint); + gfx::IntSize size = aSurface->GetSize(); + gfx::SurfaceFormat format = aSurface->GetFormat(); + auto stride = ImageDataSerializer::ComputeRGBStride(format, size.width); + RefPtr dataSurface = + gfx::Factory::CreateWrappingDataSourceSurface(shmem.get(), + stride, size, format); + return dataSurface.forget(); + } + } + RecordEvent(RecordedPrepareDataForSurface(aSurface)); gfx::IntSize ssSize = aSurface->GetSize(); @@ -362,11 +406,11 @@ already_AddRefed CanvasChild::GetDataSurface( auto* data = static_cast(mDataSurfaceShmem->memory()); auto* closure = new DataShmemHolder{do_AddRef(mDataSurfaceShmem), this}; - auto dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat); + auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width); RefPtr dataSurface = gfx::Factory::CreateWrappingDataSourceSurface( - data, dataFormatWidth, ssSize, ssFormat, + data, stride, ssSize, ssFormat, [](void* aClosure) { auto* shmemHolder = static_cast(aClosure); shmemHolder->canvasChild->ReturnDataSurfaceShmem( @@ -380,12 +424,13 @@ already_AddRefed CanvasChild::GetDataSurface( } already_AddRefed CanvasChild::WrapSurface( - const RefPtr& aSurface) { + const RefPtr& aSurface, int64_t aTextureId) { if (!aSurface) { return nullptr; } - return MakeAndAddRef(aSurface, this, mRecorder); + return MakeAndAddRef(aTextureId, aSurface, this, + mRecorder); } void CanvasChild::ReturnDataSurfaceShmem( @@ -398,5 +443,44 @@ void CanvasChild::ReturnDataSurfaceShmem( } } +void CanvasChild::DetachSurface(const RefPtr& aSurface) { + if (auto* surface = + static_cast(aSurface.get())) { + surface->DrawTargetWillChange(); + } +} + +ipc::IPCResult CanvasChild::RecvNotifyRequiresRefresh(int64_t aTextureId) { + auto it = mTextureInfo.find(aTextureId); + if (it != mTextureInfo.end()) { + it->second.mRequiresRefresh = true; + } + return IPC_OK(); +} + +bool CanvasChild::RequiresRefresh(int64_t aTextureId) const { + auto it = mTextureInfo.find(aTextureId); + if (it != mTextureInfo.end()) { + return it->second.mRequiresRefresh; + } + return false; +} + +ipc::IPCResult CanvasChild::RecvSnapshotShmem( + int64_t aTextureId, Shmem&& aShmem, SnapshotShmemResolver&& aResolve) { + auto it = mTextureInfo.find(aTextureId); + if (it != mTextureInfo.end()) { + it->second.mSnapshotShmem = std::move(aShmem); + aResolve(true); + } else { + aResolve(false); + } + return IPC_OK(); +} + +void CanvasChild::CleanupTexture(int64_t aTextureId) { + mTextureInfo.erase(aTextureId); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h index 500c61d0d072..233ef61d63d7 100644 --- a/gfx/layers/ipc/CanvasChild.h +++ b/gfx/layers/ipc/CanvasChild.h @@ -42,6 +42,13 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { ipc::IPCResult RecvDeactivate(); + ipc::IPCResult RecvBlockCanvas(); + + ipc::IPCResult RecvNotifyRequiresRefresh(int64_t aTextureId); + + ipc::IPCResult RecvSnapshotShmem(int64_t aTextureId, Shmem&& aShmem, + SnapshotShmemResolver&& aResolve); + /** * Ensures that the DrawEventRecorder has been created. * @@ -92,12 +99,13 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { /** * Create a DrawTargetRecording for a canvas texture. + * @param aTextureId the id of the new texture * @param aSize size for the DrawTarget * @param aFormat SurfaceFormat for the DrawTarget * @returns newly created DrawTargetRecording */ already_AddRefed CreateDrawTarget( - gfx::IntSize aSize, gfx::SurfaceFormat aFormat); + int64_t aTextureId, gfx::IntSize aSize, gfx::SurfaceFormat aFormat); /** * Record an event for processing by the CanvasParent's CanvasTranslator. @@ -111,21 +119,33 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { * Wrap the given surface, so that we can provide a DataSourceSurface if * required. * @param aSurface the SourceSurface to wrap + * @param aTextureId the texture id of the source TextureData * @returns a SourceSurface that can provide a DataSourceSurface if required */ already_AddRefed WrapSurface( - const RefPtr& aSurface); + const RefPtr& aSurface, int64_t aTextureId); + + /** + * The DrawTargetRecording is about to change, so detach the old snapshot. + */ + void DetachSurface(const RefPtr& aSurface); /** * Get DataSourceSurface from the translated equivalent version of aSurface in * the GPU process. + * @param aTextureId the source TextureData to read from * @param aSurface the SourceSurface in this process for which we need a * DataSourceSurface + * @param aDetached whether the surface is old * @returns a DataSourceSurface created from data for aSurface retrieve from * GPU process */ already_AddRefed GetDataSurface( - const gfx::SourceSurface* aSurface); + int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached); + + bool RequiresRefresh(int64_t aTextureId) const; + + void CleanupTexture(int64_t aTextureId); protected: void ActorDestroy(ActorDestroyReason aWhy) final; @@ -153,6 +173,11 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr { int64_t mLastWriteLockCheckpoint = 0; uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold; std::vector> mLastTransactionExternalSurfaces; + struct TextureInfo { + ipc::Shmem mSnapshotShmem; + bool mRequiresRefresh = false; + }; + std::unordered_map mTextureInfo; bool mIsInTransaction = false; bool mHasOutstandingWriteLock = false; bool mDormant = false; diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp index 011f85f44321..fdf566a7c698 100644 --- a/gfx/layers/ipc/CanvasTranslator.cpp +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -10,17 +10,21 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/CanvasManagerParent.h" #include "mozilla/gfx/CanvasRenderThread.h" +#include "mozilla/gfx/DrawTargetWebgl.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUParent.h" #include "mozilla/gfx/Logging.h" #include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/BufferTexture.h" #include "mozilla/layers/CanvasTranslator.h" +#include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/SharedSurfacesParent.h" #include "mozilla/layers/TextureClient.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/SyncRunnable.h" #include "mozilla/TaskQueue.h" #include "mozilla/Telemetry.h" +#include "GLContext.h" #include "RecordedCanvasEventImpl.h" #if defined(XP_WIN) @@ -32,6 +36,7 @@ namespace mozilla { namespace layers { TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType, + gfx::BackendType aBackendType, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { TextureData* textureData = nullptr; @@ -43,8 +48,16 @@ TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType, break; } #endif + case TextureType::Unknown: + textureData = BufferTextureData::Create( + aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR, + TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE, + ALLOC_CLEAR_BUFFER, nullptr); + break; default: - MOZ_CRASH("Unsupported TextureType for CanvasTranslator."); + textureData = TextureData::Create(aTextureType, aFormat, aSize, + ALLOC_CLEAR_BUFFER, aBackendType); + break; } return textureData; @@ -64,12 +77,7 @@ CanvasTranslator::CanvasTranslator( Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1); } -CanvasTranslator::~CanvasTranslator() { - // The textures need to be the last thing holding their DrawTargets, so that - // they can destroy them within a lock. - mDrawTargets.Clear(); - mBaseDT = nullptr; -} +CanvasTranslator::~CanvasTranslator() = default; void CanvasTranslator::DispatchToTaskQueue( already_AddRefed aRunnable) { @@ -102,16 +110,30 @@ static bool CreateAndMapShmem(RefPtr& aShmem, return true; } +bool CanvasTranslator::EnsureSharedContextWebgl() { + if (!mSharedContext || mSharedContext->IsContextLost()) { + mSharedContext = gfx::SharedContextWebgl::Create(); + if (!mSharedContext || mSharedContext->IsContextLost()) { + mSharedContext = nullptr; + BlockCanvas(); + return false; + } + } + return true; +} + mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( - const TextureType& aTextureType, Handle&& aReadHandle, - nsTArray&& aBufferHandles, uint64_t aBufferSize, - CrossProcessSemaphoreHandle&& aReaderSem, + TextureType aTextureType, gfx::BackendType aBackendType, + Handle&& aReadHandle, nsTArray&& aBufferHandles, + uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) { if (mHeaderShmem) { return IPC_FAIL(this, "RecvInitTranslator called twice."); } mTextureType = aTextureType; + mBackendType = aBackendType; + mOtherPid = OtherPid(); mHeaderShmem = MakeAndAddRef(); if (!CreateAndMapShmem(mHeaderShmem, std::move(aReadHandle), @@ -127,12 +149,15 @@ mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem))); mReaderSemaphore->CloseHandle(); -#if defined(XP_WIN) if (!CheckForFreshCanvasDevice(__LINE__)) { gfxCriticalNote << "GFX: CanvasTranslator failed to get device"; return IPC_OK(); } -#endif + + if (gfx::gfxVars::UseAcceleratedCanvas2D() && !EnsureSharedContextWebgl()) { + gfxCriticalNote + << "GFX: CanvasTranslator failed creating WebGL shared context"; + } if (!aUseIPDLThread) { mTranslationTaskQueue = gfx::CanvasRenderThread::CreateWorkerTaskQueue(); @@ -259,24 +284,29 @@ void CanvasTranslator::GetDataSurface(uint64_t aSurfaceRef) { auto dstSize = surface->GetSize(); auto srcSize = map->GetSurface()->GetSize(); - int32_t dataFormatWidth = dstSize.width * BytesPerPixel(surface->GetFormat()); + gfx::SurfaceFormat format = surface->GetFormat(); + int32_t bpp = BytesPerPixel(format); + int32_t dataFormatWidth = dstSize.width * bpp; int32_t srcStride = map->GetStride(); if (dataFormatWidth > srcStride || srcSize != dstSize) { return; } - auto requiredSize = dataFormatWidth * dstSize.height; + int32_t dstStride = + ImageDataSerializer::ComputeRGBStride(format, dstSize.width); + auto requiredSize = + ImageDataSerializer::ComputeRGBBufferSize(dstSize, format); if (requiredSize <= 0 || size_t(requiredSize) > mDataSurfaceShmem->Size()) { return; } - char* dst = static_cast(mDataSurfaceShmem->memory()); - const char* src = reinterpret_cast(map->GetData()); - const char* endSrc = src + (srcSize.height * srcStride); + uint8_t* dst = static_cast(mDataSurfaceShmem->memory()); + const uint8_t* src = map->GetData(); + const uint8_t* endSrc = src + (srcSize.height * srcStride); while (src < endSrc) { memcpy(dst, src, dataFormatWidth); src += srcStride; - dst += dataFormatWidth; + dst += dstStride; } } @@ -308,6 +338,9 @@ void CanvasTranslator::ActorDestroy(ActorDestroyReason why) { void CanvasTranslator::FinishShutdown() { MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread()); + + ClearTextureInfo(); + gfx::CanvasManagerParent::RemoveReplayTextures(this); } @@ -316,7 +349,8 @@ bool CanvasTranslator::CheckDeactivated() { return true; } - if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled())) { + if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled() && + !gfx::gfxVars::UseAcceleratedCanvas2D())) { Deactivate(); } @@ -337,18 +371,31 @@ void CanvasTranslator::Deactivate() { &CanvasTranslator::SendDeactivate)); // Unlock all of our textures. - for (auto const& entry : mTextureDatas) { - entry.second->Unlock(); + for (auto const& entry : mTextureInfo) { + if (entry.second.mTextureData) { + entry.second.mTextureData->Unlock(); + } } // Disable remote canvas for all. gfx::CanvasManagerParent::DisableRemoteCanvas(); } +void CanvasTranslator::BlockCanvas() { + if (mDeactivated || mBlocked) { + return; + } + mBlocked = true; + gfx::CanvasRenderThread::Dispatch( + NewRunnableMethod("CanvasTranslator::SendBlockCanvas", this, + &CanvasTranslator::SendBlockCanvas)); +} + void CanvasTranslator::CheckAndSignalWriter() { do { switch (mHeader->writerState) { case State::Processing: + case State::Failed: return; case State::AboutToWait: // The writer is making a decision about whether to wait. So, we must @@ -432,6 +479,15 @@ bool CanvasTranslator::ReadNextEvent(EventType& aEventType) { void CanvasTranslator::TranslateRecording() { MOZ_ASSERT(IsInTaskQueue()); + if (mSharedContext && EnsureSharedContextWebgl()) { + mSharedContext->EnterTlsScope(); + } + auto exitTlsScope = MakeScopeExit([&] { + if (mSharedContext) { + mSharedContext->ExitTlsScope(); + } + }); + mHeader->readerState = State::Processing; EventType eventType; while (ReadNextEvent(eventType)) { @@ -531,25 +587,32 @@ bool CanvasTranslator::CreateReferenceTexture() { mReferenceTextureData->Unlock(); } - mReferenceTextureData.reset(CreateTextureData( - mTextureType, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8)); + mReferenceTextureData.reset(CreateTextureData(mTextureType, mBackendType, + gfx::IntSize(1, 1), + gfx::SurfaceFormat::B8G8R8A8)); if (!mReferenceTextureData) { return false; } mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE); mBaseDT = mReferenceTextureData->BorrowDrawTarget(); + if (!mBaseDT) { // We might get a null draw target due to a device failure, just return // false so that we can recover. return false; } - mBackendType = mBaseDT->GetBackendType(); return true; } bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { + // If not on D3D11, we are not dependent on a fresh device for DT creation if + // one already exists. + if (mBaseDT && mTextureType != TextureType::D3D11) { + return false; + } + #if defined(XP_WIN) // If a new device has already been created, use that one. RefPtr device = gfx::DeviceManagerDx::Get()->GetCanvasDevice(); @@ -589,11 +652,9 @@ bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { Deactivate(); return false; } +#endif return CreateReferenceTexture(); -#else - return false; -#endif } void CanvasTranslator::NotifyDeviceChanged() { @@ -603,29 +664,118 @@ void CanvasTranslator::NotifyDeviceChanged() { &CanvasTranslator::SendNotifyDeviceChanged)); } +gfx::DrawTargetWebgl* CanvasTranslator::GetDrawTargetWebgl( + int64_t aTextureId) const { + auto result = mTextureInfo.find(aTextureId); + if (result != mTextureInfo.end() && result->second.mDrawTarget && + result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) { + return static_cast(result->second.mDrawTarget.get()); + } + return nullptr; +} + +void CanvasTranslator::NotifyRequiresRefresh(int64_t aTextureId, + bool aDispatch) { + if (aDispatch) { + DispatchToTaskQueue(NewRunnableMethod( + "CanvasTranslator::NotifyRequiresRefresh", this, + &CanvasTranslator::NotifyRequiresRefresh, aTextureId, false)); + return; + } + + if (mTextureInfo.find(aTextureId) != mTextureInfo.end()) { + Unused << SendNotifyRequiresRefresh(aTextureId); + } +} + +void CanvasTranslator::CacheSnapshotShmem(int64_t aTextureId, bool aDispatch) { + if (aDispatch) { + DispatchToTaskQueue(NewRunnableMethod( + "CanvasTranslator::CacheSnapshotShmem", this, + &CanvasTranslator::CacheSnapshotShmem, aTextureId, false)); + return; + } + + if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId)) { + if (Maybe shmem = webgl->GetShmem()) { + // Lock the DT so that it doesn't get removed while shmem is in transit. + mTextureInfo[aTextureId].mLocked++; + nsCOMPtr thread = + gfx::CanvasRenderThread::GetCanvasRenderThread(); + RefPtr translator = this; + SendSnapshotShmem(aTextureId, std::move(*shmem)) + ->Then( + thread, __func__, + [=](bool) { translator->RemoveTexture(aTextureId); }, + [=](ipc::ResponseRejectReason) { + translator->RemoveTexture(aTextureId); + }); + } + } +} + +void CanvasTranslator::PrepareShmem(int64_t aTextureId) { + if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId)) { + webgl->PrepareData(); + } +} + already_AddRefed CanvasTranslator::CreateDrawTarget( gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { + MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set"); RefPtr dt; - do { - TextureData* textureData = CreateTextureData(mTextureType, aSize, aFormat); - if (textureData) { - MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set"); - textureData->Lock(OpenMode::OPEN_READ_WRITE); - mTextureDatas[mNextTextureId] = UniquePtr(textureData); - gfx::CanvasManagerParent::AddReplayTexture(this, mNextTextureId, - textureData); - dt = textureData->BorrowDrawTarget(); + if (mNextRemoteTextureOwnerId.IsValid()) { + if (EnsureSharedContextWebgl()) { + mSharedContext->EnterTlsScope(); } - } while (!dt && CheckForFreshCanvasDevice(__LINE__)); + if (RefPtr webgl = gfx::DrawTargetWebgl::Create( + aSize, aFormat, this, mSharedContext)) { + webgl->BeginFrame(gfx::IntRect()); + dt = webgl.forget().downcast(); + if (dt) { + TextureInfo& info = mTextureInfo[mNextTextureId]; + info.mDrawTarget = dt; + info.mRemoteTextureOwnerId = mNextRemoteTextureOwnerId; + CacheSnapshotShmem(mNextTextureId); + } + } + if (!dt) { + NotifyRequiresRefresh(mNextTextureId); + } + } + + if (!dt) { + do { + TextureData* textureData = + CreateTextureData(mTextureType, mBackendType, aSize, aFormat); + if (textureData) { + TextureInfo& info = mTextureInfo[mNextTextureId]; + info.mTextureData = UniquePtr(textureData); + info.mRemoteTextureOwnerId = mNextRemoteTextureOwnerId; + if (textureData->Lock(OpenMode::OPEN_READ_WRITE)) { + dt = textureData->BorrowDrawTarget(); + } + } + } while (!dt && CheckForFreshCanvasDevice(__LINE__)); + } + AddDrawTarget(aRefPtr, dt); mNextTextureId = -1; + mNextRemoteTextureOwnerId = RemoteTextureOwnerId(); return dt.forget(); } void CanvasTranslator::RemoveTexture(int64_t aTextureId) { - mTextureDatas.erase(aTextureId); + { + // Don't erase the texture if still in use + auto result = mTextureInfo.find(aTextureId); + if (result == mTextureInfo.end() || --result->second.mLocked > 0) { + return; + } + mTextureInfo.erase(result); + } // It is possible that the texture from the content process has never been // forwarded from the GPU process, so make sure its descriptor is removed. @@ -633,11 +783,112 @@ void CanvasTranslator::RemoveTexture(int64_t aTextureId) { } TextureData* CanvasTranslator::LookupTextureData(int64_t aTextureId) { - TextureMap::const_iterator result = mTextureDatas.find(aTextureId); - if (result == mTextureDatas.end()) { + auto result = mTextureInfo.find(aTextureId); + if (result == mTextureInfo.end()) { return nullptr; } - return result->second.get(); + return result->second.mTextureData.get(); +} + +bool CanvasTranslator::LockTexture(int64_t aTextureId, OpenMode aMode, + RemoteTextureId aId) { + auto result = mTextureInfo.find(aTextureId); + if (result == mTextureInfo.end()) { + return false; + } + if (result->second.mDrawTarget && + result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) { + gfx::DrawTargetWebgl* webgl = + static_cast(result->second.mDrawTarget.get()); + webgl->BeginFrame(webgl->GetRect()); + } else if (TextureData* data = result->second.mTextureData.get()) { + if (!data->Lock(aMode)) { + return false; + } + } + return true; +} + +bool CanvasTranslator::UnlockTexture(int64_t aTextureId, RemoteTextureId aId) { + auto result = mTextureInfo.find(aTextureId); + if (result == mTextureInfo.end()) { + return false; + } + RemoteTextureOwnerId ownerId = result->second.mRemoteTextureOwnerId; + if (result->second.mDrawTarget && + result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) { + gfx::DrawTargetWebgl* webgl = + static_cast(result->second.mDrawTarget.get()); + webgl->EndFrame(); + webgl->CopyToSwapChain(aId, ownerId, mOtherPid); + if (!result->second.mNotifiedRequiresRefresh && webgl->RequiresRefresh()) { + result->second.mNotifiedRequiresRefresh = true; + NotifyRequiresRefresh(aTextureId); + } + } else if (TextureData* data = result->second.mTextureData.get()) { + if (aId.IsValid()) { + PushRemoteTexture(data, aId, ownerId); + data->Unlock(); + } else { + data->Unlock(); + gfx::CanvasManagerParent::AddReplayTexture(this, aTextureId, data); + } + } + return true; +} + +bool CanvasTranslator::PushRemoteTexture(TextureData* aData, + RemoteTextureId aId, + RemoteTextureOwnerId aOwnerId) { + if (!mRemoteTextureOwner) { + mRemoteTextureOwner = new RemoteTextureOwnerClient(mOtherPid); + } + if (!mRemoteTextureOwner->IsRegistered(aOwnerId)) { + mRemoteTextureOwner->RegisterTextureOwner( + aOwnerId, + /* aIsSyncMode */ gfx::gfxVars::WebglOopAsyncPresentForceSync()); + } + TextureData::Info info; + aData->FillInfo(info); + UniquePtr dstData; + if (mTextureType == TextureType::Unknown) { + dstData = mRemoteTextureOwner->CreateOrRecycleBufferTextureData( + aOwnerId, info.size, info.format); + } else { + dstData.reset( + CreateTextureData(mTextureType, mBackendType, info.size, info.format)); + } + bool success = false; + // Source data is already locked. + if (dstData && dstData->Lock(OpenMode::OPEN_WRITE)) { + if (RefPtr dstDT = dstData->BorrowDrawTarget()) { + if (RefPtr srcDT = aData->BorrowDrawTarget()) { + if (RefPtr snapshot = srcDT->Snapshot()) { + dstDT->CopySurface(snapshot, snapshot->GetRect(), + gfx::IntPoint(0, 0)); + success = true; + } + } + } + dstData->Unlock(); + } + if (success) { + mRemoteTextureOwner->PushTexture(aId, aOwnerId, std::move(dstData)); + } else { + mRemoteTextureOwner->PushDummyTexture(aId, aOwnerId); + } + return success; +} + +void CanvasTranslator::ClearTextureInfo() { + mTextureInfo.clear(); + mDrawTargets.Clear(); + mSharedContext = nullptr; + mBaseDT = nullptr; + if (mRemoteTextureOwner) { + mRemoteTextureOwner->UnregisterAllTextureOwners(); + mRemoteTextureOwner = nullptr; + } } already_AddRefed CanvasTranslator::LookupExternalSurface( diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h index dc4d41363020..0ddab28e8b1f 100644 --- a/gfx/layers/ipc/CanvasTranslator.h +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -17,6 +17,7 @@ #include "mozilla/layers/CanvasDrawEventRecorder.h" #include "mozilla/layers/LayersSurfaces.h" #include "mozilla/layers/PCanvasParent.h" +#include "mozilla/layers/RemoteTextureMap.h" #include "mozilla/ipc/CrossProcessSemaphore.h" #include "mozilla/Monitor.h" #include "mozilla/UniquePtr.h" @@ -26,6 +27,11 @@ namespace mozilla { using EventType = gfx::RecordedEvent::EventType; class TaskQueue; +namespace gfx { +class DrawTargetWebgl; +class SharedContextWebgl; +} // namespace gfx + namespace layers { class SharedSurfacesHolder; @@ -63,6 +69,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, * CanvasEventRingBuffer. * * @param aTextureType the TextureType the translator will create + * @param aBackendType the BackendType for texture data * @param aHeaderHandle handle for the control header * @param aBufferHandles handles for the initial buffers for translation * @param aBufferSize size of buffers and the default size @@ -71,13 +78,11 @@ class CanvasTranslator final : public gfx::InlineTranslator, * @param aUseIPDLThread if true, use the IPDL thread instead of the worker * pool for translation requests */ - ipc::IPCResult RecvInitTranslator(const TextureType& aTextureType, - Handle&& aReadHandle, - nsTArray&& aBufferHandles, - uint64_t aBufferSize, - CrossProcessSemaphoreHandle&& aReaderSem, - CrossProcessSemaphoreHandle&& aWriterSem, - bool aUseIPDLThread); + ipc::IPCResult RecvInitTranslator( + TextureType aTextureType, gfx::BackendType aBackendType, + Handle&& aReadHandle, nsTArray&& aBufferHandles, + uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem, + CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread); /** * Restart the translation from a Stopped state. @@ -134,8 +139,9 @@ class CanvasTranslator final : public gfx::InlineTranslator, * Set the texture ID that will be used as a lookup for the texture created by * the next CreateDrawTarget. */ - void SetNextTextureId(int64_t aNextTextureId) { + void SetNextTextureId(int64_t aNextTextureId, RemoteTextureOwnerId aOwnerId) { mNextTextureId = aNextTextureId; + mNextRemoteTextureOwnerId = aOwnerId; } /** @@ -175,6 +181,12 @@ class CanvasTranslator final : public gfx::InlineTranslator, */ void RemoveTexture(int64_t aTextureId); + bool LockTexture(int64_t aTextureId, OpenMode aMode, RemoteTextureId aId); + bool UnlockTexture(int64_t aTextureId, RemoteTextureId aId); + + bool PushRemoteTexture(TextureData* aData, RemoteTextureId aId, + RemoteTextureOwnerId aOwnerId); + /** * Overriden to remove any DataSourceSurfaces associated with the RefPtr. * @@ -256,6 +268,8 @@ class CanvasTranslator final : public gfx::InlineTranslator, UniquePtr GetPreparedMap( gfx::ReferencePtr aSurface); + void PrepareShmem(int64_t aTextureId); + void RecycleBuffer(); void NextBuffer(); @@ -281,11 +295,14 @@ class CanvasTranslator final : public gfx::InlineTranslator, void Deactivate(); + void BlockCanvas(); + TextureData* CreateTextureData(TextureType aTextureType, + gfx::BackendType aBackendType, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat); - void AddSurfaceDescriptor(int64_t aTextureId, TextureData* atextureData); + void ClearTextureInfo(); bool HandleExtensionEvent(int32_t aType); @@ -293,11 +310,18 @@ class CanvasTranslator final : public gfx::InlineTranslator, bool CheckForFreshCanvasDevice(int aLineNumber); void NotifyDeviceChanged(); + bool EnsureSharedContextWebgl(); + gfx::DrawTargetWebgl* GetDrawTargetWebgl(int64_t aTextureId) const; + void NotifyRequiresRefresh(int64_t aTextureId, bool aDispatch = true); + void CacheSnapshotShmem(int64_t aTextureId, bool aDispatch = true); + RefPtr mTranslationTaskQueue; RefPtr mSharedSurfacesHolder; #if defined(XP_WIN) RefPtr mDevice; #endif + RefPtr mSharedContext; + RefPtr mRemoteTextureOwner; size_t mDefaultBufferSize = 0; uint32_t mMaxSpinCount; @@ -329,13 +353,23 @@ class CanvasTranslator final : public gfx::InlineTranslator, // Sometimes during device reset our reference DrawTarget can be null, so we // hold the BackendType separately. gfx::BackendType mBackendType = gfx::BackendType::NONE; - typedef std::unordered_map> TextureMap; - TextureMap mTextureDatas; + base::ProcessId mOtherPid = base::kInvalidProcessId; + struct TextureInfo { + UniquePtr mTextureData; + RefPtr mDrawTarget; + RemoteTextureOwnerId mRemoteTextureOwnerId; + bool mNotifiedRequiresRefresh = false; + // Ref-count of how active uses of the DT. Avoids deletion when locked. + int32_t mLocked = 1; + }; + std::unordered_map mTextureInfo; int64_t mNextTextureId = -1; + RemoteTextureOwnerId mNextRemoteTextureOwnerId; nsRefPtrHashtable, gfx::DataSourceSurface> mDataSurfaces; gfx::ReferencePtr mMappedSurface; UniquePtr mPreparedMap; Atomic mDeactivated{false}; + Atomic mBlocked{false}; bool mIsInTransaction = false; bool mDeviceResetInProgress = false; }; diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index 116859ac9ef8..71c95e657884 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -517,7 +517,7 @@ PTextureChild* CompositorBridgeChild::CreateTexture( } already_AddRefed CompositorBridgeChild::GetCanvasChild() { - MOZ_ASSERT(gfx::gfxVars::RemoteCanvasEnabled()); + MOZ_ASSERT(gfxPlatform::UseRemoteCanvas()); if (auto* cm = gfx::CanvasManagerChild::Get()) { return cm->GetCanvasChild().forget(); } diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl index dc97d2f2b2c2..45594d811cff 100644 --- a/gfx/layers/ipc/PCanvas.ipdl +++ b/gfx/layers/ipc/PCanvas.ipdl @@ -11,6 +11,7 @@ include "mozilla/layers/CanvasTranslator.h"; [MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h"; using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h"; [MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; +using mozilla::gfx::BackendType from "mozilla/gfx/Types.h"; namespace mozilla { namespace layers { @@ -33,8 +34,9 @@ parent: * aUseIPDLThread if true, use the IPDL thread instead of the worker pool for * translation requests */ - async InitTranslator(TextureType aTextureType, Handle aHeaderHandle, - Handle[] aBufferHandles, uint64_t aBufferSize, + async InitTranslator(TextureType aTextureType, BackendType aBackendType, + Handle aHeaderHandle, Handle[] aBufferHandles, + uint64_t aBufferSize, CrossProcessSemaphoreHandle aReaderSem, CrossProcessSemaphoreHandle aWriterSem, bool aUseIPDLThread); @@ -67,6 +69,22 @@ child: * Deactivate remote canvas, which will cause fall back to software. */ async Deactivate(); + + /** + * Block further accelerated canvases from being created, but allow existing + * canvases to continue processing. + */ + async BlockCanvas(); + + /** + * Notify that a remote accelerated canvas requires a fallback refresh. + */ + async NotifyRequiresRefresh(int64_t aTextureId); + + /** + * Cache the shmem of the framebuffer for snapshotting. + */ + async SnapshotShmem(int64_t aTextureId, Shmem aShmem) returns (bool aSuccess); }; } // layers diff --git a/gfx/layers/wr/WebRenderImageHost.cpp b/gfx/layers/wr/WebRenderImageHost.cpp index 50d6d28d4f77..4add2f7ed708 100644 --- a/gfx/layers/wr/WebRenderImageHost.cpp +++ b/gfx/layers/wr/WebRenderImageHost.cpp @@ -143,6 +143,7 @@ void WebRenderImageHost::PushPendingRemoteTexture( // Clear when RemoteTextureOwner is different. mPendingRemoteTextureWrappers.clear(); mWaitingReadyCallback = false; + mWaitForRemoteTextureOwner = true; } } @@ -222,7 +223,8 @@ void WebRenderImageHost::UseRemoteTexture() { std::function function; RemoteTextureMap::Get()->GetRemoteTextureForDisplayList( - wrapper, std::move(function)); + wrapper, std::move(function), mWaitForRemoteTextureOwner); + mWaitForRemoteTextureOwner = false; } if (!texture || diff --git a/gfx/layers/wr/WebRenderImageHost.h b/gfx/layers/wr/WebRenderImageHost.h index 92a63d9d4590..b41fd550de1e 100644 --- a/gfx/layers/wr/WebRenderImageHost.h +++ b/gfx/layers/wr/WebRenderImageHost.h @@ -88,6 +88,7 @@ class WebRenderImageHost : public CompositableHost, public ImageComposite { std::deque mPendingRemoteTextureWrappers; bool mWaitingReadyCallback = false; + bool mWaitForRemoteTextureOwner = true; Maybe mRemoteTextureOwnerIdOfPushCallback; base::ProcessId mForPidOfPushCallback; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index ee2d26b858c5..12b2b364a8af 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -1189,7 +1189,8 @@ bool gfxPlatform::IsHeadless() { /* static */ bool gfxPlatform::UseRemoteCanvas() { - return XRE_IsContentProcess() && gfx::gfxVars::RemoteCanvasEnabled(); + return XRE_IsContentProcess() && (gfx::gfxVars::RemoteCanvasEnabled() || + gfx::gfxVars::UseAcceleratedCanvas2D()); } /* static */ @@ -3865,13 +3866,18 @@ void gfxPlatform::DisableGPUProcess() { } /* static */ void gfxPlatform::DisableRemoteCanvas() { - if (!gfxVars::RemoteCanvasEnabled()) { - return; + if (gfxVars::RemoteCanvasEnabled()) { + gfxConfig::ForceDisable(Feature::REMOTE_CANVAS, FeatureStatus::Failed, + "Disabled by runtime error", + "FEATURE_REMOTE_CANVAS_RUNTIME_ERROR"_ns); + gfxVars::SetRemoteCanvasEnabled(false); + } + if (gfxVars::UseAcceleratedCanvas2D()) { + gfxConfig::ForceDisable(Feature::ACCELERATED_CANVAS2D, + FeatureStatus::Failed, "Disabled by runtime error", + "FEATURE_ACCELERATED_CANVAS2D_RUNTIME_ERROR"_ns); + gfxVars::SetUseAcceleratedCanvas2D(false); } - gfxConfig::ForceDisable(Feature::REMOTE_CANVAS, FeatureStatus::Failed, - "Disabled by runtime error", - "FEATURE_REMOTE_CANVAS_RUNTIME_ERROR"_ns); - gfxVars::SetRemoteCanvasEnabled(false); } void gfxPlatform::ImportCachedContentDeviceData() { diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list index 9bd8fb8dffa3..cad881eebea1 100644 --- a/layout/reftests/canvas/reftest.list +++ b/layout/reftests/canvas/reftest.list @@ -104,7 +104,7 @@ fuzzy(0-1,0-43) == 1201272-1.html 1201272-1-ref.html == 1303534-1.html 1303534-1-ref.html fuzzy-if(cocoaWidget,0-1,0-1410) == 1304353-text-global-alpha-1.html 1304353-text-global-alpha-1-ref.html -fuzzy-if(cocoaWidget,0-1,0-1302) fuzzy-if(winWidget,0-1,0-578) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html +fuzzy(0-1,0-1302) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html fuzzy-if(winWidget,0-94,0-1575) fuzzy-if(cocoaWidget,0-1,0-34) == 1304353-text-global-composite-op-1.html 1304353-text-global-composite-op-1-ref.html == text-indent-1a.html text-indent-1-ref.html diff --git a/layout/reftests/svg/as-image/reftest.list b/layout/reftests/svg/as-image/reftest.list index 588517e05ee1..0aaa931e7de4 100644 --- a/layout/reftests/svg/as-image/reftest.list +++ b/layout/reftests/svg/as-image/reftest.list @@ -36,8 +36,8 @@ fails-if(useDrawSnapshot) == background-scale-with-viewbox-1.html background-sca == canvas-drawImage-scale-1b.html lime100x100-ref.html == canvas-drawImage-scale-1c.html lime100x100-ref.html -fuzzy(0-1,0-2) fuzzy-if(!remoteCanvas,0-1,0-529) fuzzy-if(remoteCanvas,0-97,0-745) fuzzy-if(Android&&device,0-64,0-1212) == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html -fuzzy(0-1,0-2) fuzzy-if(!remoteCanvas,0-1,0-529) fuzzy-if(remoteCanvas,0-97,0-745) fuzzy-if(Android&&device,0-64,0-1212) == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html +fuzzy(0-192,0-1813) == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html +fuzzy(0-192,0-1813) == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html fuzzy-if(winWidget,0-1,0-10000) fuzzy-if(azureSkia,0-1,0-10000) fuzzy-if(Android,0-1,0-10000) == canvas-drawImage-alpha-1.html canvas-drawImage-alpha-1-ref.html #Same as scale-2a but with globalAlpha: diff --git a/layout/reftests/svg/filters/css-filter-chains/reftest.list b/layout/reftests/svg/filters/css-filter-chains/reftest.list index bb66b5041c2f..f9b9523329d1 100644 --- a/layout/reftests/svg/filters/css-filter-chains/reftest.list +++ b/layout/reftests/svg/filters/css-filter-chains/reftest.list @@ -2,6 +2,6 @@ # e.g. filter: blur(3px) grayscale(0.5) invert(0.2); # Some platforms render this complex filter chain a little differently, and that's ok. -fuzzy(4-6,12000-19950) fuzzy-if(swgl,5-10,13600-20260) fuzzy-if(Android&&device&&!swgl,6-6,19986-19986) == long-chain.html long-chain-ref.html # Win10: Bug 1258241 +fuzzy(4-10,12000-20260) == long-chain.html long-chain-ref.html # Win10: Bug 1258241 == moz-element.html moz-element-ref.html -fuzzy-if(!useDrawSnapshot,13-15,7670-7982) fuzzy-if(!useDrawSnapshot&&swgl,11-12,14052-14056) fuzzy-if(Android&&device&&!swgl,13-13,13505-13505) == same-filter.html same-filter-ref.html +fuzzy(0-15,0-14056) == same-filter.html same-filter-ref.html