Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / Canvas2DLayerBridge.cpp
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 "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"
41
42 using blink::WebExternalTextureLayer;
43 using blink::WebGraphicsContext3D;
44
45 namespace {
46 enum {
47     InvalidMailboxIndex = -1,
48 };
49
50 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstanceCounter, ("Canvas2DLayerBridge"));
51 }
52
53 namespace WebCore {
54
55 static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
56 {
57     if (!gr)
58         return nullptr;
59     gr->resetContext();
60     SkImageInfo info;
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));
66 }
67
68 PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
69 {
70     TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
71     OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
72     if (!contextProvider)
73         return nullptr;
74     RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
75     if (!surface)
76         return nullptr;
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();
81 }
82
83 Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
84     : m_canvas(canvas)
85     , m_contextProvider(contextProvider)
86     , m_imageBuffer(0)
87     , m_msaaSampleCount(msaaSampleCount)
88     , m_bytesAllocated(0)
89     , m_didRecordDrawCommand(false)
90     , m_isSurfaceValid(true)
91     , m_framesPending(0)
92     , m_framesSinceMailboxRelease(0)
93     , m_destructionInProgress(false)
94     , m_rateLimitingEnabled(false)
95     , m_isHidden(false)
96     , m_next(0)
97     , m_prev(0)
98     , m_lastImageId(0)
99     , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
100 {
101     ASSERT(m_canvas);
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);
111 #ifndef NDEBUG
112     canvas2DLayerBridgeInstanceCounter.increment();
113 #endif
114 }
115
116 Canvas2DLayerBridge::~Canvas2DLayerBridge()
117 {
118     ASSERT(m_destructionInProgress);
119     m_layer.clear();
120     freeReleasedMailbox();
121 #if !ASSERT_DISABLED
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);
126     }
127 #endif
128     m_mailboxes.clear();
129 #ifndef NDEBUG
130     canvas2DLayerBridgeInstanceCounter.decrement();
131 #endif
132 }
133
134 void Canvas2DLayerBridge::beginDestruction()
135 {
136     ASSERT(!m_destructionInProgress);
137     setRateLimitingEnabled(false);
138     m_canvas->silentFlush();
139     m_imageBuffer = 0;
140     freeTransientResources();
141     setIsHidden(true);
142     m_destructionInProgress = true;
143     GraphicsLayer::unregisterContentsLayer(m_layer->layer());
144     m_canvas->setNotificationClient(0);
145     m_canvas.clear();
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
152     // with repro steps.
153     ASSERT(!m_bytesAllocated);
154 }
155
156 void Canvas2DLayerBridge::setIsHidden(bool hidden)
157 {
158     ASSERT(!m_destructionInProgress);
159     bool newHiddenValue = hidden || m_destructionInProgress;
160     if (m_isHidden == newHiddenValue)
161         return;
162
163     m_isHidden = newHiddenValue;
164     if (isHidden()) {
165         freeTransientResources();
166     }
167 }
168
169 void Canvas2DLayerBridge::freeTransientResources()
170 {
171     ASSERT(!m_destructionInProgress);
172     freeReleasedMailbox();
173     flush();
174     freeMemoryIfPossible(bytesAllocated());
175     ASSERT(!hasTransientResources());
176 }
177
178 bool Canvas2DLayerBridge::hasTransientResources() const
179 {
180     return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated());
181 }
182
183 void Canvas2DLayerBridge::limitPendingFrames()
184 {
185     ASSERT(!m_destructionInProgress);
186     if (isHidden()) {
187         freeTransientResources();
188         return;
189     }
190     if (m_didRecordDrawCommand) {
191         m_framesPending++;
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);
197         }
198         if (m_rateLimitingEnabled) {
199             flush();
200         }
201     }
202     ++m_framesSinceMailboxRelease;
203     if (releasedMailboxHasExpired()) {
204         freeReleasedMailbox();
205     }
206 }
207
208 void Canvas2DLayerBridge::prepareForDraw()
209 {
210     ASSERT(!m_destructionInProgress);
211     ASSERT(m_layer);
212     if (!checkSurfaceValid()) {
213         if (m_canvas) {
214             // drop pending commands because there is no surface to draw to
215             m_canvas->silentFlush();
216         }
217         return;
218     }
219     context()->makeContextCurrent();
220 }
221
222 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
223 {
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);
228 }
229
230 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
231 {
232     return m_canvas->storageAllocatedForRecording();
233 }
234
235 void Canvas2DLayerBridge::flushedDrawCommands()
236 {
237     ASSERT(!m_destructionInProgress);
238     storageAllocatedForRecordingChanged(storageAllocatedForRecording());
239     m_framesPending = 0;
240 }
241
242 void Canvas2DLayerBridge::skippedPendingDrawCommands()
243 {
244     ASSERT(!m_destructionInProgress);
245     // Stop triggering the rate limiter if SkDeferredCanvas is detecting
246     // and optimizing overdraw.
247     setRateLimitingEnabled(false);
248     flushedDrawCommands();
249 }
250
251 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
252 {
253     ASSERT(!m_destructionInProgress);
254     if (m_rateLimitingEnabled != enabled) {
255         m_rateLimitingEnabled = enabled;
256         m_layer->setRateLimitContext(m_rateLimitingEnabled);
257     }
258 }
259
260 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
261 {
262     ASSERT(!m_destructionInProgress);
263     size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
264     m_bytesAllocated -= bytesFreed;
265     if (bytesFreed)
266         Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
267     return bytesFreed;
268 }
269
270 void Canvas2DLayerBridge::flush()
271 {
272     ASSERT(!m_destructionInProgress);
273     if (m_canvas->hasPendingCommands()) {
274         TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
275         freeReleasedMailbox(); // To avoid unnecessary triple-buffering
276         m_canvas->flush();
277     }
278 }
279
280 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
281 {
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;
286 }
287
288 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
289 {
290     return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
291 }
292
293 bool Canvas2DLayerBridge::hasReleasedMailbox() const
294 {
295     return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
296 }
297
298 void Canvas2DLayerBridge::freeReleasedMailbox()
299 {
300     if (m_contextProvider->context3d()->isContextLost() || !m_isSurfaceValid)
301         return;
302     MailboxInfo* mailboxInfo = releasedMailboxInfo();
303     if (!mailboxInfo)
304         return;
305
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;
310     }
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();
317     }
318     mailboxInfo->m_status = MailboxAvailable;
319     m_releasedMailboxInfoIndex = InvalidMailboxIndex;
320     Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
321 }
322
323 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
324 {
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();
330 }
331
332 bool Canvas2DLayerBridge::checkSurfaceValid()
333 {
334     ASSERT(!m_destructionInProgress);
335     if (m_destructionInProgress || !m_isSurfaceValid)
336         return false;
337     if (m_contextProvider->context3d()->isContextLost()) {
338         m_isSurfaceValid = false;
339         if (m_imageBuffer)
340             m_imageBuffer->notifySurfaceInvalid();
341         setRateLimitingEnabled(false);
342     }
343     return m_isSurfaceValid;
344 }
345
346 bool Canvas2DLayerBridge::restoreSurface()
347 {
348     ASSERT(!m_destructionInProgress);
349     if (m_destructionInProgress)
350         return false;
351     ASSERT(m_layer && !m_isSurfaceValid);
352
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.
356     m_mailboxes.clear();
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();
362
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));
366         if (surface.get()) {
367             m_canvas->setSurface(surface.get());
368             m_isSurfaceValid = true;
369             // FIXME: draw sad canvas picture into new buffer crbug.com/243842
370         }
371     }
372
373     return m_isSurfaceValid;
374 }
375
376 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
377 {
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.
383         // 4. Here.
384         return false;
385     }
386     if (bitmap) {
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();
392         m_lastImageId = 0;
393         return false;
394     }
395     if (!checkSurfaceValid())
396         return false;
397
398     blink::WebGraphicsContext3D* webContext = context();
399
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();
404     flush();
405
406     RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
407
408     // Early exit if canvas was not drawn to since last prepareMailbox
409     if (image->uniqueID() == m_lastImageId)
410         return false;
411     m_lastImageId = image->uniqueID();
412
413     MailboxInfo* mailboxInfo = createMailboxInfo();
414     mailboxInfo->m_status = MailboxInUse;
415     mailboxInfo->m_image = image;
416
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();
421
422     ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
423     ASSERT(mailboxInfo->m_image.get());
424     ASSERT(mailboxInfo->m_image->getTexture());
425
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);
432     if (isHidden()) {
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();
436     } else {
437         webContext->flush();
438         mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
439     }
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);
444
445     // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
446     // live mailboxes
447     ASSERT(!mailboxInfo->m_parentLayerBridge);
448     mailboxInfo->m_parentLayerBridge = this;
449     *outMailbox = mailboxInfo->m_mailbox;
450     return true;
451 }
452
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) {
458             return mailboxInfo;
459         }
460     }
461
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());
474     return mailboxInfo;
475 }
476
477 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
478 {
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
488             // referenced.
489             m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
490             m_framesSinceMailboxRelease = 0;
491             if (isHidden()) {
492                 freeReleasedMailbox();
493             } else {
494                 ASSERT(!m_destructionInProgress);
495                 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
496             }
497             ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
498             mailboxInfo->m_parentLayerBridge.clear();
499             return;
500         }
501     }
502 }
503
504 blink::WebLayer* Canvas2DLayerBridge::layer() const
505 {
506     ASSERT(!m_destructionInProgress);
507     ASSERT(m_layer);
508     return m_layer->layer();
509 }
510
511 void Canvas2DLayerBridge::willUse()
512 {
513     ASSERT(!m_destructionInProgress);
514     Canvas2DLayerManager::get().layerDidDraw(this);
515     m_didRecordDrawCommand = true;
516 }
517
518 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
519 {
520     ASSERT(!m_destructionInProgress);
521     if (!checkSurfaceValid())
522         return 0;
523     willUse();
524     m_canvas->flush();
525     context()->flush();
526     GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
527     if (renderTarget) {
528         return renderTarget->asTexture()->getTextureHandle();
529     }
530     return 0;
531 }
532
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;
540 }
541
542 }