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