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