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