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 "platform/graphics/gpu/SharedGraphicsContext3D.h" 37 #include "public/platform/Platform.h" 38 #include "public/platform/WebCompositorSupport.h" 39 #include "public/platform/WebGraphicsContext3D.h" 40 41 using blink::WebExternalTextureLayer; 42 using blink::WebGraphicsContext3D; 43 44 namespace WebCore { 45 46 static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount) 47 { 48 ASSERT(!context3D->webContext()->isContextLost()); 49 GrContext* gr = context3D->grContext(); 50 if (!gr) 51 return 0; 52 gr->resetContext(); 53 SkImageInfo info; 54 info.fWidth = size.width(); 55 info.fHeight = size.height(); 56 info.fColorType = kPMColor_SkColorType; 57 info.fAlphaType = kPremul_SkAlphaType; 58 return adoptRef(SkSurface::NewRenderTarget(gr, info, msaaSampleCount)); 59 } 60 61 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount) 62 { 63 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); 64 RefPtr<GraphicsContext3D> context = SharedGraphicsContext3D::get(); 65 RefPtr<SkSurface> surface(createSkSurface(context.get(), size, msaaSampleCount)); 66 if (!surface) 67 return 0; 68 RefPtr<Canvas2DLayerBridge> layerBridge; 69 OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get())); 70 layerBridge = adoptRef(new Canvas2DLayerBridge(context, canvas.release(), msaaSampleCount, opacityMode)); 71 return layerBridge.release(); 72 } 73 74 Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode) 75 : m_canvas(canvas) 76 , m_context(context) 77 , m_msaaSampleCount(msaaSampleCount) 78 , m_bytesAllocated(0) 79 , m_didRecordDrawCommand(false) 80 , m_surfaceIsValid(true) 81 , m_framesPending(0) 82 , m_destructionInProgress(false) 83 , m_rateLimitingEnabled(false) 84 , m_next(0) 85 , m_prev(0) 86 , m_lastImageId(0) 87 { 88 ASSERT(m_canvas); 89 // Used by browser tests to detect the use of a Canvas2DLayerBridge. 90 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation"); 91 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this)); 92 m_layer->setOpaque(opacityMode == Opaque); 93 m_layer->setBlendBackgroundColor(opacityMode != Opaque); 94 GraphicsLayer::registerContentsLayer(m_layer->layer()); 95 m_layer->setRateLimitContext(m_rateLimitingEnabled); 96 m_canvas->setNotificationClient(this); 97 } 98 99 Canvas2DLayerBridge::~Canvas2DLayerBridge() 100 { 101 ASSERT(m_destructionInProgress); 102 m_layer.clear(); 103 Vector<MailboxInfo>::iterator mailboxInfo; 104 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) { 105 ASSERT(mailboxInfo->m_status != MailboxInUse); 106 if (mailboxInfo->m_status == MailboxReleased) { 107 if (mailboxInfo->m_mailbox.syncPoint) { 108 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); 109 mailboxInfo->m_mailbox.syncPoint = 0; 110 } 111 // Invalidate texture state in case the compositor altered it since the copy-on-write. 112 mailboxInfo->m_image->getTexture()->invalidateCachedState(); 113 } 114 } 115 m_mailboxes.clear(); 116 } 117 118 void Canvas2DLayerBridge::beginDestruction() 119 { 120 ASSERT(!m_destructionInProgress); 121 m_destructionInProgress = true; 122 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); 123 m_canvas->setNotificationClient(0); 124 m_layer->clearTexture(); 125 Canvas2DLayerManager::get().layerToBeDestroyed(this); 126 // Orphaning the layer is required to trigger the recration of a new layer 127 // in the case where destruction is caused by a canvas resize. Test: 128 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html 129 m_layer->layer()->removeFromParent(); 130 } 131 132 void Canvas2DLayerBridge::limitPendingFrames() 133 { 134 ASSERT(!m_destructionInProgress); 135 if (m_didRecordDrawCommand) { 136 m_framesPending++; 137 m_didRecordDrawCommand = false; 138 if (m_framesPending > 1) { 139 // Turn on the rate limiter if this layer tends to accumulate a 140 // non-discardable multi-frame backlog of draw commands. 141 setRateLimitingEnabled(true); 142 } 143 if (m_rateLimitingEnabled) { 144 flush(); 145 } 146 } 147 } 148 149 void Canvas2DLayerBridge::prepareForDraw() 150 { 151 ASSERT(!m_destructionInProgress); 152 ASSERT(m_layer); 153 if (!isValid()) { 154 if (m_canvas) { 155 // drop pending commands because there is no surface to draw to 156 m_canvas->silentFlush(); 157 } 158 return; 159 } 160 m_context->makeContextCurrent(); 161 } 162 163 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated) 164 { 165 ASSERT(!m_destructionInProgress); 166 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated; 167 m_bytesAllocated = bytesAllocated; 168 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, delta); 169 } 170 171 size_t Canvas2DLayerBridge::storageAllocatedForRecording() 172 { 173 ASSERT(!m_destructionInProgress); 174 return m_canvas->storageAllocatedForRecording(); 175 } 176 177 void Canvas2DLayerBridge::flushedDrawCommands() 178 { 179 ASSERT(!m_destructionInProgress); 180 storageAllocatedForRecordingChanged(storageAllocatedForRecording()); 181 m_framesPending = 0; 182 } 183 184 void Canvas2DLayerBridge::skippedPendingDrawCommands() 185 { 186 ASSERT(!m_destructionInProgress); 187 // Stop triggering the rate limiter if SkDeferredCanvas is detecting 188 // and optimizing overdraw. 189 setRateLimitingEnabled(false); 190 flushedDrawCommands(); 191 } 192 193 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled) 194 { 195 ASSERT(!m_destructionInProgress || !enabled); 196 if (m_rateLimitingEnabled != enabled) { 197 m_rateLimitingEnabled = enabled; 198 m_layer->setRateLimitContext(m_rateLimitingEnabled); 199 } 200 } 201 202 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree) 203 { 204 ASSERT(!m_destructionInProgress); 205 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree); 206 if (bytesFreed) 207 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed)); 208 m_bytesAllocated -= bytesFreed; 209 return bytesFreed; 210 } 211 212 void Canvas2DLayerBridge::flush() 213 { 214 ASSERT(!m_destructionInProgress); 215 if (m_canvas->hasPendingCommands()) { 216 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush"); 217 m_canvas->flush(); 218 m_context->flush(); 219 } 220 } 221 222 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context() 223 { 224 // Check on m_layer is necessary because context() may be called during 225 // the destruction of m_layer 226 if (m_layer) { 227 isValid(); // To ensure rate limiter is disabled if context is lost. 228 } 229 return m_context->webContext(); 230 } 231 232 bool Canvas2DLayerBridge::isValid() 233 { 234 ASSERT(m_layer); 235 if (m_destructionInProgress) 236 return false; 237 if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) { 238 // Attempt to recover. 239 m_layer->clearTexture(); 240 m_mailboxes.clear(); 241 RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get(); 242 if (!sharedContext || sharedContext->webContext()->isContextLost()) { 243 m_surfaceIsValid = false; 244 } else { 245 m_context = sharedContext; 246 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height()); 247 RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount)); 248 if (surface.get()) { 249 m_canvas->setSurface(surface.get()); 250 m_surfaceIsValid = true; 251 // FIXME: draw sad canvas picture into new buffer crbug.com/243842 252 } else { 253 // Surface allocation failed. Set m_surfaceIsValid to false to 254 // trigger subsequent retry. 255 m_surfaceIsValid = false; 256 } 257 } 258 } 259 if (!m_surfaceIsValid) 260 setRateLimitingEnabled(false); 261 return m_surfaceIsValid; 262 } 263 264 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap) 265 { 266 if (bitmap) { 267 // Using accelerated 2d canvas with software renderer, which 268 // should only happen in tests that use fake graphics contexts 269 // or in Android WebView in software mode. In this case, we do 270 // not care about producing any results for this canvas. 271 m_canvas->silentFlush(); 272 m_lastImageId = 0; 273 return false; 274 } 275 if (!isValid()) 276 return false; 277 // Release to skia textures that were previouosly released by the 278 // compositor. We do this before acquiring the next snapshot in 279 // order to cap maximum gpu memory consumption. 280 m_context->makeContextCurrent(); 281 flush(); 282 Vector<MailboxInfo>::iterator mailboxInfo; 283 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) { 284 if (mailboxInfo->m_status == MailboxReleased) { 285 if (mailboxInfo->m_mailbox.syncPoint) { 286 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); 287 mailboxInfo->m_mailbox.syncPoint = 0; 288 } 289 // Invalidate texture state in case the compositor altered it since the copy-on-write. 290 mailboxInfo->m_image->getTexture()->invalidateCachedState(); 291 mailboxInfo->m_image.reset(0); 292 mailboxInfo->m_status = MailboxAvailable; 293 } 294 } 295 SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot()); 296 // Early exit if canvas was not drawn to since last prepareMailbox 297 if (image->uniqueID() == m_lastImageId) 298 return false; 299 m_lastImageId = image->uniqueID(); 300 301 mailboxInfo = createMailboxInfo(); 302 mailboxInfo->m_status = MailboxInUse; 303 mailboxInfo->m_image.swap(&image); 304 // Because of texture sharing with the compositor, we must invalidate 305 // the state cached in skia so that the deferred copy on write 306 // in SkSurface_Gpu does not make any false assumptions. 307 mailboxInfo->m_image->getTexture()->invalidateCachedState(); 308 309 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0); 310 ASSERT(mailboxInfo->m_image.get()); 311 ASSERT(mailboxInfo->m_image->getTexture()); 312 313 m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle()); 314 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 315 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 316 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 317 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 318 context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name); 319 context()->flush(); 320 mailboxInfo->m_mailbox.syncPoint = context()->insertSyncPoint(); 321 m_context->bindTexture(GL_TEXTURE_2D, 0); 322 // Because we are changing the texture binding without going through skia, 323 // we must dirty the context. 324 m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState); 325 326 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has 327 // live mailboxes 328 ASSERT(!mailboxInfo->m_parentLayerBridge); 329 mailboxInfo->m_parentLayerBridge = this; 330 *outMailbox = mailboxInfo->m_mailbox; 331 return true; 332 } 333 334 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() { 335 ASSERT(!m_destructionInProgress); 336 MailboxInfo* mailboxInfo; 337 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) { 338 if (mailboxInfo->m_status == MailboxAvailable) { 339 return mailboxInfo; 340 } 341 } 342 343 // No available mailbox: create one. 344 m_mailboxes.grow(m_mailboxes.size() + 1); 345 mailboxInfo = &m_mailboxes.last(); 346 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name); 347 // Worst case, canvas is triple buffered. More than 3 active mailboxes 348 // means there is a problem. 349 // For the single-threaded case, this value needs to be at least 350 // kMaxSwapBuffersPending+1 (in render_widget.h). 351 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2. 352 // TODO(piman): fix this. 353 ASSERT(m_mailboxes.size() <= 4); 354 ASSERT(mailboxInfo < m_mailboxes.end()); 355 return mailboxInfo; 356 } 357 358 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox) 359 { 360 Vector<MailboxInfo>::iterator mailboxInfo; 361 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) { 362 if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) { 363 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint; 364 ASSERT(mailboxInfo->m_status == MailboxInUse); 365 mailboxInfo->m_status = MailboxReleased; 366 // Trigger Canvas2DLayerBridge self-destruction if this is the 367 // last live mailbox and the layer bridge is not externally 368 // referenced. 369 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this); 370 mailboxInfo->m_parentLayerBridge.clear(); 371 return; 372 } 373 } 374 } 375 376 blink::WebLayer* Canvas2DLayerBridge::layer() const 377 { 378 ASSERT(m_layer); 379 return m_layer->layer(); 380 } 381 382 void Canvas2DLayerBridge::willUse() 383 { 384 ASSERT(!m_destructionInProgress); 385 Canvas2DLayerManager::get().layerDidDraw(this); 386 m_didRecordDrawCommand = true; 387 } 388 389 Platform3DObject Canvas2DLayerBridge::getBackingTexture() 390 { 391 ASSERT(!m_destructionInProgress); 392 if (!isValid()) 393 return 0; 394 willUse(); 395 m_canvas->flush(); 396 m_context->flush(); 397 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget(); 398 if (renderTarget) { 399 return renderTarget->asTexture()->getTextureHandle(); 400 } 401 return 0; 402 } 403 404 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) { 405 // This copy constructor should only be used for Vector reallocation 406 // Assuming 'other' is to be destroyed, we swap m_image ownership 407 // rather than do a refcount dance. 408 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox)); 409 m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image)); 410 m_status = other.m_status; 411 } 412 413 } 414