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/dom/WorkerPrivate.h"
9 #include "mozilla/dom/WorkerRef.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/Swizzle.h"
12 #include "mozilla/ipc/Endpoint.h"
13 #include "mozilla/layers/CanvasChild.h"
14 #include "mozilla/layers/CompositorManagerChild.h"
15 #include "mozilla/webgpu/WebGPUChild.h"
17 using namespace mozilla::dom
;
18 using namespace mozilla::layers
;
20 namespace mozilla::gfx
{
22 // The IPDL actor holds a strong reference to CanvasManagerChild which we use
23 // to keep it alive. The owning thread will tell us to close when it is
24 // shutdown, either via CanvasManagerChild::Shutdown for the main thread, or
25 // via a shutdown callback from IPCWorkerRef for worker threads.
26 MOZ_THREAD_LOCAL(CanvasManagerChild
*) CanvasManagerChild::sLocalManager
;
28 Atomic
<uint32_t> CanvasManagerChild::sNextId(1);
30 CanvasManagerChild::CanvasManagerChild(uint32_t aId
) : mId(aId
) {}
31 CanvasManagerChild::~CanvasManagerChild() = default;
33 void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason
) {
34 if (sLocalManager
.get() == this) {
35 sLocalManager
.set(nullptr);
39 void CanvasManagerChild::Destroy() {
41 mCanvasChild
->Destroy();
42 mCanvasChild
= nullptr;
45 // The caller has a strong reference. ActorDestroy will clear sLocalManager.
50 /* static */ void CanvasManagerChild::Shutdown() {
51 MOZ_ASSERT(NS_IsMainThread());
53 // The worker threads should destroy their own CanvasManagerChild instances
54 // during their shutdown sequence. We just need to take care of the main
55 // thread. We need to init here because we may have never created a
56 // CanvasManagerChild for the main thread in the first place.
57 if (sLocalManager
.init()) {
58 RefPtr
<CanvasManagerChild
> manager
= sLocalManager
.get();
65 /* static */ bool CanvasManagerChild::CreateParent(
66 ipc::Endpoint
<PCanvasManagerParent
>&& aEndpoint
) {
67 MOZ_ASSERT(NS_IsMainThread());
69 CompositorManagerChild
* manager
= CompositorManagerChild::GetInstance();
70 if (!manager
|| !manager
->CanSend()) {
74 return manager
->SendInitCanvasManager(std::move(aEndpoint
));
77 /* static */ CanvasManagerChild
* CanvasManagerChild::Get() {
78 if (NS_WARN_IF(!sLocalManager
.init())) {
82 CanvasManagerChild
* managerWeak
= sLocalManager
.get();
87 // We are only used on the main thread, or on worker threads.
88 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
89 MOZ_ASSERT_IF(!worker
, NS_IsMainThread());
91 ipc::Endpoint
<PCanvasManagerParent
> parentEndpoint
;
92 ipc::Endpoint
<PCanvasManagerChild
> childEndpoint
;
94 auto compositorPid
= CompositorManagerChild::GetOtherPid();
95 if (NS_WARN_IF(!compositorPid
)) {
99 nsresult rv
= PCanvasManager::CreateEndpoints(
100 compositorPid
, base::GetCurrentProcId(), &parentEndpoint
, &childEndpoint
);
101 if (NS_WARN_IF(NS_FAILED(rv
))) {
105 auto manager
= MakeRefPtr
<CanvasManagerChild
>(sNextId
++);
108 // The IPCWorkerRef will let us know when the worker is shutting down. This
109 // will let us clear our threadlocal reference and close the actor. We rely
110 // upon an explicit shutdown for the main thread.
111 manager
->mWorkerRef
= IPCWorkerRef::Create(
112 worker
, "CanvasManager", [manager
]() { manager
->Destroy(); });
113 if (NS_WARN_IF(!manager
->mWorkerRef
)) {
118 if (NS_WARN_IF(!childEndpoint
.Bind(manager
))) {
122 // We can't talk to the compositor process directly from worker threads, but
123 // the main thread can via CompositorManagerChild.
125 worker
->DispatchToMainThread(NS_NewRunnableFunction(
126 "CanvasManagerChild::CreateParent",
127 [parentEndpoint
= std::move(parentEndpoint
)]() {
129 std::move(const_cast<ipc::Endpoint
<PCanvasManagerParent
>&&>(
132 } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint
)))) {
136 manager
->SendInitialize(manager
->Id());
137 sLocalManager
.set(manager
);
141 void CanvasManagerChild::EndCanvasTransaction() {
146 mCanvasChild
->EndTransaction();
147 if (mCanvasChild
->ShouldBeCleanedUp()) {
148 mCanvasChild
->Destroy();
149 mCanvasChild
= nullptr;
153 void CanvasManagerChild::DeactivateCanvas() {
156 mCanvasChild
->Destroy();
157 mCanvasChild
= nullptr;
161 RefPtr
<layers::CanvasChild
> CanvasManagerChild::GetCanvasChild() {
163 MOZ_ASSERT(!mCanvasChild
);
168 mCanvasChild
= MakeAndAddRef
<layers::CanvasChild
>();
169 if (!SendPCanvasConstructor(mCanvasChild
)) {
170 mCanvasChild
= nullptr;
177 RefPtr
<webgpu::WebGPUChild
> CanvasManagerChild::GetWebGPUChild() {
179 mWebGPUChild
= MakeAndAddRef
<webgpu::WebGPUChild
>();
180 if (!SendPWebGPUConstructor(mWebGPUChild
)) {
181 mWebGPUChild
= nullptr;
188 already_AddRefed
<DataSourceSurface
> CanvasManagerChild::GetSnapshot(
189 uint32_t aManagerId
, int32_t aProtocolId
,
190 const Maybe
<RemoteTextureOwnerId
>& aOwnerId
, SurfaceFormat aFormat
,
191 bool aPremultiply
, bool aYFlip
) {
196 webgl::FrontBufferSnapshotIpc res
;
197 if (!SendGetSnapshot(aManagerId
, aProtocolId
, aOwnerId
, &res
)) {
201 if (!res
.shmem
|| !res
.shmem
->IsReadable()) {
205 auto guard
= MakeScopeExit([&] { DeallocShmem(res
.shmem
.ref()); });
207 if (!res
.surfSize
.x
|| !res
.surfSize
.y
|| res
.surfSize
.x
> INT32_MAX
||
208 res
.surfSize
.y
> INT32_MAX
) {
212 IntSize
size(res
.surfSize
.x
, res
.surfSize
.y
);
213 CheckedInt32 stride
= CheckedInt32(size
.width
) * sizeof(uint32_t);
214 if (!stride
.isValid()) {
218 CheckedInt32 length
= stride
* size
.height
;
219 if (!length
.isValid() ||
220 size_t(length
.value()) != res
.shmem
->Size
<uint8_t>()) {
224 SurfaceFormat format
=
225 IsOpaque(aFormat
) ? SurfaceFormat::B8G8R8X8
: SurfaceFormat::B8G8R8A8
;
226 RefPtr
<DataSourceSurface
> surface
=
227 Factory::CreateDataSourceSurfaceWithStride(size
, format
, stride
.value(),
233 gfx::DataSourceSurface::ScopedMap
map(surface
,
234 gfx::DataSourceSurface::READ_WRITE
);
235 if (!map
.IsMapped()) {
239 // The buffer we may readback from the canvas could be R8G8B8A8, not
240 // premultiplied, and/or has its rows iverted. For the general case, we want
241 // surfaces represented as premultiplied B8G8R8A8, with its rows ordered top
242 // to bottom. Given this path is used for screenshots/SurfaceFromElement,
243 // that's the representation we need.
246 if (!PremultiplyYFlipData(res
.shmem
->get
<uint8_t>(), stride
.value(),
247 aFormat
, map
.GetData(), map
.GetStride(), format
,
252 if (!SwizzleYFlipData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
253 map
.GetData(), map
.GetStride(), format
, size
)) {
257 } else if (aPremultiply
) {
258 if (!PremultiplyData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
259 map
.GetData(), map
.GetStride(), format
, size
)) {
263 if (!SwizzleData(res
.shmem
->get
<uint8_t>(), stride
.value(), aFormat
,
264 map
.GetData(), map
.GetStride(), format
, size
)) {
268 return surface
.forget();
271 } // namespace mozilla::gfx