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::NewRenderTarget(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)
87 , m_msaaSampleCount(msaaSampleCount)
89 , m_didRecordDrawCommand(false)
90 , m_isSurfaceValid(true)
92 , m_framesSinceMailboxRelease(0)
93 , m_destructionInProgress(false)
94 , m_rateLimitingEnabled(false)
99 , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
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);
112 canvas2DLayerBridgeInstanceCounter.increment();
116 Canvas2DLayerBridge::~Canvas2DLayerBridge()
118 ASSERT(m_destructionInProgress);
120 freeReleasedMailbox();
122 Vector<MailboxInfo>::iterator mailboxInfo;
123 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
124 ASSERT(mailboxInfo->m_status != MailboxInUse);
125 ASSERT(mailboxInfo->m_status != MailboxReleased || m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid);
130 canvas2DLayerBridgeInstanceCounter.decrement();
134 void Canvas2DLayerBridge::beginDestruction()
136 ASSERT(!m_destructionInProgress);
137 setRateLimitingEnabled(false);
138 m_canvas->silentFlush();
140 freeTransientResources();
142 m_destructionInProgress = true;
143 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
144 m_canvas->setNotificationClient(0);
146 m_layer->clearTexture();
147 // Orphaning the layer is required to trigger the recration of a new layer
148 // in the case where destruction is caused by a canvas resize. Test:
149 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
150 m_layer->layer()->removeFromParent();
151 // To anyone who ever hits this assert: Please update crbug.com/344666
153 ASSERT(!m_bytesAllocated);
156 void Canvas2DLayerBridge::setIsHidden(bool hidden)
158 ASSERT(!m_destructionInProgress);
159 bool newHiddenValue = hidden || m_destructionInProgress;
160 if (m_isHidden == newHiddenValue)
163 m_isHidden = newHiddenValue;
165 freeTransientResources();
169 void Canvas2DLayerBridge::freeTransientResources()
171 ASSERT(!m_destructionInProgress);
172 freeReleasedMailbox();
174 freeMemoryIfPossible(bytesAllocated());
175 ASSERT(!hasTransientResources());
178 bool Canvas2DLayerBridge::hasTransientResources() const
180 return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated());
183 void Canvas2DLayerBridge::limitPendingFrames()
185 ASSERT(!m_destructionInProgress);
187 freeTransientResources();
190 if (m_didRecordDrawCommand) {
192 m_didRecordDrawCommand = false;
193 if (m_framesPending > 1) {
194 // Turn on the rate limiter if this layer tends to accumulate a
195 // non-discardable multi-frame backlog of draw commands.
196 setRateLimitingEnabled(true);
198 if (m_rateLimitingEnabled) {
202 ++m_framesSinceMailboxRelease;
203 if (releasedMailboxHasExpired()) {
204 freeReleasedMailbox();
208 void Canvas2DLayerBridge::prepareForDraw()
210 ASSERT(!m_destructionInProgress);
212 if (!checkSurfaceValid()) {
214 // drop pending commands because there is no surface to draw to
215 m_canvas->silentFlush();
219 context()->makeContextCurrent();
222 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
224 ASSERT(!m_destructionInProgress);
225 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
226 m_bytesAllocated = bytesAllocated;
227 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta);
230 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
232 return m_canvas->storageAllocatedForRecording();
235 void Canvas2DLayerBridge::flushedDrawCommands()
237 ASSERT(!m_destructionInProgress);
238 storageAllocatedForRecordingChanged(storageAllocatedForRecording());
242 void Canvas2DLayerBridge::skippedPendingDrawCommands()
244 ASSERT(!m_destructionInProgress);
245 // Stop triggering the rate limiter if SkDeferredCanvas is detecting
246 // and optimizing overdraw.
247 setRateLimitingEnabled(false);
248 flushedDrawCommands();
251 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
253 ASSERT(!m_destructionInProgress);
254 if (m_rateLimitingEnabled != enabled) {
255 m_rateLimitingEnabled = enabled;
256 m_layer->setRateLimitContext(m_rateLimitingEnabled);
260 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
262 ASSERT(!m_destructionInProgress);
263 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
264 m_bytesAllocated -= bytesFreed;
266 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
270 void Canvas2DLayerBridge::flush()
272 ASSERT(!m_destructionInProgress);
273 if (m_canvas->hasPendingCommands()) {
274 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
275 freeReleasedMailbox(); // To avoid unnecessary triple-buffering
280 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
282 // This heuristic indicates that the canvas is not being
283 // actively presented by the compositor (3 frames rendered since
284 // last mailbox release), suggesting that double buffering is not required.
285 return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
288 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
290 return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
293 bool Canvas2DLayerBridge::hasReleasedMailbox() const
295 return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
298 void Canvas2DLayerBridge::freeReleasedMailbox()
300 if (m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid)
302 MailboxInfo* mailboxInfo = releasedMailboxInfo();
306 ASSERT(mailboxInfo->m_status == MailboxReleased);
307 if (mailboxInfo->m_mailbox.syncPoint) {
308 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
309 mailboxInfo->m_mailbox.syncPoint = 0;
311 // Invalidate texture state in case the compositor altered it since the copy-on-write.
312 if (mailboxInfo->m_image) {
313 if (isHidden() || releasedMailboxHasExpired())
314 mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
315 mailboxInfo->m_image->getTexture()->invalidateCachedState();
316 mailboxInfo->m_image.clear();
318 mailboxInfo->m_status = MailboxAvailable;
319 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
320 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
323 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
325 // Check on m_layer is necessary because context() may be called during
326 // the destruction of m_layer
327 if (m_layer && !m_destructionInProgress)
328 checkSurfaceValid(); // To ensure rate limiter is disabled if context is lost.
329 return m_contextProvider->context3d();
332 bool Canvas2DLayerBridge::checkSurfaceValid()
334 ASSERT(!m_destructionInProgress);
335 if (m_destructionInProgress || !m_isSurfaceValid)
337 if (m_contextProvider->context3d()->isContextLost()) {
338 m_isSurfaceValid = false;
340 m_imageBuffer->notifySurfaceInvalid();
341 setRateLimitingEnabled(false);
343 return m_isSurfaceValid;
346 bool Canvas2DLayerBridge::restoreSurface()
348 ASSERT(!m_destructionInProgress);
349 if (m_destructionInProgress)
351 ASSERT(m_layer && !m_isSurfaceValid);
353 blink::WebGraphicsContext3D* sharedContext = 0;
354 // We must clear the mailboxes before calling m_layer->clearTexture() to prevent
355 // re-entry via mailboxReleased from operating on defunct GrContext objects.
357 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
358 m_layer->clearTexture();
359 m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
360 if (m_contextProvider)
361 sharedContext = m_contextProvider->context3d();
363 if (sharedContext && !sharedContext->isContextLost()) {
364 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
365 RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
367 m_canvas->setSurface(surface.get());
368 m_isSurfaceValid = true;
369 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
373 return m_isSurfaceValid;
376 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
378 if (m_destructionInProgress) {
379 // It can be hit in the following sequence.
380 // 1. Canvas draws something.
381 // 2. The compositor begins the frame.
382 // 3. Javascript makes a context be lost.
387 // Using accelerated 2d canvas with software renderer, which
388 // should only happen in tests that use fake graphics contexts
389 // or in Android WebView in software mode. In this case, we do
390 // not care about producing any results for this canvas.
391 m_canvas->silentFlush();
395 if (!checkSurfaceValid())
398 blink::WebGraphicsContext3D* webContext = context();
400 // Release to skia textures that were previouosly released by the
401 // compositor. We do this before acquiring the next snapshot in
402 // order to cap maximum gpu memory consumption.
403 webContext->makeContextCurrent();
406 RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
408 // Early exit if canvas was not drawn to since last prepareMailbox
409 if (image->uniqueID() == m_lastImageId)
411 m_lastImageId = image->uniqueID();
413 MailboxInfo* mailboxInfo = createMailboxInfo();
414 mailboxInfo->m_status = MailboxInUse;
415 mailboxInfo->m_image = image;
417 // Because of texture sharing with the compositor, we must invalidate
418 // the state cached in skia so that the deferred copy on write
419 // in SkSurface_Gpu does not make any false assumptions.
420 mailboxInfo->m_image->getTexture()->invalidateCachedState();
422 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
423 ASSERT(mailboxInfo->m_image.get());
424 ASSERT(mailboxInfo->m_image->getTexture());
426 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
427 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
428 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
429 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
430 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
431 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
433 // With hidden canvases, we release the SkImage immediately because
434 // there is no need for animations to be double buffered.
435 mailboxInfo->m_image.clear();
438 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
440 webContext->bindTexture(GL_TEXTURE_2D, 0);
441 // Because we are changing the texture binding without going through skia,
442 // we must dirty the context.
443 m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
445 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
447 ASSERT(!mailboxInfo->m_parentLayerBridge);
448 mailboxInfo->m_parentLayerBridge = this;
449 *outMailbox = mailboxInfo->m_mailbox;
453 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
454 ASSERT(!m_destructionInProgress);
455 MailboxInfo* mailboxInfo;
456 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
457 if (mailboxInfo->m_status == MailboxAvailable) {
462 // No available mailbox: create one.
463 m_mailboxes.grow(m_mailboxes.size() + 1);
464 mailboxInfo = &m_mailboxes.last();
465 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
466 // Worst case, canvas is triple buffered. More than 3 active mailboxes
467 // means there is a problem.
468 // For the single-threaded case, this value needs to be at least
469 // kMaxSwapBuffersPending+1 (in render_widget.h).
470 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
471 // TODO(piman): fix this.
472 ASSERT(m_mailboxes.size() <= 4);
473 ASSERT(mailboxInfo < m_mailboxes.end());
477 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
479 freeReleasedMailbox(); // Never have more than one mailbox in the released state.
480 Vector<MailboxInfo>::iterator mailboxInfo;
481 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
482 if (nameEquals(mailboxInfo->m_mailbox, mailbox)) {
483 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
484 ASSERT(mailboxInfo->m_status == MailboxInUse);
485 mailboxInfo->m_status = MailboxReleased;
486 // Trigger Canvas2DLayerBridge self-destruction if this is the
487 // last live mailbox and the layer bridge is not externally
489 m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
490 m_framesSinceMailboxRelease = 0;
492 freeReleasedMailbox();
494 ASSERT(!m_destructionInProgress);
495 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
497 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
498 mailboxInfo->m_parentLayerBridge.clear();
504 blink::WebLayer* Canvas2DLayerBridge::layer() const
506 ASSERT(!m_destructionInProgress);
508 return m_layer->layer();
511 void Canvas2DLayerBridge::willUse()
513 ASSERT(!m_destructionInProgress);
514 Canvas2DLayerManager::get().layerDidDraw(this);
515 m_didRecordDrawCommand = true;
518 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
520 ASSERT(!m_destructionInProgress);
521 if (!checkSurfaceValid())
526 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
528 return renderTarget->asTexture()->getTextureHandle();
533 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
534 // This copy constructor should only be used for Vector reallocation
535 // Assuming 'other' is to be destroyed, we transfer m_image ownership
536 // rather than do a refcount dance.
537 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
538 m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
539 m_status = other.m_status;