2011-04-11 Vangelis Kokkevis <vangelis@chromium.org>
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / ContentLayerChromium.cpp
1 /*
2  * Copyright (C) 2010 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if USE(ACCELERATED_COMPOSITING)
34
35 #include "ContentLayerChromium.h"
36
37 #include "cc/CCLayerImpl.h"
38 #include "GraphicsContext3D.h"
39 #include "LayerRendererChromium.h"
40 #include "LayerTexture.h"
41 #include "RenderLayerBacking.h"
42 #include "TextStream.h"
43
44 namespace WebCore {
45
46 PassRefPtr<ContentLayerChromium> ContentLayerChromium::create(GraphicsLayerChromium* owner)
47 {
48     return adoptRef(new ContentLayerChromium(owner));
49 }
50
51 ContentLayerChromium::ContentLayerChromium(GraphicsLayerChromium* owner)
52     : LayerChromium(owner)
53     , m_contentsTexture(0)
54     , m_skipsDraw(false)
55 {
56 }
57
58 ContentLayerChromium::~ContentLayerChromium()
59 {
60     cleanupResources();
61 }
62
63 void ContentLayerChromium::cleanupResources()
64 {
65     LayerChromium::cleanupResources();
66     m_contentsTexture.clear();
67 }
68
69 bool ContentLayerChromium::requiresClippedUpdateRect()
70 {
71     // To avoid allocating excessively large textures, switch into "large layer mode" if
72     // one of the layer's dimensions is larger than 2000 pixels or the size of
73     // surface it's rendering into. This is a temporary measure until layer tiling is implemented.
74     static const int maxLayerSize = 2000;
75     return (bounds().width() > max(maxLayerSize, ccLayerImpl()->targetRenderSurface()->contentRect().width())
76             || bounds().height() > max(maxLayerSize, ccLayerImpl()->targetRenderSurface()->contentRect().height())
77             || !layerRenderer()->checkTextureSize(bounds()));
78 }
79
80 void ContentLayerChromium::paintContentsIfDirty()
81 {
82     RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client());
83     if (!backing || backing->paintingGoesToWindow())
84         return;
85
86     ASSERT(drawsContent());
87
88     ASSERT(layerRenderer());
89
90     IntRect dirtyRect;
91     IntRect boundsRect(IntPoint(0, 0), bounds());
92     IntPoint paintingOffset;
93
94     // FIXME: Remove this test when tiled layers are implemented.
95     if (requiresClippedUpdateRect()) {
96         // Calculate the region of this layer that is currently visible.
97         const IntRect clipRect = ccLayerImpl()->targetRenderSurface()->contentRect();
98
99         TransformationMatrix layerOriginTransform = ccLayerImpl()->drawTransform();
100         layerOriginTransform.translate3d(-0.5 * bounds().width(), -0.5 * bounds().height(), 0);
101
102         // We compute the visible portion of the layer by back-mapping the current RenderSurface
103         // content area to the layer. To do that, we invert the drawing matrix of the layer
104         // and project the content area rectangle to it. If the layer transform is not invertible
105         // then we skip rendering the layer.
106         if (!layerOriginTransform.isInvertible()) {
107             m_skipsDraw = true;
108             return;
109         }
110         TransformationMatrix targetToLayerMatrix = layerOriginTransform.inverse();
111         FloatQuad mappedClipToLayer = targetToLayerMatrix.projectQuad(FloatRect(clipRect));
112         IntRect visibleRectInLayerCoords = mappedClipToLayer.enclosingBoundingBox();
113         visibleRectInLayerCoords.intersect(IntRect(0, 0, bounds().width(), bounds().height()));
114
115         // If this is still too large to render, then skip the layer completely.
116         if (!layerRenderer()->checkTextureSize(visibleRectInLayerCoords.size())) {
117             m_skipsDraw = true;
118             return;
119         }
120
121         // If we need to resize the upload buffer we have to repaint everything.
122         if (m_canvas.size() != visibleRectInLayerCoords.size()) {
123             resizeUploadBuffer(visibleRectInLayerCoords.size());
124             m_dirtyRect = boundsRect;
125         }
126         // If the visible portion of the layer is different from the last upload.
127         if (visibleRectInLayerCoords != m_visibleRectInLayerCoords)
128             m_dirtyRect = boundsRect;
129         m_visibleRectInLayerCoords = visibleRectInLayerCoords;
130
131         // Calculate the portion of the dirty rectangle that is visible.  m_dirtyRect is in layer space.
132         IntRect visibleDirtyRectInLayerSpace = enclosingIntRect(m_dirtyRect);
133         visibleDirtyRectInLayerSpace.intersect(visibleRectInLayerCoords);
134
135         // What the rectangles mean:
136         //   dirtyRect: The region of this layer that will be updated.
137         //   m_uploadUpdateRect: The region of the layer's texture that will be uploaded into.
138         dirtyRect = visibleDirtyRectInLayerSpace;
139         m_uploadUpdateRect = dirtyRect;
140         IntSize visibleRectOffsetInLayerCoords(visibleRectInLayerCoords.x(), visibleRectInLayerCoords.y());
141         paintingOffset = IntPoint(visibleRectOffsetInLayerCoords);
142         m_uploadUpdateRect.move(-visibleRectOffsetInLayerCoords);
143     } else {
144         dirtyRect = IntRect(m_dirtyRect);
145         // If the texture needs to be reallocated then we must redraw the entire
146         // contents of the layer.
147         if (m_canvas.size() != bounds()) {
148             resizeUploadBuffer(bounds());
149             dirtyRect = boundsRect;
150         } else {
151             // Clip the dirtyRect to the size of the layer to avoid drawing
152             // outside the bounds of the backing texture.
153             dirtyRect.intersect(boundsRect);
154         }
155         m_uploadUpdateRect = dirtyRect;
156     }
157
158     if (dirtyRect.isEmpty())
159         return;
160
161     PlatformCanvas::Painter painter(&m_canvas);
162     painter.context()->save();
163     painter.context()->translate(-paintingOffset.x(), -paintingOffset.y());
164     painter.context()->clearRect(dirtyRect);
165     painter.context()->clip(dirtyRect);
166
167     m_owner->paintGraphicsLayerContents(*painter.context(), dirtyRect);
168     painter.context()->restore();
169 }
170
171 void ContentLayerChromium::resizeUploadBuffer(const IntSize& size)
172 {
173     m_canvas.resize(size);
174 }
175
176 void ContentLayerChromium::updateTextureIfNeeded()
177 {
178     PlatformCanvas::AutoLocker locker(&m_canvas);
179     updateTexture(locker.pixels(), m_canvas.size());
180 }
181
182 void ContentLayerChromium::updateTexture(const uint8_t* pixels, const IntSize& size)
183 {
184     if (!pixels)
185         return;
186
187     GraphicsContext3D* context = layerRendererContext();
188     if (!m_contentsTexture)
189         m_contentsTexture = LayerTexture::create(context, layerRenderer()->textureManager());
190
191     // If we have to allocate a new texture we have to upload the full contents.
192     if (!m_contentsTexture->isValid(size, GraphicsContext3D::RGBA))
193         m_uploadUpdateRect = IntRect(IntPoint(0, 0), size);
194
195     if (m_uploadUpdateRect.isEmpty())
196         return;
197
198     if (!m_contentsTexture->reserve(size, GraphicsContext3D::RGBA)) {
199         m_skipsDraw = true;
200         return;
201     }
202
203     IntRect srcRect = IntRect(IntPoint(0, 0), size);
204     if (requiresClippedUpdateRect())
205         srcRect = m_visibleRectInLayerCoords;
206
207     const size_t destStride = m_uploadUpdateRect.width() * 4;
208     const size_t srcStride = srcRect.width() * 4;
209
210     const uint8_t* uploadPixels = pixels + srcStride * m_uploadUpdateRect.y();
211     Vector<uint8_t> uploadBuffer;
212     if (srcStride != destStride || m_uploadUpdateRect.x()) {
213         uploadBuffer.resize(m_uploadUpdateRect.height() * destStride);
214         for (int row = 0; row < m_uploadUpdateRect.height(); ++row) {
215             size_t srcOffset = (m_uploadUpdateRect.y() + row) * srcStride + m_uploadUpdateRect.x() * 4;
216             ASSERT(srcOffset + destStride <= static_cast<size_t>(size.width() * size.height() * 4));
217             size_t destOffset = row * destStride;
218             ASSERT(destOffset  + destStride <= uploadBuffer.size());
219             memcpy(uploadBuffer.data() + destOffset, pixels + srcOffset, destStride);
220         }
221         uploadPixels = uploadBuffer.data();
222     }
223
224     m_contentsTexture->bindTexture();
225     GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0,
226                                         m_uploadUpdateRect.x(), m_uploadUpdateRect.y(), m_uploadUpdateRect.width(), m_uploadUpdateRect.height(),
227                                         GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE,
228                                         uploadPixels));
229
230     m_uploadUpdateRect = IntRect();
231     m_dirtyRect.setSize(FloatSize());
232     // Large layers always stay dirty, because they need to update when the content rect changes.
233     m_contentsDirty = requiresClippedUpdateRect();
234 }
235
236 void ContentLayerChromium::draw()
237 {
238     if (m_skipsDraw)
239         return;
240
241     ASSERT(layerRenderer());
242
243     const ContentLayerChromium::Program* program = layerRenderer()->contentLayerProgram();
244     ASSERT(program && program->initialized());
245     GraphicsContext3D* context = layerRendererContext();
246     GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0));
247     bindContentsTexture();
248     layerRenderer()->useShader(program->program());
249     GLC(context, context->uniform1i(program->fragmentShader().samplerLocation(), 0));
250     GLC(context, context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA));
251
252     if (requiresClippedUpdateRect()) {
253         // Compute the offset between the layer's center point and the center of the visible portion
254         // of the layer.
255         FloatPoint visibleRectCenterOffset = FloatRect(m_visibleRectInLayerCoords).center();
256         visibleRectCenterOffset.move(-0.5 * bounds().width(), -0.5 * bounds().height());
257
258         TransformationMatrix transform = ccLayerImpl()->drawTransform();
259         transform.translate(visibleRectCenterOffset.x(), visibleRectCenterOffset.y());
260
261         drawTexturedQuad(context, layerRenderer()->projectionMatrix(),
262                          transform, m_visibleRectInLayerCoords.width(),
263                          m_visibleRectInLayerCoords.height(), ccLayerImpl()->drawOpacity(),
264                          program->vertexShader().matrixLocation(),
265                          program->fragmentShader().alphaLocation());
266     } else {
267         drawTexturedQuad(context, layerRenderer()->projectionMatrix(),
268                          ccLayerImpl()->drawTransform(), bounds().width(), bounds().height(),
269                          ccLayerImpl()->drawOpacity(), program->vertexShader().matrixLocation(),
270                          program->fragmentShader().alphaLocation());
271     }
272     unreserveContentsTexture();
273 }
274
275 void ContentLayerChromium::updateCompositorResources()
276 {
277     updateTextureIfNeeded();
278 }
279
280 void ContentLayerChromium::unreserveContentsTexture()
281 {
282     if (!m_skipsDraw && m_contentsTexture)
283         m_contentsTexture->unreserve();
284 }
285
286 void ContentLayerChromium::bindContentsTexture()
287 {
288     if (!m_skipsDraw && m_contentsTexture)
289         m_contentsTexture->bindTexture();
290 }
291
292 static void writeIndent(TextStream& ts, int indent)
293 {
294     for (int i = 0; i != indent; ++i)
295         ts << "  ";
296 }
297
298 void ContentLayerChromium::dumpLayerProperties(TextStream& ts, int indent) const
299 {
300     LayerChromium::dumpLayerProperties(ts, indent);
301     writeIndent(ts, indent);
302     ts << "skipsDraw: " << m_skipsDraw << "\n";
303 }
304
305 }
306 #endif // USE(ACCELERATED_COMPOSITING)