1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 28 #include "platform/graphics/Canvas2DLayerBridge.h" 29 30 #include "GrContext.h" 31 #include "SkDevice.h" 32 #include "SkSurface.h" 33 #include "platform/TraceEvent.h" 34 #include "platform/graphics/Canvas2DLayerManager.h" 35 #include "platform/graphics/GraphicsLayer.h" 36 #include "public/platform/Platform.h" 37 #include "public/platform/WebCompositorSupport.h" 38 #include "public/platform/WebGraphicsContext3D.h" 39 #include "public/platform/WebGraphicsContext3DProvider.h" 40 #include "wtf/RefCountedLeakCounter.h" 41 42 using blink::WebExternalTextureLayer; 43 using blink::WebGraphicsContext3D; 44 45 namespace { 46 enum { 47 InvalidMailboxIndex = -1, 48 }; 49 50 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstanceCounter, ("Canvas2DLayerBridge")); 51 } 52 53 namespace WebCore { 54 55 static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0) 56 { 57 if (!gr) 58 return nullptr; 59 gr->resetContext(); 60 SkImageInfo info; 61 info.fWidth = size.width(); 62 info.fHeight = size.height(); 63 info.fColorType = kPMColor_SkColorType; 64 info.fAlphaType = kPremul_SkAlphaType; 65 return adoptRef(SkSurface::NewRenderTarget(gr, info, msaaSampleCount)); 66 } 67 68 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount) 69 { 70 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); 71 OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider()); 72 if (!contextProvider) 73 return nullptr; 74 RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount)); 75 if (!surface) 76 return nullptr; 77 RefPtr<Canvas2DLayerBridge> layerBridge; 78 OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get())); 79 layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), msaaSampleCount, opacityMode)); 80 return layerBridge.release(); 81 } 82 83 Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode) 84 : m_canvas(canvas) 85 , m_contextProvider(contextProvider) 86 , m_imageBuffer(0) 87 , m_msaaSampleCount(msaaSampleCount) 88 , m_bytesAllocated(0) 89 , m_didRecordDrawCommand(false) 90 , m_isSurfaceValid(true) 91 , m_framesPending(0) 92 , m_framesSinceMailboxRelease(0) 93 , m_destructionInProgress(false) 94 , m_rateLimitingEnabled(false) 95 , m_isHidden(false) 96 , m_next(0) 97 , m_prev(0) 98 , m_lastImageId(0) 99 , m_releasedMailboxInfoIndex(InvalidMailboxIndex) 100 { 101 ASSERT(m_canvas); 102 ASSERT(m_contextProvider); 103 // Used by browser tests to detect the use of a Canvas2DLayerBridge. 104 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); 105 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this)); 106 m_layer->setOpaque(opacityMode == Opaque); 107 m_layer->setBlendBackgroundColor(opacityMode != Opaque); 108 GraphicsLayer::registerContentsLayer(m_layer->layer()); 109 m_layer->setRateLimitContext(m_rateLimitingEnabled); 110 m_canvas->setNotificationClient(this); 111 #ifndef NDEBUG 112 canvas2DLayerBridgeInstanceCounter.increment(); 113 #endif 114 } 115 116 Canvas2DLayerBridge::~Canvas2DLayerBridge() 117 { 118 ASSERT(m_destructionInProgress); 119 ASSERT(!Canvas2DLayerManager::get().isInList(this)); 120 m_layer.clear(); 121 freeReleasedMailbox(); 122 #if ASSERT_ENABLED 123 Vector<MailboxInfo>::iterator mailboxInfo; 124 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) { 125 ASSERT(mailboxInfo->m_status != MailboxInUse); 126 ASSERT(mailboxInfo->m_status != MailboxReleased || m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid); 127 } 128 #endif 129 m_mailboxes.clear(); 130 #ifndef NDEBUG 131 canvas2DLayerBridgeInstanceCounter.decrement(); 132 #endif 133 } 134 135 void Canvas2DLayerBridge::beginDestruction() 136 { 137 ASSERT(!m_destructionInProgress); 138 setRateLimitingEnabled(false); 139 m_canvas->silentFlush(); 140 m_imageBuffer = 0; 141 freeTransientResources(); 142 setIsHidden(true); 143 m_destructionInProgress = true; 144 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); 145 m_canvas->setNotificationClient(0); 146 m_canvas.clear(); 147 m_layer->clearTexture(); 148 // Orphaning the layer is required to trigger the recration of a new layer 149 // in the case where destruction is caused by a canvas resize. Test: 150 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html 151 m_layer->layer()->removeFromParent(); 152 // To anyone who ever hits this assert: Please update crbug.com/344666 153 // with repro steps. 154 ASSERT(!m_bytesAllocated); 155 } 156 157 void Canvas2DLayerBridge::setIsHidden(bool hidden) 158 { 159 ASSERT(!m_destructionInProgress); 160 bool newHiddenValue = hidden || m_destructionInProgress; 161 if (m_isHidden == newHiddenValue) 162 return; 163 164 m_isHidden = newHiddenValue; 165 if (isHidden()) { 166 freeTransientResources(); 167 } 168 } 169 170 void Canvas2DLayerBridge::freeTransientResources() 171 { 172 ASSERT(!m_destructionInProgress); 173 freeReleasedMailbox(); 174 flush(); 175 freeMemoryIfPossible(bytesAllocated()); 176 ASSERT(!hasTransientResources()); 177 } 178 179 bool Canvas2DLayerBridge::hasTransientResources() const 180 { 181 return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated()); 182 } 183 184 void Canvas2DLayerBridge::limitPendingFrames() 185 { 186 ASSERT(!m_destructionInProgress); 187 if (isHidden()) { 188 freeTransientResources(); 189 return; 190 } 191 if (m_didRecordDrawCommand) { 192 m_framesPending++; 193 m_didRecordDrawCommand = false; 194 if (m_framesPending > 1) { 195 // Turn on the rate limiter if this layer tends to accumulate a 196 // non-discardable multi-frame backlog of draw commands. 197 setRateLimitingEnabled(true); 198 } 199 if (m_rateLimitingEnabled) { 200 flush(); 201 } 202 } 203 ++m_framesSinceMailboxRelease; 204 if (releasedMailboxHasExpired()) { 205 freeReleasedMailbox(); 206 } 207 } 208 209 void Canvas2DLayerBridge::prepareForDraw() 210 { 211 ASSERT(!m_destructionInProgress); 212 ASSERT(m_layer); 213 if (!checkSurfaceValid()) { 214 if (m_canvas) { 215 // drop pending commands because there is no surface to draw to 216 m_canvas->silentFlush(); 217 } 218 return; 219 } 220 context()->makeContextCurrent(); 221 } 222 223 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated) 224 { 225 ASSERT(!m_destructionInProgress); 226 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated; 227 m_bytesAllocated = bytesAllocated; 228 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta); 229 } 230 231 size_t Canvas2DLayerBridge::storageAllocatedForRecording() 232 { 233 return m_canvas->storageAllocatedForRecording(); 234 } 235 236 void Canvas2DLayerBridge::flushedDrawCommands() 237 { 238 ASSERT(!m_destructionInProgress); 239 storageAllocatedForRecordingChanged(storageAllocatedForRecording()); 240 m_framesPending = 0; 241 } 242 243 void Canvas2DLayerBridge::skippedPendingDrawCommands() 244 { 245 ASSERT(!m_destructionInProgress); 246 // Stop triggering the rate limiter if SkDeferredCanvas is detecting 247 // and optimizing overdraw. 248 setRateLimitingEnabled(false); 249 flushedDrawCommands(); 250 } 251 252 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled) 253 { 254 ASSERT(!m_destructionInProgress); 255 if (m_rateLimitingEnabled != enabled) { 256 m_rateLimitingEnabled = enabled; 257 m_layer->setRateLimitContext(m_rateLimitingEnabled); 258 } 259 } 260 261 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree) 262 { 263 ASSERT(!m_destructionInProgress); 264 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree); 265 m_bytesAllocated -= bytesFreed; 266 if (bytesFreed) 267 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed)); 268 return bytesFreed; 269 } 270 271 void Canvas2DLayerBridge::flush() 272 { 273 ASSERT(!m_destructionInProgress); 274 if (m_canvas->hasPendingCommands()) { 275 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush"); 276 freeReleasedMailbox(); // To avoid unnecessary triple-buffering 277 m_canvas->flush(); 278 } 279 } 280 281 bool Canvas2DLayerBridge::releasedMailboxHasExpired() 282 { 283 // This heuristic indicates that the canvas is not being 284 // actively presented by the compositor (3 frames rendered since 285 // last mailbox release), suggesting that double buffering is not required. 286 return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2; 287 } 288 289 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo() 290 { 291 return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0; 292 } 293 294 bool Canvas2DLayerBridge::hasReleasedMailbox() const 295 { 296 return m_releasedMailboxInfoIndex != InvalidMailboxIndex; 297 } 298 299 void Canvas2DLayerBridge::freeReleasedMailbox() 300 { 301 if (m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid) 302 return; 303 MailboxInfo* mailboxInfo = releasedMailboxInfo(); 304 if (!mailboxInfo) 305 return; 306 307 ASSERT(mailboxInfo->m_status == MailboxReleased); 308 if (mailboxInfo->m_mailbox.syncPoint) { 309 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); 310 mailboxInfo->m_mailbox.syncPoint = 0; 311 } 312 // Invalidate texture state in case the compositor altered it since the copy-on-write. 313 if (mailboxInfo->m_image) { 314 if (isHidden() || releasedMailboxHasExpired()) 315 mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit)); 316 mailboxInfo->m_image->getTexture()->textureParamsModified(); 317 mailboxInfo->m_image.clear(); 318 } 319 mailboxInfo->m_status = MailboxAvailable; 320 m_releasedMailboxInfoIndex = InvalidMailboxIndex; 321 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this); 322 } 323 324 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context() 325 { 326 // Check on m_layer is necessary because context() may be called during 327 // the destruction of m_layer 328 if (m_layer && !m_destructionInProgress) 329 checkSurfaceValid(); // To ensure rate limiter is disabled if context is lost. 330 return m_contextProvider->context3d(); 331 } 332 333 bool Canvas2DLayerBridge::checkSurfaceValid() 334 { 335 ASSERT(!m_destructionInProgress); 336 if (m_destructionInProgress || !m_isSurfaceValid) 337 return false; 338 if (m_contextProvider->context3d()->isContextLost()) { 339 m_isSurfaceValid = false; 340 if (m_imageBuffer) 341 m_imageBuffer->notifySurfaceInvalid(); 342 setRateLimitingEnabled(false); 343 } 344 return m_isSurfaceValid; 345 } 346 347 bool Canvas2DLayerBridge::restoreSurface() 348 { 349 ASSERT(!m_destructionInProgress); 350 if (m_destructionInProgress) 351 return false; 352 ASSERT(m_layer && !m_isSurfaceValid); 353 354 blink::WebGraphicsContext3D* sharedContext = 0; 355 // We must clear the mailboxes before calling m_layer->clearTexture() to prevent 356 // re-entry via mailboxReleased from operating on defunct GrContext objects. 357 m_mailboxes.clear(); 358 m_releasedMailboxInfoIndex = InvalidMailboxIndex; 359 m_layer->clearTexture(); 360 m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider()); 361 if (m_contextProvider) 362 sharedContext = m_contextProvider->context3d(); 363 364 if (sharedContext && !sharedContext->isContextLost()) { 365 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height()); 366 RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount)); 367 if (surface.get()) { 368 m_canvas->setSurface(surface.get()); 369 m_isSurfaceValid = true; 370 // FIXME: draw sad canvas picture into new buffer crbug.com/243842 371 } 372 } 373 374 return m_isSurfaceValid; 375 } 376 377 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap) 378 { 379 if (m_destructionInProgress) { 380 // It can be hit in the following sequence. 381 // 1. Canvas draws something. 382 // 2. The compositor begins the frame. 383 // 3. Javascript makes a context be lost. 384 // 4. Here. 385 return false; 386 } 387 if (bitmap) { 388 // Using accelerated 2d canvas with software renderer, which 389 // should only happen in tests that use fake graphics contexts 390 // or in Android WebView in software mode. In this case, we do 391 // not care about producing any results for this canvas. 392 m_canvas->silentFlush(); 393 m_lastImageId = 0; 394 return false; 395 } 396 if (!checkSurfaceValid()) 397 return false; 398 399 blink::WebGraphicsContext3D* webContext = context(); 400 401 // Release to skia textures that were previouosly released by the 402 // compositor. We do this before acquiring the next snapshot in 403 // order to cap maximum gpu memory consumption. 404 webContext->makeContextCurrent(); 405 flush(); 406 407 RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot()); 408 409 // Early exit if canvas was not drawn to since last prepareMailbox 410 if (image->uniqueID() == m_lastImageId) 411 return false; 412 m_lastImageId = image->uniqueID(); 413 414 MailboxInfo* mailboxInfo = createMailboxInfo(); 415 mailboxInfo->m_status = MailboxInUse; 416 mailboxInfo->m_image = image; 417 418 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0); 419 ASSERT(mailboxInfo->m_image.get()); 420 ASSERT(mailboxInfo->m_image->getTexture()); 421 422 // Because of texture sharing with the compositor, we must invalidate 423 // the state cached in skia so that the deferred copy on write 424 // in SkSurface_Gpu does not make any false assumptions. 425 mailboxInfo->m_image->getTexture()->textureParamsModified(); 426 427 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle()); 428 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 429 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 430 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 431 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 432 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name); 433 if (isHidden()) { 434 // With hidden canvases, we release the SkImage immediately because 435 // there is no need for animations to be double buffered. 436 mailboxInfo->m_image.clear(); 437 } else { 438 webContext->flush(); 439 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint(); 440 } 441 webContext->bindTexture(GL_TEXTURE_2D, 0); 442 // Because we are changing the texture binding without going through skia, 443 // we must dirty the context. 444 m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState); 445 446 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has 447 // live mailboxes 448 ASSERT(!mailboxInfo->m_parentLayerBridge); 449 mailboxInfo->m_parentLayerBridge = this; 450 *outMailbox = mailboxInfo->m_mailbox; 451 return true; 452 } 453 454 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() { 455 ASSERT(!m_destructionInProgress); 456 MailboxInfo* mailboxInfo; 457 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) { 458 if (mailboxInfo->m_status == MailboxAvailable) { 459 return mailboxInfo; 460 } 461 } 462 463 // No available mailbox: create one. 464 m_mailboxes.grow(m_mailboxes.size() + 1); 465 mailboxInfo = &m_mailboxes.last(); 466 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name); 467 // Worst case, canvas is triple buffered. More than 3 active mailboxes 468 // means there is a problem. 469 // For the single-threaded case, this value needs to be at least 470 // kMaxSwapBuffersPending+1 (in render_widget.h). 471 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2. 472 // TODO(piman): fix this. 473 ASSERT(m_mailboxes.size() <= 4); 474 ASSERT(mailboxInfo < m_mailboxes.end()); 475 return mailboxInfo; 476 } 477 478 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox) 479 { 480 freeReleasedMailbox(); // Never have more than one mailbox in the released state. 481 Vector<MailboxInfo>::iterator mailboxInfo; 482 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) { 483 if (nameEquals(mailboxInfo->m_mailbox, mailbox)) { 484 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint; 485 ASSERT(mailboxInfo->m_status == MailboxInUse); 486 mailboxInfo->m_status = MailboxReleased; 487 // Trigger Canvas2DLayerBridge self-destruction if this is the 488 // last live mailbox and the layer bridge is not externally 489 // referenced. 490 m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin(); 491 m_framesSinceMailboxRelease = 0; 492 if (isHidden()) { 493 freeReleasedMailbox(); 494 } else { 495 ASSERT(!m_destructionInProgress); 496 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this); 497 } 498 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this); 499 mailboxInfo->m_parentLayerBridge.clear(); 500 return; 501 } 502 } 503 } 504 505 blink::WebLayer* Canvas2DLayerBridge::layer() const 506 { 507 ASSERT(!m_destructionInProgress); 508 ASSERT(m_layer); 509 return m_layer->layer(); 510 } 511 512 void Canvas2DLayerBridge::willUse() 513 { 514 ASSERT(!m_destructionInProgress); 515 Canvas2DLayerManager::get().layerDidDraw(this); 516 m_didRecordDrawCommand = true; 517 } 518 519 Platform3DObject Canvas2DLayerBridge::getBackingTexture() 520 { 521 ASSERT(!m_destructionInProgress); 522 if (!checkSurfaceValid()) 523 return 0; 524 willUse(); 525 m_canvas->flush(); 526 context()->flush(); 527 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget(); 528 if (renderTarget) { 529 return renderTarget->asTexture()->getTextureHandle(); 530 } 531 return 0; 532 } 533 534 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) { 535 // This copy constructor should only be used for Vector reallocation 536 // Assuming 'other' is to be destroyed, we transfer m_image ownership 537 // rather than do a refcount dance. 538 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox)); 539 m_image = const_cast<MailboxInfo*>(&other)->m_image.release(); 540 m_status = other.m_status; 541 } 542 543 } 544