1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CanvasManagerChild.h"
8 #include "mozilla/AppShutdown.h"
9 #include "mozilla/dom/CanvasRenderingContext2D.h"
10 #include "mozilla/dom/WorkerPrivate.h"
11 #include "mozilla/dom/WorkerRef.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/Swizzle.h"
14 #include "mozilla/ipc/Endpoint.h"
15 #include "mozilla/layers/ActiveResource.h"
16 #include "mozilla/layers/CanvasChild.h"
17 #include "mozilla/layers/CompositorManagerChild.h"
18 #include "mozilla/webgpu/WebGPUChild.h"
20 using namespace mozilla::dom
;
21 using namespace mozilla::layers
;
23 namespace mozilla::gfx
{
25 // The IPDL actor holds a strong reference to CanvasManagerChild which we use
26 // to keep it alive. The owning thread will tell us to close when it is
27 // shutdown, either via CanvasManagerChild::Shutdown for the main thread, or
28 // via a shutdown callback from ThreadSafeWorkerRef for worker threads.
29 MOZ_THREAD_LOCAL(CanvasManagerChild
*) CanvasManagerChild::sLocalManager
;
31 Atomic
<uint32_t> CanvasManagerChild::sNextId(1);
33 CanvasManagerChild::CanvasManagerChild(uint32_t aId
) : mId(aId
) {}
34 CanvasManagerChild::~CanvasManagerChild() = default;
36 void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason
) {
38 if (sLocalManager
.get() == this) {
39 sLocalManager
.set(nullptr);
44 void CanvasManagerChild::DestroyInternal() {
45 std::set
<CanvasRenderingContext2D
*> activeCanvas
= std::move(mActiveCanvas
);
46 for (const auto& i
: activeCanvas
) {
50 if (mActiveResourceTracker
) {
51 mActiveResourceTracker
->AgeAllGenerations();
52 mActiveResourceTracker
.reset();
56 mCanvasChild
->Destroy();
57 mCanvasChild
= nullptr;
61 void CanvasManagerChild::Destroy() {
64 // The caller has a strong reference. ActorDestroy will clear sLocalManager
69 /* static */ void CanvasManagerChild::Shutdown() {
70 MOZ_ASSERT(NS_IsMainThread());
72 // The worker threads should destroy their own CanvasManagerChild instances
73 // during their shutdown sequence. We just need to take care of the main
74 // thread. We need to init here because we may have never created a
75 // CanvasManagerChild for the main thread in the first place.
76 if (sLocalManager
.init()) {
77 RefPtr
<CanvasManagerChild
> manager
= sLocalManager
.get();
84 /* static */ bool CanvasManagerChild::CreateParent(
85 ipc::Endpoint
<PCanvasManagerParent
>&& aEndpoint
) {
86 MOZ_ASSERT(NS_IsMainThread());
88 CompositorManagerChild
* manager
= CompositorManagerChild::GetInstance();
89 if (!manager
|| !manager
->CanSend()) {
93 return manager
->SendInitCanvasManager(std::move(aEndpoint
));
96 /* static */ CanvasManagerChild
* CanvasManagerChild::Get() {
97 if (NS_WARN_IF(!sLocalManager
.init())) {
101 CanvasManagerChild
* managerWeak
= sLocalManager
.get();
106 // We are only used on the main thread, or on worker threads.
107 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
108 MOZ_ASSERT_IF(!worker
, NS_IsMainThread());
110 ipc::Endpoint
<PCanvasManagerParent
> parentEndpoint
;
111 ipc::Endpoint
<PCanvasManagerChild
> childEndpoint
;
113 auto compositorPid
= CompositorManagerChild::GetOtherPid();
114 if (NS_WARN_IF(!compositorPid
)) {
118 nsresult rv
= PCanvasManager::CreateEndpoints(
119 compositorPid
, base::GetCurrentProcId(), &parentEndpoint
, &childEndpoint
);
120 if (NS_WARN_IF(NS_FAILED(rv
))) {
124 auto manager
= MakeRefPtr
<CanvasManagerChild
>(sNextId
++);
127 // The ThreadSafeWorkerRef will let us know when the worker is shutting
128 // down. This will let us clear our threadlocal reference and close the
129 // actor. We rely upon an explicit shutdown for the main thread.
130 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
131 worker
, "CanvasManager", [manager
]() { manager
->Destroy(); });
132 if (NS_WARN_IF(!workerRef
)) {
136 manager
->mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
137 } else if (NS_IsMainThread()) {
139 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed
))) {
143 MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!");
147 if (NS_WARN_IF(!childEndpoint
.Bind(manager
))) {
151 // We can't talk to the compositor process directly from worker threads, but
152 // the main thread can via CompositorManagerChild.
154 worker
->DispatchToMainThread(NS_NewRunnableFunction(
155 "CanvasManagerChild::CreateParent",
156 [parentEndpoint
= std::move(parentEndpoint
)]() {
158 std::move(const_cast<ipc::Endpoint
<PCanvasManagerParent
>&&>(
161 } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint
)))) {
165 manager
->SendInitialize(manager
->Id());
166 sLocalManager
.set(manager
);
170 /* static */ CanvasManagerChild
* CanvasManagerChild::MaybeGet() {
171 if (!sLocalManager
.initialized()) {
175 return sLocalManager
.get();
178 void CanvasManagerChild::AddShutdownObserver(
179 dom::CanvasRenderingContext2D
* aCanvas
) {
180 mActiveCanvas
.insert(aCanvas
);
183 void CanvasManagerChild::RemoveShutdownObserver(
184 dom::CanvasRenderingContext2D
* aCanvas
) {
185 mActiveCanvas
.erase(aCanvas
);
188 void CanvasManagerChild::EndCanvasTransaction() {
193 mCanvasChild
->EndTransaction();
194 if (mCanvasChild
->ShouldBeCleanedUp()) {
195 mCanvasChild
->Destroy();
196 mCanvasChild
= nullptr;
200 void CanvasManagerChild::ClearCachedResources() {
202 mCanvasChild
->ClearCachedResources();
206 void CanvasManagerChild::DeactivateCanvas() {
209 mCanvasChild
->Destroy();
210 mCanvasChild
= nullptr;
214 void CanvasManagerChild::BlockCanvas() { mBlocked
= true; }
216 RefPtr
<layers::CanvasChild
> CanvasManagerChild::GetCanvasChild() {
222 MOZ_ASSERT(!mCanvasChild
);
227 mCanvasChild
= MakeAndAddRef
<layers::CanvasChild
>(mWorkerRef
);
228 if (!SendPCanvasConstructor(mCanvasChild
)) {
229 mCanvasChild
->Destroy();
230 mCanvasChild
= nullptr;
237 RefPtr
<webgpu::WebGPUChild
> CanvasManagerChild::GetWebGPUChild() {
239 mWebGPUChild
= MakeAndAddRef
<webgpu::WebGPUChild
>();
240 if (!SendPWebGPUConstructor(mWebGPUChild
)) {
241 mWebGPUChild
= nullptr;
248 layers::ActiveResourceTracker
* CanvasManagerChild::GetActiveResourceTracker() {
249 if (!mActiveResourceTracker
) {
250 mActiveResourceTracker
= MakeUnique
<ActiveResourceTracker
>(
251 1000, "CanvasManagerChild", GetCurrentSerialEventTarget());
253 return mActiveResourceTracker
.get();
256 already_AddRefed
<DataSourceSurface
> CanvasManagerChild::GetSnapshot(
257 uint32_t aManagerId
, int32_t aProtocolId
,
258 const Maybe
<RemoteTextureOwnerId
>& aOwnerId
, SurfaceFormat aFormat
,
259 bool aPremultiply
, bool aYFlip
) {
264 webgl::FrontBufferSnapshotIpc res
;
265 if (!SendGetSnapshot(aManagerId
, aProtocolId
, aOwnerId
, &res
)) {
269 if (!res
.shmem
|| !res
.shmem
->IsReadable()) {
273 auto guard
= MakeScopeExit([&] { DeallocShmem(res
.shmem
.ref()); });
275 if (!res
.surfSize
.x
|| !res
.surfSize
.y
|| res
.surfSize
.x
> INT32_MAX
||
276 res
.surfSize
.y
> INT32_MAX
) {
280 IntSize
size(res
.surfSize
.x
, res
.surfSize
.y
);
281 CheckedInt32 stride
= CheckedInt32(size
.width
) * sizeof(uint32_t);
282 if (!stride
.isValid()) {
286 CheckedInt32 length
= stride
* size
.height
;
287 if (!length
.isValid() ||
288 size_t(length
.value()) != res
.shmem
->Size
<uint8_t>()) {
292 SurfaceFormat format
=
293 IsOpaque(aFormat
) ? SurfaceFormat::B8G8R8X8
: SurfaceFormat::B8G8R8A8
;
294 RefPtr
<DataSourceSurface
> surface
=
295 Factory::CreateDataSourceSurfaceWithStride(size
, format
, stride
.value(),
301 gfx::DataSourceSurface::ScopedMap
map(surface
,
302 gfx::DataSourceSurface::READ_WRITE
);
303 if (!map
.IsMapped()) {
307 // The buffer we may readback from the canvas could be R8G8B8A8, not
308 // premultiplied, and/or has its rows iverted. For the general case, we want
309 // surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
310 // to bottom. Given this path is used for screenshots/SurfaceFromElement,
311 // that's the representation we need.
314 if (!PremultiplyYFlipData(res
.shmem
->get
<uint8_t>(), stride
.value(),
315 aFormat
, map
.GetData(), map
.GetStride(), format
,
320 if (!SwizzleYFlipData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
321 map
.GetData(), map
.GetStride(), format
, size
)) {
325 } else if (aPremultiply
) {
326 if (!PremultiplyData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
327 map
.GetData(), map
.GetStride(), format
, size
)) {
331 if (!SwizzleData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
332 map
.GetData(), map
.GetStride(), format
, size
)) {
336 return surface
.forget();
339 } // namespace mozilla::gfx