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"
41 using blink::WebExternalTextureLayer;
42 using blink::WebGraphicsContext3D;
46 InvalidMailboxIndex = -1,
52 static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
58 info.fWidth = size.width();
59 info.fHeight = size.height();
60 info.fColorType = kPMColor_SkColorType;
61 info.fAlphaType = kPremul_SkAlphaType;
62 return adoptRef(SkSurface::NewRenderTarget(gr, info, msaaSampleCount));
65 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
67 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
68 OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
71 RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
74 RefPtr<Canvas2DLayerBridge> layerBridge;
75 OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
76 layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), msaaSampleCount, opacityMode));
77 return layerBridge.release();
80 Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
82 , m_contextProvider(contextProvider)
83 , m_msaaSampleCount(msaaSampleCount)
85 , m_didRecordDrawCommand(false)
86 , m_surfaceIsValid(true)
88 , m_framesSinceMailboxRelease(0)
89 , m_destructionInProgress(false)
90 , m_rateLimitingEnabled(false)
95 , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
98 ASSERT(m_contextProvider);
99 // Used by browser tests to detect the use of a Canvas2DLayerBridge.
100 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
101 m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
102 m_layer->setOpaque(opacityMode == Opaque);
103 m_layer->setBlendBackgroundColor(opacityMode != Opaque);
104 GraphicsLayer::registerContentsLayer(m_layer->layer());
105 m_layer->setRateLimitContext(m_rateLimitingEnabled);
106 m_canvas->setNotificationClient(this);
109 Canvas2DLayerBridge::~Canvas2DLayerBridge()
111 ASSERT(m_destructionInProgress);
113 freeReleasedMailbox();
115 Vector<MailboxInfo>::iterator mailboxInfo;
116 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
117 ASSERT(mailboxInfo->m_status != MailboxInUse);
118 ASSERT(mailboxInfo->m_status != MailboxReleased);
124 void Canvas2DLayerBridge::beginDestruction()
126 ASSERT(!m_destructionInProgress);
127 m_destructionInProgress = true;
128 freeTransientResources();
130 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
131 m_canvas->setNotificationClient(0);
132 m_layer->clearTexture();
133 // Orphaning the layer is required to trigger the recration of a new layer
134 // in the case where destruction is caused by a canvas resize. Test:
135 // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
136 m_layer->layer()->removeFromParent();
139 void Canvas2DLayerBridge::setIsHidden(bool hidden)
141 bool newHiddenValue = hidden || m_destructionInProgress;
142 if (m_isHidden == newHiddenValue)
145 m_isHidden = newHiddenValue;
147 freeTransientResources();
151 void Canvas2DLayerBridge::freeTransientResources()
153 freeReleasedMailbox();
155 freeMemoryIfPossible(bytesAllocated());
156 ASSERT(!hasTransientResources());
159 bool Canvas2DLayerBridge::hasTransientResources() const
161 return hasReleasedMailbox() || bytesAllocated();
164 void Canvas2DLayerBridge::limitPendingFrames()
166 ASSERT(!m_destructionInProgress);
168 freeTransientResources();
171 if (m_didRecordDrawCommand) {
173 m_didRecordDrawCommand = false;
174 if (m_framesPending > 1) {
175 // Turn on the rate limiter if this layer tends to accumulate a
176 // non-discardable multi-frame backlog of draw commands.
177 setRateLimitingEnabled(true);
179 if (m_rateLimitingEnabled) {
183 ++m_framesSinceMailboxRelease;
184 if (releasedMailboxHasExpired()) {
185 freeReleasedMailbox();
189 void Canvas2DLayerBridge::prepareForDraw()
194 // drop pending commands because there is no surface to draw to
195 m_canvas->silentFlush();
199 context()->makeContextCurrent();
202 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
204 intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
205 m_bytesAllocated = bytesAllocated;
206 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta);
209 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
211 return m_canvas->storageAllocatedForRecording();
214 void Canvas2DLayerBridge::flushedDrawCommands()
216 storageAllocatedForRecordingChanged(storageAllocatedForRecording());
220 void Canvas2DLayerBridge::skippedPendingDrawCommands()
222 // Stop triggering the rate limiter if SkDeferredCanvas is detecting
223 // and optimizing overdraw.
224 setRateLimitingEnabled(false);
225 flushedDrawCommands();
228 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
230 ASSERT(!m_destructionInProgress || !enabled);
231 if (m_rateLimitingEnabled != enabled) {
232 m_rateLimitingEnabled = enabled;
233 m_layer->setRateLimitContext(m_rateLimitingEnabled);
237 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
239 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
240 m_bytesAllocated -= bytesFreed;
242 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
246 void Canvas2DLayerBridge::flush()
248 if (m_canvas->hasPendingCommands()) {
249 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
250 freeReleasedMailbox(); // To avoid unnecessary triple-buffering
255 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
257 // This heuristic indicates that the canvas is not being
258 // actively presented by the compositor (3 frames rendered since
259 // last mailbox release), suggesting that double buffering is not required.
260 return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
263 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
265 return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
268 bool Canvas2DLayerBridge::hasReleasedMailbox() const
270 return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
273 void Canvas2DLayerBridge::freeReleasedMailbox()
275 MailboxInfo* mailboxInfo = releasedMailboxInfo();
279 ASSERT(mailboxInfo->m_status == MailboxReleased);
280 if (mailboxInfo->m_mailbox.syncPoint) {
281 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
282 mailboxInfo->m_mailbox.syncPoint = 0;
284 // Invalidate texture state in case the compositor altered it since the copy-on-write.
285 if (mailboxInfo->m_image) {
286 if (isHidden() || releasedMailboxHasExpired())
287 mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
288 mailboxInfo->m_image->getTexture()->invalidateCachedState();
289 mailboxInfo->m_image.clear();
291 mailboxInfo->m_status = MailboxAvailable;
292 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
293 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
296 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
298 // Check on m_layer is necessary because context() may be called during
299 // the destruction of m_layer
301 isValid(); // To ensure rate limiter is disabled if context is lost.
303 return m_contextProvider->context3d();
306 bool Canvas2DLayerBridge::isValid()
309 if (m_destructionInProgress)
311 if (m_contextProvider->context3d()->isContextLost() || !m_surfaceIsValid) {
312 // Attempt to recover.
313 blink::WebGraphicsContext3D* sharedContext = 0;
314 m_layer->clearTexture();
316 m_releasedMailboxInfoIndex = InvalidMailboxIndex;
317 m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
318 if (m_contextProvider)
319 sharedContext = m_contextProvider->context3d();
321 if (!sharedContext || sharedContext->isContextLost()) {
322 m_surfaceIsValid = false;
324 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
325 RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
327 m_canvas->setSurface(surface.get());
328 m_surfaceIsValid = true;
329 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
331 // Surface allocation failed. Set m_surfaceIsValid to false to
332 // trigger subsequent retry.
333 m_surfaceIsValid = false;
337 if (!m_surfaceIsValid)
338 setRateLimitingEnabled(false);
339 return m_surfaceIsValid;
342 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
345 // Using accelerated 2d canvas with software renderer, which
346 // should only happen in tests that use fake graphics contexts.
347 // In this case, we do not care about producing any results for
349 m_canvas->silentFlush();
355 blink::WebGraphicsContext3D* webContext = context();
357 // Release to skia textures that were previouosly released by the
358 // compositor. We do this before acquiring the next snapshot in
359 // order to cap maximum gpu memory consumption.
360 webContext->makeContextCurrent();
363 RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
365 // Early exit if canvas was not drawn to since last prepareMailbox
366 if (image->uniqueID() == m_lastImageId)
368 m_lastImageId = image->uniqueID();
370 MailboxInfo* mailboxInfo = createMailboxInfo();
371 mailboxInfo->m_status = MailboxInUse;
372 mailboxInfo->m_image = image;
374 // Because of texture sharing with the compositor, we must invalidate
375 // the state cached in skia so that the deferred copy on write
376 // in SkSurface_Gpu does not make any false assumptions.
377 mailboxInfo->m_image->getTexture()->invalidateCachedState();
379 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
380 ASSERT(mailboxInfo->m_image.get());
381 ASSERT(mailboxInfo->m_image->getTexture());
383 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
384 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
385 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
386 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
387 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
388 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
390 // With hidden canvases, we release the SkImage immediately because
391 // there is no need for animations to be double buffered.
392 mailboxInfo->m_image.clear();
395 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
397 webContext->bindTexture(GL_TEXTURE_2D, 0);
398 // Because we are changing the texture binding without going through skia,
399 // we must dirty the context.
400 m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
402 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
404 ASSERT(!mailboxInfo->m_parentLayerBridge);
405 mailboxInfo->m_parentLayerBridge = this;
406 *outMailbox = mailboxInfo->m_mailbox;
410 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
411 ASSERT(!m_destructionInProgress);
412 MailboxInfo* mailboxInfo;
413 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
414 if (mailboxInfo->m_status == MailboxAvailable) {
419 // No available mailbox: create one.
420 m_mailboxes.grow(m_mailboxes.size() + 1);
421 mailboxInfo = &m_mailboxes.last();
422 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
423 // Worst case, canvas is triple buffered. More than 3 active mailboxes
424 // means there is a problem.
425 // For the single-threaded case, this value needs to be at least
426 // kMaxSwapBuffersPending+1 (in render_widget.h).
427 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
428 // TODO(piman): fix this.
429 ASSERT(m_mailboxes.size() <= 4);
430 ASSERT(mailboxInfo < m_mailboxes.end());
434 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
436 freeReleasedMailbox(); // Never have more than one mailbox in the released state.
437 Vector<MailboxInfo>::iterator mailboxInfo;
438 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
439 if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
440 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
441 ASSERT(mailboxInfo->m_status == MailboxInUse);
442 mailboxInfo->m_status = MailboxReleased;
443 // Trigger Canvas2DLayerBridge self-destruction if this is the
444 // last live mailbox and the layer bridge is not externally
446 m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
447 m_framesSinceMailboxRelease = 0;
449 freeReleasedMailbox();
451 ASSERT(!m_destructionInProgress);
452 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
454 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
455 mailboxInfo->m_parentLayerBridge.clear();
461 blink::WebLayer* Canvas2DLayerBridge::layer() const
464 return m_layer->layer();
467 void Canvas2DLayerBridge::willUse()
469 ASSERT(!m_destructionInProgress);
470 Canvas2DLayerManager::get().layerDidDraw(this);
471 m_didRecordDrawCommand = true;
474 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
476 ASSERT(!m_destructionInProgress);
482 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
484 return renderTarget->asTexture()->getTextureHandle();
489 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
490 // This copy constructor should only be used for Vector reallocation
491 // Assuming 'other' is to be destroyed, we transfer m_image ownership
492 // rather than do a refcount dance.
493 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
494 m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
495 m_status = other.m_status;