• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010, 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 "Tile"
27 #define LOG_NDEBUG 1
28 
29 #include "config.h"
30 #include "Tile.h"
31 
32 #if USE(ACCELERATED_COMPOSITING)
33 
34 #include "AndroidLog.h"
35 #include "GLUtils.h"
36 #include "BaseRenderer.h"
37 #include "TextureInfo.h"
38 #include "TileTexture.h"
39 #include "TilesManager.h"
40 
41 // If the dirty portion of a tile exceeds this ratio, fully repaint.
42 // Lower values give fewer partial repaints, thus fewer front-to-back
43 // texture copies (cost will vary by device). It's a tradeoff between
44 // the rasterization cost and the FBO texture recopy cost when using
45 // GPU for the transfer queue.
46 #define MAX_INVAL_AREA 0.6
47 
48 namespace WebCore {
49 
Tile(bool isLayerTile)50 Tile::Tile(bool isLayerTile)
51     : m_x(-1)
52     , m_y(-1)
53     , m_frontTexture(0)
54     , m_backTexture(0)
55     , m_lastDrawnTexture(0)
56     , m_scale(1)
57     , m_dirty(true)
58     , m_repaintsPending(0)
59     , m_fullRepaint(true)
60     , m_isLayerTile(isLayerTile)
61     , m_drawCount(0)
62     , m_state(Unpainted)
63 {
64 #ifdef DEBUG_COUNT
65     ClassTracker::instance()->increment("Tile");
66 #endif
67 }
68 
~Tile()69 Tile::~Tile()
70 {
71     if (m_backTexture)
72         m_backTexture->release(this);
73     if (m_frontTexture)
74         m_frontTexture->release(this);
75 
76 #ifdef DEBUG_COUNT
77     ClassTracker::instance()->decrement("Tile");
78 #endif
79 }
80 
81 // All the following functions must be called from the main GL thread.
82 
setContents(int x,int y,float scale,bool isExpandedPrefetchTile)83 void Tile::setContents(int x, int y, float scale, bool isExpandedPrefetchTile)
84 {
85     // TODO: investigate whether below check/discard is necessary
86     if ((m_x != x)
87         || (m_y != y)
88         || (m_scale != scale)) {
89         // neither texture is relevant
90         discardTextures();
91     }
92 
93     android::AutoMutex lock(m_atomicSync);
94     m_x = x;
95     m_y = y;
96     m_scale = scale;
97     m_drawCount = TilesManager::instance()->getDrawGLCount();
98     if (isExpandedPrefetchTile)
99         m_drawCount--; // deprioritize expanded painting region
100 }
101 
reserveTexture()102 void Tile::reserveTexture()
103 {
104     TileTexture* texture = TilesManager::instance()->getAvailableTexture(this);
105 
106     android::AutoMutex lock(m_atomicSync);
107     if (texture && m_backTexture != texture) {
108         ALOGV("tile %p reserving texture %p, back was %p (front %p)",
109               this, texture, m_backTexture, m_frontTexture);
110         m_state = Unpainted;
111         m_backTexture = texture;
112     }
113 
114     if (m_state == UpToDate) {
115         ALOGV("moving tile %p to unpainted, since it reserved while up to date", this);
116         m_dirty = true;
117         m_state = Unpainted;
118     }
119 }
120 
removeTexture(TileTexture * texture)121 bool Tile::removeTexture(TileTexture* texture)
122 {
123     ALOGV("%p removeTexture %p, back %p front %p",
124           this, texture, m_backTexture, m_frontTexture);
125     // We update atomically, so paintBitmap() can see the correct value
126     android::AutoMutex lock(m_atomicSync);
127     if (m_frontTexture == texture) {
128         if (m_state == UpToDate) {
129             ALOGV("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture);
130             m_state = Unpainted;
131         }
132 
133         m_frontTexture = 0;
134     }
135     if (m_backTexture == texture) {
136         m_state = Unpainted;
137         m_backTexture = 0;
138     }
139 
140     // mark dirty regardless of which texture was taken - the back texture may
141     // have been ready to swap
142     m_dirty = true;
143 
144     return true;
145 }
146 
markAsDirty()147 void Tile::markAsDirty()
148 {
149     android::AutoMutex lock(m_atomicSync);
150     m_dirtyArea.setEmpty(); // empty dirty rect prevents fast blit path
151     markAsDirtyInternal();
152 }
153 
markAsDirty(const SkRegion & dirtyArea)154 void Tile::markAsDirty(const SkRegion& dirtyArea)
155 {
156     if (dirtyArea.isEmpty())
157         return;
158     android::AutoMutex lock(m_atomicSync);
159     m_dirtyArea.op(dirtyArea, SkRegion::kUnion_Op);
160 
161     // Check if we actually intersect with the area
162     bool intersect = false;
163     SkRegion::Iterator cliperator(dirtyArea);
164     SkRect realTileRect;
165     SkRect dirtyRect;
166     while (!cliperator.done()) {
167         dirtyRect.set(cliperator.rect());
168         if (intersectWithRect(m_x, m_y, TilesManager::tileWidth(), TilesManager::tileHeight(),
169                               m_scale, dirtyRect, realTileRect)) {
170             intersect = true;
171             break;
172         }
173         cliperator.next();
174     }
175 
176     if (!intersect)
177         return;
178 
179     markAsDirtyInternal();
180 }
181 
markAsDirtyInternal()182 void Tile::markAsDirtyInternal()
183 {
184     // NOTE: callers must hold lock on m_atomicSync
185 
186     m_dirty = true;
187     if (m_state == UpToDate) {
188         // We only mark a tile as unpainted in 'markAsDirty' if its status is
189         // UpToDate: marking dirty means we need to repaint, but don't stop the
190         // current paint
191         m_state = Unpainted;
192     } else if (m_state != Unpainted) {
193         // TODO: fix it so that they can paint while deferring the markAsDirty
194         // call (or block updates)
195         ALOGV("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d",
196               this, m_x, m_y, isLayerTile(), m_state);
197 
198         // prefetch tiles can be marked dirty while in the process of painting,
199         // due to not using an update lock. force them to fail validate step.
200         m_state = Unpainted;
201     }
202 }
203 
isDirty()204 bool Tile::isDirty()
205 {
206     android::AutoMutex lock(m_atomicSync);
207     return m_dirty;
208 }
209 
isRepaintPending()210 bool Tile::isRepaintPending()
211 {
212     android::AutoMutex lock(m_atomicSync);
213     return m_repaintsPending != 0;
214 }
215 
setRepaintPending(bool pending)216 void Tile::setRepaintPending(bool pending)
217 {
218     android::AutoMutex lock(m_atomicSync);
219     m_repaintsPending += pending ? 1 : -1;
220 }
221 
drawGL(float opacity,const SkRect & rect,float scale,const TransformationMatrix * transform,bool forceBlending,bool usePointSampling,const FloatRect & fillPortion)222 bool Tile::drawGL(float opacity, const SkRect& rect, float scale,
223                   const TransformationMatrix* transform,
224                   bool forceBlending, bool usePointSampling,
225                   const FloatRect& fillPortion)
226 {
227     if (m_x < 0 || m_y < 0 || m_scale != scale)
228         return false;
229 
230     // No need to mutex protect reads of m_backTexture as it is only written to by
231     // the consumer thread.
232     if (!m_frontTexture)
233         return false;
234 
235     if (fillPortion.maxX() < 1.0f || fillPortion.maxY() < 1.0f
236         || fillPortion.x() > 0.0f || fillPortion.y() > 0.0f)
237         ALOGV("drawing tile %p (%d, %d with fill portions %f %f->%f, %f",
238               this, m_x, m_y, fillPortion.x(), fillPortion.y(),
239               fillPortion.maxX(), fillPortion.maxY());
240 
241     m_frontTexture->drawGL(isLayerTile(), rect, opacity, transform,
242                            forceBlending, usePointSampling, fillPortion);
243     m_lastDrawnTexture = m_frontTexture;
244     return true;
245 }
246 
isTileReady()247 bool Tile::isTileReady()
248 {
249     // Return true if the tile's most recently drawn texture is up to date
250     android::AutoMutex lock(m_atomicSync);
251     TileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture;
252 
253     if (!texture)
254         return false;
255 
256     if (texture->owner() != this)
257         return false;
258 
259     if (m_dirty)
260         return false;
261 
262     if (m_state != ReadyToSwap && m_state != UpToDate)
263         return false;
264 
265     return true;
266 }
267 
intersectWithRect(int x,int y,int tileWidth,int tileHeight,float scale,const SkRect & dirtyRect,SkRect & realTileRect)268 bool Tile::intersectWithRect(int x, int y, int tileWidth, int tileHeight,
269                              float scale, const SkRect& dirtyRect,
270                              SkRect& realTileRect)
271 {
272     // compute the rect to corresponds to pixels
273     realTileRect.fLeft = x * tileWidth;
274     realTileRect.fTop = y * tileHeight;
275     realTileRect.fRight = realTileRect.fLeft + tileWidth;
276     realTileRect.fBottom = realTileRect.fTop + tileHeight;
277 
278     // scale the dirtyRect for intersect computation.
279     SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale,
280                                           dirtyRect.height() * scale);
281     realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale);
282 
283     if (!realTileRect.intersect(realDirtyRect))
284         return false;
285     return true;
286 }
287 
isTileVisible(const IntRect & viewTileBounds)288 bool Tile::isTileVisible(const IntRect& viewTileBounds)
289 {
290     return (m_x >= viewTileBounds.x()
291             && m_x < viewTileBounds.x() + viewTileBounds.width()
292             && m_y >= viewTileBounds.y()
293             && m_y < viewTileBounds.y() + viewTileBounds.height());
294 }
295 
296 // This is called from the texture generation thread
paintBitmap(TilePainter * painter,BaseRenderer * renderer)297 void Tile::paintBitmap(TilePainter* painter, BaseRenderer* renderer)
298 {
299     // We acquire the values below atomically. This ensures that we are reading
300     // values correctly across cores. Further, once we have these values they
301     // can be updated by other threads without consequence.
302     m_atomicSync.lock();
303     bool dirty = m_dirty;
304     TileTexture* texture = m_backTexture;
305     SkRegion dirtyArea = m_dirtyArea;
306     float scale = m_scale;
307     const int x = m_x;
308     const int y = m_y;
309 
310     if (!dirty || !texture) {
311         m_atomicSync.unlock();
312         return;
313     }
314     if (m_state != Unpainted) {
315         ALOGV("Warning: started painting tile %p, but was at state %d, ft %p bt %p",
316               this, m_state, m_frontTexture, m_backTexture);
317     }
318     m_state = PaintingStarted;
319     TextureInfo* textureInfo = texture->getTextureInfo();
320     m_atomicSync.unlock();
321 
322     // at this point we can safely check the ownership (if the texture got
323     // transferred to another Tile under us)
324     if (texture->owner() != this) {
325         return;
326     }
327 
328     // setup the common renderInfo fields;
329     TileRenderInfo renderInfo;
330     renderInfo.x = x;
331     renderInfo.y = y;
332     renderInfo.scale = scale;
333     renderInfo.tileSize = texture->getSize();
334     renderInfo.tilePainter = painter;
335     renderInfo.baseTile = this;
336     renderInfo.textureInfo = textureInfo;
337 
338     const float tileWidth = renderInfo.tileSize.width();
339     const float tileHeight = renderInfo.tileSize.height();
340 
341     renderer->renderTiledContent(renderInfo);
342 
343     m_atomicSync.lock();
344 
345     if (texture == m_backTexture) {
346         // set the fullrepaint flags
347         m_fullRepaint = false;
348 
349         // The various checks to see if we are still dirty...
350 
351         m_dirty = false;
352 
353         if (m_scale != scale)
354             m_dirty = true;
355 
356         m_dirtyArea.setEmpty();
357 
358         ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty);
359 
360         validatePaint();
361     } else {
362         ALOGV("tile %p no longer owns texture %p, m_state %d. ft %p bt %p",
363               this, texture, m_state, m_frontTexture, m_backTexture);
364     }
365 
366     m_atomicSync.unlock();
367 }
368 
discardTextures()369 void Tile::discardTextures() {
370     android::AutoMutex lock(m_atomicSync);
371     ALOGV("%p discarding bt %p, ft %p",
372           this, m_backTexture, m_frontTexture);
373     if (m_frontTexture) {
374         m_frontTexture->release(this);
375         m_frontTexture = 0;
376     }
377     if (m_backTexture) {
378         m_backTexture->release(this);
379         m_backTexture = 0;
380     }
381     m_dirtyArea.setEmpty();
382     m_fullRepaint = true;
383 
384     m_dirty = true;
385     m_state = Unpainted;
386 }
387 
discardBackTexture()388 void Tile::discardBackTexture() {
389     android::AutoMutex lock(m_atomicSync);
390     if (m_backTexture) {
391         m_backTexture->release(this);
392         m_backTexture = 0;
393     }
394     m_state = Unpainted;
395     m_dirty = true;
396 }
397 
swapTexturesIfNeeded()398 bool Tile::swapTexturesIfNeeded() {
399     android::AutoMutex lock(m_atomicSync);
400     if (m_state == ReadyToSwap) {
401         // discard old texture and swap the new one in its place
402         if (m_frontTexture)
403             m_frontTexture->release(this);
404 
405         m_frontTexture = m_backTexture;
406         m_backTexture = 0;
407         m_state = UpToDate;
408         ALOGV("display texture for %p at %d, %d front is now %p, back is %p",
409               this, m_x, m_y, m_frontTexture, m_backTexture);
410 
411         return true;
412     }
413     return false;
414 }
415 
backTextureTransfer()416 void Tile::backTextureTransfer() {
417     android::AutoMutex lock(m_atomicSync);
418     if (m_state == PaintingStarted)
419         m_state = TransferredUnvalidated;
420     else if (m_state == ValidatedUntransferred)
421         m_state = ReadyToSwap;
422     else {
423         // shouldn't have transferred a tile in any other state, log
424         ALOGV("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d",
425               this, m_x, m_y, m_state);
426     }
427 }
428 
backTextureTransferFail()429 void Tile::backTextureTransferFail() {
430     // transfer failed for some reason, mark dirty so it will (repaint and) be
431     // retransferred.
432     android::AutoMutex lock(m_atomicSync);
433     m_state = Unpainted;
434     m_dirty = true;
435     // whether validatePaint is called before or after, it won't do anything
436 }
437 
onBlitUpdate()438 void Tile::onBlitUpdate()
439 {
440     // The front texture was directly updated with a blit, so mark this as clean
441     android::AutoMutex lock(m_atomicSync);
442     m_dirty = false;
443     m_dirtyArea.setEmpty();
444     m_state = Tile::UpToDate;
445 }
446 
validatePaint()447 void Tile::validatePaint() {
448     // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap())
449 
450     if (!m_dirty) {
451         // since after the paint, the tile isn't dirty, 'validate' it - this
452         // may happed before or after the transfer queue operation. Only
453         // when both have happened, mark as 'ReadyToSwap'
454         if (m_state == PaintingStarted)
455             m_state = ValidatedUntransferred;
456         else if (m_state == TransferredUnvalidated) {
457             // When the backTexture has been marked pureColor, we will skip the
458             // transfer and marked as ReadyToSwap, in this case, we don't want
459             // to reset m_dirty bit to true.
460             m_state = ReadyToSwap;
461         } else {
462             ALOGV("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d",
463                   this, m_x, m_y, m_state);
464             // failed transferring, in which case mark dirty (since
465             // paintBitmap() may have cleared m_dirty)
466             m_dirty = true;
467         }
468     } else {
469         ALOGV("Note: paint was unsuccessful.");
470         m_state = Unpainted;
471     }
472 
473 }
474 
475 } // namespace WebCore
476 
477 #endif // USE(ACCELERATED_COMPOSITING)
478