• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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 
27 #include "config.h"
28 
29 #if USE(ACCELERATED_COMPOSITING)
30 
31 #include "LayerTilerChromium.h"
32 
33 #include "GraphicsContext.h"
34 #include "GraphicsContext3D.h"
35 #include "LayerRendererChromium.h"
36 #include "LayerTexture.h"
37 #include "TraceEvent.h"
38 
39 #include <wtf/PassOwnArrayPtr.h>
40 
41 using namespace std;
42 
43 static int minTextureSize = 16;
44 
45 namespace WebCore {
46 
create(LayerRendererChromium * layerRenderer,const IntSize & tileSize,BorderTexelOption border)47 PassOwnPtr<LayerTilerChromium> LayerTilerChromium::create(LayerRendererChromium* layerRenderer, const IntSize& tileSize, BorderTexelOption border)
48 {
49     if (!layerRenderer || tileSize.isEmpty())
50         return 0;
51 
52     return adoptPtr(new LayerTilerChromium(layerRenderer, tileSize, border));
53 }
54 
LayerTilerChromium(LayerRendererChromium * layerRenderer,const IntSize & tileSize,BorderTexelOption border)55 LayerTilerChromium::LayerTilerChromium(LayerRendererChromium* layerRenderer, const IntSize& tileSize, BorderTexelOption border)
56     : m_skipsDraw(false)
57     , m_tilingData(max(tileSize.width(), tileSize.height()), 0, 0, border == HasBorderTexels)
58     , m_layerRenderer(layerRenderer)
59 {
60     setTileSize(tileSize);
61 }
62 
setLayerRenderer(LayerRendererChromium * layerRenderer)63 void LayerTilerChromium::setLayerRenderer(LayerRendererChromium* layerRenderer)
64 {
65     if (m_layerRenderer != layerRenderer)
66         reset();
67     m_layerRenderer = layerRenderer;
68 }
69 
~LayerTilerChromium()70 LayerTilerChromium::~LayerTilerChromium()
71 {
72     reset();
73 }
74 
layerRendererContext() const75 GraphicsContext3D* LayerTilerChromium::layerRendererContext() const
76 {
77     ASSERT(layerRenderer());
78     return layerRenderer()->context();
79 }
80 
setTileSize(const IntSize & requestedSize)81 void LayerTilerChromium::setTileSize(const IntSize& requestedSize)
82 {
83     IntSize size(max(minTextureSize, requestedSize.width()), max(minTextureSize, requestedSize.height()));
84 
85     if (m_tileSize == size)
86         return;
87 
88     reset();
89 
90     m_tileSize = size;
91     m_tilePixels = adoptArrayPtr(new uint8_t[m_tileSize.width() * m_tileSize.height() * 4]);
92     m_tilingData.setMaxTextureSize(max(size.width(), size.height()));
93 }
94 
getSingleTexture()95 LayerTexture* LayerTilerChromium::getSingleTexture()
96 {
97     Tile* tile = tileAt(0, 0);
98     return tile ? tile->texture() : 0;
99 }
100 
reset()101 void LayerTilerChromium::reset()
102 {
103     m_tiles.clear();
104     m_unusedTiles.clear();
105     m_tilingData.setTotalSize(0, 0);
106 }
107 
createTile(int i,int j)108 LayerTilerChromium::Tile* LayerTilerChromium::createTile(int i, int j)
109 {
110     ASSERT(!tileAt(i, j));
111 
112     RefPtr<Tile> tile;
113     if (m_unusedTiles.size() > 0) {
114         tile = m_unusedTiles.last().release();
115         m_unusedTiles.removeLast();
116         ASSERT(tile->refCount() == 1);
117     } else {
118         GraphicsContext3D* context = layerRendererContext();
119         TextureManager* manager = layerRenderer()->textureManager();
120         tile = adoptRef(new Tile(LayerTexture::create(context, manager)));
121     }
122     m_tiles.add(make_pair(i, j), tile);
123 
124     tile->moveTo(i, j);
125     tile->m_dirtyLayerRect = tileLayerRect(tile.get());
126 
127     return tile.get();
128 }
129 
invalidateTiles(const IntRect & contentRect)130 void LayerTilerChromium::invalidateTiles(const IntRect& contentRect)
131 {
132     if (!m_tiles.size())
133         return;
134 
135     Vector<TileMapKey> removeKeys;
136     for (TileMap::iterator iter = m_tiles.begin(); iter != m_tiles.end(); ++iter) {
137         Tile* tile = iter->second.get();
138         IntRect tileRect = tileContentRect(tile);
139         if (tileRect.intersects(contentRect))
140             continue;
141         removeKeys.append(iter->first);
142     }
143 
144     for (size_t i = 0; i < removeKeys.size(); ++i)
145         m_unusedTiles.append(m_tiles.take(removeKeys[i]));
146 }
147 
contentRectToTileIndices(const IntRect & contentRect,int & left,int & top,int & right,int & bottom) const148 void LayerTilerChromium::contentRectToTileIndices(const IntRect& contentRect, int& left, int& top, int& right, int& bottom) const
149 {
150     const IntRect layerRect = contentRectToLayerRect(contentRect);
151 
152     left = m_tilingData.tileXIndexFromSrcCoord(layerRect.x());
153     top = m_tilingData.tileYIndexFromSrcCoord(layerRect.y());
154     right = m_tilingData.tileXIndexFromSrcCoord(layerRect.maxX() - 1);
155     bottom = m_tilingData.tileYIndexFromSrcCoord(layerRect.maxY() - 1);
156 }
157 
contentRectToLayerRect(const IntRect & contentRect) const158 IntRect LayerTilerChromium::contentRectToLayerRect(const IntRect& contentRect) const
159 {
160     IntPoint pos(contentRect.x() - m_layerPosition.x(), contentRect.y() - m_layerPosition.y());
161     IntRect layerRect(pos, contentRect.size());
162 
163     // Clip to the position.
164     if (pos.x() < 0 || pos.y() < 0)
165         layerRect = IntRect(IntPoint(0, 0), IntSize(contentRect.width() + pos.x(), contentRect.height() + pos.y()));
166     return layerRect;
167 }
168 
layerRectToContentRect(const IntRect & layerRect) const169 IntRect LayerTilerChromium::layerRectToContentRect(const IntRect& layerRect) const
170 {
171     IntRect contentRect = layerRect;
172     contentRect.move(m_layerPosition.x(), m_layerPosition.y());
173     return contentRect;
174 }
175 
tileAt(int i,int j) const176 LayerTilerChromium::Tile* LayerTilerChromium::tileAt(int i, int j) const
177 {
178     Tile* tile = m_tiles.get(make_pair(i, j)).get();
179     ASSERT(!tile || tile->refCount() == 1);
180     return tile;
181 }
182 
tileContentRect(const Tile * tile) const183 IntRect LayerTilerChromium::tileContentRect(const Tile* tile) const
184 {
185     IntRect contentRect = tileLayerRect(tile);
186     contentRect.move(m_layerPosition.x(), m_layerPosition.y());
187     return contentRect;
188 }
189 
tileLayerRect(const Tile * tile) const190 IntRect LayerTilerChromium::tileLayerRect(const Tile* tile) const
191 {
192     const int index = m_tilingData.tileIndex(tile->i(), tile->j());
193     IntRect layerRect = m_tilingData.tileBoundsWithBorder(index);
194     layerRect.setSize(m_tileSize);
195     return layerRect;
196 }
197 
invalidateRect(const IntRect & contentRect)198 void LayerTilerChromium::invalidateRect(const IntRect& contentRect)
199 {
200     if (contentRect.isEmpty() || m_skipsDraw)
201         return;
202 
203     growLayerToContain(contentRect);
204 
205     // Dirty rects are always in layer space, as the layer could be repositioned
206     // after invalidation.
207     const IntRect layerRect = contentRectToLayerRect(contentRect);
208 
209     int left, top, right, bottom;
210     contentRectToTileIndices(contentRect, left, top, right, bottom);
211     for (int j = top; j <= bottom; ++j) {
212         for (int i = left; i <= right; ++i) {
213             Tile* tile = tileAt(i, j);
214             if (!tile)
215                 continue;
216             IntRect bound = tileLayerRect(tile);
217             bound.intersect(layerRect);
218             tile->m_dirtyLayerRect.unite(bound);
219         }
220     }
221 }
222 
invalidateEntireLayer()223 void LayerTilerChromium::invalidateEntireLayer()
224 {
225     for (TileMap::iterator iter = m_tiles.begin(); iter != m_tiles.end(); ++iter) {
226         ASSERT(iter->second->refCount() == 1);
227         m_unusedTiles.append(iter->second.release());
228     }
229     m_tiles.clear();
230 
231     m_tilingData.setTotalSize(0, 0);
232 }
233 
update(TilePaintInterface & painter,const IntRect & contentRect)234 void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& contentRect)
235 {
236     if (m_skipsDraw)
237         return;
238 
239     // Invalidate old tiles that were previously used but aren't in use this
240     // frame so that they can get reused for new tiles.
241     invalidateTiles(contentRect);
242     growLayerToContain(contentRect);
243 
244     // Create tiles as needed, expanding a dirty rect to contain all
245     // the dirty regions currently being drawn.
246     IntRect dirtyLayerRect;
247     int left, top, right, bottom;
248     contentRectToTileIndices(contentRect, left, top, right, bottom);
249     for (int j = top; j <= bottom; ++j) {
250         for (int i = left; i <= right; ++i) {
251             Tile* tile = tileAt(i, j);
252             if (!tile)
253                 tile = createTile(i, j);
254             if (!tile->texture()->isValid(m_tileSize, GraphicsContext3D::RGBA))
255                 tile->m_dirtyLayerRect = tileLayerRect(tile);
256             else
257                 tile->texture()->reserve(m_tileSize, GraphicsContext3D::RGBA);
258             dirtyLayerRect.unite(tile->m_dirtyLayerRect);
259         }
260     }
261 
262     // Due to borders, when the paint rect is extended to tile boundaries, it
263     // may end up overlapping more tiles than the original content rect. Record
264     // that original rect so we don't upload more tiles than necessary.
265     m_updateRect = contentRect;
266 
267     m_paintRect = layerRectToContentRect(dirtyLayerRect);
268     if (dirtyLayerRect.isEmpty())
269         return;
270 
271     m_canvas.resize(m_paintRect.size());
272 
273     // Assumption: if a tiler is using border texels, then it is because the
274     // layer is likely to be filtered or transformed. Because of it might be
275     // transformed, draw the text in grayscale instead of subpixel antialiasing.
276     PlatformCanvas::Painter::TextOption textOption = m_tilingData.borderTexels() ? PlatformCanvas::Painter::GrayscaleText : PlatformCanvas::Painter::SubpixelText;
277     PlatformCanvas::Painter canvasPainter(&m_canvas, textOption);
278     canvasPainter.context()->translate(-m_paintRect.x(), -m_paintRect.y());
279     {
280         TRACE_EVENT("LayerTilerChromium::update::paint", this, 0);
281         painter.paint(*canvasPainter.context(), m_paintRect);
282     }
283 }
284 
uploadCanvas()285 void LayerTilerChromium::uploadCanvas()
286 {
287     PlatformCanvas::AutoLocker locker(&m_canvas);
288     {
289         TRACE_EVENT("LayerTilerChromium::updateFromPixels", this, 0);
290         updateFromPixels(m_updateRect, m_paintRect, locker.pixels());
291     }
292 }
293 
updateFromPixels(const IntRect & contentRect,const IntRect & paintRect,const uint8_t * paintPixels)294 void LayerTilerChromium::updateFromPixels(const IntRect& contentRect, const IntRect& paintRect, const uint8_t* paintPixels)
295 {
296     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
297     if (!m_tilingData.totalSizeX() || !m_tilingData.totalSizeY())
298         return;
299 
300     GraphicsContext3D* context = layerRendererContext();
301 
302     int left, top, right, bottom;
303     contentRectToTileIndices(contentRect, left, top, right, bottom);
304     for (int j = top; j <= bottom; ++j) {
305         for (int i = left; i <= right; ++i) {
306             Tile* tile = tileAt(i, j);
307             if (!tile)
308                 tile = createTile(i, j);
309             else if (!tile->dirty())
310                 continue;
311 
312             // Calculate page-space rectangle to copy from.
313             IntRect sourceRect = tileContentRect(tile);
314             const IntPoint anchor = sourceRect.location();
315             sourceRect.intersect(layerRectToContentRect(tile->m_dirtyLayerRect));
316             // Paint rect not guaranteed to line up on tile boundaries, so
317             // make sure that sourceRect doesn't extend outside of it.
318             sourceRect.intersect(paintRect);
319             if (sourceRect.isEmpty())
320                 continue;
321 
322             if (!tile->texture()->isReserved()) {
323                 if (!tile->texture()->reserve(m_tileSize, GraphicsContext3D::RGBA)) {
324                     m_skipsDraw = true;
325                     reset();
326                     return;
327                 }
328             }
329 
330             // Calculate tile-space rectangle to upload into.
331             IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
332             if (destRect.x() < 0)
333                 CRASH();
334             if (destRect.y() < 0)
335                 CRASH();
336 
337             // Offset from paint rectangle to this tile's dirty rectangle.
338             IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y());
339             if (paintOffset.x() < 0)
340                 CRASH();
341             if (paintOffset.y() < 0)
342                 CRASH();
343             if (paintOffset.x() + destRect.width() > paintRect.width())
344                 CRASH();
345             if (paintOffset.y() + destRect.height() > paintRect.height())
346                 CRASH();
347 
348             const uint8_t* pixelSource;
349             if (paintRect.width() == sourceRect.width() && !paintOffset.x())
350                 pixelSource = &paintPixels[4 * paintOffset.y() * paintRect.width()];
351             else {
352                 // Strides not equal, so do a row-by-row memcpy from the
353                 // paint results into a temp buffer for uploading.
354                 for (int row = 0; row < destRect.height(); ++row)
355                     memcpy(&m_tilePixels[destRect.width() * 4 * row],
356                            &paintPixels[4 * (paintOffset.x() + (paintOffset.y() + row) * paintRect.width())],
357                            destRect.width() * 4);
358 
359                 pixelSource = &m_tilePixels[0];
360             }
361 
362             tile->texture()->bindTexture();
363 
364             const GC3Dint filter = m_tilingData.borderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
365             GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, filter));
366             GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, filter));
367 
368             GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, destRect.x(), destRect.y(), destRect.width(), destRect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixelSource));
369 
370             tile->clearDirty();
371         }
372     }
373 }
374 
setLayerPosition(const IntPoint & layerPosition)375 void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition)
376 {
377     m_layerPosition = layerPosition;
378 }
379 
draw(const IntRect & contentRect,const TransformationMatrix & globalTransform,float opacity)380 void LayerTilerChromium::draw(const IntRect& contentRect, const TransformationMatrix& globalTransform, float opacity)
381 {
382     if (m_skipsDraw || !m_tiles.size())
383         return;
384 
385     GraphicsContext3D* context = layerRendererContext();
386     const LayerTilerChromium::Program* program = layerRenderer()->tilerProgram();
387     layerRenderer()->useShader(program->program());
388     GLC(context, context->uniform1i(program->fragmentShader().samplerLocation(), 0));
389 
390     int left, top, right, bottom;
391     contentRectToTileIndices(contentRect, left, top, right, bottom);
392     for (int j = top; j <= bottom; ++j) {
393         for (int i = left; i <= right; ++i) {
394             Tile* tile = tileAt(i, j);
395             if (!tile)
396                 continue;
397 
398             tile->texture()->bindTexture();
399 
400             TransformationMatrix tileMatrix(globalTransform);
401 
402             // Don't use tileContentRect here, as that contains the full
403             // rect with border texels which shouldn't be drawn.
404             IntRect tileRect = m_tilingData.tileBounds(m_tilingData.tileIndex(tile->i(), tile->j()));
405             tileRect.move(m_layerPosition.x(), m_layerPosition.y());
406             tileMatrix.translate3d(tileRect.x() + tileRect.width() / 2.0, tileRect.y() + tileRect.height() / 2.0, 0);
407 
408             IntPoint texOffset = m_tilingData.textureOffset(tile->i(), tile->j());
409             float tileWidth = static_cast<float>(m_tileSize.width());
410             float tileHeight = static_cast<float>(m_tileSize.height());
411             float texTranslateX = texOffset.x() / tileWidth;
412             float texTranslateY = texOffset.y() / tileHeight;
413             float texScaleX = tileRect.width() / tileWidth;
414             float texScaleY = tileRect.height() / tileHeight;
415 
416             drawTexturedQuad(context, layerRenderer()->projectionMatrix(), tileMatrix, tileRect.width(), tileRect.height(), opacity, texTranslateX, texTranslateY, texScaleX, texScaleY, program);
417         }
418     }
419 }
420 
growLayerToContain(const IntRect & contentRect)421 void LayerTilerChromium::growLayerToContain(const IntRect& contentRect)
422 {
423     // Grow the tile array to contain this content rect.
424     IntRect layerRect = contentRectToLayerRect(contentRect);
425     IntSize rectSize = IntSize(layerRect.maxX(), layerRect.maxY());
426 
427     IntSize oldLayerSize(m_tilingData.totalSizeX(), m_tilingData.totalSizeY());
428     IntSize newSize = rectSize.expandedTo(oldLayerSize);
429     m_tilingData.setTotalSize(newSize.width(), newSize.height());
430 }
431 
drawTexturedQuad(GraphicsContext3D * context,const TransformationMatrix & projectionMatrix,const TransformationMatrix & drawMatrix,float width,float height,float opacity,float texTranslateX,float texTranslateY,float texScaleX,float texScaleY,const LayerTilerChromium::Program * program)432 void LayerTilerChromium::drawTexturedQuad(GraphicsContext3D* context, const TransformationMatrix& projectionMatrix, const TransformationMatrix& drawMatrix,
433                                      float width, float height, float opacity,
434                                      float texTranslateX, float texTranslateY,
435                                      float texScaleX, float texScaleY,
436                                      const LayerTilerChromium::Program* program)
437 {
438     static float glMatrix[16];
439 
440     TransformationMatrix renderMatrix = drawMatrix;
441 
442     // Apply a scaling factor to size the quad from 1x1 to its intended size.
443     renderMatrix.scale3d(width, height, 1);
444 
445     // Apply the projection matrix before sending the transform over to the shader.
446     LayerChromium::toGLMatrix(&glMatrix[0], projectionMatrix * renderMatrix);
447 
448     GLC(context, context->uniformMatrix4fv(program->vertexShader().matrixLocation(), false, &glMatrix[0], 1));
449 
450     GLC(context, context->uniform1f(program->fragmentShader().alphaLocation(), opacity));
451 
452     GLC(context, context->uniform4f(program->vertexShader().texTransformLocation(),
453         texTranslateX, texTranslateY, texScaleX, texScaleY));
454 
455     GLC(context, context->drawElements(GraphicsContext3D::TRIANGLES, 6, GraphicsContext3D::UNSIGNED_SHORT, 0));
456 }
457 
458 } // namespace WebCore
459 
460 #endif // USE(ACCELERATED_COMPOSITING)
461