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 "platform/graphics/gpu/SharedGraphicsContext3D.h"
37 #include "public/platform/Platform.h"
38 #include "public/platform/WebCompositorSupport.h"
39 #include "public/platform/WebGraphicsContext3D.h"
41 using blink::WebExternalTextureLayer;
42 using blink::WebGraphicsContext3D;
46 static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount)
48 ASSERT(!context3D->webContext()->isContextLost());
49 GrContext* gr = context3D->grContext();
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));
61 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
63 TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
64 RefPtr<GraphicsContext3D> context = SharedGraphicsContext3D::get();
65 RefPtr<SkSurface> surface(createSkSurface(context.get(), size, msaaSampleCount));
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();
74 Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
77 , m_msaaSampleCount(msaaSampleCount)
79 , m_didRecordDrawCommand(false)
80 , m_surfaceIsValid(true)
82 , m_destructionInProgress(false)
83 , m_rateLimitingEnabled(false)
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);
99 Canvas2DLayerBridge::~Canvas2DLayerBridge()
101 ASSERT(m_destructionInProgress);
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;
111 // Invalidate texture state in case the compositor altered it since the copy-on-write.
112 mailboxInfo->m_image->getTexture()->invalidateCachedState();
118 void Canvas2DLayerBridge::beginDestruction()
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();
132 void Canvas2DLayerBridge::limitPendingFrames()
134 ASSERT(!m_destructionInProgress);
135 if (m_didRecordDrawCommand) {
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);
143 if (m_rateLimitingEnabled) {
149 void Canvas2DLayerBridge::prepareForDraw()
151 ASSERT(!m_destructionInProgress);
155 // drop pending commands because there is no surface to draw to
156 m_canvas->silentFlush();
160 context()->makeContextCurrent();
163 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
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);
171 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
173 ASSERT(!m_destructionInProgress);
174 return m_canvas->storageAllocatedForRecording();
177 void Canvas2DLayerBridge::flushedDrawCommands()
179 ASSERT(!m_destructionInProgress);
180 storageAllocatedForRecordingChanged(storageAllocatedForRecording());
184 void Canvas2DLayerBridge::skippedPendingDrawCommands()
186 ASSERT(!m_destructionInProgress);
187 // Stop triggering the rate limiter if SkDeferredCanvas is detecting
188 // and optimizing overdraw.
189 setRateLimitingEnabled(false);
190 flushedDrawCommands();
193 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
195 ASSERT(!m_destructionInProgress || !enabled);
196 if (m_rateLimitingEnabled != enabled) {
197 m_rateLimitingEnabled = enabled;
198 m_layer->setRateLimitContext(m_rateLimitingEnabled);
202 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
204 ASSERT(!m_destructionInProgress);
205 size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
207 Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed));
208 m_bytesAllocated -= bytesFreed;
212 void Canvas2DLayerBridge::flush()
214 ASSERT(!m_destructionInProgress);
215 if (m_canvas->hasPendingCommands()) {
216 TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
221 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
223 // Check on m_layer is necessary because context() may be called during
224 // the destruction of m_layer
226 isValid(); // To ensure rate limiter is disabled if context is lost.
228 return m_context->webContext();
231 bool Canvas2DLayerBridge::isValid()
234 if (m_destructionInProgress)
236 if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) {
237 // Attempt to recover.
238 m_layer->clearTexture();
240 RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get();
241 if (!sharedContext || sharedContext->webContext()->isContextLost()) {
242 m_surfaceIsValid = false;
244 m_context = sharedContext;
245 IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
246 RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount));
248 m_canvas->setSurface(surface.get());
249 m_surfaceIsValid = true;
250 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
252 // Surface allocation failed. Set m_surfaceIsValid to false to
253 // trigger subsequent retry.
254 m_surfaceIsValid = false;
258 if (!m_surfaceIsValid)
259 setRateLimitingEnabled(false);
260 return m_surfaceIsValid;
263 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
266 // Using accelerated 2d canvas with software renderer, which
267 // should only happen in tests that use fake graphics contexts.
268 // In this case, we do not care about producing any results for
270 m_canvas->silentFlush();
276 blink::WebGraphicsContext3D* webContext = context();
278 // Release to skia textures that were previouosly released by the
279 // compositor. We do this before acquiring the next snapshot in
280 // order to cap maximum gpu memory consumption.
281 webContext->makeContextCurrent();
283 Vector<MailboxInfo>::iterator mailboxInfo;
284 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
285 if (mailboxInfo->m_status == MailboxReleased) {
286 if (mailboxInfo->m_mailbox.syncPoint) {
287 webContext->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
288 mailboxInfo->m_mailbox.syncPoint = 0;
290 // Invalidate texture state in case the compositor altered it since the copy-on-write.
291 mailboxInfo->m_image->getTexture()->invalidateCachedState();
292 mailboxInfo->m_image.reset(0);
293 mailboxInfo->m_status = MailboxAvailable;
296 SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot());
297 // Early exit if canvas was not drawn to since last prepareMailbox
298 if (image->uniqueID() == m_lastImageId)
300 m_lastImageId = image->uniqueID();
302 mailboxInfo = createMailboxInfo();
303 mailboxInfo->m_status = MailboxInUse;
304 mailboxInfo->m_image.swap(&image);
305 // Because of texture sharing with the compositor, we must invalidate
306 // the state cached in skia so that the deferred copy on write
307 // in SkSurface_Gpu does not make any false assumptions.
308 mailboxInfo->m_image->getTexture()->invalidateCachedState();
310 ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
311 ASSERT(mailboxInfo->m_image.get());
312 ASSERT(mailboxInfo->m_image->getTexture());
314 webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
315 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
316 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
317 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
318 webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
319 webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
321 mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
322 webContext->bindTexture(GL_TEXTURE_2D, 0);
323 // Because we are changing the texture binding without going through skia,
324 // we must dirty the context.
325 m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState);
327 // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
329 ASSERT(!mailboxInfo->m_parentLayerBridge);
330 mailboxInfo->m_parentLayerBridge = this;
331 *outMailbox = mailboxInfo->m_mailbox;
335 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
336 ASSERT(!m_destructionInProgress);
337 MailboxInfo* mailboxInfo;
338 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
339 if (mailboxInfo->m_status == MailboxAvailable) {
344 // No available mailbox: create one.
345 m_mailboxes.grow(m_mailboxes.size() + 1);
346 mailboxInfo = &m_mailboxes.last();
347 context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
348 // Worst case, canvas is triple buffered. More than 3 active mailboxes
349 // means there is a problem.
350 // For the single-threaded case, this value needs to be at least
351 // kMaxSwapBuffersPending+1 (in render_widget.h).
352 // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
353 // TODO(piman): fix this.
354 ASSERT(m_mailboxes.size() <= 4);
355 ASSERT(mailboxInfo < m_mailboxes.end());
359 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
361 Vector<MailboxInfo>::iterator mailboxInfo;
362 for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
363 if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
364 mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
365 ASSERT(mailboxInfo->m_status == MailboxInUse);
366 mailboxInfo->m_status = MailboxReleased;
367 // Trigger Canvas2DLayerBridge self-destruction if this is the
368 // last live mailbox and the layer bridge is not externally
370 ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
371 mailboxInfo->m_parentLayerBridge.clear();
377 blink::WebLayer* Canvas2DLayerBridge::layer() const
380 return m_layer->layer();
383 void Canvas2DLayerBridge::willUse()
385 ASSERT(!m_destructionInProgress);
386 Canvas2DLayerManager::get().layerDidDraw(this);
387 m_didRecordDrawCommand = true;
390 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
392 ASSERT(!m_destructionInProgress);
398 GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
400 return renderTarget->asTexture()->getTextureHandle();
405 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
406 // This copy constructor should only be used for Vector reallocation
407 // Assuming 'other' is to be destroyed, we swap m_image ownership
408 // rather than do a refcount dance.
409 memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
410 m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image));
411 m_status = other.m_status;