Home | History | Annotate | Download | only in chromium
      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 
     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 
     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 
     63 void LayerTilerChromium::setLayerRenderer(LayerRendererChromium* layerRenderer)
     64 {
     65     if (m_layerRenderer != layerRenderer)
     66         reset();
     67     m_layerRenderer = layerRenderer;
     68 }
     69 
     70 LayerTilerChromium::~LayerTilerChromium()
     71 {
     72     reset();
     73 }
     74 
     75 GraphicsContext3D* LayerTilerChromium::layerRendererContext() const
     76 {
     77     ASSERT(layerRenderer());
     78     return layerRenderer()->context();
     79 }
     80 
     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 
     95 LayerTexture* LayerTilerChromium::getSingleTexture()
     96 {
     97     Tile* tile = tileAt(0, 0);
     98     return tile ? tile->texture() : 0;
     99 }
    100 
    101 void LayerTilerChromium::reset()
    102 {
    103     m_tiles.clear();
    104     m_unusedTiles.clear();
    105     m_tilingData.setTotalSize(0, 0);
    106 }
    107 
    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 
    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 
    148 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 
    158 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 
    169 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 
    176 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 
    183 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 
    190 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 
    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 
    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 
    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 
    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 
    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 
    375 void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition)
    376 {
    377     m_layerPosition = layerPosition;
    378 }
    379 
    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 
    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 
    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