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"
42 using blink::WebExternalTextureLayer;
43 using blink::WebGraphicsContext3D;
47 InvalidMailboxIndex = -1,
50 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstanceCounter, ("Canvas2DLayerBridge"));
55 static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
61 info.fWidth = size.width();
62 info.fHeight = size.height();
63 info.fColorType = kPMColor_SkColorType;
64 info.fAlphaType = kPremul_SkAlphaType;
65 return adoptRef(SkSurface::NewScratchRenderTarget(gr, info, msaaSampleCount));
68 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
70 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
71 OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
74 RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
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();
83 Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
85 , m_contextProvider(contextProvider)
86 , m_msaaSampleCount(msaaSampleCount)
88 , m_didRecordDrawCommand(false)
89 , m_surfaceIsValid(true)
91 , m_framesSinceMailboxRelease(0)
92 , m_destructionInProgress(false)
93 , m_rateLimitingEnabled(false)
98 , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
101 ASSERT(m_contextProvider);
102 // Used by browser tests to detect the use of a Canvas2DLayerBridge.
103 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
104 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
105 m_layer->setOpaque(opacityMode == Opaque);
106 m_layer->setBlendBackgroundColor(opacityMode != Opaque);
107 GraphicsLayer::registerContentsLayer(m_layer->layer());
108 m_layer->setRateLimitContext(m_rateLimitingEnabled);
109 m_canvas->setNotificationClient(this);
111 canvas2DLayerBridgeInstanceCounter.increment();
115 Canvas2DLayerBridge::~Canvas2DLayerBridge()
117 ASSERT(m_destructionInProgress);
119 freeReleasedMailbox();
121 Vector<MailboxInfo>::iterator mailboxInfo;
122 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
123 ASSERT(mailboxInfo->m_status != MailboxInUse);
124 ASSERT(mailboxInfo->m_status != MailboxReleased);
129 canvas2DLayerBridgeInstanceCounter.decrement();
133 void Canvas2DLayerBridge::beginDestruction()
135 ASSERT(!m_destructionInProgress);
136 setRateLimitingEnabled(false);
137 m_canvas->silentFlush();
138 freeTransientResources();
140 m_destructionInProgress = true;
141 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
142 m_canvas->setNotificationClient(0);
144 m_layer->clearTexture();
145 // Orphaning the layer is required to trigger the recration of a new layer
146 // in the case where destruction is caused by a canvas resize. Test:
147 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
148 m_layer->layer()->removeFromParent();
149 // To anyone who ever hits this assert: Please update crbug.com/344666
151 ASSERT(!m_bytesAllocated);
154 void Canvas2DLayerBridge::setIsHidden(bool hidden)
156 ASSERT(!m_destructionInProgress);
157 bool newHiddenValue = hidden || m_destructionInProgress;
158 if (m_isHidden == newHiddenValue)
161 m_isHidden = newHiddenValue;
163 freeTransientResources();
167 void Canvas2DLayerBridge::freeTransientResources()
169 ASSERT(!m_destructionInProgress);
170 freeReleasedMailbox();
172 freeMemoryIfPossible(bytesAllocated());
173 ASSERT(!hasTransientResources());
176 bool Canvas2DLayerBridge::hasTransientResources() const
178 return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated());
181 void Canvas2DLayerBridge::limitPendingFrames()
183 ASSERT(!m_destructionInProgress);
185 freeTransientResources();
188 if (m_didRecordDrawCommand) {
190 m_didRecordDrawCommand = false;
191 if (m_framesPending > 1) {
192 // Turn on the rate limiter if this layer tends to accumulate a
193 // non-discardable multi-frame backlog of draw commands.
194 setRateLimitingEnabled(true);
196 if (m_rateLimitingEnabled) {
200 ++m_framesSinceMailboxRelease;
201 if (releasedMailboxHasExpired()) {
202 freeReleasedMailbox();
206 void Canvas2DLayerBridge::prepareForDraw()
208 ASSERT(!m_destructionInProgress);
210 if (!surfaceIsValid() && !recoverSurface()) {
212 // drop pending commands because there is no surface to draw to
213 m_canvas->silentFlush();
217 context()->makeContextCurrent();
220 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
222 ASSERT(!m_destructionInProgress);
223 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
224 m_bytesAllocated = bytesAllocated;
225 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta);
228 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
230 return m_canvas->storageAllocatedForRecording();
233 void Canvas2DLayerBridge::flushedDrawCommands()
235 ASSERT(!m_destructionInProgress);
236 storageAllocatedForRecordingChanged(storageAllocatedForRecording());
240 void Canvas2DLayerBridge::skippedPendingDrawCommands()
242 ASSERT(!m_destructionInProgress);
243 // Stop triggering the rate limiter if SkDeferredCanvas is detecting
244 // and optimizing overdraw.
245 setRateLimitingEnabled(false);
246 flushedDrawCommands();
249 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
251 ASSERT(!m_destructionInProgress);
252 if (m_rateLimitingEnabled != enabled) {
253 m_rateLimitingEnabled = enabled;
254 m_layer->setRateLimitContext(m_rateLimitingEnabled);
258 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
260 ASSERT(!m_destructionInProgress);
261 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
262 m_bytesAllocated -= bytesFreed;
264 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
268 void Canvas2DLayerBridge::flush()
270 ASSERT(!m_destructionInProgress);
271 if (m_canvas->hasPendingCommands()) {
272 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
273 freeReleasedMailbox(); // To avoid unnecessary triple-buffering
278 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
280 // This heuristic indicates that the canvas is not being
281 // actively presented by the compositor (3 frames rendered since
282 // last mailbox release), suggesting that double buffering is not required.
283 return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
286 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
288 return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
291 bool Canvas2DLayerBridge::hasReleasedMailbox() const
293 return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
296 void Canvas2DLayerBridge::freeReleasedMailbox()
298 if (m_contextProvider->context3d()->isContextLost() || !m_surfaceIsValid)
300 MailboxInfo* mailboxInfo = releasedMailboxInfo();
304 ASSERT(mailboxInfo->m_status == MailboxReleased);
305 if (mailboxInfo->m_mailbox.syncPoint) {
306 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
307 mailboxInfo->m_mailbox.syncPoint = 0;
309 // Invalidate texture state in case the compositor altered it since the copy-on-write.
310 if (mailboxInfo->m_image) {
311 if (isHidden() || releasedMailboxHasExpired())
312 mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
313 mailboxInfo->m_image->getTexture()->invalidateCachedState();
314 mailboxInfo->m_image.clear();
316 mailboxInfo->m_status = MailboxAvailable;
317 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
318 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
321 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
323 // Check on m_layer is necessary because context() may be called during
324 // the destruction of m_layer
325 if (m_layer && !m_destructionInProgress && !surfaceIsValid()) {
326 recoverSurface(); // To ensure rate limiter is disabled if context is lost.
328 return m_contextProvider->context3d();
331 bool Canvas2DLayerBridge::surfaceIsValid()
333 ASSERT(!m_destructionInProgress);
334 return !m_destructionInProgress && !m_contextProvider->context3d()->isContextLost() && m_surfaceIsValid;
337 bool Canvas2DLayerBridge::recoverSurface()
339 ASSERT(!m_destructionInProgress);
340 ASSERT(m_layer && !surfaceIsValid());
341 if (m_destructionInProgress)
344 blink::WebGraphicsContext3D* sharedContext = 0;
345 // We must clear the mailboxes before calling m_layer->clearTexture() to prevent
346 // re-entry via mailboxReleased from operating on defunct GrContext objects.
348 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
349 m_layer->clearTexture();
350 m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
351 if (m_contextProvider)
352 sharedContext = m_contextProvider->context3d();
354 if (!sharedContext || sharedContext->isContextLost()) {
355 m_surfaceIsValid = false;
357 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
358 RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
360 m_canvas->setSurface(surface.get());
361 m_surfaceIsValid = true;
362 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
364 // Surface allocation failed. Set m_surfaceIsValid to false to
365 // trigger subsequent retry.
366 m_surfaceIsValid = false;
369 if (!m_surfaceIsValid)
370 setRateLimitingEnabled(false);
372 return m_surfaceIsValid;
375 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
377 ASSERT(!m_destructionInProgress);
379 // Using accelerated 2d canvas with software renderer, which
380 // should only happen in tests that use fake graphics contexts
381 // or in Android WebView in software mode. In this case, we do
382 // not care about producing any results for this canvas.
383 m_canvas->silentFlush();
387 if (!surfaceIsValid() && !recoverSurface())
390 blink::WebGraphicsContext3D* webContext = context();
392 // Release to skia textures that were previouosly released by the
393 // compositor. We do this before acquiring the next snapshot in
394 // order to cap maximum gpu memory consumption.
395 webContext->makeContextCurrent();
398 RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
400 // Early exit if canvas was not drawn to since last prepareMailbox
401 if (image->uniqueID() == m_lastImageId)
403 m_lastImageId = image->uniqueID();
405 MailboxInfo* mailboxInfo = createMailboxInfo();
406 mailboxInfo->m_status = MailboxInUse;
407 mailboxInfo->m_image = image;
409 // Because of texture sharing with the compositor, we must invalidate
410 // the state cached in skia so that the deferred copy on write
411 // in SkSurface_Gpu does not make any false assumptions.
412 mailboxInfo->m_image->getTexture()->invalidateCachedState();
414 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
415 ASSERT(mailboxInfo->m_image.get());
416 ASSERT(mailboxInfo->m_image->getTexture());
418 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
419 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
420 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
421 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
422 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
423 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
425 // With hidden canvases, we release the SkImage immediately because
426 // there is no need for animations to be double buffered.
427 mailboxInfo->m_image.clear();
430 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
432 webContext->bindTexture(GL_TEXTURE_2D, 0);
433 // Because we are changing the texture binding without going through skia,
434 // we must dirty the context.
435 m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
437 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
439 ASSERT(!mailboxInfo->m_parentLayerBridge);
440 mailboxInfo->m_parentLayerBridge = this;
441 *outMailbox = mailboxInfo->m_mailbox;
445 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
446 ASSERT(!m_destructionInProgress);
447 MailboxInfo* mailboxInfo;
448 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
449 if (mailboxInfo->m_status == MailboxAvailable) {
454 // No available mailbox: create one.
455 m_mailboxes.grow(m_mailboxes.size() + 1);
456 mailboxInfo = &m_mailboxes.last();
457 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
458 // Worst case, canvas is triple buffered. More than 3 active mailboxes
459 // means there is a problem.
460 // For the single-threaded case, this value needs to be at least
461 // kMaxSwapBuffersPending+1 (in render_widget.h).
462 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
463 // TODO(piman): fix this.
464 ASSERT(m_mailboxes.size() <= 4);
465 ASSERT(mailboxInfo < m_mailboxes.end());
469 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
471 freeReleasedMailbox(); // Never have more than one mailbox in the released state.
472 Vector<MailboxInfo>::iterator mailboxInfo;
473 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
474 if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
475 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
476 ASSERT(mailboxInfo->m_status == MailboxInUse);
477 mailboxInfo->m_status = MailboxReleased;
478 // Trigger Canvas2DLayerBridge self-destruction if this is the
479 // last live mailbox and the layer bridge is not externally
481 m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
482 m_framesSinceMailboxRelease = 0;
484 freeReleasedMailbox();
486 ASSERT(!m_destructionInProgress);
487 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
489 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
490 mailboxInfo->m_parentLayerBridge.clear();
496 blink::WebLayer* Canvas2DLayerBridge::layer() const
498 ASSERT(!m_destructionInProgress);
500 return m_layer->layer();
503 void Canvas2DLayerBridge::willUse()
505 ASSERT(!m_destructionInProgress);
506 Canvas2DLayerManager::get().layerDidDraw(this);
507 m_didRecordDrawCommand = true;
510 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
512 ASSERT(!m_destructionInProgress);
513 if (!surfaceIsValid() && !recoverSurface())
518 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
520 return renderTarget->asTexture()->getTextureHandle();
525 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
526 // This copy constructor should only be used for Vector reallocation
527 // Assuming 'other' is to be destroyed, we transfer m_image ownership
528 // rather than do a refcount dance.
529 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
530 m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
531 m_status = other.m_status;