From 40a355a42d4a9444dc753c04c6608dade2f06a23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:27 +0200 Subject: Adding upstream version 125.0.1. Signed-off-by: Daniel Baumann --- dom/canvas/OffscreenCanvasDisplayHelper.cpp | 164 ++++++++++++++++++---------- 1 file changed, 107 insertions(+), 57 deletions(-) (limited to 'dom/canvas/OffscreenCanvasDisplayHelper.cpp') diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.cpp b/dom/canvas/OffscreenCanvasDisplayHelper.cpp index b1e2ec1694..9e1cbb3e75 100644 --- a/dom/canvas/OffscreenCanvasDisplayHelper.cpp +++ b/dom/canvas/OffscreenCanvasDisplayHelper.cpp @@ -16,6 +16,7 @@ #include "mozilla/layers/PersistentBufferProvider.h" #include "mozilla/layers/TextureClientSharedSurface.h" #include "mozilla/layers/TextureWrapperImage.h" +#include "mozilla/StaticPrefs_gfx.h" #include "mozilla/SVGObserverUtils.h" #include "nsICanvasRenderingContextInternal.h" #include "nsRFPService.h" @@ -37,11 +38,25 @@ void OffscreenCanvasDisplayHelper::DestroyElement() { MOZ_ASSERT(NS_IsMainThread()); MutexAutoLock lock(mMutex); + if (mImageContainer) { + mImageContainer->ClearAllImages(); + mImageContainer = nullptr; + } + mFrontBufferSurface = nullptr; mCanvasElement = nullptr; } void OffscreenCanvasDisplayHelper::DestroyCanvas() { + if (auto* cm = gfx::CanvasManagerChild::Get()) { + cm->EndCanvasTransaction(); + } + MutexAutoLock lock(mMutex); + if (mImageContainer) { + mImageContainer->ClearAllImages(); + mImageContainer = nullptr; + } + mFrontBufferSurface = nullptr; mOffscreenCanvas = nullptr; mWorkerRef = nullptr; } @@ -140,6 +155,12 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor( nsICanvasRenderingContextInternal* aContext, layers::TextureType aTextureType, const Maybe& aData) { + auto endTransaction = MakeScopeExit([&]() { + if (auto* cm = gfx::CanvasManagerChild::Get()) { + cm->EndCanvasTransaction(); + } + }); + MutexAutoLock lock(mMutex); gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8; @@ -393,81 +414,110 @@ already_AddRefed OffscreenCanvasDisplayHelper::GetSurfaceSnapshot() { MOZ_ASSERT(NS_IsMainThread()); + class SnapshotWorkerRunnable final : public MainThreadWorkerRunnable { + public: + SnapshotWorkerRunnable(WorkerPrivate* aWorkerPrivate, + OffscreenCanvasDisplayHelper* aDisplayHelper) + : MainThreadWorkerRunnable(aWorkerPrivate, "SnapshotWorkerRunnable"), + mMonitor("SnapshotWorkerRunnable::mMonitor"), + mDisplayHelper(aDisplayHelper) {} + + bool WorkerRun(JSContext*, WorkerPrivate*) override { + // The OffscreenCanvas can only be freed on the worker thread, so we + // cannot be racing with an OffscreenCanvas::DestroyCanvas call and its + // destructor. We just need to make sure we don't call into + // OffscreenCanvas while holding the lock since it calls back into the + // OffscreenCanvasDisplayHelper. + RefPtr canvas; + { + MutexAutoLock lock(mDisplayHelper->mMutex); + canvas = mDisplayHelper->mOffscreenCanvas; + } + + // Now that we are on the correct thread, we can extract the snapshot. If + // it is a Skia surface, perform a copy to threading issues. + RefPtr surface; + if (canvas) { + if (auto* context = canvas->GetContext()) { + surface = + context->GetFrontBufferSnapshot(/* requireAlphaPremult */ false); + if (surface && surface->GetType() == gfx::SurfaceType::SKIA) { + surface = gfx::Factory::CopyDataSourceSurface( + static_cast(surface.get())); + } + } + } + + MonitorAutoLock lock(mMonitor); + mSurface = std::move(surface); + mComplete = true; + lock.NotifyAll(); + return true; + } + + already_AddRefed Wait(int32_t aTimeoutMs) { + MonitorAutoLock lock(mMonitor); + + TimeDuration timeout = TimeDuration::FromMilliseconds(aTimeoutMs); + while (!mComplete) { + if (lock.Wait(timeout) == CVStatus::Timeout) { + return nullptr; + } + } + + return mSurface.forget(); + } + + private: + Monitor mMonitor; + RefPtr mDisplayHelper; + RefPtr mSurface MOZ_GUARDED_BY(mMonitor); + bool mComplete MOZ_GUARDED_BY(mMonitor) = false; + }; + bool hasAlpha; bool isAlphaPremult; gl::OriginPos originPos; - Maybe managerId; - Maybe childId; HTMLCanvasElement* canvasElement; RefPtr surface; - Maybe ownerId; + RefPtr workerRunnable; { MutexAutoLock lock(mMutex); +#ifdef MOZ_WIDGET_ANDROID + // On Android, we cannot both display a GL context and read back the pixels. + if (mCanvasElement) { + return nullptr; + } +#endif + hasAlpha = !mData.mIsOpaque; isAlphaPremult = mData.mIsAlphaPremult; originPos = mData.mOriginPos; - ownerId = mData.mOwnerId; - managerId = mContextManagerId; - childId = mContextChildId; canvasElement = mCanvasElement; - surface = mFrontBufferSurface; - } - - if (surface) { - // We already have a copy of the front buffer in our process. - return TransformSurface(surface, hasAlpha, isAlphaPremult, originPos); - } - -#ifdef MOZ_WIDGET_ANDROID - // On Android, we cannot both display a GL context and read back the pixels. - if (canvasElement) { - return nullptr; - } -#endif - - if (managerId && childId) { - // We don't have a usable surface, and the context lives in the compositor - // process. - return gfx::CanvasManagerChild::Get()->GetSnapshot( - managerId.value(), childId.value(), ownerId, - hasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8, - hasAlpha && !isAlphaPremult, originPos == gl::OriginPos::BottomLeft); - } - - if (!canvasElement) { - return nullptr; - } - - // If we don't have any protocol IDs, or an existing surface, it is possible - // it is a main thread OffscreenCanvas instance. If so, then the element's - // OffscreenCanvas is not neutered and has access to the context. We can use - // that to get the snapshot directly. - const auto* offscreenCanvas = canvasElement->GetOffscreenCanvas(); - if (nsICanvasRenderingContextInternal* context = - offscreenCanvas->GetContext()) { - surface = context->GetFrontBufferSnapshot(/* requireAlphaPremult */ false); - surface = TransformSurface(surface, hasAlpha, isAlphaPremult, originPos); - if (surface) { - return surface.forget(); + if (mWorkerRef) { + workerRunnable = + MakeRefPtr(mWorkerRef->Private(), this); + workerRunnable->Dispatch(); } } - // Finally, we can try peeking into the image container to see if we are able - // to do a readback via the TextureClient. - if (layers::ImageContainer* container = canvasElement->GetImageContainer()) { - AutoTArray images; - uint32_t generationCounter; - container->GetCurrentImages(&images, &generationCounter); - if (!images.IsEmpty()) { - if (layers::Image* image = images.LastElement().mImage) { - surface = image->GetAsSourceSurface(); - return TransformSurface(surface, hasAlpha, isAlphaPremult, originPos); - } + if (workerRunnable) { + // We transferred to a DOM worker, so we need to do the readback on the + // owning thread and wait for the result. + surface = workerRunnable->Wait( + StaticPrefs::gfx_offscreencanvas_snapshot_timeout_ms()); + } else if (canvasElement) { + // If we have a context, it is owned by the main thread. + const auto* offscreenCanvas = canvasElement->GetOffscreenCanvas(); + if (nsICanvasRenderingContextInternal* context = + offscreenCanvas->GetContext()) { + surface = + context->GetFrontBufferSnapshot(/* requireAlphaPremult */ false); } } - return nullptr; + return TransformSurface(surface, hasAlpha, isAlphaPremult, originPos); } already_AddRefed OffscreenCanvasDisplayHelper::GetAsImage() { -- cgit v1.2.3