64788f59ace118c3c1f35e835860a2ff062de923
[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::NewScratchRenderTarget(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_msaaSampleCount(msaaSampleCount)
87     , m_bytesAllocated(0)
88     , m_didRecordDrawCommand(false)
89     , m_surfaceIsValid(true)
90     , m_framesPending(0)
91     , m_framesSinceMailboxRelease(0)
92     , m_destructionInProgress(false)
93     , m_rateLimitingEnabled(false)
94     , m_isHidden(false)
95     , m_next(0)
96     , m_prev(0)
97     , m_lastImageId(0)
98     , m_releasedMailboxInfoIndex(InvalidMailboxIndex)
99 {
100     ASSERT(m_canvas);
101     ASSERT(m_contextProvider);
102     // Used by browser tests to detect the use of a Canvas2DLayerBridge.
103     TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
104     m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
105     m_layer->setOpaque(opacityMode == Opaque);
106     m_layer->setBlendBackgroundColor(opacityMode != Opaque);
107     GraphicsLayer::registerContentsLayer(m_layer->layer());
108     m_layer->setRateLimitContext(m_rateLimitingEnabled);
109     m_canvas->setNotificationClient(this);
110 #ifndef NDEBUG
111     canvas2DLayerBridgeInstanceCounter.increment();
112 #endif
113 }
114
115 Canvas2DLayerBridge::~Canvas2DLayerBridge()
116 {
117     ASSERT(m_destructionInProgress);
118     m_layer.clear();
119     freeReleasedMailbox();
120 #if !ASSERT_DISABLED
121     Vector<MailboxInfo>::iterator mailboxInfo;
122     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
123         ASSERT(mailboxInfo->m_status != MailboxInUse);
124         ASSERT(mailboxInfo->m_status != MailboxReleased);
125     }
126 #endif
127     m_mailboxes.clear();
128 #ifndef NDEBUG
129     canvas2DLayerBridgeInstanceCounter.decrement();
130 #endif
131 }
132
133 void Canvas2DLayerBridge::beginDestruction()
134 {
135     ASSERT(!m_destructionInProgress);
136     setRateLimitingEnabled(false);
137     m_canvas->silentFlush();
138     freeTransientResources();
139     setIsHidden(true);
140     m_destructionInProgress = true;
141     GraphicsLayer::unregisterContentsLayer(m_layer->layer());
142     m_canvas->setNotificationClient(0);
143     m_canvas.clear();
144     m_layer->clearTexture();
145     // Orphaning the layer is required to trigger the recration of a new layer
146     // in the case where destruction is caused by a canvas resize. Test:
147     // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
148     m_layer->layer()->removeFromParent();
149     // To anyone who ever hits this assert: Please update crbug.com/344666
150     // with repro steps.
151     ASSERT(!m_bytesAllocated);
152 }
153
154 void Canvas2DLayerBridge::setIsHidden(bool hidden)
155 {
156     ASSERT(!m_destructionInProgress);
157     bool newHiddenValue = hidden || m_destructionInProgress;
158     if (m_isHidden == newHiddenValue)
159         return;
160
161     m_isHidden = newHiddenValue;
162     if (isHidden()) {
163         freeTransientResources();
164     }
165 }
166
167 void Canvas2DLayerBridge::freeTransientResources()
168 {
169     ASSERT(!m_destructionInProgress);
170     freeReleasedMailbox();
171     flush();
172     freeMemoryIfPossible(bytesAllocated());
173     ASSERT(!hasTransientResources());
174 }
175
176 bool Canvas2DLayerBridge::hasTransientResources() const
177 {
178     return !m_destructionInProgress && (hasReleasedMailbox() || bytesAllocated());
179 }
180
181 void Canvas2DLayerBridge::limitPendingFrames()
182 {
183     ASSERT(!m_destructionInProgress);
184     if (isHidden()) {
185         freeTransientResources();
186         return;
187     }
188     if (m_didRecordDrawCommand) {
189         m_framesPending++;
190         m_didRecordDrawCommand = false;
191         if (m_framesPending > 1) {
192             // Turn on the rate limiter if this layer tends to accumulate a
193             // non-discardable multi-frame backlog of draw commands.
194             setRateLimitingEnabled(true);
195         }
196         if (m_rateLimitingEnabled) {
197             flush();
198         }
199     }
200     ++m_framesSinceMailboxRelease;
201     if (releasedMailboxHasExpired()) {
202         freeReleasedMailbox();
203     }
204 }
205
206 void Canvas2DLayerBridge::prepareForDraw()
207 {
208     ASSERT(!m_destructionInProgress);
209     ASSERT(m_layer);
210     if (!surfaceIsValid() && !recoverSurface()) {
211         if (m_canvas) {
212             // drop pending commands because there is no surface to draw to
213             m_canvas->silentFlush();
214         }
215         return;
216     }
217     context()->makeContextCurrent();
218 }
219
220 void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated)
221 {
222     ASSERT(!m_destructionInProgress);
223     intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated;
224     m_bytesAllocated = bytesAllocated;
225     Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta);
226 }
227
228 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
229 {
230     return m_canvas->storageAllocatedForRecording();
231 }
232
233 void Canvas2DLayerBridge::flushedDrawCommands()
234 {
235     ASSERT(!m_destructionInProgress);
236     storageAllocatedForRecordingChanged(storageAllocatedForRecording());
237     m_framesPending = 0;
238 }
239
240 void Canvas2DLayerBridge::skippedPendingDrawCommands()
241 {
242     ASSERT(!m_destructionInProgress);
243     // Stop triggering the rate limiter if SkDeferredCanvas is detecting
244     // and optimizing overdraw.
245     setRateLimitingEnabled(false);
246     flushedDrawCommands();
247 }
248
249 void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled)
250 {
251     ASSERT(!m_destructionInProgress);
252     if (m_rateLimitingEnabled != enabled) {
253         m_rateLimitingEnabled = enabled;
254         m_layer->setRateLimitContext(m_rateLimitingEnabled);
255     }
256 }
257
258 size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree)
259 {
260     ASSERT(!m_destructionInProgress);
261     size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree);
262     m_bytesAllocated -= bytesFreed;
263     if (bytesFreed)
264         Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed));
265     return bytesFreed;
266 }
267
268 void Canvas2DLayerBridge::flush()
269 {
270     ASSERT(!m_destructionInProgress);
271     if (m_canvas->hasPendingCommands()) {
272         TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
273         freeReleasedMailbox(); // To avoid unnecessary triple-buffering
274         m_canvas->flush();
275     }
276 }
277
278 bool Canvas2DLayerBridge::releasedMailboxHasExpired()
279 {
280     // This heuristic indicates that the canvas is not being
281     // actively presented by the compositor (3 frames rendered since
282     // last mailbox release), suggesting that double buffering is not required.
283     return hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
284 }
285
286 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
287 {
288     return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
289 }
290
291 bool Canvas2DLayerBridge::hasReleasedMailbox() const
292 {
293     return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
294 }
295
296 void Canvas2DLayerBridge::freeReleasedMailbox()
297 {
298     if (m_contextProvider->context3d()->isContextLost() || !m_surfaceIsValid)
299         return;
300     MailboxInfo* mailboxInfo = releasedMailboxInfo();
301     if (!mailboxInfo)
302         return;
303
304     ASSERT(mailboxInfo->m_status == MailboxReleased);
305     if (mailboxInfo->m_mailbox.syncPoint) {
306         context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
307         mailboxInfo->m_mailbox.syncPoint = 0;
308     }
309     // Invalidate texture state in case the compositor altered it since the copy-on-write.
310     if (mailboxInfo->m_image) {
311         if (isHidden() || releasedMailboxHasExpired())
312             mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
313         mailboxInfo->m_image->getTexture()->invalidateCachedState();
314         mailboxInfo->m_image.clear();
315     }
316     mailboxInfo->m_status = MailboxAvailable;
317     m_releasedMailboxInfoIndex = InvalidMailboxIndex;
318     Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
319 }
320
321 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
322 {
323     // Check on m_layer is necessary because context() may be called during
324     // the destruction of m_layer
325     if (m_layer && !m_destructionInProgress && !surfaceIsValid()) {
326         recoverSurface(); // To ensure rate limiter is disabled if context is lost.
327     }
328     return m_contextProvider->context3d();
329 }
330
331 bool Canvas2DLayerBridge::surfaceIsValid()
332 {
333     ASSERT(!m_destructionInProgress);
334     return !m_destructionInProgress && !m_contextProvider->context3d()->isContextLost() && m_surfaceIsValid;
335 }
336
337 bool Canvas2DLayerBridge::recoverSurface()
338 {
339     ASSERT(!m_destructionInProgress);
340     ASSERT(m_layer && !surfaceIsValid());
341     if (m_destructionInProgress)
342         return false;
343
344     blink::WebGraphicsContext3D* sharedContext = 0;
345     // We must clear the mailboxes before calling m_layer->clearTexture() to prevent
346     // re-entry via mailboxReleased from operating on defunct GrContext objects.
347     m_mailboxes.clear();
348     m_releasedMailboxInfoIndex = InvalidMailboxIndex;
349     m_layer->clearTexture();
350     m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
351     if (m_contextProvider)
352         sharedContext = m_contextProvider->context3d();
353
354     if (!sharedContext || sharedContext->isContextLost()) {
355         m_surfaceIsValid = false;
356     } else {
357         IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
358         RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
359         if (surface.get()) {
360             m_canvas->setSurface(surface.get());
361             m_surfaceIsValid = true;
362             // FIXME: draw sad canvas picture into new buffer crbug.com/243842
363         } else {
364             // Surface allocation failed. Set m_surfaceIsValid to false to
365             // trigger subsequent retry.
366             m_surfaceIsValid = false;
367         }
368     }
369     if (!m_surfaceIsValid)
370         setRateLimitingEnabled(false);
371
372     return m_surfaceIsValid;
373 }
374
375 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
376 {
377     ASSERT(!m_destructionInProgress);
378     if (bitmap) {
379         // Using accelerated 2d canvas with software renderer, which
380         // should only happen in tests that use fake graphics contexts
381         // or in Android WebView in software mode. In this case, we do
382         // not care about producing any results for this canvas.
383         m_canvas->silentFlush();
384         m_lastImageId = 0;
385         return false;
386     }
387     if (!surfaceIsValid() && !recoverSurface())
388         return false;
389
390     blink::WebGraphicsContext3D* webContext = context();
391
392     // Release to skia textures that were previouosly released by the
393     // compositor. We do this before acquiring the next snapshot in
394     // order to cap maximum gpu memory consumption.
395     webContext->makeContextCurrent();
396     flush();
397
398     RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot());
399
400     // Early exit if canvas was not drawn to since last prepareMailbox
401     if (image->uniqueID() == m_lastImageId)
402         return false;
403     m_lastImageId = image->uniqueID();
404
405     MailboxInfo* mailboxInfo = createMailboxInfo();
406     mailboxInfo->m_status = MailboxInUse;
407     mailboxInfo->m_image = image;
408
409     // Because of texture sharing with the compositor, we must invalidate
410     // the state cached in skia so that the deferred copy on write
411     // in SkSurface_Gpu does not make any false assumptions.
412     mailboxInfo->m_image->getTexture()->invalidateCachedState();
413
414     ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
415     ASSERT(mailboxInfo->m_image.get());
416     ASSERT(mailboxInfo->m_image->getTexture());
417
418     webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
419     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
420     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
421     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
422     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
423     webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
424     if (isHidden()) {
425         // With hidden canvases, we release the SkImage immediately because
426         // there is no need for animations to be double buffered.
427         mailboxInfo->m_image.clear();
428     } else {
429         webContext->flush();
430         mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
431     }
432     webContext->bindTexture(GL_TEXTURE_2D, 0);
433     // Because we are changing the texture binding without going through skia,
434     // we must dirty the context.
435     m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
436
437     // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
438     // live mailboxes
439     ASSERT(!mailboxInfo->m_parentLayerBridge);
440     mailboxInfo->m_parentLayerBridge = this;
441     *outMailbox = mailboxInfo->m_mailbox;
442     return true;
443 }
444
445 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
446     ASSERT(!m_destructionInProgress);
447     MailboxInfo* mailboxInfo;
448     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
449         if (mailboxInfo->m_status == MailboxAvailable) {
450             return mailboxInfo;
451         }
452     }
453
454     // No available mailbox: create one.
455     m_mailboxes.grow(m_mailboxes.size() + 1);
456     mailboxInfo = &m_mailboxes.last();
457     context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
458     // Worst case, canvas is triple buffered.  More than 3 active mailboxes
459     // means there is a problem.
460     // For the single-threaded case, this value needs to be at least
461     // kMaxSwapBuffersPending+1 (in render_widget.h).
462     // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
463     // TODO(piman): fix this.
464     ASSERT(m_mailboxes.size() <= 4);
465     ASSERT(mailboxInfo < m_mailboxes.end());
466     return mailboxInfo;
467 }
468
469 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
470 {
471     freeReleasedMailbox(); // Never have more than one mailbox in the released state.
472     Vector<MailboxInfo>::iterator mailboxInfo;
473     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
474         if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
475             mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
476             ASSERT(mailboxInfo->m_status == MailboxInUse);
477             mailboxInfo->m_status = MailboxReleased;
478             // Trigger Canvas2DLayerBridge self-destruction if this is the
479             // last live mailbox and the layer bridge is not externally
480             // referenced.
481             m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
482             m_framesSinceMailboxRelease = 0;
483             if (isHidden()) {
484                 freeReleasedMailbox();
485             } else {
486                 ASSERT(!m_destructionInProgress);
487                 Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
488             }
489             ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
490             mailboxInfo->m_parentLayerBridge.clear();
491             return;
492         }
493     }
494 }
495
496 blink::WebLayer* Canvas2DLayerBridge::layer() const
497 {
498     ASSERT(!m_destructionInProgress);
499     ASSERT(m_layer);
500     return m_layer->layer();
501 }
502
503 void Canvas2DLayerBridge::willUse()
504 {
505     ASSERT(!m_destructionInProgress);
506     Canvas2DLayerManager::get().layerDidDraw(this);
507     m_didRecordDrawCommand = true;
508 }
509
510 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
511 {
512     ASSERT(!m_destructionInProgress);
513     if (!surfaceIsValid() && !recoverSurface())
514         return 0;
515     willUse();
516     m_canvas->flush();
517     context()->flush();
518     GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
519     if (renderTarget) {
520         return renderTarget->asTexture()->getTextureHandle();
521     }
522     return 0;
523 }
524
525 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
526     // This copy constructor should only be used for Vector reallocation
527     // Assuming 'other' is to be destroyed, we transfer m_image ownership
528     // rather than do a refcount dance.
529     memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
530     m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
531     m_status = other.m_status;
532 }
533
534 }