Bug 1822393 - Consume GeckoView directly in Android Components for CI builds. r=owlis...
[gecko.git] / gfx / ipc / CanvasManagerChild.cpp
blob4e9e2e6acfdc8afb891e66a63b432995f1910a78
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) {
37 DestroyInternal();
38 if (sLocalManager.get() == this) {
39 sLocalManager.set(nullptr);
41 mWorkerRef = nullptr;
44 void CanvasManagerChild::DestroyInternal() {
45 std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas);
46 for (const auto& i : activeCanvas) {
47 i->OnShutdown();
50 if (mActiveResourceTracker) {
51 mActiveResourceTracker->AgeAllGenerations();
52 mActiveResourceTracker.reset();
55 if (mCanvasChild) {
56 mCanvasChild->Destroy();
57 mCanvasChild = nullptr;
61 void CanvasManagerChild::Destroy() {
62 DestroyInternal();
64 // The caller has a strong reference. ActorDestroy will clear sLocalManager
65 // and mWorkerRef.
66 Close();
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();
78 if (manager) {
79 manager->Destroy();
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()) {
90 return false;
93 return manager->SendInitCanvasManager(std::move(aEndpoint));
96 /* static */ CanvasManagerChild* CanvasManagerChild::Get() {
97 if (NS_WARN_IF(!sLocalManager.init())) {
98 return nullptr;
101 CanvasManagerChild* managerWeak = sLocalManager.get();
102 if (managerWeak) {
103 return managerWeak;
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)) {
115 return nullptr;
118 nsresult rv = PCanvasManager::CreateEndpoints(
119 compositorPid, base::GetCurrentProcId(), &parentEndpoint, &childEndpoint);
120 if (NS_WARN_IF(NS_FAILED(rv))) {
121 return nullptr;
124 auto manager = MakeRefPtr<CanvasManagerChild>(sNextId++);
126 if (worker) {
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)) {
133 return nullptr;
136 manager->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
137 } else if (NS_IsMainThread()) {
138 if (NS_WARN_IF(
139 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
140 return nullptr;
142 } else {
143 MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!");
144 return nullptr;
147 if (NS_WARN_IF(!childEndpoint.Bind(manager))) {
148 return nullptr;
151 // We can't talk to the compositor process directly from worker threads, but
152 // the main thread can via CompositorManagerChild.
153 if (worker) {
154 worker->DispatchToMainThread(NS_NewRunnableFunction(
155 "CanvasManagerChild::CreateParent",
156 [parentEndpoint = std::move(parentEndpoint)]() {
157 CreateParent(
158 std::move(const_cast<ipc::Endpoint<PCanvasManagerParent>&&>(
159 parentEndpoint)));
160 }));
161 } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint)))) {
162 return nullptr;
165 manager->SendInitialize(manager->Id());
166 sLocalManager.set(manager);
167 return manager;
170 /* static */ CanvasManagerChild* CanvasManagerChild::MaybeGet() {
171 if (!sLocalManager.initialized()) {
172 return nullptr;
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() {
189 if (!mCanvasChild) {
190 return;
193 mCanvasChild->EndTransaction();
194 if (mCanvasChild->ShouldBeCleanedUp()) {
195 mCanvasChild->Destroy();
196 mCanvasChild = nullptr;
200 void CanvasManagerChild::ClearCachedResources() {
201 if (mCanvasChild) {
202 mCanvasChild->ClearCachedResources();
206 void CanvasManagerChild::DeactivateCanvas() {
207 mActive = false;
208 if (mCanvasChild) {
209 mCanvasChild->Destroy();
210 mCanvasChild = nullptr;
214 void CanvasManagerChild::BlockCanvas() { mBlocked = true; }
216 RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() {
217 if (mBlocked) {
218 return nullptr;
221 if (!mActive) {
222 MOZ_ASSERT(!mCanvasChild);
223 return nullptr;
226 if (!mCanvasChild) {
227 mCanvasChild = MakeAndAddRef<layers::CanvasChild>(mWorkerRef);
228 if (!SendPCanvasConstructor(mCanvasChild)) {
229 mCanvasChild->Destroy();
230 mCanvasChild = nullptr;
234 return mCanvasChild;
237 RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() {
238 if (!mWebGPUChild) {
239 mWebGPUChild = MakeAndAddRef<webgpu::WebGPUChild>();
240 if (!SendPWebGPUConstructor(mWebGPUChild)) {
241 mWebGPUChild = nullptr;
245 return mWebGPUChild;
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) {
260 if (!CanSend()) {
261 return nullptr;
264 webgl::FrontBufferSnapshotIpc res;
265 if (!SendGetSnapshot(aManagerId, aProtocolId, aOwnerId, &res)) {
266 return nullptr;
269 if (!res.shmem || !res.shmem->IsReadable()) {
270 return nullptr;
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) {
277 return nullptr;
280 IntSize size(res.surfSize.x, res.surfSize.y);
281 CheckedInt32 stride = CheckedInt32(size.width) * sizeof(uint32_t);
282 if (!stride.isValid()) {
283 return nullptr;
286 CheckedInt32 length = stride * size.height;
287 if (!length.isValid() ||
288 size_t(length.value()) != res.shmem->Size<uint8_t>()) {
289 return nullptr;
292 SurfaceFormat format =
293 IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
294 RefPtr<DataSourceSurface> surface =
295 Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(),
296 /* aZero */ false);
297 if (!surface) {
298 return nullptr;
301 gfx::DataSourceSurface::ScopedMap map(surface,
302 gfx::DataSourceSurface::READ_WRITE);
303 if (!map.IsMapped()) {
304 return nullptr;
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.
312 if (aYFlip) {
313 if (aPremultiply) {
314 if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(),
315 aFormat, map.GetData(), map.GetStride(), format,
316 size)) {
317 return nullptr;
319 } else {
320 if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
321 map.GetData(), map.GetStride(), format, size)) {
322 return nullptr;
325 } else if (aPremultiply) {
326 if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
327 map.GetData(), map.GetStride(), format, size)) {
328 return nullptr;
330 } else {
331 if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat,
332 map.GetData(), map.GetStride(), format, size)) {
333 return nullptr;
336 return surface.forget();
339 } // namespace mozilla::gfx