1 /*
2 * Copyright 2011, The Android Open Source Project
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 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #define LOG_TAG "TileGrid"
27 #define LOG_NDEBUG 1
28
29 #include "config.h"
30 #include "TileGrid.h"
31
32 #include "AndroidLog.h"
33 #include "DrawQuadData.h"
34 #include "GLWebViewState.h"
35 #include "PaintTileOperation.h"
36 #include "Tile.h"
37 #include "TileTexture.h"
38 #include "TilesManager.h"
39
40 #include <wtf/CurrentTime.h>
41
42 #define EXPANDED_BOUNDS_INFLATE 1
43 #define EXPANDED_PREFETCH_BOUNDS_Y_INFLATE 1
44
45 namespace WebCore {
46
TileGrid(bool isBaseSurface)47 TileGrid::TileGrid(bool isBaseSurface)
48 : m_prevTileY(0)
49 , m_scale(1)
50 , m_isBaseSurface(isBaseSurface)
51 {
52 m_dirtyRegion.setEmpty();
53 #ifdef DEBUG_COUNT
54 ClassTracker::instance()->increment("TileGrid");
55 #endif
56 }
57
~TileGrid()58 TileGrid::~TileGrid()
59 {
60 #ifdef DEBUG_COUNT
61 ClassTracker::instance()->decrement("TileGrid");
62 #endif
63 removeTiles();
64 }
65
isReady()66 bool TileGrid::isReady()
67 {
68 bool tilesAllReady = true;
69 bool tilesVisible = false;
70 for (unsigned int i = 0; i < m_tiles.size(); i++) {
71 Tile* tile = m_tiles[i];
72 if (tile->isTileVisible(m_area)) {
73 tilesVisible = true;
74 if (!tile->isTileReady()) {
75 tilesAllReady = false;
76 break;
77 }
78 }
79 }
80 // For now, if no textures are available, consider ourselves as ready
81 // in order to unblock the zooming process.
82 // FIXME: have a better system -- maybe keeping the last scale factor
83 // able to fully render everything
84 ALOGV("TG %p, ready %d, visible %d, texturesRemain %d",
85 this, tilesAllReady, tilesVisible,
86 TilesManager::instance()->layerTexturesRemain());
87
88 return !TilesManager::instance()->layerTexturesRemain()
89 || !tilesVisible || tilesAllReady;
90 }
91
isMissingContent()92 bool TileGrid::isMissingContent()
93 {
94 for (unsigned int i = 0; i < m_tiles.size(); i++)
95 if (m_tiles[i]->isTileVisible(m_area) && !m_tiles[i]->frontTexture())
96 return true;
97 return false;
98 }
99
swapTiles()100 bool TileGrid::swapTiles()
101 {
102 int swaps = 0;
103 for (unsigned int i = 0; i < m_tiles.size(); i++)
104 if (m_tiles[i]->swapTexturesIfNeeded())
105 swaps++;
106 ALOGV("TG %p swapping, swaps = %d", this, swaps);
107 return swaps != 0;
108 }
109
computeTilesArea(const IntRect & contentArea,float scale)110 IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale)
111 {
112 IntRect computedArea;
113 IntRect area(contentArea.x() * scale,
114 contentArea.y() * scale,
115 ceilf(contentArea.width() * scale),
116 ceilf(contentArea.height() * scale));
117
118 ALOGV("TG prepare, scale %f, area %d x %d", scale, area.width(), area.height());
119
120 if (area.width() == 0 && area.height() == 0) {
121 computedArea.setWidth(0);
122 computedArea.setHeight(0);
123 return computedArea;
124 }
125
126 int tileWidth = TilesManager::tileWidth();
127 int tileHeight = TilesManager::tileHeight();
128
129 computedArea.setX(area.x() / tileWidth);
130 computedArea.setY(area.y() / tileHeight);
131 float right = (area.x() + area.width()) / (float) tileWidth;
132 float bottom = (area.y() + area.height()) / (float) tileHeight;
133 computedArea.setWidth(ceilf(right) - computedArea.x());
134 computedArea.setHeight(ceilf(bottom) - computedArea.y());
135 return computedArea;
136 }
137
prepareGL(GLWebViewState * state,float scale,const IntRect & prepareArea,const IntRect & fullContentArea,TilePainter * painter,int regionFlags,bool isLowResPrefetch,bool updateWithBlit)138 void TileGrid::prepareGL(GLWebViewState* state, float scale,
139 const IntRect& prepareArea, const IntRect& fullContentArea,
140 TilePainter* painter, int regionFlags, bool isLowResPrefetch,
141 bool updateWithBlit)
142 {
143 // first, how many tiles do we need
144 m_area = computeTilesArea(prepareArea, scale);
145 if (m_area.isEmpty())
146 return;
147
148 ALOGV("prepare TileGrid %p with scale %.2f, prepareArea "
149 " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles",
150 this, scale,
151 prepareArea.x(), prepareArea.y(),
152 prepareArea.width(), prepareArea.height(),
153 m_area.x(), m_area.y(),
154 m_area.width(), m_area.height());
155
156 bool goingDown = m_prevTileY < m_area.y();
157 m_prevTileY = m_area.y();
158
159 TilesManager* tilesManager = TilesManager::instance();
160 if (scale != m_scale)
161 tilesManager->removeOperationsForFilter(new ScaleFilter(painter, m_scale));
162
163 m_scale = scale;
164
165 // apply dirty region to affected tiles
166 if (!m_dirtyRegion.isEmpty()) {
167 for (unsigned int i = 0; i < m_tiles.size(); i++)
168 m_tiles[i]->markAsDirty(m_dirtyRegion);
169
170 // log inval region for the base surface
171 if (m_isBaseSurface && tilesManager->getProfiler()->enabled()) {
172 SkRegion::Iterator iterator(m_dirtyRegion);
173 while (!iterator.done()) {
174 SkIRect r = iterator.rect();
175 tilesManager->getProfiler()->nextInval(r, scale);
176 iterator.next();
177 }
178 }
179 m_dirtyRegion.setEmpty();
180 }
181
182 if (regionFlags & StandardRegion) {
183 for (int i = 0; i < m_area.width(); i++) {
184 if (goingDown) {
185 for (int j = 0; j < m_area.height(); j++)
186 prepareTile(m_area.x() + i, m_area.y() + j,
187 painter, state, isLowResPrefetch, false, updateWithBlit);
188 } else {
189 for (int j = m_area.height() - 1; j >= 0; j--)
190 prepareTile(m_area.x() + i, m_area.y() + j,
191 painter, state, isLowResPrefetch, false, updateWithBlit);
192 }
193 }
194 }
195
196 if (regionFlags & ExpandedRegion) {
197 IntRect fullArea = computeTilesArea(fullContentArea, scale);
198 IntRect expandedArea = m_area;
199
200 // on systems reporting highEndGfx=true and useMinimalMemory not set, use expanded bounds
201 if (tilesManager->highEndGfx() && !tilesManager->useMinimalMemory())
202 expandedArea.inflate(EXPANDED_BOUNDS_INFLATE);
203
204 if (isLowResPrefetch)
205 expandedArea.inflateY(EXPANDED_PREFETCH_BOUNDS_Y_INFLATE);
206
207 // clip painting area to content
208 expandedArea.intersect(fullArea);
209
210 for (int i = expandedArea.x(); i < expandedArea.maxX(); i++)
211 for (int j = expandedArea.y(); j < expandedArea.maxY(); j++)
212 if (!m_area.contains(i, j))
213 prepareTile(i, j, painter, state, isLowResPrefetch, true, updateWithBlit);
214 }
215 }
216
markAsDirty(const SkRegion & invalRegion)217 void TileGrid::markAsDirty(const SkRegion& invalRegion)
218 {
219 ALOGV("TG %p markAsDirty, current region empty %d, new empty %d",
220 this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty());
221 m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
222 }
223
prepareTile(int x,int y,TilePainter * painter,GLWebViewState * state,bool isLowResPrefetch,bool isExpandPrefetch,bool shouldTryUpdateWithBlit)224 void TileGrid::prepareTile(int x, int y, TilePainter* painter,
225 GLWebViewState* state, bool isLowResPrefetch,
226 bool isExpandPrefetch, bool shouldTryUpdateWithBlit)
227 {
228 Tile* tile = getTile(x, y);
229 if (!tile) {
230 bool isLayerTile = !m_isBaseSurface;
231 tile = new Tile(isLayerTile);
232 m_tiles.append(tile);
233 }
234
235 ALOGV("preparing tile %p at %d, %d, painter is %p", tile, x, y, painter);
236
237 tile->setContents(x, y, m_scale, isExpandPrefetch);
238
239 if (shouldTryUpdateWithBlit && tryBlitFromContents(tile, painter))
240 return;
241
242 if (tile->isDirty() || !tile->frontTexture())
243 tile->reserveTexture();
244
245 if (tile->backTexture() && tile->isDirty()) {
246 TilesManager* tilesManager = TilesManager::instance();
247
248 // if a scheduled repaint is still outstanding, update it with the new painter
249 if (tile->isRepaintPending() && tilesManager->tryUpdateOperationWithPainter(tile, painter))
250 return;
251
252 ALOGV("painting TG %p's tile %d %d for LG %p, scale %f", this, x, y, painter, m_scale);
253 PaintTileOperation *operation = new PaintTileOperation(tile, painter,
254 state, isLowResPrefetch);
255 tilesManager->scheduleOperation(operation);
256 }
257 }
258
tryBlitFromContents(Tile * tile,TilePainter * painter)259 bool TileGrid::tryBlitFromContents(Tile* tile, TilePainter* painter)
260 {
261 return tile->frontTexture()
262 && !tile->frontTexture()->isPureColor()
263 && tile->frontTexture()->m_ownTextureId
264 && !tile->isRepaintPending()
265 && painter->blitFromContents(tile);
266 }
267
getTile(int x,int y)268 Tile* TileGrid::getTile(int x, int y)
269 {
270 for (unsigned int i = 0; i <m_tiles.size(); i++) {
271 Tile* tile = m_tiles[i];
272 if (tile->x() == x && tile->y() == y)
273 return tile;
274 }
275 return 0;
276 }
277
getImageTextureId()278 unsigned int TileGrid::getImageTextureId()
279 {
280 if (m_tiles.size() == 1) {
281 if (m_tiles[0]->frontTexture())
282 return m_tiles[0]->frontTexture()->m_ownTextureId;
283 }
284 return 0;
285 }
286
nbTextures(const IntRect & area,float scale)287 int TileGrid::nbTextures(const IntRect& area, float scale)
288 {
289 IntRect tileBounds = computeTilesArea(area, scale);
290 int numberTextures = tileBounds.width() * tileBounds.height();
291
292 // add the number of dirty tiles in the bounds, as they take up double
293 // textures for double buffering
294 for (unsigned int i = 0; i <m_tiles.size(); i++) {
295 Tile* tile = m_tiles[i];
296 if (tile->isDirty()
297 && tile->x() >= tileBounds.x() && tile->x() <= tileBounds.maxX()
298 && tile->y() >= tileBounds.y() && tile->y() <= tileBounds.maxY())
299 numberTextures++;
300 }
301 return numberTextures;
302 }
303
drawGL(const IntRect & visibleContentArea,float opacity,const TransformationMatrix * transform,const Color * background)304 void TileGrid::drawGL(const IntRect& visibleContentArea, float opacity,
305 const TransformationMatrix* transform,
306 const Color* background)
307 {
308 m_area = computeTilesArea(visibleContentArea, m_scale);
309 if (m_area.width() == 0 || m_area.height() == 0)
310 return;
311
312 float invScale = 1.0 / m_scale;
313 const float tileWidth = TilesManager::tileWidth() * invScale;
314 const float tileHeight = TilesManager::tileHeight() * invScale;
315
316 int drawn = 0;
317
318 SkRegion missingRegion;
319 bool semiOpaqueBaseSurface =
320 background ? (background->hasAlpha() && background->alpha() > 0) : false;
321 if (semiOpaqueBaseSurface) {
322 SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(),
323 m_area.width(), m_area.height());
324 missingRegion = SkRegion(totalArea);
325 }
326
327 bool usePointSampling =
328 TilesManager::instance()->shader()->usePointSampling(m_scale, transform);
329
330 float minTileX = visibleContentArea.x() / tileWidth;
331 float minTileY = visibleContentArea.y() / tileWidth;
332 float maxTileWidth = visibleContentArea.maxX() / tileWidth;
333 float maxTileHeight = visibleContentArea.maxY() / tileWidth;
334 ALOGV("minTileX, minTileY, maxTileWidth, maxTileHeight %f, %f, %f %f",
335 minTileX, minTileY, maxTileWidth, maxTileHeight);
336 for (unsigned int i = 0; i < m_tiles.size(); i++) {
337 Tile* tile = m_tiles[i];
338
339 bool tileInView = tile->isTileVisible(m_area);
340 if (tileInView) {
341 SkRect rect;
342 rect.fLeft = tile->x() * tileWidth;
343 rect.fTop = tile->y() * tileHeight;
344 rect.fRight = rect.fLeft + tileWidth;
345 rect.fBottom = rect.fTop + tileHeight;
346 ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
347 tile, tile->isLayerTile(), tile->x(), tile->y(),
348 tile->scale(), m_scale, tile->isTileReady(), tile->isDirty());
349
350 bool forceBaseBlending = background ? background->hasAlpha() : false;
351
352 float left = std::max(minTileX - tile->x(), 0.0f);
353 float top = std::max(minTileY - tile->y(), 0.0f);
354 float right = std::min(maxTileWidth - tile->x(), 1.0f);
355 float bottom = std::min(maxTileHeight - tile->y(), 1.0f);
356 if (left > 1.0f || top > 1.0f || right < 0.0f || bottom < 0.0f) {
357 ALOGE("Unexpected portion:left, top, right, bottom %f %f %f %f",
358 left, top, right, bottom);
359 left = 0.0f;
360 top = 0.0f;
361 right = 1.0f;
362 bottom = 1.0f;
363 }
364 FloatRect fillPortion(left, top, right - left, bottom - top);
365
366 bool success = tile->drawGL(opacity, rect, m_scale, transform,
367 forceBaseBlending, usePointSampling, fillPortion);
368 if (semiOpaqueBaseSurface && success) {
369 // Cut the successful drawn tile area from the missing region.
370 missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1),
371 SkRegion::kDifference_Op);
372 }
373 if (tile->frontTexture())
374 drawn++;
375 }
376
377 // log tile information for base, high res tiles
378 if (m_isBaseSurface && background)
379 TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView);
380 }
381
382 // Draw missing Regions with blend turned on
383 if (semiOpaqueBaseSurface)
384 drawMissingRegion(missingRegion, opacity, background);
385
386 ALOGV("TG %p drew %d tiles, scale %f",
387 this, drawn, m_scale);
388 }
389
drawMissingRegion(const SkRegion & region,float opacity,const Color * background)390 void TileGrid::drawMissingRegion(const SkRegion& region, float opacity,
391 const Color* background)
392 {
393 SkRegion::Iterator iterator(region);
394 const float tileWidth = TilesManager::tileWidth() / m_scale;
395 const float tileHeight = TilesManager::tileHeight() / m_scale;
396 while (!iterator.done()) {
397 SkIRect r = iterator.rect();
398 SkRect rect;
399 rect.fLeft = r.x() * tileWidth;
400 rect.fTop = r.y() * tileHeight;
401 rect.fRight = rect.fLeft + tileWidth * r.width();
402 rect.fBottom = rect.fTop + tileHeight * r.height();
403 ALOGV("draw tile x y, %d %d (%d %d) opacity %f", r.x(), r.y(),
404 r.width(), r.height(), opacity);
405 // Skia is using pre-multiplied color.
406 Color postAlpha = Color(background->red() * background->alpha() / 255,
407 background->green() * background->alpha() / 255,
408 background->blue() * background->alpha() / 255,
409 background->alpha() );
410
411 PureColorQuadData backGroundData(postAlpha, BaseQuad, 0, &rect, opacity);
412 TilesManager::instance()->shader()->drawQuad(&backGroundData);
413 iterator.next();
414 }
415 }
416
removeTiles()417 void TileGrid::removeTiles()
418 {
419 for (unsigned int i = 0; i < m_tiles.size(); i++) {
420 delete m_tiles[i];
421 }
422 m_tiles.clear();
423 }
424
discardTextures()425 void TileGrid::discardTextures()
426 {
427 ALOGV("TG %p discarding textures", this);
428 for (unsigned int i = 0; i < m_tiles.size(); i++)
429 m_tiles[i]->discardTextures();
430 }
431
432 } // namespace WebCore
433