• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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