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