2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "platform/graphics/Canvas2DLayerBridge.h"
30 #include "GrContext.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"
44 InvalidMailboxIndex = -1,
47 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstanceCounter, ("Canvas2DLayerBridge"));
52 static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
57 SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
58 return adoptRef(SkSurface::NewRenderTarget(gr, info, msaaSampleCount));
61 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
63 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
64 OwnPtr<WebGraphicsContext3DProvider> contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
67 RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
70 RefPtr<Canvas2DLayerBridge> layerBridge;
71 OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
72 layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), surface.release(), msaaSampleCount, opacityMode));
73 return layerBridge.release();
76 Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, PassRefPtr<SkSurface> surface, int msaaSampleCount, OpacityMode opacityMode)
79 , m_contextProvider(contextProvider)
81 , m_msaaSampleCount(msaaSampleCount)
83 , m_didRecordDrawCommand(false)
84 , m_isSurfaceValid(true)
86 , m_framesSinceMailboxRelease(0)
87 , m_destructionInProgress(false)
88 , m_rateLimitingEnabled(false)
93 , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
97 ASSERT(m_contextProvider);
98 // Used by browser tests to detect the use of a Canvas2DLayerBridge.
99 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
100 m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
101 m_layer->setOpaque(opacityMode == Opaque);
102 m_layer->setBlendBackgroundColor(opacityMode != Opaque);
103 GraphicsLayer::registerContentsLayer(m_layer->layer());
104 m_layer->setRateLimitContext(m_rateLimitingEnabled);
105 m_canvas->setNotificationClient(this);
107 canvas2DLayerBridgeInstanceCounter.increment();
111 Canvas2DLayerBridge::~Canvas2DLayerBridge()
113 ASSERT(m_destructionInProgress);
114 ASSERT(!Canvas2DLayerManager::get().isInList(this));
116 freeReleasedMailbox();
118 Vector<MailboxInfo>::iterator mailboxInfo;
119 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
120 ASSERT(mailboxInfo->m_status != MailboxInUse);
121 ASSERT(mailboxInfo->m_status != MailboxReleased || m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid);
126 canvas2DLayerBridgeInstanceCounter.decrement();
130 void Canvas2DLayerBridge::beginDestruction()
132 ASSERT(!m_destructionInProgress);
133 setRateLimitingEnabled(false);
134 m_canvas->silentFlush();
136 freeTransientResources();
138 m_destructionInProgress = true;
139 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
140 m_canvas->setNotificationClient(0);
143 m_layer->clearTexture();
144 // Orphaning the layer is required to trigger the recration of a new layer
145 // in the case where destruction is caused by a canvas resize. Test:
146 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
147 m_layer->layer()->removeFromParent();
148 // To anyone who ever hits this assert: Please update crbug.com/344666
150 ASSERT(!m_bytesAllocated);
153 void Canvas2DLayerBridge::setIsHidden(bool hidden)
155 ASSERT(!m_destructionInProgress);
156 bool newHiddenValue = hidden || m_destructionInProgress;
157 if (m_isHidden == newHiddenValue)
160 m_isHidden = newHiddenValue;
162 freeTransientResources();
166 void Canvas2DLayerBridge::willAccessPixels()
168 // A readback operation may alter the texture parameters, which may affect
169 // the compositor's behavior. Therefore, we must trigger copy-on-write
170 // even though we are not technically writing to the texture, only to its
172 m_surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
175 void Canvas2DLayerBridge::freeTransientResources()
177 ASSERT(!m_destructionInProgress);
178 if (!m_isSurfaceValid)
180 freeReleasedMailbox();
182 freeMemoryIfPossible(bytesAllocated());
183 ASSERT(!hasTransientResources());
186 bool Canvas2DLayerBridge::hasTransientResources() const
188 return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated());
191 void Canvas2DLayerBridge::limitPendingFrames()
193 ASSERT(!m_destructionInProgress);
195 freeTransientResources();
198 if (m_didRecordDrawCommand) {
200 m_didRecordDrawCommand = false;
201 if (m_framesPending > 1) {
202 // Turn on the rate limiter if this layer tends to accumulate a
203 // non-discardable multi-frame backlog of draw commands.
204 setRateLimitingEnabled(true);
206 if (m_rateLimitingEnabled) {
210 ++m_framesSinceMailboxRelease;
211 if (releasedMailboxHasExpired()) {
212 freeReleasedMailbox();
216 void Canvas2DLayerBridge::prepareForDraw()
218 ASSERT(!m_destructionInProgress);
220 if (!checkSurfaceValid()) {
222 // drop pending commands because there is no surface to draw to
223 m_canvas->silentFlush();
227 context()->makeContextCurrent();
230 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
232 ASSERT(!m_destructionInProgress);
233 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
234 m_bytesAllocated = bytesAllocated;
235 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta);
238 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
240 return m_canvas->storageAllocatedForRecording();
243 void Canvas2DLayerBridge::flushedDrawCommands()
245 ASSERT(!m_destructionInProgress);
246 storageAllocatedForRecordingChanged(storageAllocatedForRecording());
250 void Canvas2DLayerBridge::skippedPendingDrawCommands()
252 ASSERT(!m_destructionInProgress);
253 // Stop triggering the rate limiter if SkDeferredCanvas is detecting
254 // and optimizing overdraw.
255 setRateLimitingEnabled(false);
256 flushedDrawCommands();
259 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
261 ASSERT(!m_destructionInProgress);
262 if (m_rateLimitingEnabled != enabled) {
263 m_rateLimitingEnabled = enabled;
264 m_layer->setRateLimitContext(m_rateLimitingEnabled);
268 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
270 ASSERT(!m_destructionInProgress);
271 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
272 m_bytesAllocated -= bytesFreed;
274 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
278 void Canvas2DLayerBridge::flush()
280 ASSERT(!m_destructionInProgress);
281 if (m_canvas->hasPendingCommands()) {
282 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
283 freeReleasedMailbox(); // To avoid unnecessary triple-buffering
288 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
290 // This heuristic indicates that the canvas is not being
291 // actively presented by the compositor (3 frames rendered since
292 // last mailbox release), suggesting that double buffering is not required.
293 return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
296 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
298 return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
301 bool Canvas2DLayerBridge::hasReleasedMailbox() const
303 return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
306 void Canvas2DLayerBridge::freeReleasedMailbox()
308 if (!m_isSurfaceValid || m_contextProvider->context3d()->isContextLost())
310 MailboxInfo* mailboxInfo = releasedMailboxInfo();
314 ASSERT(mailboxInfo->m_status == MailboxReleased);
315 if (mailboxInfo->m_mailbox.syncPoint) {
316 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
317 mailboxInfo->m_mailbox.syncPoint = 0;
319 // Invalidate texture state in case the compositor altered it since the copy-on-write.
320 if (mailboxInfo->m_image) {
321 if (isHidden() || releasedMailboxHasExpired())
322 mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
323 mailboxInfo->m_image->getTexture()->textureParamsModified();
324 mailboxInfo->m_image.clear();
326 mailboxInfo->m_status = MailboxAvailable;
327 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
328 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
331 WebGraphicsContext3D* Canvas2DLayerBridge::context()
333 // Check on m_layer is necessary because context() may be called during
334 // the destruction of m_layer
335 if (m_layer && !m_destructionInProgress)
336 checkSurfaceValid(); // To ensure rate limiter is disabled if context is lost.
337 return m_contextProvider ? m_contextProvider->context3d() : 0;
340 bool Canvas2DLayerBridge::checkSurfaceValid()
342 ASSERT(!m_destructionInProgress);
343 if (m_destructionInProgress || !m_isSurfaceValid)
345 if (m_contextProvider->context3d()->isContextLost()) {
346 m_isSurfaceValid = false;
349 m_imageBuffer->notifySurfaceInvalid();
350 setRateLimitingEnabled(false);
352 return m_isSurfaceValid;
355 bool Canvas2DLayerBridge::restoreSurface()
357 ASSERT(!m_destructionInProgress);
358 if (m_destructionInProgress)
360 ASSERT(m_layer && !m_isSurfaceValid);
362 WebGraphicsContext3D* sharedContext = 0;
363 // We must clear the mailboxes before calling m_layer->clearTexture() to prevent
364 // re-entry via mailboxReleased from operating on defunct GrContext objects.
366 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
367 m_layer->clearTexture();
368 m_contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
369 if (m_contextProvider)
370 sharedContext = m_contextProvider->context3d();
372 if (sharedContext && !sharedContext->isContextLost()) {
373 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
374 RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
376 m_surface = surface.release();
377 m_canvas->setSurface(m_surface.get());
378 m_isSurfaceValid = true;
379 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
383 return m_isSurfaceValid;
386 bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap)
388 if (m_destructionInProgress) {
389 // It can be hit in the following sequence.
390 // 1. Canvas draws something.
391 // 2. The compositor begins the frame.
392 // 3. Javascript makes a context be lost.
397 // Using accelerated 2d canvas with software renderer, which
398 // should only happen in tests that use fake graphics contexts
399 // or in Android WebView in software mode. In this case, we do
400 // not care about producing any results for this canvas.
401 m_canvas->silentFlush();
405 if (!checkSurfaceValid())
408 WebGraphicsContext3D* webContext = context();
410 // Release to skia textures that were previouosly released by the
411 // compositor. We do this before acquiring the next snapshot in
412 // order to cap maximum gpu memory consumption.
413 webContext->makeContextCurrent();
416 RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
418 // Early exit if canvas was not drawn to since last prepareMailbox
419 if (image->uniqueID() == m_lastImageId)
421 m_lastImageId = image->uniqueID();
423 MailboxInfo* mailboxInfo = createMailboxInfo();
424 mailboxInfo->m_status = MailboxInUse;
425 mailboxInfo->m_image = image;
427 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
428 ASSERT(mailboxInfo->m_image.get());
430 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
432 ASSERT(!mailboxInfo->m_parentLayerBridge);
433 mailboxInfo->m_parentLayerBridge = this;
434 *outMailbox = mailboxInfo->m_mailbox;
436 GrContext* grContext = m_contextProvider->grContext();
438 return true; // for testing: skip gl stuff when using a mock graphics context.
440 ASSERT(mailboxInfo->m_image->getTexture());
442 // Because of texture sharing with the compositor, we must invalidate
443 // the state cached in skia so that the deferred copy on write
444 // in SkSurface_Gpu does not make any false assumptions.
445 mailboxInfo->m_image->getTexture()->textureParamsModified();
447 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
448 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
449 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
450 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
451 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
452 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
454 // With hidden canvases, we release the SkImage immediately because
455 // there is no need for animations to be double buffered.
456 mailboxInfo->m_image.clear();
459 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
461 webContext->bindTexture(GL_TEXTURE_2D, 0);
462 // Because we are changing the texture binding without going through skia,
463 // we must dirty the context.
464 grContext->resetContext(kTextureBinding_GrGLBackendState);
469 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
470 ASSERT(!m_destructionInProgress);
471 MailboxInfo* mailboxInfo;
472 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
473 if (mailboxInfo->m_status == MailboxAvailable) {
478 // No available mailbox: create one.
479 m_mailboxes.grow(m_mailboxes.size() + 1);
480 mailboxInfo = &m_mailboxes.last();
481 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
482 // Worst case, canvas is triple buffered. More than 3 active mailboxes
483 // means there is a problem.
484 // For the single-threaded case, this value needs to be at least
485 // kMaxSwapBuffersPending+1 (in render_widget.h).
486 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
487 // TODO(piman): fix this.
488 ASSERT(m_mailboxes.size() <= 4);
489 ASSERT(mailboxInfo < m_mailboxes.end());
493 void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource)
495 freeReleasedMailbox(); // Never have more than one mailbox in the released state.
496 bool contextLost = !m_isSurfaceValid || m_contextProvider->context3d()->isContextLost();
497 Vector<MailboxInfo>::iterator mailboxInfo;
498 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
499 if (nameEquals(mailboxInfo->m_mailbox, mailbox)) {
500 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
501 ASSERT(mailboxInfo->m_status == MailboxInUse);
502 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
505 // No need to clean up the mailbox resource, but make sure the
506 // mailbox can also be reusable once the context is restored.
507 mailboxInfo->m_status = MailboxAvailable;
508 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
509 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
510 } else if (lostResource) {
511 // In case of the resource is lost, we need to delete the backing
512 // texture and remove the mailbox from list to avoid reusing it
514 if (mailboxInfo->m_image) {
515 GrTexture* texture = mailboxInfo->m_image->getTexture();
517 texture->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
518 texture->textureParamsModified();
520 mailboxInfo->m_image.clear();
522 if (m_destructionInProgress) {
523 mailboxInfo->m_status = MailboxAvailable; // To satisfy assert in destructor
525 // The following line may trigger self destruction. We do not care about
526 // not cleaning up m_mailboxes during destruction sequence because
527 // mailboxes will not be recycled after this point. Calling remove()
528 // could trigger a memory use after free, so we just clear the self
529 // reference to be safe, and we let the Canvas2DLayerBridge destructor
530 // take care of freeing m_mailboxes.
531 mailboxInfo->m_parentLayerBridge.clear();
533 size_t i = mailboxInfo - m_mailboxes.begin();
534 m_mailboxes.remove(i); // indirectly clears mailboxInfo->m_parentLayerBridge
535 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
537 // mailboxInfo is not valid from this point, so we return immediately.
540 mailboxInfo->m_status = MailboxReleased;
541 m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
542 m_framesSinceMailboxRelease = 0;
544 freeReleasedMailbox();
546 ASSERT(!m_destructionInProgress);
547 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
550 // Trigger Canvas2DLayerBridge self-destruction if this is the
551 // last live mailbox and the layer bridge is not externally
553 mailboxInfo->m_parentLayerBridge.clear();
559 WebLayer* Canvas2DLayerBridge::layer() const
561 ASSERT(!m_destructionInProgress);
563 return m_layer->layer();
566 void Canvas2DLayerBridge::finalizeFrame(const FloatRect &dirtyRect)
568 ASSERT(!m_destructionInProgress);
569 Canvas2DLayerManager::get().layerDidDraw(this);
570 m_layer->layer()->invalidateRect(dirtyRect);
571 m_didRecordDrawCommand = true;
574 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
576 ASSERT(!m_destructionInProgress);
577 if (!checkSurfaceValid())
581 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
583 return renderTarget->asTexture()->getTextureHandle();
588 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
589 // This copy constructor should only be used for Vector reallocation
590 // Assuming 'other' is to be destroyed, we transfer m_image and
591 // m_parentLayerBridge ownership rather than do a refcount dance.
592 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
593 m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
594 m_parentLayerBridge = const_cast<MailboxInfo*>(&other)->m_parentLayerBridge.release();
595 m_status = other.m_status;