CanvasManagerChild.cpp (9440B)
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/. */ 6 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/CanvasShutdownManager.h" 14 #include "mozilla/gfx/Swizzle.h" 15 #include "mozilla/ipc/Endpoint.h" 16 #include "mozilla/layers/ActiveResource.h" 17 #include "mozilla/layers/CanvasChild.h" 18 #include "mozilla/layers/CompositorManagerChild.h" 19 #include "mozilla/webgpu/WebGPUChild.h" 20 21 namespace mozilla::gfx { 22 23 using namespace layers; 24 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; 30 31 Atomic<uint32_t> CanvasManagerChild::sNextId(1); 32 33 CanvasManagerChild::CanvasManagerChild(dom::ThreadSafeWorkerRef* aWorkerRef, 34 uint32_t aId) 35 : mWorkerRef(aWorkerRef), mId(aId) {} 36 37 CanvasManagerChild::~CanvasManagerChild() = default; 38 39 void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) { 40 DestroyInternal(); 41 if (sLocalManager.get() == this) { 42 sLocalManager.set(nullptr); 43 } 44 mWorkerRef = nullptr; 45 } 46 47 void CanvasManagerChild::DestroyInternal() { 48 if (mActiveResourceTracker) { 49 mActiveResourceTracker->AgeAllGenerations(); 50 mActiveResourceTracker.reset(); 51 } 52 53 if (mCanvasChild) { 54 mCanvasChild->Destroy(); 55 mCanvasChild = nullptr; 56 } 57 58 if (auto* shutdownManager = CanvasShutdownManager::Get()) { 59 shutdownManager->OnRemoteCanvasLost(); 60 } 61 } 62 63 void CanvasManagerChild::Destroy() { 64 DestroyInternal(); 65 66 // The caller has a strong reference. ActorDestroy will clear sLocalManager 67 // and mWorkerRef. 68 Close(); 69 } 70 71 /* static */ void CanvasManagerChild::Shutdown() { 72 if (sLocalManager.init()) { 73 RefPtr<CanvasManagerChild> manager = sLocalManager.get(); 74 if (manager) { 75 manager->Destroy(); 76 } 77 } 78 } 79 80 /* static */ bool CanvasManagerChild::CreateParent( 81 ipc::Endpoint<PCanvasManagerParent>&& aEndpoint) { 82 MOZ_ASSERT(NS_IsMainThread()); 83 84 CompositorManagerChild* manager = CompositorManagerChild::GetInstance(); 85 if (!manager || !manager->CanSend()) { 86 return false; 87 } 88 89 return manager->SendInitCanvasManager(std::move(aEndpoint)); 90 } 91 92 /* static */ CanvasManagerChild* CanvasManagerChild::Get() { 93 if (NS_WARN_IF(!sLocalManager.init())) { 94 return nullptr; 95 } 96 97 CanvasManagerChild* managerWeak = sLocalManager.get(); 98 if (managerWeak) { 99 return managerWeak; 100 } 101 102 auto* shutdownManager = CanvasShutdownManager::Get(); 103 if (NS_WARN_IF(!shutdownManager)) { 104 return nullptr; 105 } 106 107 // We are only used on the main thread, or on worker threads. 108 dom::WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate(); 109 MOZ_ASSERT_IF(!worker, NS_IsMainThread()); 110 111 ipc::Endpoint<PCanvasManagerParent> parentEndpoint; 112 ipc::Endpoint<PCanvasManagerChild> childEndpoint; 113 114 ipc::EndpointProcInfo compositorInfo = 115 CompositorManagerChild::GetCompositorProcInfo(); 116 if (NS_WARN_IF(compositorInfo == ipc::EndpointProcInfo::Invalid())) { 117 return nullptr; 118 } 119 120 nsresult rv = PCanvasManager::CreateEndpoints( 121 compositorInfo, ipc::EndpointProcInfo::Current(), &parentEndpoint, 122 &childEndpoint); 123 if (NS_WARN_IF(NS_FAILED(rv))) { 124 return nullptr; 125 } 126 127 auto manager = MakeRefPtr<CanvasManagerChild>(shutdownManager->GetWorkerRef(), 128 sNextId++); 129 if (NS_WARN_IF(!childEndpoint.Bind(manager))) { 130 return nullptr; 131 } 132 133 // We can't talk to the compositor process directly from worker threads, but 134 // the main thread can via CompositorManagerChild. 135 if (worker) { 136 worker->DispatchToMainThread(NS_NewRunnableFunction( 137 "CanvasManagerChild::CreateParent", 138 [parentEndpoint = std::move(parentEndpoint)]() { 139 CreateParent( 140 std::move(const_cast<ipc::Endpoint<PCanvasManagerParent>&&>( 141 parentEndpoint))); 142 })); 143 } else if (NS_WARN_IF(!CreateParent(std::move(parentEndpoint)))) { 144 return nullptr; 145 } 146 147 manager->SendInitialize(manager->Id()); 148 shutdownManager->OnRemoteCanvasRestored(); 149 sLocalManager.set(manager); 150 return manager; 151 } 152 153 /* static */ CanvasManagerChild* CanvasManagerChild::MaybeGet() { 154 if (!sLocalManager.initialized()) { 155 return nullptr; 156 } 157 158 return sLocalManager.get(); 159 } 160 161 void CanvasManagerChild::EndCanvasTransaction() { 162 if (!mCanvasChild) { 163 return; 164 } 165 166 mCanvasChild->EndTransaction(); 167 if (mCanvasChild->ShouldBeCleanedUp()) { 168 mCanvasChild->Destroy(); 169 mCanvasChild = nullptr; 170 } 171 } 172 173 void CanvasManagerChild::ClearCachedResources() { 174 if (mCanvasChild) { 175 mCanvasChild->ClearCachedResources(); 176 } 177 } 178 179 void CanvasManagerChild::DeactivateCanvas() { 180 mActive = false; 181 if (mCanvasChild) { 182 mCanvasChild->Destroy(); 183 mCanvasChild = nullptr; 184 } 185 } 186 187 void CanvasManagerChild::BlockCanvas() { mBlocked = true; } 188 189 RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() { 190 if (mBlocked) { 191 return nullptr; 192 } 193 194 if (!mActive) { 195 MOZ_ASSERT(!mCanvasChild); 196 return nullptr; 197 } 198 199 if (!mCanvasChild) { 200 mCanvasChild = MakeAndAddRef<layers::CanvasChild>(mWorkerRef); 201 if (!SendPCanvasConstructor(mCanvasChild)) { 202 mCanvasChild->Destroy(); 203 mCanvasChild = nullptr; 204 } 205 } 206 207 return mCanvasChild; 208 } 209 210 RefPtr<webgpu::WebGPUChild> CanvasManagerChild::GetWebGPUChild() { 211 if (PWebGPUChild* actor = LoneManagedOrNullAsserts(ManagedPWebGPUChild())) { 212 return static_cast<webgpu::WebGPUChild*>(actor); 213 } 214 215 auto actor = MakeRefPtr<webgpu::WebGPUChild>(); 216 if (!SendPWebGPUConstructor(actor)) { 217 return nullptr; 218 } 219 return actor; 220 } 221 222 layers::ActiveResourceTracker* CanvasManagerChild::GetActiveResourceTracker() { 223 if (!mActiveResourceTracker) { 224 mActiveResourceTracker = MakeUnique<ActiveResourceTracker>( 225 1000, "CanvasManagerChild"_ns, GetCurrentSerialEventTarget()); 226 } 227 return mActiveResourceTracker.get(); 228 } 229 230 already_AddRefed<DataSourceSurface> CanvasManagerChild::GetSnapshot( 231 uint32_t aManagerId, ActorId aProtocolId, 232 const Maybe<RemoteTextureOwnerId>& aOwnerId, 233 const Maybe<RawId>& aCommandEncoderId, const Maybe<RawId>& aCommandBufferId, 234 SurfaceFormat aFormat, bool aPremultiply, bool aYFlip) { 235 if (!CanSend()) { 236 return nullptr; 237 } 238 239 webgl::FrontBufferSnapshotIpc res; 240 if (!SendGetSnapshot(aManagerId, aProtocolId, aOwnerId, aCommandEncoderId, 241 aCommandBufferId, &res)) { 242 return nullptr; 243 } 244 245 if (!res.shmem || !res.shmem->IsReadable()) { 246 return nullptr; 247 } 248 249 auto guard = MakeScopeExit([&] { DeallocShmem(res.shmem.ref()); }); 250 251 if (!res.surfSize.x || !res.surfSize.y || res.surfSize.x > INT32_MAX || 252 res.surfSize.y > INT32_MAX) { 253 return nullptr; 254 } 255 256 IntSize size(res.surfSize.x, res.surfSize.y); 257 CheckedInt32 stride = CheckedInt32(res.byteStride); 258 if (!stride.isValid()) { 259 return nullptr; 260 } 261 262 CheckedInt32 length = stride * size.height; 263 if (!length.isValid() || 264 size_t(length.value()) != res.shmem->Size<uint8_t>()) { 265 return nullptr; 266 } 267 268 SurfaceFormat format = 269 IsOpaque(aFormat) ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8; 270 RefPtr<DataSourceSurface> surface = 271 Factory::CreateDataSourceSurfaceWithStride(size, format, stride.value(), 272 /* aZero */ false); 273 if (!surface) { 274 return nullptr; 275 } 276 277 gfx::DataSourceSurface::ScopedMap map(surface, 278 gfx::DataSourceSurface::READ_WRITE); 279 if (!map.IsMapped()) { 280 return nullptr; 281 } 282 283 // The buffer we may readback from the canvas could be R8G8B8A8, not 284 // premultiplied, and/or has its rows iverted. For the general case, we want 285 // surfaces represented as premultiplied B8G8R8A8, with its rows ordered top 286 // to bottom. Given this path is used for screenshots/SurfaceFromElement, 287 // that's the representation we need. 288 if (aYFlip) { 289 if (aPremultiply) { 290 if (!PremultiplyYFlipData(res.shmem->get<uint8_t>(), stride.value(), 291 aFormat, map.GetData(), map.GetStride(), format, 292 size)) { 293 return nullptr; 294 } 295 } else { 296 if (!SwizzleYFlipData(res.shmem->get<uint8_t>(), stride.value(), aFormat, 297 map.GetData(), map.GetStride(), format, size)) { 298 return nullptr; 299 } 300 } 301 } else if (aPremultiply) { 302 if (!PremultiplyData(res.shmem->get<uint8_t>(), stride.value(), aFormat, 303 map.GetData(), map.GetStride(), format, size)) { 304 return nullptr; 305 } 306 } else { 307 if (!SwizzleData(res.shmem->get<uint8_t>(), stride.value(), aFormat, 308 map.GetData(), map.GetStride(), format, size)) { 309 return nullptr; 310 } 311 } 312 return surface.forget(); 313 } 314 315 } // namespace mozilla::gfx