• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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.
13  *
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.
24  */
25 
26 #include "config.h"
27 
28 #include "platform/graphics/Canvas2DLayerBridge.h"
29 
30 #include "GrContext.h"
31 #include "SkDevice.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"
40 
41 using blink::WebExternalTextureLayer;
42 using blink::WebGraphicsContext3D;
43 
44 namespace WebCore {
45 
createSkSurface(GraphicsContext3D * context3D,const IntSize & size,int msaaSampleCount)46 static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount)
47 {
48     ASSERT(!context3D->webContext()->isContextLost());
49     GrContext* gr = context3D->grContext();
50     if (!gr)
51         return 0;
52     gr->resetContext();
53     SkImageInfo info;
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));
59 }
60 
create(const IntSize & size,OpacityMode opacityMode,int msaaSampleCount)61 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
62 {
63     TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
64     RefPtr<GraphicsContext3D> context = SharedGraphicsContext3D::get();
65     RefPtr<SkSurface> surface(createSkSurface(context.get(), size, msaaSampleCount));
66     if (!surface)
67         return 0;
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();
72 }
73 
Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context,PassOwnPtr<SkDeferredCanvas> canvas,int msaaSampleCount,OpacityMode opacityMode)74 Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
75     : m_canvas(canvas)
76     , m_context(context)
77     , m_msaaSampleCount(msaaSampleCount)
78     , m_bytesAllocated(0)
79     , m_didRecordDrawCommand(false)
80     , m_surfaceIsValid(true)
81     , m_framesPending(0)
82     , m_destructionInProgress(false)
83     , m_rateLimitingEnabled(false)
84     , m_next(0)
85     , m_prev(0)
86     , m_lastImageId(0)
87 {
88     ASSERT(m_canvas);
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);
97 }
98 
~Canvas2DLayerBridge()99 Canvas2DLayerBridge::~Canvas2DLayerBridge()
100 {
101     ASSERT(m_destructionInProgress);
102     m_layer.clear();
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;
110             }
111             // Invalidate texture state in case the compositor altered it since the copy-on-write.
112             mailboxInfo->m_image->getTexture()->invalidateCachedState();
113         }
114     }
115     m_mailboxes.clear();
116 }
117 
beginDestruction()118 void Canvas2DLayerBridge::beginDestruction()
119 {
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();
130 }
131 
limitPendingFrames()132 void Canvas2DLayerBridge::limitPendingFrames()
133 {
134     ASSERT(!m_destructionInProgress);
135     if (m_didRecordDrawCommand) {
136         m_framesPending++;
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);
142         }
143         if (m_rateLimitingEnabled) {
144             flush();
145         }
146     }
147 }
148 
prepareForDraw()149 void Canvas2DLayerBridge::prepareForDraw()
150 {
151     ASSERT(!m_destructionInProgress);
152     ASSERT(m_layer);
153     if (!isValid()) {
154         if (m_canvas) {
155             // drop pending commands because there is no surface to draw to
156             m_canvas->silentFlush();
157         }
158         return;
159     }
160     m_context->makeContextCurrent();
161 }
162 
storageAllocatedForRecordingChanged(size_t bytesAllocated)163 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
164 {
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);
169 }
170 
storageAllocatedForRecording()171 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
172 {
173     ASSERT(!m_destructionInProgress);
174     return m_canvas->storageAllocatedForRecording();
175 }
176 
flushedDrawCommands()177 void Canvas2DLayerBridge::flushedDrawCommands()
178 {
179     ASSERT(!m_destructionInProgress);
180     storageAllocatedForRecordingChanged(storageAllocatedForRecording());
181     m_framesPending = 0;
182 }
183 
skippedPendingDrawCommands()184 void Canvas2DLayerBridge::skippedPendingDrawCommands()
185 {
186     ASSERT(!m_destructionInProgress);
187     // Stop triggering the rate limiter if SkDeferredCanvas is detecting
188     // and optimizing overdraw.
189     setRateLimitingEnabled(false);
190     flushedDrawCommands();
191 }
192 
setRateLimitingEnabled(bool enabled)193 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
194 {
195     ASSERT(!m_destructionInProgress || !enabled);
196     if (m_rateLimitingEnabled != enabled) {
197         m_rateLimitingEnabled = enabled;
198         m_layer->setRateLimitContext(m_rateLimitingEnabled);
199     }
200 }
201 
freeMemoryIfPossible(size_t bytesToFree)202 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
203 {
204     ASSERT(!m_destructionInProgress);
205     size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
206     if (bytesFreed)
207         Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed));
208     m_bytesAllocated -= bytesFreed;
209     return bytesFreed;
210 }
211 
flush()212 void Canvas2DLayerBridge::flush()
213 {
214     ASSERT(!m_destructionInProgress);
215     if (m_canvas->hasPendingCommands()) {
216         TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
217         m_canvas->flush();
218         m_context->flush();
219     }
220 }
221 
context()222 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
223 {
224     // Check on m_layer is necessary because context() may be called during
225     // the destruction of m_layer
226     if (m_layer) {
227         isValid(); // To ensure rate limiter is disabled if context is lost.
228     }
229     return m_context->webContext();
230 }
231 
isValid()232 bool Canvas2DLayerBridge::isValid()
233 {
234     ASSERT(m_layer);
235     if (m_destructionInProgress)
236         return false;
237     if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) {
238         // Attempt to recover.
239         m_layer->clearTexture();
240         m_mailboxes.clear();
241         RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get();
242         if (!sharedContext || sharedContext->webContext()->isContextLost()) {
243             m_surfaceIsValid = false;
244         } else {
245             m_context = sharedContext;
246             IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
247             RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount));
248             if (surface.get()) {
249                 m_canvas->setSurface(surface.get());
250                 m_surfaceIsValid = true;
251                 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
252             } else {
253                 // Surface allocation failed. Set m_surfaceIsValid to false to
254                 // trigger subsequent retry.
255                 m_surfaceIsValid = false;
256             }
257         }
258     }
259     if (!m_surfaceIsValid)
260         setRateLimitingEnabled(false);
261     return m_surfaceIsValid;
262 }
263 
prepareMailbox(blink::WebExternalTextureMailbox * outMailbox,blink::WebExternalBitmap * bitmap)264 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
265 {
266     if (bitmap) {
267         // Using accelerated 2d canvas with software renderer, which
268         // should only happen in tests that use fake graphics contexts
269         // or in Android WebView in software mode. In this case, we do
270         // not care about producing any results for this canvas.
271         m_canvas->silentFlush();
272         m_lastImageId = 0;
273         return false;
274     }
275     if (!isValid())
276         return false;
277     // Release to skia textures that were previouosly released by the
278     // compositor. We do this before acquiring the next snapshot in
279     // order to cap maximum gpu memory consumption.
280     m_context->makeContextCurrent();
281     flush();
282     Vector<MailboxInfo>::iterator mailboxInfo;
283     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
284         if (mailboxInfo->m_status == MailboxReleased) {
285             if (mailboxInfo->m_mailbox.syncPoint) {
286                 context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
287                 mailboxInfo->m_mailbox.syncPoint = 0;
288             }
289             // Invalidate texture state in case the compositor altered it since the copy-on-write.
290             mailboxInfo->m_image->getTexture()->invalidateCachedState();
291             mailboxInfo->m_image.reset(0);
292             mailboxInfo->m_status = MailboxAvailable;
293         }
294     }
295     SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot());
296     // Early exit if canvas was not drawn to since last prepareMailbox
297     if (image->uniqueID() == m_lastImageId)
298         return false;
299     m_lastImageId = image->uniqueID();
300 
301     mailboxInfo = createMailboxInfo();
302     mailboxInfo->m_status = MailboxInUse;
303     mailboxInfo->m_image.swap(&image);
304     // Because of texture sharing with the compositor, we must invalidate
305     // the state cached in skia so that the deferred copy on write
306     // in SkSurface_Gpu does not make any false assumptions.
307     mailboxInfo->m_image->getTexture()->invalidateCachedState();
308 
309     ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
310     ASSERT(mailboxInfo->m_image.get());
311     ASSERT(mailboxInfo->m_image->getTexture());
312 
313     m_context->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
314     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
315     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
316     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
317     m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
318     context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
319     context()->flush();
320     mailboxInfo->m_mailbox.syncPoint = context()->insertSyncPoint();
321     m_context->bindTexture(GL_TEXTURE_2D, 0);
322     // Because we are changing the texture binding without going through skia,
323     // we must dirty the context.
324     m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState);
325 
326     // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
327     // live mailboxes
328     ASSERT(!mailboxInfo->m_parentLayerBridge);
329     mailboxInfo->m_parentLayerBridge = this;
330     *outMailbox = mailboxInfo->m_mailbox;
331     return true;
332 }
333 
createMailboxInfo()334 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
335     ASSERT(!m_destructionInProgress);
336     MailboxInfo* mailboxInfo;
337     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
338         if (mailboxInfo->m_status == MailboxAvailable) {
339             return mailboxInfo;
340         }
341     }
342 
343     // No available mailbox: create one.
344     m_mailboxes.grow(m_mailboxes.size() + 1);
345     mailboxInfo = &m_mailboxes.last();
346     context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
347     // Worst case, canvas is triple buffered.  More than 3 active mailboxes
348     // means there is a problem.
349     // For the single-threaded case, this value needs to be at least
350     // kMaxSwapBuffersPending+1 (in render_widget.h).
351     // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
352     // TODO(piman): fix this.
353     ASSERT(m_mailboxes.size() <= 4);
354     ASSERT(mailboxInfo < m_mailboxes.end());
355     return mailboxInfo;
356 }
357 
mailboxReleased(const blink::WebExternalTextureMailbox & mailbox)358 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
359 {
360     Vector<MailboxInfo>::iterator mailboxInfo;
361     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
362         if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
363             mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
364             ASSERT(mailboxInfo->m_status == MailboxInUse);
365             mailboxInfo->m_status = MailboxReleased;
366             // Trigger Canvas2DLayerBridge self-destruction if this is the
367             // last live mailbox and the layer bridge is not externally
368             // referenced.
369             ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
370             mailboxInfo->m_parentLayerBridge.clear();
371             return;
372         }
373     }
374 }
375 
layer() const376 blink::WebLayer* Canvas2DLayerBridge::layer() const
377 {
378     ASSERT(m_layer);
379     return m_layer->layer();
380 }
381 
willUse()382 void Canvas2DLayerBridge::willUse()
383 {
384     ASSERT(!m_destructionInProgress);
385     Canvas2DLayerManager::get().layerDidDraw(this);
386     m_didRecordDrawCommand = true;
387 }
388 
getBackingTexture()389 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
390 {
391     ASSERT(!m_destructionInProgress);
392     if (!isValid())
393         return 0;
394     willUse();
395     m_canvas->flush();
396     m_context->flush();
397     GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
398     if (renderTarget) {
399         return renderTarget->asTexture()->getTextureHandle();
400     }
401     return 0;
402 }
403 
MailboxInfo(const MailboxInfo & other)404 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
405     // This copy constructor should only be used for Vector reallocation
406     // Assuming 'other' is to be destroyed, we swap m_image ownership
407     // rather than do a refcount dance.
408     memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
409     m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image));
410     m_status = other.m_status;
411 }
412 
413 }
414