7767b6b184ff7e3eb1814eb4cad69e56f45d5ce4
[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 "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
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
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
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
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
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
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
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     context()->makeContextCurrent();
161 }
162
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
171 size_t Canvas2DLayerBridge::storageAllocatedForRecording()
172 {
173     ASSERT(!m_destructionInProgress);
174     return m_canvas->storageAllocatedForRecording();
175 }
176
177 void Canvas2DLayerBridge::flushedDrawCommands()
178 {
179     ASSERT(!m_destructionInProgress);
180     storageAllocatedForRecordingChanged(storageAllocatedForRecording());
181     m_framesPending = 0;
182 }
183
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
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
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
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     }
219 }
220
221 blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
222 {
223     // Check on m_layer is necessary because context() may be called during
224     // the destruction of m_layer
225     if (m_layer) {
226         isValid(); // To ensure rate limiter is disabled if context is lost.
227     }
228     return m_context->webContext();
229 }
230
231 bool Canvas2DLayerBridge::isValid()
232 {
233     ASSERT(m_layer);
234     if (m_destructionInProgress)
235         return false;
236     if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) {
237         // Attempt to recover.
238         m_layer->clearTexture();
239         m_mailboxes.clear();
240         RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get();
241         if (!sharedContext || sharedContext->webContext()->isContextLost()) {
242             m_surfaceIsValid = false;
243         } else {
244             m_context = sharedContext;
245             IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
246             RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount));
247             if (surface.get()) {
248                 m_canvas->setSurface(surface.get());
249                 m_surfaceIsValid = true;
250                 // FIXME: draw sad canvas picture into new buffer crbug.com/243842
251             } else {
252                 // Surface allocation failed. Set m_surfaceIsValid to false to
253                 // trigger subsequent retry.
254                 m_surfaceIsValid = false;
255             }
256         }
257     }
258     if (!m_surfaceIsValid)
259         setRateLimitingEnabled(false);
260     return m_surfaceIsValid;
261 }
262
263 bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMailbox, blink::WebExternalBitmap* bitmap)
264 {
265     if (bitmap) {
266         // Using accelerated 2d canvas with software renderer, which
267         // should only happen in tests that use fake graphics contexts.
268         // In this case, we do not care about producing any results for
269         // compositing.
270         m_canvas->silentFlush();
271         return false;
272     }
273     if (!isValid())
274         return false;
275
276     blink::WebGraphicsContext3D* webContext = context();
277
278     // Release to skia textures that were previouosly released by the
279     // compositor. We do this before acquiring the next snapshot in
280     // order to cap maximum gpu memory consumption.
281     webContext->makeContextCurrent();
282     flush();
283     Vector<MailboxInfo>::iterator mailboxInfo;
284     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
285         if (mailboxInfo->m_status == MailboxReleased) {
286             if (mailboxInfo->m_mailbox.syncPoint) {
287                 webContext->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint);
288                 mailboxInfo->m_mailbox.syncPoint = 0;
289             }
290             // Invalidate texture state in case the compositor altered it since the copy-on-write.
291             mailboxInfo->m_image->getTexture()->invalidateCachedState();
292             mailboxInfo->m_image.reset(0);
293             mailboxInfo->m_status = MailboxAvailable;
294         }
295     }
296     SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot());
297     // Early exit if canvas was not drawn to since last prepareMailbox
298     if (image->uniqueID() == m_lastImageId)
299         return false;
300     m_lastImageId = image->uniqueID();
301
302     mailboxInfo = createMailboxInfo();
303     mailboxInfo->m_status = MailboxInUse;
304     mailboxInfo->m_image.swap(&image);
305     // Because of texture sharing with the compositor, we must invalidate
306     // the state cached in skia so that the deferred copy on write
307     // in SkSurface_Gpu does not make any false assumptions.
308     mailboxInfo->m_image->getTexture()->invalidateCachedState();
309
310     ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
311     ASSERT(mailboxInfo->m_image.get());
312     ASSERT(mailboxInfo->m_image->getTexture());
313
314     webContext->bindTexture(GL_TEXTURE_2D, mailboxInfo->m_image->getTexture()->getTextureHandle());
315     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
316     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
317     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
318     webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
319     webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name);
320     webContext->flush();
321     mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint();
322     webContext->bindTexture(GL_TEXTURE_2D, 0);
323     // Because we are changing the texture binding without going through skia,
324     // we must dirty the context.
325     m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState);
326
327     // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
328     // live mailboxes
329     ASSERT(!mailboxInfo->m_parentLayerBridge);
330     mailboxInfo->m_parentLayerBridge = this;
331     *outMailbox = mailboxInfo->m_mailbox;
332     return true;
333 }
334
335 Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() {
336     ASSERT(!m_destructionInProgress);
337     MailboxInfo* mailboxInfo;
338     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); mailboxInfo++) {
339         if (mailboxInfo->m_status == MailboxAvailable) {
340             return mailboxInfo;
341         }
342     }
343
344     // No available mailbox: create one.
345     m_mailboxes.grow(m_mailboxes.size() + 1);
346     mailboxInfo = &m_mailboxes.last();
347     context()->genMailboxCHROMIUM(mailboxInfo->m_mailbox.name);
348     // Worst case, canvas is triple buffered.  More than 3 active mailboxes
349     // means there is a problem.
350     // For the single-threaded case, this value needs to be at least
351     // kMaxSwapBuffersPending+1 (in render_widget.h).
352     // Because of crbug.com/247874, it needs to be kMaxSwapBuffersPending+2.
353     // TODO(piman): fix this.
354     ASSERT(m_mailboxes.size() <= 4);
355     ASSERT(mailboxInfo < m_mailboxes.end());
356     return mailboxInfo;
357 }
358
359 void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox)
360 {
361     Vector<MailboxInfo>::iterator mailboxInfo;
362     for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) {
363         if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) {
364             mailboxInfo->m_mailbox.syncPoint = mailbox.syncPoint;
365             ASSERT(mailboxInfo->m_status == MailboxInUse);
366             mailboxInfo->m_status = MailboxReleased;
367             // Trigger Canvas2DLayerBridge self-destruction if this is the
368             // last live mailbox and the layer bridge is not externally
369             // referenced.
370             ASSERT(mailboxInfo->m_parentLayerBridge.get() == this);
371             mailboxInfo->m_parentLayerBridge.clear();
372             return;
373         }
374     }
375 }
376
377 blink::WebLayer* Canvas2DLayerBridge::layer() const
378 {
379     ASSERT(m_layer);
380     return m_layer->layer();
381 }
382
383 void Canvas2DLayerBridge::willUse()
384 {
385     ASSERT(!m_destructionInProgress);
386     Canvas2DLayerManager::get().layerDidDraw(this);
387     m_didRecordDrawCommand = true;
388 }
389
390 Platform3DObject Canvas2DLayerBridge::getBackingTexture()
391 {
392     ASSERT(!m_destructionInProgress);
393     if (!isValid())
394         return 0;
395     willUse();
396     m_canvas->flush();
397     context()->flush();
398     GrRenderTarget* renderTarget = m_canvas->getTopDevice()->accessRenderTarget();
399     if (renderTarget) {
400         return renderTarget->asTexture()->getTextureHandle();
401     }
402     return 0;
403 }
404
405 Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
406     // This copy constructor should only be used for Vector reallocation
407     // Assuming 'other' is to be destroyed, we swap m_image ownership
408     // rather than do a refcount dance.
409     memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
410     m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image));
411     m_status = other.m_status;
412 }
413
414 }