CanvasTranslator.cpp (62903B)
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 https://mozilla.org/MPL/2.0/. */ 6 7 #include "CanvasTranslator.h" 8 9 #include "gfxGradientCache.h" 10 #include "mozilla/DataMutex.h" 11 #include "mozilla/gfx/2D.h" 12 #include "mozilla/gfx/CanvasManagerParent.h" 13 #include "mozilla/gfx/CanvasRenderThread.h" 14 #include "mozilla/gfx/DataSourceSurfaceWrapper.h" 15 #include "mozilla/gfx/DrawTargetWebgl.h" 16 #include "mozilla/gfx/gfxVars.h" 17 #include "mozilla/gfx/GPUParent.h" 18 #include "mozilla/gfx/GPUProcessManager.h" 19 #include "mozilla/gfx/Logging.h" 20 #include "mozilla/gfx/Swizzle.h" 21 #include "mozilla/ipc/Endpoint.h" 22 #include "mozilla/ipc/SharedMemoryHandle.h" 23 #include "mozilla/layers/BufferTexture.h" 24 #include "mozilla/layers/CanvasTranslator.h" 25 #include "mozilla/layers/ImageDataSerializer.h" 26 #include "mozilla/layers/SharedSurfacesParent.h" 27 #include "mozilla/layers/TextureClient.h" 28 #include "mozilla/layers/VideoBridgeParent.h" 29 #include "mozilla/StaticPrefs_gfx.h" 30 #include "mozilla/SyncRunnable.h" 31 #include "mozilla/TaskQueue.h" 32 #include "GLContext.h" 33 #include "HostWebGLContext.h" 34 #include "SharedSurface.h" 35 #include "WebGLParent.h" 36 #include "RecordedCanvasEventImpl.h" 37 38 #if defined(XP_WIN) 39 # include "mozilla/gfx/DeviceManagerDx.h" 40 # include "mozilla/layers/TextureD3D11.h" 41 #endif 42 43 namespace mozilla { 44 namespace layers { 45 46 UniquePtr<TextureData> CanvasTranslator::CreateTextureData( 47 const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, bool aClear) { 48 TextureData* textureData = nullptr; 49 TextureAllocationFlags allocFlags = 50 aClear ? ALLOC_CLEAR_BUFFER : ALLOC_DEFAULT; 51 switch (mTextureType) { 52 case TextureType::Unknown: 53 textureData = BufferTextureData::Create( 54 aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR, 55 TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE, 56 allocFlags, nullptr); 57 break; 58 default: 59 textureData = TextureData::Create(mTextureType, aFormat, aSize, 60 allocFlags, mBackendType); 61 break; 62 } 63 64 return WrapUnique(textureData); 65 } 66 67 CanvasTranslator::CanvasTranslator( 68 layers::SharedSurfacesHolder* aSharedSurfacesHolder, 69 const dom::ContentParentId& aContentId, uint32_t aManagerId) 70 : mSharedSurfacesHolder(aSharedSurfacesHolder), 71 mMaxSpinCount(StaticPrefs::gfx_canvas_remote_max_spin_count()), 72 mContentId(aContentId), 73 mManagerId(aManagerId), 74 mCanvasTranslatorEventsLock( 75 "CanvasTranslator::mCanvasTranslatorEventsLock") { 76 mNextEventTimeout = TimeDuration::FromMilliseconds( 77 StaticPrefs::gfx_canvas_remote_event_timeout_ms()); 78 } 79 80 CanvasTranslator::~CanvasTranslator() = default; 81 82 void CanvasTranslator::DispatchToTaskQueue( 83 already_AddRefed<nsIRunnable> aRunnable) { 84 gfx::CanvasRenderThread::Dispatch(std::move(aRunnable)); 85 } 86 87 bool CanvasTranslator::IsInTaskQueue() const { 88 return gfx::CanvasRenderThread::IsInCanvasRenderThread(); 89 } 90 91 StaticRefPtr<gfx::SharedContextWebgl> CanvasTranslator::sSharedContext; 92 93 bool CanvasTranslator::EnsureSharedContextWebgl() { 94 if (!mSharedContext || mSharedContext->IsContextLost()) { 95 if (mSharedContext) { 96 ForceDrawTargetWebglFallback(); 97 if (mRemoteTextureOwner) { 98 // Ensure any shared surfaces referring to the old context go away. 99 mRemoteTextureOwner->ClearRecycledTextures(); 100 } 101 } 102 // Check if the global shared context is still valid. If not, instantiate 103 // a new one before we try to use it. 104 if (!sSharedContext || sSharedContext->IsContextLost()) { 105 sSharedContext = gfx::SharedContextWebgl::Create(); 106 } 107 mSharedContext = sSharedContext; 108 // If we can't get a new context, then the only thing left to do is block 109 // new canvases. 110 if (!mSharedContext || mSharedContext->IsContextLost()) { 111 mSharedContext = nullptr; 112 BlockCanvas(); 113 return false; 114 } 115 } 116 return true; 117 } 118 119 void CanvasTranslator::Shutdown() { 120 if (sSharedContext) { 121 gfx::CanvasRenderThread::Dispatch(NS_NewRunnableFunction( 122 "CanvasTranslator::Shutdown", []() { sSharedContext = nullptr; })); 123 } 124 } 125 126 mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( 127 TextureType aTextureType, TextureType aWebglTextureType, 128 gfx::BackendType aBackendType, ipc::MutableSharedMemoryHandle&& aReadHandle, 129 nsTArray<ipc::ReadOnlySharedMemoryHandle>&& aBufferHandles, 130 CrossProcessSemaphoreHandle&& aReaderSem, 131 CrossProcessSemaphoreHandle&& aWriterSem) { 132 if (mHeaderShmem) { 133 return IPC_FAIL(this, "RecvInitTranslator called twice."); 134 } 135 136 mTextureType = aTextureType; 137 mWebglTextureType = aWebglTextureType; 138 mBackendType = aBackendType; 139 mOtherPid = OtherPid(); 140 141 mHeaderShmem = aReadHandle.Map(); 142 if (!mHeaderShmem) { 143 Deactivate(); 144 return IPC_FAIL(this, "Failed to map canvas header shared memory."); 145 } 146 147 mHeader = mHeaderShmem.DataAs<Header>(); 148 149 mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem))); 150 mWriterSemaphore->CloseHandle(); 151 152 mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem))); 153 mReaderSemaphore->CloseHandle(); 154 155 if (!CreateReferenceTexture()) { 156 gfxCriticalNote << "GFX: CanvasTranslator failed to get device"; 157 Deactivate(); 158 return IPC_OK(); 159 } 160 161 if (gfx::gfxVars::UseAcceleratedCanvas2D() && !EnsureSharedContextWebgl()) { 162 gfxCriticalNote 163 << "GFX: CanvasTranslator failed creating WebGL shared context"; 164 } 165 166 // Use the first buffer as our current buffer. 167 if (aBufferHandles.IsEmpty()) { 168 Deactivate(); 169 return IPC_FAIL(this, "No canvas buffer shared memory supplied."); 170 } 171 mDefaultBufferSize = aBufferHandles[0].Size(); 172 auto handleIter = aBufferHandles.begin(); 173 mCurrentShmem.shmem = std::move(*handleIter).Map(); 174 if (!mCurrentShmem.shmem) { 175 Deactivate(); 176 return IPC_FAIL(this, "Failed to map canvas buffer shared memory."); 177 } 178 mCurrentMemReader = mCurrentShmem.CreateMemReader(); 179 180 // Add all other buffers to our recycled CanvasShmems. 181 for (handleIter++; handleIter < aBufferHandles.end(); handleIter++) { 182 CanvasShmem newShmem; 183 newShmem.shmem = std::move(*handleIter).Map(); 184 if (!newShmem.shmem) { 185 Deactivate(); 186 return IPC_FAIL(this, "Failed to map canvas buffer shared memory."); 187 } 188 mCanvasShmems.emplace(std::move(newShmem)); 189 } 190 191 if (UsePendingCanvasTranslatorEvents()) { 192 MutexAutoLock lock(mCanvasTranslatorEventsLock); 193 mPendingCanvasTranslatorEvents.push_back( 194 CanvasTranslatorEvent::TranslateRecording()); 195 PostCanvasTranslatorEvents(lock); 196 } else { 197 DispatchToTaskQueue( 198 NewRunnableMethod("CanvasTranslator::TranslateRecording", this, 199 &CanvasTranslator::TranslateRecording)); 200 } 201 return IPC_OK(); 202 } 203 204 ipc::IPCResult CanvasTranslator::RecvRestartTranslation() { 205 if (mDeactivated) { 206 // The other side might have sent a message before we deactivated. 207 return IPC_OK(); 208 } 209 210 if (UsePendingCanvasTranslatorEvents()) { 211 MutexAutoLock lock(mCanvasTranslatorEventsLock); 212 mPendingCanvasTranslatorEvents.push_back( 213 CanvasTranslatorEvent::TranslateRecording()); 214 PostCanvasTranslatorEvents(lock); 215 } else { 216 DispatchToTaskQueue( 217 NewRunnableMethod("CanvasTranslator::TranslateRecording", this, 218 &CanvasTranslator::TranslateRecording)); 219 } 220 221 return IPC_OK(); 222 } 223 224 ipc::IPCResult CanvasTranslator::RecvAddBuffer( 225 ipc::ReadOnlySharedMemoryHandle&& aBufferHandle) { 226 if (mDeactivated) { 227 // The other side might have sent a resume message before we deactivated. 228 return IPC_OK(); 229 } 230 231 if (UsePendingCanvasTranslatorEvents()) { 232 MutexAutoLock lock(mCanvasTranslatorEventsLock); 233 mPendingCanvasTranslatorEvents.push_back( 234 CanvasTranslatorEvent::AddBuffer(std::move(aBufferHandle))); 235 PostCanvasTranslatorEvents(lock); 236 } else { 237 DispatchToTaskQueue(NewRunnableMethod<ipc::ReadOnlySharedMemoryHandle&&>( 238 "CanvasTranslator::AddBuffer", this, &CanvasTranslator::AddBuffer, 239 std::move(aBufferHandle))); 240 } 241 242 return IPC_OK(); 243 } 244 245 bool CanvasTranslator::AddBuffer( 246 ipc::ReadOnlySharedMemoryHandle&& aBufferHandle) { 247 MOZ_ASSERT(IsInTaskQueue()); 248 if (mHeader->readerState == State::Failed) { 249 // We failed before we got to the pause event. 250 return false; 251 } 252 253 if (mHeader->readerState != State::Paused) { 254 gfxCriticalNote << "CanvasTranslator::AddBuffer bad state " 255 << uint32_t(State(mHeader->readerState)); 256 #ifndef FUZZING_SNAPSHOT 257 MOZ_DIAGNOSTIC_CRASH("mHeader->readerState == State::Paused"); 258 #endif 259 Deactivate(); 260 return false; 261 } 262 263 MOZ_ASSERT(mDefaultBufferSize != 0); 264 265 // Check and signal the writer when we finish with a buffer, because it 266 // might have hit the buffer count limit and be waiting to use our old one. 267 CheckAndSignalWriter(); 268 269 // Default sized buffers will have been queued for recycling. 270 if (mCurrentShmem.IsValid() && mCurrentShmem.Size() == mDefaultBufferSize) { 271 mCanvasShmems.emplace(std::move(mCurrentShmem)); 272 } 273 274 CanvasShmem newShmem; 275 newShmem.shmem = aBufferHandle.Map(); 276 if (!newShmem.shmem) { 277 return false; 278 } 279 280 mCurrentShmem = std::move(newShmem); 281 mCurrentMemReader = mCurrentShmem.CreateMemReader(); 282 283 return TranslateRecording(); 284 } 285 286 ipc::IPCResult CanvasTranslator::RecvSetDataSurfaceBuffer( 287 uint32_t aId, ipc::MutableSharedMemoryHandle&& aBufferHandle) { 288 if (mDeactivated) { 289 // The other side might have sent a resume message before we deactivated. 290 return IPC_OK(); 291 } 292 293 if (UsePendingCanvasTranslatorEvents()) { 294 MutexAutoLock lock(mCanvasTranslatorEventsLock); 295 mPendingCanvasTranslatorEvents.push_back( 296 CanvasTranslatorEvent::SetDataSurfaceBuffer(aId, 297 std::move(aBufferHandle))); 298 PostCanvasTranslatorEvents(lock); 299 } else { 300 DispatchToTaskQueue( 301 NewRunnableMethod<uint32_t, ipc::MutableSharedMemoryHandle&&>( 302 "CanvasTranslator::SetDataSurfaceBuffer", this, 303 &CanvasTranslator::SetDataSurfaceBuffer, aId, 304 std::move(aBufferHandle))); 305 } 306 307 return IPC_OK(); 308 } 309 310 void CanvasTranslator::UnlinkDataSurfaceShmemOwner( 311 const RefPtr<gfx::DataSourceSurface>& aSurface) { 312 if (!aSurface) { 313 return; 314 } 315 // Remove the associated id key 316 aSurface->RemoveUserData(&mDataSurfaceShmemIdKey); 317 // Force copy-on-write of contained shmem if applicable 318 gfx::DataSourceSurface::ScopedMap map( 319 aSurface, gfx::DataSourceSurface::MapType::READ_WRITE); 320 } 321 322 void CanvasTranslator::DataSurfaceBufferWillChange(uint32_t aId, 323 bool aKeepAlive, 324 size_t aLimit) { 325 if (aId) { 326 // If a specific id is changing, then attempt to copy it. 327 auto it = mDataSurfaceShmems.find(aId); 328 if (it != mDataSurfaceShmems.end()) { 329 RefPtr<gfx::DataSourceSurface> owner(it->second.mOwner); 330 if (owner) { 331 UnlinkDataSurfaceShmemOwner(owner); 332 it->second.mOwner = nullptr; 333 } 334 // Erase the shmem if it's not the last used id. 335 if (!aKeepAlive || aId != mLastDataSurfaceShmemId) { 336 mDataSurfaceShmems.erase(it); 337 } 338 } 339 } else { 340 // If no limit is requested, clear everything. 341 if (!aLimit) { 342 aLimit = mDataSurfaceShmems.size(); 343 } 344 // Ensure all shmems still alive are copied. 345 DataSurfaceShmem lastShmem; 346 auto it = mDataSurfaceShmems.begin(); 347 for (; aLimit > 0 && it != mDataSurfaceShmems.end(); ++it, --aLimit) { 348 RefPtr<gfx::DataSourceSurface> owner(it->second.mOwner); 349 if (owner) { 350 UnlinkDataSurfaceShmemOwner(owner); 351 it->second.mOwner = nullptr; 352 } 353 // If the last shmem id needs to be kept alive, move it before clearing 354 // the hashtable. 355 if (aKeepAlive && it->first == mLastDataSurfaceShmemId) { 356 lastShmem = std::move(it->second); 357 } 358 } 359 // Clear all iterated shmem mappings. 360 if (it == mDataSurfaceShmems.end()) { 361 mDataSurfaceShmems.clear(); 362 } else if (it != mDataSurfaceShmems.begin()) { 363 mDataSurfaceShmems.erase(mDataSurfaceShmems.begin(), it); 364 } 365 // Restore the last shmem id if necessary. 366 if (lastShmem.mShmem.IsValid()) { 367 mDataSurfaceShmems[mLastDataSurfaceShmemId] = std::move(lastShmem); 368 } 369 } 370 } 371 372 bool CanvasTranslator::SetDataSurfaceBuffer( 373 uint32_t aId, ipc::MutableSharedMemoryHandle&& aBufferHandle) { 374 MOZ_ASSERT(IsInTaskQueue()); 375 if (mHeader->readerState == State::Failed) { 376 // We failed before we got to the pause event. 377 return false; 378 } 379 380 if (mHeader->readerState != State::Paused) { 381 gfxCriticalNote << "CanvasTranslator::SetDataSurfaceBuffer bad state " 382 << uint32_t(State(mHeader->readerState)); 383 #ifndef FUZZING_SNAPSHOT 384 MOZ_DIAGNOSTIC_CRASH("mHeader->readerState == State::Paused"); 385 #endif 386 Deactivate(); 387 return false; 388 } 389 390 if (!aId) { 391 return false; 392 } 393 394 if (aId < mLastDataSurfaceShmemId) { 395 // The id space has overflowed, so clear out all existing mapping. 396 DataSurfaceBufferWillChange(0, false); 397 } else if (mLastDataSurfaceShmemId != aId) { 398 // The last shmem id will be kept alive, even if there is no owner. The last 399 // shmem id is about to change, so if the current last id has no owner, it 400 // needs to be erased. 401 auto it = mDataSurfaceShmems.find(mLastDataSurfaceShmemId); 402 if (it != mDataSurfaceShmems.end() && it->second.mOwner.IsDead()) { 403 mDataSurfaceShmems.erase(it); 404 } 405 // Ensure the current shmems don't exceed the maximum allowed. 406 size_t maxShmems = StaticPrefs::gfx_canvas_accelerated_max_data_shmems(); 407 if (maxShmems > 0 && mDataSurfaceShmems.size() >= maxShmems) { 408 DataSurfaceBufferWillChange(0, false, 409 (mDataSurfaceShmems.size() - maxShmems) + 1); 410 } 411 } 412 mLastDataSurfaceShmemId = aId; 413 414 // If the id is being reused, then ensure the old contents is copied before 415 // the buffer is changed. 416 DataSurfaceBufferWillChange(aId); 417 418 // Finally, change the shmem mapping. 419 { 420 auto& dataSurfaceShmem = mDataSurfaceShmems[aId]; 421 dataSurfaceShmem.mShmem = aBufferHandle.Map(); 422 if (!dataSurfaceShmem.mShmem) { 423 // Try clearing out old mappings to see if resource limits were reached. 424 DataSurfaceBufferWillChange(0, false); 425 // Try mapping one last time. 426 dataSurfaceShmem.mShmem = aBufferHandle.Map(); 427 if (!dataSurfaceShmem.mShmem) { 428 return false; 429 } 430 } 431 } 432 433 return TranslateRecording(); 434 } 435 436 void CanvasTranslator::GetDataSurface(uint32_t aId, uint64_t aSurfaceRef) { 437 MOZ_ASSERT(IsInTaskQueue()); 438 439 ReferencePtr surfaceRef = reinterpret_cast<void*>(aSurfaceRef); 440 RefPtr<gfx::DataSourceSurface> dataSurface = LookupDataSurface(surfaceRef); 441 if (!dataSurface) { 442 gfx::SourceSurface* surface = LookupSourceSurface(surfaceRef); 443 if (!surface) { 444 return; 445 } 446 dataSurface = surface->GetDataSurface(); 447 if (!dataSurface) { 448 return; 449 } 450 } 451 auto dstSize = dataSurface->GetSize(); 452 gfx::SurfaceFormat format = dataSurface->GetFormat(); 453 int32_t dstStride = 454 ImageDataSerializer::ComputeRGBStride(format, dstSize.width); 455 auto requiredSize = 456 ImageDataSerializer::ComputeRGBBufferSize(dstSize, format); 457 if (requiredSize <= 0) { 458 return; 459 } 460 461 // Ensure any old references to the shmem are copied before modification. 462 DataSurfaceBufferWillChange(aId); 463 464 // Ensure a shmem exists with the given id. 465 auto it = mDataSurfaceShmems.find(aId); 466 if (it == mDataSurfaceShmems.end()) { 467 return; 468 } 469 470 // Try directly reading the data surface into shmem to avoid further copies. 471 if (size_t(requiredSize) > it->second.mShmem.Size()) { 472 return; 473 } 474 475 uint8_t* dst = it->second.mShmem.DataAs<uint8_t>(); 476 if (dataSurface->ReadDataInto(dst, dstStride)) { 477 // If reading directly into the shmem, then mark the data surface as the 478 // shmem's owner. 479 it->second.mOwner = dataSurface; 480 dataSurface->AddUserData(&mDataSurfaceShmemIdKey, 481 reinterpret_cast<void*>(aId), nullptr); 482 return; 483 } 484 485 // Otherwise, map the data surface and do an explicit copy. 486 gfx::DataSourceSurface::ScopedMap map(dataSurface, 487 gfx::DataSourceSurface::MapType::READ); 488 if (!map.IsMapped()) { 489 return; 490 } 491 492 gfx::SwizzleData(map.GetData(), map.GetStride(), format, dst, dstStride, 493 format, dstSize); 494 } 495 496 already_AddRefed<gfx::SourceSurface> CanvasTranslator::WaitForSurface( 497 uintptr_t aId, Maybe<layers::SurfaceDescriptor>* aDesc) { 498 // If it's not safe to flush the event queue, then don't try to wait. 499 if (!gfx::gfxVars::UseAcceleratedCanvas2D() || 500 !UsePendingCanvasTranslatorEvents() || !IsInTaskQueue()) { 501 return nullptr; 502 } 503 ReferencePtr idRef(aId); 504 ExportSurface* surf = LookupExportSurface(idRef); 505 if (!surf || !surf->mData) { 506 if (!HasPendingEvent()) { 507 return nullptr; 508 } 509 510 // If the surface doesn't exist yet, that may be because the events 511 // that produce it still need to be processed. Flush out any events 512 // currently in the queue, that by now should have been placed in 513 // the queue but for which processing has not yet occurred.. 514 mFlushCheckpoint = mHeader->eventCount; 515 HandleCanvasTranslatorEvents(); 516 mFlushCheckpoint = 0; 517 // If there is still no surface, then it is unlikely to be produced 518 // now, so give up. 519 surf = LookupExportSurface(idRef); 520 if (!surf || !surf->mData) { 521 return nullptr; 522 } 523 } 524 // If we need to export a surface descriptor, then ensure we can export 525 // to an accelerated type from the WebGL context. 526 if (aDesc && mWebglTextureType != TextureType::Unknown && mSharedContext && 527 !mSharedContext->IsContextLost()) { 528 surf->mSharedSurface = 529 mSharedContext->ExportSharedSurface(mWebglTextureType, surf->mData); 530 if (surf->mSharedSurface) { 531 surf->mSharedSurface->BeginRead(); 532 *aDesc = surf->mSharedSurface->ToSurfaceDescriptor(); 533 surf->mSharedSurface->EndRead(); 534 } 535 } 536 return do_AddRef(surf->mData); 537 } 538 539 void CanvasTranslator::RemoveExportSurface(gfx::ReferencePtr aRefPtr) { 540 auto it = mExportSurfaces.find(aRefPtr); 541 if (it != mExportSurfaces.end()) { 542 mExportSurfaces.erase(it); 543 } 544 } 545 546 void CanvasTranslator::RecycleBuffer() { 547 if (!mCurrentShmem.IsValid()) { 548 return; 549 } 550 551 mCanvasShmems.emplace(std::move(mCurrentShmem)); 552 NextBuffer(); 553 } 554 555 void CanvasTranslator::NextBuffer() { 556 if (mCanvasShmems.empty()) { 557 return; 558 } 559 560 // Check and signal the writer when we finish with a buffer, because it 561 // might have hit the buffer count limit and be waiting to use our old one. 562 CheckAndSignalWriter(); 563 564 mCurrentShmem = std::move(mCanvasShmems.front()); 565 mCanvasShmems.pop(); 566 mCurrentMemReader = mCurrentShmem.CreateMemReader(); 567 } 568 569 void CanvasTranslator::ActorDestroy(ActorDestroyReason why) { 570 MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread()); 571 572 // Since we might need to access the actor status off the owning IPDL thread, 573 // we need to cache it here. 574 mIPDLClosed = true; 575 576 { 577 MutexAutoLock lock(mCanvasTranslatorEventsLock); 578 mPendingCanvasTranslatorEvents.clear(); 579 } 580 581 DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::ClearTextureInfo", 582 this, 583 &CanvasTranslator::ClearTextureInfo)); 584 } 585 586 void CanvasTranslator::Deactivate() { 587 if (mDeactivated) { 588 return; 589 } 590 mDeactivated = true; 591 if (mHeader) { 592 mHeader->readerState = State::Failed; 593 } 594 595 // We need to tell the other side to deactivate. Make sure the stream is 596 // marked as bad so that the writing side won't wait for space to write. 597 gfx::CanvasRenderThread::Dispatch( 598 NewRunnableMethod("CanvasTranslator::SendDeactivate", this, 599 &CanvasTranslator::SendDeactivate)); 600 601 // Disable remote canvas for all. 602 gfx::CanvasManagerParent::DisableRemoteCanvas(); 603 } 604 605 inline gfx::DrawTargetWebgl* CanvasTranslator::TextureInfo::GetDrawTargetWebgl( 606 bool aCheckForFallback) const { 607 if ((!mTextureData || !aCheckForFallback) && mDrawTarget && 608 mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) { 609 return static_cast<gfx::DrawTargetWebgl*>(mDrawTarget.get()); 610 } 611 return nullptr; 612 } 613 614 bool CanvasTranslator::TryDrawTargetWebglFallback( 615 const RemoteTextureOwnerId aTextureOwnerId, gfx::DrawTargetWebgl* aWebgl) { 616 NotifyRequiresRefresh(aTextureOwnerId); 617 618 const auto& info = mTextureInfo[aTextureOwnerId]; 619 if (info.mTextureData) { 620 return true; 621 } 622 if (RefPtr<gfx::DrawTarget> dt = 623 CreateFallbackDrawTarget(info.mRefPtr, aTextureOwnerId, 624 aWebgl->GetSize(), aWebgl->GetFormat())) { 625 bool success = aWebgl->CopyToFallback(dt); 626 if (info.mRefPtr) { 627 AddDrawTarget(info.mRefPtr, dt); 628 } 629 return success; 630 } 631 return false; 632 } 633 634 void CanvasTranslator::ForceDrawTargetWebglFallback() { 635 // This looks for any DrawTargetWebgls that have a cached data snapshot that 636 // can be used to recover a fallback TextureData in the event of a context 637 // loss. 638 RemoteTextureOwnerIdSet lost; 639 for (const auto& entry : mTextureInfo) { 640 const auto& ownerId = entry.first; 641 const auto& info = entry.second; 642 if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) { 643 if (!TryDrawTargetWebglFallback(entry.first, webgl)) { 644 // No fallback could be created, so we need to notify the compositor the 645 // texture won't be pushed. 646 if (mRemoteTextureOwner && mRemoteTextureOwner->IsRegistered(ownerId)) { 647 lost.insert(ownerId); 648 } 649 } 650 } 651 } 652 if (!lost.empty()) { 653 NotifyDeviceReset(lost); 654 } 655 } 656 657 void CanvasTranslator::BlockCanvas() { 658 if (mDeactivated || mBlocked) { 659 return; 660 } 661 mBlocked = true; 662 gfx::CanvasRenderThread::Dispatch( 663 NewRunnableMethod("CanvasTranslator::SendBlockCanvas", this, 664 &CanvasTranslator::SendBlockCanvas)); 665 } 666 667 void CanvasTranslator::CheckAndSignalWriter() { 668 do { 669 switch (mHeader->writerState) { 670 case State::Processing: 671 case State::Failed: 672 return; 673 case State::AboutToWait: 674 // The writer is making a decision about whether to wait. So, we must 675 // wait until it has decided to avoid races. Check if the writer is 676 // closed to avoid hangs. 677 if (mIPDLClosed) { 678 return; 679 } 680 continue; 681 case State::Waiting: 682 if (mHeader->processedCount >= mHeader->writerWaitCount) { 683 mHeader->writerState = State::Processing; 684 mWriterSemaphore->Signal(); 685 } 686 return; 687 default: 688 MOZ_ASSERT_UNREACHABLE("Invalid waiting state."); 689 return; 690 } 691 } while (true); 692 } 693 694 bool CanvasTranslator::HasPendingEvent() { 695 return mHeader->processedCount < mHeader->eventCount; 696 } 697 698 bool CanvasTranslator::ReadPendingEvent(EventType& aEventType) { 699 ReadElementConstrained(mCurrentMemReader, aEventType, 700 EventType::DRAWTARGETCREATION, LAST_CANVAS_EVENT_TYPE); 701 if (!mCurrentMemReader.good()) { 702 mHeader->readerState = State::Failed; 703 return false; 704 } 705 706 return true; 707 } 708 709 bool CanvasTranslator::ReadNextEvent(EventType& aEventType) { 710 MOZ_DIAGNOSTIC_ASSERT(mHeader->readerState == State::Processing); 711 712 uint32_t spinCount = mMaxSpinCount; 713 do { 714 if (HasPendingEvent()) { 715 return ReadPendingEvent(aEventType); 716 } 717 } while (--spinCount != 0); 718 719 Flush(); 720 mHeader->readerState = State::AboutToWait; 721 if (HasPendingEvent()) { 722 mHeader->readerState = State::Processing; 723 return ReadPendingEvent(aEventType); 724 } 725 726 if (!mIsInTransaction) { 727 mHeader->readerState = State::Stopped; 728 return false; 729 } 730 731 // When in a transaction we wait for a short time because we're expecting more 732 // events from the content process. We don't want to wait for too long in case 733 // other content processes are waiting for events to process. 734 mHeader->readerState = State::Waiting; 735 736 if (mReaderSemaphore->Wait(Some(mNextEventTimeout))) { 737 MOZ_RELEASE_ASSERT(HasPendingEvent()); 738 MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing); 739 return ReadPendingEvent(aEventType); 740 } 741 742 // We have to use compareExchange here because the writer can change our 743 // state if we are waiting. 744 if (!mHeader->readerState.compareExchange(State::Waiting, State::Stopped)) { 745 MOZ_RELEASE_ASSERT(HasPendingEvent()); 746 MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing); 747 // The writer has just signaled us, so consume it before returning 748 MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait()); 749 return ReadPendingEvent(aEventType); 750 } 751 752 return false; 753 } 754 755 bool CanvasTranslator::TranslateRecording() { 756 MOZ_ASSERT(IsInTaskQueue()); 757 MOZ_DIAGNOSTIC_ASSERT_IF(mFlushCheckpoint, HasPendingEvent()); 758 759 if (mHeader->readerState == State::Failed) { 760 return false; 761 } 762 763 if (mSharedContext && EnsureSharedContextWebgl()) { 764 mSharedContext->EnterTlsScope(); 765 } 766 auto exitTlsScope = MakeScopeExit([&] { 767 if (mSharedContext) { 768 mSharedContext->ExitTlsScope(); 769 } 770 }); 771 772 auto start = TimeStamp::Now(); 773 mHeader->readerState = State::Processing; 774 EventType eventType = EventType::INVALID; 775 while (ReadNextEvent(eventType)) { 776 bool success = RecordedEvent::DoWithEventFromReader( 777 mCurrentMemReader, eventType, 778 [&](RecordedEvent* recordedEvent) -> bool { 779 // Make sure that the whole event was read from the stream. 780 if (!mCurrentMemReader.good()) { 781 if (mIPDLClosed) { 782 // The other side has closed only warn about read failure. 783 gfxWarning() << "Failed to read event type: " 784 << recordedEvent->GetType(); 785 } else { 786 gfxCriticalNote << "Failed to read event type: " 787 << recordedEvent->GetType(); 788 } 789 return false; 790 } 791 792 return recordedEvent->PlayEvent(this); 793 }); 794 795 // Check the stream is good here or we will log the issue twice. 796 if (!mCurrentMemReader.good()) { 797 mHeader->readerState = State::Failed; 798 return false; 799 } 800 801 if (!success && !HandleExtensionEvent(eventType)) { 802 gfxCriticalNote << "Failed to play canvas event type: " << eventType; 803 804 if (!mCurrentMemReader.good()) { 805 mHeader->readerState = State::Failed; 806 return false; 807 } 808 } 809 810 mHeader->processedCount++; 811 812 if (mHeader->readerState == State::Paused || PauseUntilSync()) { 813 // We're waiting for an IPDL message return false, because we will resume 814 // translation after it is received. 815 Flush(); 816 return false; 817 } 818 819 if (mFlushCheckpoint) { 820 // If we processed past the checkpoint return true to ensure translation 821 // after the checkpoint resumes later. 822 if (mHeader->processedCount >= mFlushCheckpoint) { 823 return true; 824 } 825 } else { 826 if (UsePendingCanvasTranslatorEvents()) { 827 const auto maxDurationMs = 100; 828 const auto now = TimeStamp::Now(); 829 const auto waitDurationMs = 830 static_cast<uint32_t>((now - start).ToMilliseconds()); 831 if (waitDurationMs > maxDurationMs) { 832 return true; 833 } 834 } 835 } 836 } 837 838 return false; 839 } 840 841 bool CanvasTranslator::UsePendingCanvasTranslatorEvents() { 842 return StaticPrefs::gfx_canvas_remote_use_canvas_translator_event_AtStartup(); 843 } 844 845 void CanvasTranslator::PostCanvasTranslatorEvents( 846 const MutexAutoLock& aProofOfLock) { 847 if (mIPDLClosed) { 848 return; 849 } 850 851 // Runnable has already been triggered. 852 if (mCanvasTranslatorEventsRunnable) { 853 return; 854 } 855 856 RefPtr<nsIRunnable> runnable = 857 NewRunnableMethod("CanvasTranslator::HandleCanvasTranslatorEvents", this, 858 &CanvasTranslator::HandleCanvasTranslatorEvents); 859 mCanvasTranslatorEventsRunnable = runnable; 860 861 // Runnable has not been triggered yet. 862 DispatchToTaskQueue(runnable.forget()); 863 } 864 865 void CanvasTranslator::HandleCanvasTranslatorEvents() { 866 MOZ_ASSERT(IsInTaskQueue()); 867 868 UniquePtr<CanvasTranslatorEvent> event; 869 { 870 MutexAutoLock lock(mCanvasTranslatorEventsLock); 871 MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty()); 872 if (mPendingCanvasTranslatorEvents.empty() || PauseUntilSync()) { 873 mCanvasTranslatorEventsRunnable = nullptr; 874 return; 875 } 876 auto& front = mPendingCanvasTranslatorEvents.front(); 877 event = std::move(front); 878 mPendingCanvasTranslatorEvents.pop_front(); 879 } 880 881 MOZ_RELEASE_ASSERT(event.get()); 882 883 bool dispatchTranslate = false; 884 while (!dispatchTranslate && event) { 885 switch (event->mTag) { 886 case CanvasTranslatorEvent::Tag::TranslateRecording: 887 dispatchTranslate = TranslateRecording(); 888 break; 889 case CanvasTranslatorEvent::Tag::AddBuffer: 890 dispatchTranslate = AddBuffer(event->TakeBufferHandle()); 891 break; 892 case CanvasTranslatorEvent::Tag::SetDataSurfaceBuffer: 893 dispatchTranslate = SetDataSurfaceBuffer( 894 event->mId, event->TakeDataSurfaceBufferHandle()); 895 break; 896 case CanvasTranslatorEvent::Tag::ClearCachedResources: 897 ClearCachedResources(); 898 break; 899 case CanvasTranslatorEvent::Tag::DropFreeBuffersWhenDormant: 900 DropFreeBuffersWhenDormant(); 901 break; 902 } 903 904 event.reset(nullptr); 905 906 { 907 MutexAutoLock lock(mCanvasTranslatorEventsLock); 908 MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty()); 909 if (mIPDLClosed) { 910 return; 911 } 912 if (PauseUntilSync()) { 913 mCanvasTranslatorEventsRunnable = nullptr; 914 mPendingCanvasTranslatorEvents.push_front( 915 CanvasTranslatorEvent::TranslateRecording()); 916 return; 917 } 918 if (!mIPDLClosed && !dispatchTranslate && 919 !mPendingCanvasTranslatorEvents.empty()) { 920 auto& front = mPendingCanvasTranslatorEvents.front(); 921 event = std::move(front); 922 mPendingCanvasTranslatorEvents.pop_front(); 923 } 924 } 925 } 926 927 MOZ_ASSERT(!event); 928 929 { 930 MutexAutoLock lock(mCanvasTranslatorEventsLock); 931 mCanvasTranslatorEventsRunnable = nullptr; 932 933 MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty()); 934 if (mIPDLClosed) { 935 return; 936 } 937 938 if (dispatchTranslate) { 939 // Handle TranslateRecording at first in next 940 // HandleCanvasTranslatorEvents(). 941 mPendingCanvasTranslatorEvents.push_front( 942 CanvasTranslatorEvent::TranslateRecording()); 943 } 944 945 if (!mPendingCanvasTranslatorEvents.empty()) { 946 PostCanvasTranslatorEvents(lock); 947 } 948 } 949 } 950 951 #define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \ 952 case _typeenum: { \ 953 auto e = _class(mCurrentMemReader); \ 954 if (!mCurrentMemReader.good()) { \ 955 if (mIPDLClosed) { \ 956 /* The other side has closed only warn about read failure. */ \ 957 gfxWarning() << "Failed to read event type: " << _typeenum; \ 958 } else { \ 959 gfxCriticalNote << "Failed to read event type: " << _typeenum; \ 960 } \ 961 return false; \ 962 } \ 963 return e.PlayCanvasEvent(this); \ 964 } 965 966 bool CanvasTranslator::HandleExtensionEvent(int32_t aType) { 967 // This is where we handle extensions to the Moz2D Recording events to handle 968 // canvas specific things. 969 switch (aType) { 970 FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE) 971 default: 972 return false; 973 } 974 } 975 976 void CanvasTranslator::BeginTransaction() { 977 PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {}, 978 "CanvasTranslator::BeginTransaction"_ns); 979 mIsInTransaction = true; 980 } 981 982 void CanvasTranslator::Flush() {} 983 984 void CanvasTranslator::EndTransaction() { 985 Flush(); 986 mIsInTransaction = false; 987 } 988 989 void CanvasTranslator::DeviceResetAcknowledged() { 990 if (mRemoteTextureOwner) { 991 mRemoteTextureOwner->NotifyContextRestored(); 992 } 993 } 994 995 bool CanvasTranslator::CreateReferenceTexture() { 996 if (mReferenceTextureData) { 997 if (mBaseDT) { 998 mReferenceTextureData->ReturnDrawTarget(mBaseDT.forget()); 999 } 1000 mReferenceTextureData->Unlock(); 1001 } 1002 1003 mReferenceTextureData = 1004 CreateTextureData(gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, true); 1005 if (!mReferenceTextureData) { 1006 return false; 1007 } 1008 1009 if (NS_WARN_IF(!mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE))) { 1010 gfxCriticalNote << "CanvasTranslator::CreateReferenceTexture lock failed"; 1011 mReferenceTextureData.reset(); 1012 return false; 1013 } 1014 1015 mBaseDT = mReferenceTextureData->BorrowDrawTarget(); 1016 if (!mBaseDT) { 1017 return false; 1018 } 1019 1020 return true; 1021 } 1022 1023 void CanvasTranslator::NotifyDeviceReset(const RemoteTextureOwnerIdSet& aIds) { 1024 if (aIds.empty()) { 1025 return; 1026 } 1027 if (mRemoteTextureOwner) { 1028 mRemoteTextureOwner->NotifyContextLost(&aIds); 1029 } 1030 nsTArray<RemoteTextureOwnerId> idArray(aIds.size()); 1031 for (const auto& id : aIds) { 1032 idArray.AppendElement(id); 1033 } 1034 gfx::CanvasRenderThread::Dispatch( 1035 NewRunnableMethod<nsTArray<RemoteTextureOwnerId>&&>( 1036 "CanvasTranslator::SendNotifyDeviceReset", this, 1037 &CanvasTranslator::SendNotifyDeviceReset, std::move(idArray))); 1038 } 1039 1040 gfx::DrawTargetWebgl* CanvasTranslator::GetDrawTargetWebgl( 1041 const RemoteTextureOwnerId aTextureOwnerId, bool aCheckForFallback) const { 1042 auto result = mTextureInfo.find(aTextureOwnerId); 1043 if (result != mTextureInfo.end()) { 1044 return result->second.GetDrawTargetWebgl(aCheckForFallback); 1045 } 1046 return nullptr; 1047 } 1048 1049 void CanvasTranslator::NotifyRequiresRefresh( 1050 const RemoteTextureOwnerId aTextureOwnerId, bool aDispatch) { 1051 if (aDispatch) { 1052 auto& info = mTextureInfo[aTextureOwnerId]; 1053 if (!info.mNotifiedRequiresRefresh) { 1054 info.mNotifiedRequiresRefresh = true; 1055 DispatchToTaskQueue(NewRunnableMethod<RemoteTextureOwnerId, bool>( 1056 "CanvasTranslator::NotifyRequiresRefresh", this, 1057 &CanvasTranslator::NotifyRequiresRefresh, aTextureOwnerId, false)); 1058 } 1059 return; 1060 } 1061 1062 if (mTextureInfo.find(aTextureOwnerId) != mTextureInfo.end()) { 1063 (void)SendNotifyRequiresRefresh(aTextureOwnerId); 1064 } 1065 } 1066 1067 void CanvasTranslator::CacheSnapshotShmem( 1068 const RemoteTextureOwnerId aTextureOwnerId, bool aDispatch) { 1069 if (aDispatch) { 1070 DispatchToTaskQueue(NewRunnableMethod<RemoteTextureOwnerId, bool>( 1071 "CanvasTranslator::CacheSnapshotShmem", this, 1072 &CanvasTranslator::CacheSnapshotShmem, aTextureOwnerId, false)); 1073 return; 1074 } 1075 1076 if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureOwnerId)) { 1077 if (auto shmemHandle = webgl->TakeShmemHandle()) { 1078 // Lock the DT so that it doesn't get removed while shmem is in transit. 1079 AddTextureKeepAlive(aTextureOwnerId); 1080 nsCOMPtr<nsIThread> thread = 1081 gfx::CanvasRenderThread::GetCanvasRenderThread(); 1082 RefPtr<CanvasTranslator> translator = this; 1083 SendSnapshotShmem(aTextureOwnerId, std::move(shmemHandle)) 1084 ->Then( 1085 thread, __func__, 1086 [=](bool) { 1087 translator->RemoveTextureKeepAlive(aTextureOwnerId); 1088 }, 1089 [=](ipc::ResponseRejectReason) { 1090 translator->RemoveTextureKeepAlive(aTextureOwnerId); 1091 }); 1092 } 1093 } 1094 } 1095 1096 void CanvasTranslator::PrepareShmem( 1097 const RemoteTextureOwnerId aTextureOwnerId) { 1098 if (gfx::DrawTargetWebgl* webgl = 1099 GetDrawTargetWebgl(aTextureOwnerId, false)) { 1100 if (RefPtr<gfx::DrawTarget> dt = 1101 mTextureInfo[aTextureOwnerId].mFallbackDrawTarget) { 1102 // If there was a fallback, copy the fallback to the software framebuffer 1103 // shmem for reading. 1104 if (RefPtr<gfx::SourceSurface> snapshot = dt->Snapshot()) { 1105 webgl->CopySurface(snapshot, snapshot->GetRect(), gfx::IntPoint(0, 0)); 1106 } 1107 } else { 1108 // Otherwise, just ensure the software framebuffer is up to date. 1109 webgl->PrepareShmem(); 1110 } 1111 } 1112 } 1113 1114 void CanvasTranslator::CacheDataSnapshots() { 1115 DataSurfaceBufferWillChange(); 1116 1117 if (mSharedContext) { 1118 // If there are any DrawTargetWebgls, then try to cache their framebuffers 1119 // in software surfaces, just in case the GL context is lost. So long as 1120 // there is a software copy of the framebuffer, it can be copied into a 1121 // fallback TextureData later even if the GL context goes away. 1122 for (auto const& entry : mTextureInfo) { 1123 if (gfx::DrawTargetWebgl* webgl = entry.second.GetDrawTargetWebgl()) { 1124 webgl->EnsureDataSnapshot(); 1125 } 1126 } 1127 } 1128 } 1129 1130 void CanvasTranslator::ClearCachedResources() { 1131 mUsedDataSurfaceForSurfaceDescriptor = nullptr; 1132 mUsedWrapperForSurfaceDescriptor = nullptr; 1133 mUsedSurfaceDescriptorForSurfaceDescriptor = Nothing(); 1134 1135 if (mSharedContext) { 1136 mSharedContext->OnMemoryPressure(); 1137 } 1138 1139 CacheDataSnapshots(); 1140 } 1141 1142 ipc::IPCResult CanvasTranslator::RecvClearCachedResources() { 1143 if (mDeactivated) { 1144 // The other side might have sent a message before we deactivated. 1145 return IPC_OK(); 1146 } 1147 1148 if (UsePendingCanvasTranslatorEvents()) { 1149 MutexAutoLock lock(mCanvasTranslatorEventsLock); 1150 mPendingCanvasTranslatorEvents.emplace_back( 1151 CanvasTranslatorEvent::ClearCachedResources()); 1152 PostCanvasTranslatorEvents(lock); 1153 } else { 1154 DispatchToTaskQueue( 1155 NewRunnableMethod("CanvasTranslator::ClearCachedResources", this, 1156 &CanvasTranslator::ClearCachedResources)); 1157 } 1158 return IPC_OK(); 1159 } 1160 1161 void CanvasTranslator::DropFreeBuffersWhenDormant() { CacheDataSnapshots(); } 1162 1163 ipc::IPCResult CanvasTranslator::RecvDropFreeBuffersWhenDormant() { 1164 if (mDeactivated) { 1165 // The other side might have sent a message before we deactivated. 1166 return IPC_OK(); 1167 } 1168 1169 if (UsePendingCanvasTranslatorEvents()) { 1170 MutexAutoLock lock(mCanvasTranslatorEventsLock); 1171 mPendingCanvasTranslatorEvents.emplace_back( 1172 CanvasTranslatorEvent::DropFreeBuffersWhenDormant()); 1173 PostCanvasTranslatorEvents(lock); 1174 } else { 1175 DispatchToTaskQueue( 1176 NewRunnableMethod("CanvasTranslator::DropFreeBuffersWhenDormant", this, 1177 &CanvasTranslator::DropFreeBuffersWhenDormant)); 1178 } 1179 return IPC_OK(); 1180 } 1181 1182 static const OpenMode kInitMode = OpenMode::OPEN_READ_WRITE; 1183 1184 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateFallbackDrawTarget( 1185 gfx::ReferencePtr aRefPtr, const RemoteTextureOwnerId aTextureOwnerId, 1186 const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { 1187 UniquePtr<TextureData> textureData = 1188 CreateOrRecycleTextureData(aSize, aFormat); 1189 if (NS_WARN_IF(!textureData)) { 1190 return nullptr; 1191 } 1192 1193 if (NS_WARN_IF(!textureData->Lock(kInitMode))) { 1194 gfxCriticalNote << "CanvasTranslator::CreateDrawTarget lock failed"; 1195 return nullptr; 1196 } 1197 1198 RefPtr<gfx::DrawTarget> dt = textureData->BorrowDrawTarget(); 1199 if (NS_WARN_IF(!dt)) { 1200 textureData->Unlock(); 1201 return nullptr; 1202 } 1203 // Recycled buffer contents may be uninitialized. 1204 dt->ClearRect(gfx::Rect(dt->GetRect())); 1205 1206 TextureInfo& info = mTextureInfo[aTextureOwnerId]; 1207 info.mRefPtr = aRefPtr; 1208 info.mFallbackDrawTarget = dt; 1209 info.mTextureData = std::move(textureData); 1210 info.mTextureLockMode = kInitMode; 1211 1212 return dt.forget(); 1213 } 1214 1215 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget( 1216 gfx::ReferencePtr aRefPtr, const RemoteTextureOwnerId aTextureOwnerId, 1217 const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { 1218 if (!aTextureOwnerId.IsValid()) { 1219 #ifndef FUZZING_SNAPSHOT 1220 MOZ_DIAGNOSTIC_CRASH("No texture owner set"); 1221 #endif 1222 return nullptr; 1223 } 1224 1225 { 1226 auto result = mTextureInfo.find(aTextureOwnerId); 1227 if (result != mTextureInfo.end()) { 1228 const TextureInfo& info = result->second; 1229 if (info.mTextureData || info.mDrawTarget) { 1230 #ifndef FUZZING_SNAPSHOT 1231 MOZ_DIAGNOSTIC_CRASH("DrawTarget already exists"); 1232 #endif 1233 return nullptr; 1234 } 1235 } 1236 } 1237 1238 RefPtr<gfx::DrawTarget> dt; 1239 if (gfx::gfxVars::UseAcceleratedCanvas2D()) { 1240 if (EnsureSharedContextWebgl()) { 1241 mSharedContext->EnterTlsScope(); 1242 } 1243 if (RefPtr<gfx::DrawTargetWebgl> webgl = 1244 gfx::DrawTargetWebgl::Create(aSize, aFormat, mSharedContext)) { 1245 webgl->BeginFrame(true); 1246 dt = webgl.forget().downcast<gfx::DrawTarget>(); 1247 if (dt) { 1248 TextureInfo& info = mTextureInfo[aTextureOwnerId]; 1249 info.mRefPtr = aRefPtr; 1250 info.mDrawTarget = dt; 1251 info.mTextureLockMode = kInitMode; 1252 CacheSnapshotShmem(aTextureOwnerId); 1253 } 1254 } 1255 if (!dt) { 1256 NotifyRequiresRefresh(aTextureOwnerId); 1257 } 1258 } 1259 1260 if (!dt) { 1261 dt = CreateFallbackDrawTarget(aRefPtr, aTextureOwnerId, aSize, aFormat); 1262 } 1263 1264 if (dt && aRefPtr) { 1265 AddDrawTarget(aRefPtr, dt); 1266 } 1267 return dt.forget(); 1268 } 1269 1270 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget( 1271 gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize, 1272 gfx::SurfaceFormat aFormat) { 1273 #ifndef FUZZING_SNAPSHOT 1274 MOZ_DIAGNOSTIC_CRASH("Unexpected CreateDrawTarget call!"); 1275 #endif 1276 return nullptr; 1277 } 1278 1279 void CanvasTranslator::NotifyTextureDestruction( 1280 const RemoteTextureOwnerId aTextureOwnerId) { 1281 MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread()); 1282 1283 if (mIPDLClosed) { 1284 return; 1285 } 1286 (void)SendNotifyTextureDestruction(aTextureOwnerId); 1287 } 1288 1289 void CanvasTranslator::AddTextureKeepAlive(const RemoteTextureOwnerId& aId) { 1290 auto result = mTextureInfo.find(aId); 1291 if (result == mTextureInfo.end()) { 1292 return; 1293 } 1294 auto& info = result->second; 1295 ++info.mKeepAlive; 1296 } 1297 1298 void CanvasTranslator::RemoveTextureKeepAlive(const RemoteTextureOwnerId& aId) { 1299 RemoveTexture(aId, 0, 0, false); 1300 } 1301 1302 void CanvasTranslator::RemoveTexture(const RemoteTextureOwnerId aTextureOwnerId, 1303 RemoteTextureTxnType aTxnType, 1304 RemoteTextureTxnId aTxnId, 1305 bool aFinalize) { 1306 // Don't erase the texture if still in use 1307 auto result = mTextureInfo.find(aTextureOwnerId); 1308 if (result == mTextureInfo.end()) { 1309 return; 1310 } 1311 auto& info = result->second; 1312 if (mRemoteTextureOwner && aTxnType && aTxnId) { 1313 mRemoteTextureOwner->WaitForTxn(aTextureOwnerId, aTxnType, aTxnId); 1314 } 1315 // Remove the DrawTarget only if this is being called from a recorded event 1316 // or if there are no remaining keepalives. If this is being called only to 1317 // remove a keepalive without forcing removal, then the DrawTarget is still 1318 // being used by the recording. 1319 if ((aFinalize || info.mKeepAlive <= 1) && info.mRefPtr) { 1320 RemoveDrawTarget(info.mRefPtr); 1321 info.mRefPtr = ReferencePtr(); 1322 } 1323 if (--info.mKeepAlive > 0) { 1324 return; 1325 } 1326 if (info.mTextureData) { 1327 if (info.mFallbackDrawTarget) { 1328 info.mTextureData->ReturnDrawTarget(info.mFallbackDrawTarget.forget()); 1329 } 1330 info.mTextureData->Unlock(); 1331 } 1332 if (mRemoteTextureOwner) { 1333 // If this texture id was manually registered as a remote texture owner, 1334 // unregister it so it does not stick around after the texture id goes away. 1335 if (aTextureOwnerId.IsValid()) { 1336 mRemoteTextureOwner->UnregisterTextureOwner(aTextureOwnerId); 1337 } 1338 } 1339 1340 gfx::CanvasRenderThread::Dispatch(NewRunnableMethod<RemoteTextureOwnerId>( 1341 "CanvasTranslator::NotifyTextureDestruction", this, 1342 &CanvasTranslator::NotifyTextureDestruction, aTextureOwnerId)); 1343 1344 mTextureInfo.erase(result); 1345 } 1346 1347 bool CanvasTranslator::LockTexture(const RemoteTextureOwnerId aTextureOwnerId, 1348 OpenMode aMode, bool aInvalidContents) { 1349 if (aMode == OpenMode::OPEN_NONE) { 1350 return false; 1351 } 1352 auto result = mTextureInfo.find(aTextureOwnerId); 1353 if (result == mTextureInfo.end()) { 1354 return false; 1355 } 1356 auto& info = result->second; 1357 if (info.mTextureLockMode != OpenMode::OPEN_NONE) { 1358 return (info.mTextureLockMode & aMode) == aMode; 1359 } 1360 if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) { 1361 if (aMode & OpenMode::OPEN_WRITE) { 1362 webgl->BeginFrame(aInvalidContents); 1363 } 1364 } 1365 info.mTextureLockMode = aMode; 1366 return true; 1367 } 1368 1369 bool CanvasTranslator::UnlockTexture( 1370 const RemoteTextureOwnerId aTextureOwnerId) { 1371 auto result = mTextureInfo.find(aTextureOwnerId); 1372 if (result == mTextureInfo.end()) { 1373 return false; 1374 } 1375 auto& info = result->second; 1376 if (info.mTextureLockMode == OpenMode::OPEN_NONE) { 1377 return false; 1378 } 1379 1380 if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) { 1381 if (info.mTextureLockMode & OpenMode::OPEN_WRITE) { 1382 webgl->EndFrame(); 1383 if (webgl->RequiresRefresh()) { 1384 NotifyRequiresRefresh(aTextureOwnerId); 1385 } 1386 } 1387 } 1388 info.mTextureLockMode = OpenMode::OPEN_NONE; 1389 return true; 1390 } 1391 1392 bool CanvasTranslator::PresentTexture( 1393 const RemoteTextureOwnerId aTextureOwnerId, RemoteTextureId aId) { 1394 AUTO_PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {}, 1395 "CanvasTranslator::PresentTexture"_ns); 1396 auto result = mTextureInfo.find(aTextureOwnerId); 1397 if (result == mTextureInfo.end()) { 1398 return false; 1399 } 1400 auto& info = result->second; 1401 if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) { 1402 EnsureRemoteTextureOwner(aTextureOwnerId); 1403 if (webgl->CopyToSwapChain(mWebglTextureType, aId, aTextureOwnerId, 1404 mRemoteTextureOwner)) { 1405 return true; 1406 } 1407 if (mSharedContext && mSharedContext->IsContextLost()) { 1408 // If the context was lost, try to create a fallback to push instead. 1409 EnsureSharedContextWebgl(); 1410 } else { 1411 // CopyToSwapChain failed for an unknown reason other than context loss. 1412 // Try to read into fallback data if possible to recover, otherwise force 1413 // the loss of the individual texture. 1414 webgl->EnsureDataSnapshot(); 1415 if (!TryDrawTargetWebglFallback(aTextureOwnerId, webgl)) { 1416 RemoteTextureOwnerIdSet lost = {aTextureOwnerId}; 1417 NotifyDeviceReset(lost); 1418 } 1419 } 1420 } 1421 if (TextureData* data = info.mTextureData.get()) { 1422 PushRemoteTexture(aTextureOwnerId, data, aId, aTextureOwnerId); 1423 } 1424 return true; 1425 } 1426 1427 void CanvasTranslator::EnsureRemoteTextureOwner(RemoteTextureOwnerId aOwnerId) { 1428 if (!mRemoteTextureOwner) { 1429 mRemoteTextureOwner = new RemoteTextureOwnerClient(mOtherPid); 1430 } 1431 if (aOwnerId.IsValid() && !mRemoteTextureOwner->IsRegistered(aOwnerId)) { 1432 mRemoteTextureOwner->RegisterTextureOwner(aOwnerId, 1433 /* aSharedRecycling */ true); 1434 } 1435 } 1436 1437 UniquePtr<TextureData> CanvasTranslator::CreateOrRecycleTextureData( 1438 const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { 1439 if (mRemoteTextureOwner) { 1440 if (mTextureType == TextureType::Unknown) { 1441 return mRemoteTextureOwner->CreateOrRecycleBufferTextureData(aSize, 1442 aFormat); 1443 } 1444 if (UniquePtr<TextureData> data = 1445 mRemoteTextureOwner->GetRecycledTextureData(aSize, aFormat, 1446 mTextureType)) { 1447 return data; 1448 } 1449 } 1450 return CreateTextureData(aSize, aFormat, false); 1451 } 1452 1453 bool CanvasTranslator::PushRemoteTexture( 1454 const RemoteTextureOwnerId aTextureOwnerId, TextureData* aData, 1455 RemoteTextureId aId, RemoteTextureOwnerId aOwnerId) { 1456 EnsureRemoteTextureOwner(aOwnerId); 1457 TextureData::Info info; 1458 aData->FillInfo(info); 1459 UniquePtr<TextureData> dstData = 1460 CreateOrRecycleTextureData(info.size, info.format); 1461 bool success = false; 1462 // Source data is already locked. 1463 if (dstData) { 1464 if (dstData->Lock(OpenMode::OPEN_WRITE)) { 1465 if (RefPtr<gfx::DrawTarget> dstDT = dstData->BorrowDrawTarget()) { 1466 if (RefPtr<gfx::DrawTarget> srcDT = aData->BorrowDrawTarget()) { 1467 if (RefPtr<gfx::SourceSurface> snapshot = srcDT->Snapshot()) { 1468 dstDT->CopySurface(snapshot, snapshot->GetRect(), 1469 gfx::IntPoint(0, 0)); 1470 dstDT->Flush(); 1471 success = true; 1472 } 1473 } 1474 } 1475 dstData->Unlock(); 1476 } else { 1477 gfxCriticalNote << "CanvasTranslator::PushRemoteTexture dst lock failed"; 1478 } 1479 } 1480 if (success) { 1481 mRemoteTextureOwner->PushTexture(aId, aOwnerId, std::move(dstData)); 1482 } else { 1483 mRemoteTextureOwner->PushDummyTexture(aId, aOwnerId); 1484 } 1485 return success; 1486 } 1487 1488 void CanvasTranslator::ClearTextureInfo() { 1489 MOZ_ASSERT(mIPDLClosed); 1490 1491 DataSurfaceBufferWillChange(0, false); 1492 1493 mUsedDataSurfaceForSurfaceDescriptor = nullptr; 1494 mUsedWrapperForSurfaceDescriptor = nullptr; 1495 mUsedSurfaceDescriptorForSurfaceDescriptor = Nothing(); 1496 1497 for (auto& entry : mTextureInfo) { 1498 auto& info = entry.second; 1499 if (info.mTextureData) { 1500 if (info.mFallbackDrawTarget) { 1501 info.mTextureData->ReturnDrawTarget(info.mFallbackDrawTarget.forget()); 1502 } 1503 info.mTextureData->Unlock(); 1504 } 1505 } 1506 mTextureInfo.clear(); 1507 mDrawTargets.Clear(); 1508 mSharedContext = nullptr; 1509 // If the global shared context's ref is the last ref left, then clear out 1510 // any internal caches and textures from the context, but still keep it 1511 // alive. This saves on startup costs while not contributing significantly 1512 // to memory usage. 1513 if (sSharedContext && sSharedContext->hasOneRef()) { 1514 sSharedContext->ClearCaches(); 1515 } 1516 if (mReferenceTextureData) { 1517 if (mBaseDT) { 1518 mReferenceTextureData->ReturnDrawTarget(mBaseDT.forget()); 1519 } 1520 mReferenceTextureData->Unlock(); 1521 } 1522 if (mRemoteTextureOwner) { 1523 mRemoteTextureOwner->UnregisterAllTextureOwners(); 1524 mRemoteTextureOwner = nullptr; 1525 } 1526 } 1527 1528 already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface( 1529 uint64_t aKey) { 1530 return mSharedSurfacesHolder->Get(wr::ToExternalImageId(aKey)); 1531 } 1532 1533 // Check if the surface descriptor describes a GPUVideo texture for which we 1534 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to 1535 // derive the actual texture from. 1536 static bool SDIsSupportedRemoteDecoder(const SurfaceDescriptor& sd) { 1537 if (sd.type() != SurfaceDescriptor::TSurfaceDescriptorGPUVideo) { 1538 return false; 1539 } 1540 1541 const auto& sdv = sd.get_SurfaceDescriptorGPUVideo(); 1542 const auto& sdvType = sdv.type(); 1543 if (sdvType != SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder) { 1544 return false; 1545 } 1546 1547 const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder(); 1548 const auto& subdesc = sdrd.subdesc(); 1549 const auto& subdescType = subdesc.type(); 1550 1551 if (subdescType == RemoteDecoderVideoSubDescriptor::Tnull_t || 1552 subdescType == 1553 RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorMacIOSurface || 1554 subdescType == RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10) { 1555 return true; 1556 } 1557 1558 return false; 1559 } 1560 1561 already_AddRefed<gfx::DataSourceSurface> 1562 CanvasTranslator::MaybeRecycleDataSurfaceForSurfaceDescriptor( 1563 TextureHost* aTextureHost, 1564 const SurfaceDescriptorRemoteDecoder& aSurfaceDescriptor) { 1565 if (!StaticPrefs::gfx_canvas_remote_recycle_used_data_surface()) { 1566 return nullptr; 1567 } 1568 1569 auto& usedSurf = mUsedDataSurfaceForSurfaceDescriptor; 1570 auto& usedWrapper = mUsedWrapperForSurfaceDescriptor; 1571 auto& usedDescriptor = mUsedSurfaceDescriptorForSurfaceDescriptor; 1572 1573 if (usedDescriptor.isSome() && usedDescriptor.ref() == aSurfaceDescriptor) { 1574 MOZ_ASSERT(usedSurf); 1575 MOZ_ASSERT(usedWrapper); 1576 MOZ_ASSERT(aTextureHost->GetSize() == usedSurf->GetSize()); 1577 1578 // Since the data is the same as before, the DataSourceSurfaceWrapper can be 1579 // reused. 1580 return do_AddRef(usedWrapper); 1581 } 1582 1583 bool isYuvVideo = false; 1584 if (aTextureHost->AsMacIOSurfaceTextureHost()) { 1585 if (aTextureHost->GetFormat() == SurfaceFormat::NV12 || 1586 aTextureHost->GetFormat() == SurfaceFormat::YUY2) { 1587 isYuvVideo = true; 1588 } 1589 } else if (aTextureHost->GetFormat() == gfx::SurfaceFormat::YUV420) { 1590 isYuvVideo = true; 1591 } 1592 1593 // Reuse previously used DataSourceSurface if it is not used and same 1594 // size/format. 1595 bool reuseSurface = isYuvVideo && usedSurf && usedSurf->refCount() == 1 && 1596 usedSurf->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 && 1597 aTextureHost->GetSize() == usedSurf->GetSize(); 1598 usedSurf = 1599 aTextureHost->GetAsSurface(reuseSurface ? usedSurf.get() : nullptr); 1600 if (NS_WARN_IF(!usedSurf)) { 1601 usedWrapper = nullptr; 1602 usedDescriptor = Nothing(); 1603 return nullptr; 1604 } 1605 // Wrap DataSourceSurface with DataSourceSurfaceWrapper to force upload in 1606 // DrawTargetWebgl::DrawSurface(). 1607 usedDescriptor = Some(aSurfaceDescriptor); 1608 usedWrapper = new gfx::DataSourceSurfaceWrapper(usedSurf); 1609 return do_AddRef(usedWrapper); 1610 } 1611 1612 already_AddRefed<gfx::SourceSurface> 1613 CanvasTranslator::LookupSourceSurfaceFromSurfaceDescriptor( 1614 const SurfaceDescriptor& aDesc) { 1615 if (!SDIsSupportedRemoteDecoder(aDesc)) { 1616 return nullptr; 1617 } 1618 1619 const auto& sdrd = aDesc.get_SurfaceDescriptorGPUVideo() 1620 .get_SurfaceDescriptorRemoteDecoder(); 1621 const auto& subdesc = sdrd.subdesc(); 1622 const auto& subdescType = subdesc.type(); 1623 1624 RefPtr<VideoBridgeParent> parent = 1625 VideoBridgeParent::GetSingleton(sdrd.source()); 1626 if (!parent) { 1627 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1628 gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent"; 1629 return nullptr; 1630 } 1631 RefPtr<TextureHost> texture = 1632 parent->LookupTexture(mContentId, sdrd.handle()); 1633 if (!texture) { 1634 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1635 gfxCriticalNote << "TexUnpackSurface failed to get TextureHost"; 1636 return nullptr; 1637 } 1638 1639 #if defined(XP_WIN) 1640 if (subdescType == RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10) { 1641 auto* textureHostD3D11 = texture->AsDXGITextureHostD3D11(); 1642 if (!textureHostD3D11) { 1643 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1644 return nullptr; 1645 } 1646 auto& usedSurf = mUsedDataSurfaceForSurfaceDescriptor; 1647 auto& usedDescriptor = mUsedSurfaceDescriptorForSurfaceDescriptor; 1648 1649 // TODO reuse DataSourceSurface if no update. 1650 1651 if (RefPtr<ID3D11Device> device = 1652 gfx::DeviceManagerDx::Get()->GetCanvasDevice()) { 1653 usedSurf = textureHostD3D11->GetAsSurfaceWithDevice(device); 1654 } else { 1655 usedSurf = nullptr; 1656 } 1657 if (!usedSurf) { 1658 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1659 usedDescriptor = Nothing(); 1660 return nullptr; 1661 } 1662 usedDescriptor = Some(sdrd); 1663 1664 return do_AddRef(usedSurf); 1665 } 1666 #endif 1667 1668 if (subdescType == 1669 RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorMacIOSurface) { 1670 MOZ_ASSERT(texture->AsMacIOSurfaceTextureHost()); 1671 1672 RefPtr<gfx::DataSourceSurface> surf = 1673 MaybeRecycleDataSurfaceForSurfaceDescriptor(texture, sdrd); 1674 return surf.forget(); 1675 } 1676 1677 if (subdescType == RemoteDecoderVideoSubDescriptor::Tnull_t) { 1678 RefPtr<gfx::DataSourceSurface> surf = 1679 MaybeRecycleDataSurfaceForSurfaceDescriptor(texture, sdrd); 1680 return surf.forget(); 1681 } 1682 1683 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1684 return nullptr; 1685 } 1686 1687 void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); } 1688 1689 void CanvasTranslator::PauseTranslation() { 1690 mHeader->readerState = State::Paused; 1691 } 1692 1693 void CanvasTranslator::AwaitTranslationSync(uint64_t aSyncId) { 1694 if (NS_WARN_IF(!UsePendingCanvasTranslatorEvents()) || 1695 NS_WARN_IF(!IsInTaskQueue()) || NS_WARN_IF(mAwaitSyncId >= aSyncId)) { 1696 return; 1697 } 1698 1699 mAwaitSyncId = aSyncId; 1700 } 1701 1702 void CanvasTranslator::SyncTranslation(uint64_t aSyncId) { 1703 if (NS_WARN_IF(!IsInTaskQueue()) || NS_WARN_IF(aSyncId <= mLastSyncId)) { 1704 return; 1705 } 1706 1707 bool wasPaused = PauseUntilSync(); 1708 mLastSyncId = aSyncId; 1709 // If translation was previously paused waiting on a sync-id, check if sync-id 1710 // encountered requires restarting translation. 1711 if (wasPaused && !PauseUntilSync()) { 1712 HandleCanvasTranslatorEvents(); 1713 } 1714 } 1715 1716 mozilla::ipc::IPCResult CanvasTranslator::RecvSnapshotExternalCanvas( 1717 uint64_t aSyncId, uint32_t aManagerId, ActorId aCanvasId) { 1718 if (NS_WARN_IF(!IsInTaskQueue())) { 1719 return IPC_FAIL(this, 1720 "RecvSnapshotExternalCanvas used outside of task queue."); 1721 } 1722 1723 // Verify that snapshot requests are not received out of order order. 1724 if (NS_WARN_IF(aSyncId <= mLastSyncId)) { 1725 return IPC_FAIL(this, "RecvSnapShotExternalCanvas received too late."); 1726 } 1727 1728 // Attempt to snapshot an external canvas that is associated with the same 1729 // content process as this canvas. On success, associate it with the sync-id. 1730 ExternalSnapshot snapshot; 1731 if (auto* actor = gfx::CanvasManagerParent::GetCanvasActor( 1732 mContentId, aManagerId, aCanvasId)) { 1733 switch (actor->GetProtocolId()) { 1734 case ProtocolId::PWebGLMsgStart: 1735 if (auto* hostContext = 1736 static_cast<dom::WebGLParent*>(actor)->GetHostWebGLContext()) { 1737 if (auto* webgl = hostContext->GetWebGLContext()) { 1738 if (mWebglTextureType != TextureType::Unknown) { 1739 snapshot.mSharedSurface = 1740 webgl->GetBackBufferSnapshotSharedSurface(mWebglTextureType, 1741 true, true, true); 1742 if (snapshot.mSharedSurface) { 1743 snapshot.mWebgl = webgl; 1744 snapshot.mDescriptor = 1745 snapshot.mSharedSurface->ToSurfaceDescriptor(); 1746 } 1747 } 1748 if (!snapshot.mDescriptor) { 1749 snapshot.mData = webgl->GetBackBufferSnapshot(true); 1750 } 1751 } 1752 } 1753 break; 1754 default: 1755 MOZ_ASSERT_UNREACHABLE("Unsupported protocol"); 1756 break; 1757 } 1758 } 1759 1760 if (!snapshot.mDescriptor && !snapshot.mData) { 1761 // No available surface, but sync translation so it may resume after 1762 // attempting snapshot. 1763 SyncTranslation(aSyncId); 1764 return IPC_FAIL(this, "SnapshotExternalCanvas failed to get surface."); 1765 } 1766 1767 mExternalSnapshots.insert({aSyncId, std::move(snapshot)}); 1768 1769 // Sync translation so it may resume with the snapshot. 1770 SyncTranslation(aSyncId); 1771 return IPC_OK(); 1772 } 1773 1774 bool CanvasTranslator::ResolveExternalSnapshot(uint64_t aSyncId, 1775 ReferencePtr aRefPtr, 1776 const IntSize& aSize, 1777 SurfaceFormat aFormat, 1778 DrawTarget* aDT) { 1779 MOZ_ASSERT(IsInTaskQueue()); 1780 uint64_t prevSyncId = mLastSyncId; 1781 if (NS_WARN_IF(aSyncId > mLastSyncId)) { 1782 // If arriving here, a previous SnapshotExternalCanvas IPDL message never 1783 // arrived for some reason. Sync translation here to avoid locking up. 1784 SyncTranslation(aSyncId); 1785 } 1786 1787 // Check if the snapshot was added. This should only ever be called once per 1788 // snapshot, as it is removed from the table when resolved. 1789 auto it = mExternalSnapshots.find(aSyncId); 1790 if (it == mExternalSnapshots.end()) { 1791 // There was no snapshot available, which can happen if this was called 1792 // before or without a corresponding SnapshotExternalCanvas, or if called 1793 // multiple times. 1794 if (aSyncId > prevSyncId) { 1795 gfxCriticalNoteOnce 1796 << "External canvas snapshot resolved before creation."; 1797 } else { 1798 gfxCriticalNoteOnce << "Exernal canvas snapshot already resolved."; 1799 } 1800 return false; 1801 } 1802 1803 ExternalSnapshot snapshot = std::move(it->second); 1804 mExternalSnapshots.erase(it); 1805 1806 RefPtr<gfx::SourceSurface> resolved; 1807 if (snapshot.mSharedSurface) { 1808 snapshot.mSharedSurface->BeginRead(); 1809 } 1810 if (snapshot.mDescriptor) { 1811 if (aDT) { 1812 resolved = 1813 aDT->ImportSurfaceDescriptor(*snapshot.mDescriptor, aSize, aFormat); 1814 } 1815 if (!resolved && gfx::gfxVars::UseAcceleratedCanvas2D() && 1816 EnsureSharedContextWebgl()) { 1817 // If we can't import the surface using the DT, then try using the global 1818 // shared context to allow for a readback. 1819 resolved = mSharedContext->ImportSurfaceDescriptor(*snapshot.mDescriptor, 1820 aSize, aFormat); 1821 } 1822 } 1823 if (snapshot.mSharedSurface) { 1824 snapshot.mSharedSurface->EndRead(); 1825 if (snapshot.mWebgl) { 1826 snapshot.mWebgl->RecycleSnapshotSharedSurface(snapshot.mSharedSurface); 1827 } 1828 } 1829 if (!resolved) { 1830 // There was no descriptor, but check if there is at least a data surface. 1831 resolved = snapshot.mData; 1832 } 1833 if (resolved) { 1834 AddSourceSurface(aRefPtr, resolved); 1835 return true; 1836 } 1837 return false; 1838 } 1839 1840 already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops( 1841 gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops, 1842 uint32_t aNumStops, gfx::ExtendMode aExtendMode) { 1843 MOZ_ASSERT(aDrawTarget); 1844 nsTArray<gfx::GradientStop> rawStopArray(aRawStops, aNumStops); 1845 return gfx::gfxGradientCache::GetOrCreateGradientStops( 1846 aDrawTarget, rawStopArray, aExtendMode); 1847 } 1848 1849 gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface( 1850 gfx::ReferencePtr aRefPtr) { 1851 return mDataSurfaces.GetWeak(aRefPtr); 1852 } 1853 1854 void CanvasTranslator::AddDataSurface( 1855 gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) { 1856 mDataSurfaces.InsertOrUpdate(aRefPtr, std::move(aSurface)); 1857 } 1858 1859 void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) { 1860 RefPtr<gfx::DataSourceSurface> surface; 1861 if (mDataSurfaces.Remove(aRefPtr, getter_AddRefs(surface))) { 1862 // If the data surface is the assigned owner of a shmem, and if the shmem 1863 // is not the last shmem id used, then erase the shmem. 1864 if (auto id = reinterpret_cast<uintptr_t>( 1865 surface->GetUserData(&mDataSurfaceShmemIdKey))) { 1866 if (id != mLastDataSurfaceShmemId) { 1867 auto it = mDataSurfaceShmems.find(id); 1868 if (it != mDataSurfaceShmems.end()) { 1869 // If this is not the last reference to the surface, then the shmem 1870 // contents needs to be copied. 1871 if (!surface->hasOneRef()) { 1872 UnlinkDataSurfaceShmemOwner(surface); 1873 } 1874 mDataSurfaceShmems.erase(it); 1875 } 1876 } 1877 } 1878 } 1879 } 1880 1881 } // namespace layers 1882 } // namespace mozilla