• 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 #include "config.h"
27 #include "BaseTile.h"
28 
29 #if USE(ACCELERATED_COMPOSITING)
30 
31 #include "GLUtils.h"
32 #include "RasterRenderer.h"
33 #include "TextureInfo.h"
34 #include "TilesManager.h"
35 
36 #include <cutils/atomic.h>
37 
38 #include <cutils/log.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/text/CString.h>
41 
42 #undef XLOGC
43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__)
44 
45 #ifdef DEBUG
46 
47 #undef XLOG
48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTile", __VA_ARGS__)
49 
50 #else
51 
52 #undef XLOG
53 #define XLOG(...)
54 
55 #endif // DEBUG
56 
57 namespace WebCore {
58 
BaseTile(bool isLayerTile)59 BaseTile::BaseTile(bool isLayerTile)
60     : m_glWebViewState(0)
61     , m_painter(0)
62     , m_x(-1)
63     , m_y(-1)
64     , m_page(0)
65     , m_frontTexture(0)
66     , m_backTexture(0)
67     , m_scale(1)
68     , m_dirty(true)
69     , m_repaintPending(false)
70     , m_lastDirtyPicture(0)
71     , m_isTexturePainted(false)
72     , m_isLayerTile(isLayerTile)
73     , m_drawCount(0)
74     , m_state(Unpainted)
75 {
76 #ifdef DEBUG_COUNT
77     ClassTracker::instance()->increment("BaseTile");
78 #endif
79     m_currentDirtyAreaIndex = 0;
80 
81     // For EglImage Mode, the internal buffer should be 2.
82     // For Surface Texture mode, we only need one.
83     if (TilesManager::instance()->getSharedTextureMode() == EglImageMode)
84         m_maxBufferNumber = 2;
85     else
86         m_maxBufferNumber = 1;
87 
88     m_dirtyArea = new SkRegion[m_maxBufferNumber];
89     m_fullRepaint = new bool[m_maxBufferNumber];
90     for (int i = 0; i < m_maxBufferNumber; i++)
91         m_fullRepaint[i] = true;
92 
93     m_renderer = BaseRenderer::createRenderer();
94 }
95 
~BaseTile()96 BaseTile::~BaseTile()
97 {
98     if (m_backTexture)
99         m_backTexture->release(this);
100     if (m_frontTexture)
101         m_frontTexture->release(this);
102 
103     delete m_renderer;
104     delete[] m_dirtyArea;
105     delete[] m_fullRepaint;
106 
107 #ifdef DEBUG_COUNT
108     ClassTracker::instance()->decrement("BaseTile");
109 #endif
110 }
111 
112 // All the following functions must be called from the main GL thread.
113 
setContents(TilePainter * painter,int x,int y,float scale)114 void BaseTile::setContents(TilePainter* painter, int x, int y, float scale)
115 {
116     if ((m_painter != painter)
117         || (m_x != x)
118         || (m_y != y)
119         || (m_scale != scale)) {
120         // neither texture is relevant
121         discardTextures();
122     }
123 
124     android::AutoMutex lock(m_atomicSync);
125     m_painter = painter;
126     m_x = x;
127     m_y = y;
128     m_scale = scale;
129     m_drawCount = TilesManager::instance()->getDrawGLCount();
130 }
131 
reserveTexture()132 void BaseTile::reserveTexture()
133 {
134     BaseTileTexture* texture = TilesManager::instance()->getAvailableTexture(this);
135 
136     android::AutoMutex lock(m_atomicSync);
137     if (texture && m_backTexture != texture) {
138         XLOG("tile %p reserving texture %p, back was %p (front %p)",
139              this, texture, m_backTexture, m_frontTexture);
140         m_state = Unpainted;
141         m_backTexture = texture;
142     }
143 
144     if (m_state == UpToDate) {
145         XLOG("moving tile %p to unpainted, since it reserved while up to date", this);
146         m_dirty = true;
147         m_state = Unpainted;
148     }
149 }
150 
removeTexture(BaseTileTexture * texture)151 bool BaseTile::removeTexture(BaseTileTexture* texture)
152 {
153     XLOG("%p removeTexture %p, back %p front %p... page %p",
154          this, texture, m_backTexture, m_frontTexture, m_page);
155     // We update atomically, so paintBitmap() can see the correct value
156     android::AutoMutex lock(m_atomicSync);
157     if (m_frontTexture == texture) {
158         if (m_state == UpToDate) {
159             XLOG("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture);
160             m_state = Unpainted;
161         }
162 
163         m_frontTexture = 0;
164     }
165     if (m_backTexture == texture) {
166         m_state = Unpainted;
167         m_backTexture = 0;
168     }
169 
170     // mark dirty regardless of which texture was taken - the back texture may
171     // have been ready to swap
172     m_dirty = true;
173 
174     return true;
175 }
176 
markAsDirty(int unsigned pictureCount,const SkRegion & dirtyArea)177 void BaseTile::markAsDirty(int unsigned pictureCount,
178                            const SkRegion& dirtyArea)
179 {
180     if (dirtyArea.isEmpty())
181         return;
182     android::AutoMutex lock(m_atomicSync);
183     m_lastDirtyPicture = pictureCount;
184     for (int i = 0; i < m_maxBufferNumber; i++)
185         m_dirtyArea[i].op(dirtyArea, SkRegion::kUnion_Op);
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         XLOG("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d, page %p",
196               this, m_x, m_y, isLayerTile(), m_state, m_page);
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 BaseTile::isDirty()
205 {
206     android::AutoMutex lock(m_atomicSync);
207     return m_dirty;
208 }
209 
isRepaintPending()210 bool BaseTile::isRepaintPending()
211 {
212     android::AutoMutex lock(m_atomicSync);
213     return m_repaintPending;
214 }
215 
setRepaintPending(bool pending)216 void BaseTile::setRepaintPending(bool pending)
217 {
218     android::AutoMutex lock(m_atomicSync);
219     m_repaintPending = pending;
220 }
221 
draw(float transparency,SkRect & rect,float scale)222 void BaseTile::draw(float transparency, SkRect& rect, float scale)
223 {
224     if (m_x < 0 || m_y < 0 || m_scale != scale)
225         return;
226 
227     // No need to mutex protect reads of m_backTexture as it is only written to by
228     // the consumer thread.
229     if (!m_frontTexture)
230         return;
231 
232     // Early return if set to un-usable in purpose!
233     m_atomicSync.lock();
234     bool isTexturePainted = m_isTexturePainted;
235     m_atomicSync.unlock();
236 
237     if (!isTexturePainted)
238         return;
239 
240     TextureInfo* textureInfo = m_frontTexture->consumerLock();
241     if (!textureInfo) {
242         m_frontTexture->consumerRelease();
243         return;
244     }
245 
246     if (m_frontTexture->readyFor(this)) {
247         if (isLayerTile())
248             TilesManager::instance()->shader()->drawLayerQuad(*m_painter->transform(),
249                                                               rect, m_frontTexture->m_ownTextureId,
250                                                               transparency, true);
251         else
252             TilesManager::instance()->shader()->drawQuad(rect, m_frontTexture->m_ownTextureId,
253                                                          transparency);
254     } else {
255         XLOG("tile %p at %d, %d not readyfor (at draw),", this, m_x, m_y);
256     }
257 
258     m_frontTexture->consumerRelease();
259 }
260 
isTileReady()261 bool BaseTile::isTileReady()
262 {
263     // Return true if the tile's most recently drawn texture is up to date
264     android::AutoMutex lock(m_atomicSync);
265     BaseTileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture;
266 
267     if (!texture)
268         return false;
269 
270     if (texture->owner() != this)
271         return false;
272 
273     if (m_dirty)
274         return false;
275 
276     if (m_state != ReadyToSwap && m_state != UpToDate)
277         return false;
278 
279     texture->consumerLock();
280     bool ready = texture->readyFor(this);
281     texture->consumerRelease();
282 
283     if (ready)
284         return true;
285 
286     XLOG("tile %p at %d, %d not readyfor (at isTileReady)", this, m_x, m_y);
287 
288     return false;
289 }
290 
intersectWithRect(int x,int y,int tileWidth,int tileHeight,float scale,const SkRect & dirtyRect,SkRect & realTileRect)291 bool BaseTile::intersectWithRect(int x, int y, int tileWidth, int tileHeight,
292                                  float scale, const SkRect& dirtyRect,
293                                  SkRect& realTileRect)
294 {
295     // compute the rect to corresponds to pixels
296     realTileRect.fLeft = x * tileWidth;
297     realTileRect.fTop = y * tileHeight;
298     realTileRect.fRight = realTileRect.fLeft + tileWidth;
299     realTileRect.fBottom = realTileRect.fTop + tileHeight;
300 
301     // scale the dirtyRect for intersect computation.
302     SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale,
303                                           dirtyRect.height() * scale);
304     realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale);
305 
306     if (!realTileRect.intersect(realDirtyRect))
307         return false;
308     return true;
309 }
310 
isTileVisible(const IntRect & viewTileBounds)311 bool BaseTile::isTileVisible(const IntRect& viewTileBounds)
312 {
313     return (m_x >= viewTileBounds.x()
314             && m_x < viewTileBounds.x() + viewTileBounds.width()
315             && m_y >= viewTileBounds.y()
316             && m_y < viewTileBounds.y() + viewTileBounds.height());
317 }
318 
319 // This is called from the texture generation thread
paintBitmap()320 void BaseTile::paintBitmap()
321 {
322     // We acquire the values below atomically. This ensures that we are reading
323     // values correctly across cores. Further, once we have these values they
324     // can be updated by other threads without consequence.
325     m_atomicSync.lock();
326     bool dirty = m_dirty;
327     BaseTileTexture* texture = m_backTexture;
328     SkRegion dirtyArea = m_dirtyArea[m_currentDirtyAreaIndex];
329     float scale = m_scale;
330     const int x = m_x;
331     const int y = m_y;
332     TilePainter* painter = m_painter;
333 
334     if (!dirty || !texture) {
335         m_atomicSync.unlock();
336         return;
337     }
338     if (m_state != Unpainted) {
339         XLOG("Warning: started painting tile %p, but was at state %d, ft %p bt %p",
340               this, m_state, m_frontTexture, m_backTexture);
341     }
342     m_state = PaintingStarted;
343 
344     texture->producerAcquireContext();
345     TextureInfo* textureInfo = texture->producerLock();
346     m_atomicSync.unlock();
347 
348     // at this point we can safely check the ownership (if the texture got
349     // transferred to another BaseTile under us)
350     if (texture->owner() != this) {
351         texture->producerRelease();
352         return;
353     }
354 
355     unsigned int pictureCount = 0;
356 
357     // swap out the renderer if necessary
358     BaseRenderer::swapRendererIfNeeded(m_renderer);
359 
360     // setup the common renderInfo fields;
361     TileRenderInfo renderInfo;
362     renderInfo.x = x;
363     renderInfo.y = y;
364     renderInfo.scale = scale;
365     renderInfo.tileSize = texture->getSize();
366     renderInfo.tilePainter = painter;
367     renderInfo.baseTile = this;
368     renderInfo.textureInfo = textureInfo;
369 
370     const float tileWidth = renderInfo.tileSize.width();
371     const float tileHeight = renderInfo.tileSize.height();
372 
373     SkRegion::Iterator cliperator(dirtyArea);
374 
375     bool fullRepaint = false;
376 
377     if (m_fullRepaint[m_currentDirtyAreaIndex]
378         || textureInfo->m_width != tileWidth
379         || textureInfo->m_height != tileHeight) {
380         fullRepaint = true;
381     }
382 
383     bool surfaceTextureMode = textureInfo->getSharedTextureMode() == SurfaceTextureMode;
384 
385     if (surfaceTextureMode)
386         fullRepaint = true;
387 
388     while (!fullRepaint && !cliperator.done()) {
389         SkRect realTileRect;
390         SkRect dirtyRect;
391         dirtyRect.set(cliperator.rect());
392         bool intersect = intersectWithRect(x, y, tileWidth, tileHeight,
393                                            scale, dirtyRect, realTileRect);
394 
395         // With SurfaceTexture, just repaint the entire tile if we intersect
396         // TODO: Implement the partial invalidate in Surface Texture Mode
397         if (intersect && surfaceTextureMode) {
398             fullRepaint = true;
399             break;
400         }
401 
402         if (intersect && !surfaceTextureMode) {
403             // initialize finalRealRect to the rounded values of realTileRect
404             SkIRect finalRealRect;
405             realTileRect.roundOut(&finalRealRect);
406 
407             // stash the int values of the current width and height
408             const int iWidth = finalRealRect.width();
409             const int iHeight = finalRealRect.height();
410 
411             if (iWidth == tileWidth || iHeight == tileHeight) {
412                 fullRepaint = true;
413                 break;
414             }
415 
416             // translate the rect into tile space coordinates
417             finalRealRect.fLeft = finalRealRect.fLeft % static_cast<int>(tileWidth);
418             finalRealRect.fTop = finalRealRect.fTop % static_cast<int>(tileHeight);
419             finalRealRect.fRight = finalRealRect.fLeft + iWidth;
420             finalRealRect.fBottom = finalRealRect.fTop + iHeight;
421 
422             renderInfo.invalRect = &finalRealRect;
423             renderInfo.measurePerf = false;
424 
425             pictureCount = m_renderer->renderTiledContent(renderInfo);
426         }
427 
428         cliperator.next();
429     }
430 
431     // Do a full repaint if needed
432     if (fullRepaint) {
433         SkIRect rect;
434         rect.set(0, 0, tileWidth, tileHeight);
435 
436         renderInfo.invalRect = &rect;
437         renderInfo.measurePerf = TilesManager::instance()->getShowVisualIndicator();
438 
439         pictureCount = m_renderer->renderTiledContent(renderInfo);
440     }
441 
442     m_atomicSync.lock();
443 
444 #if DEPRECATED_SURFACE_TEXTURE_MODE
445     texture->setTile(textureInfo, x, y, scale, painter, pictureCount);
446 #endif
447     texture->producerReleaseAndSwap();
448     if (texture == m_backTexture) {
449         m_isTexturePainted = true;
450 
451         // set the fullrepaint flags
452         m_fullRepaint[m_currentDirtyAreaIndex] = false;
453 
454         // The various checks to see if we are still dirty...
455 
456         m_dirty = false;
457 
458         if (m_scale != scale)
459             m_dirty = true;
460 
461         if (fullRepaint)
462             m_dirtyArea[m_currentDirtyAreaIndex].setEmpty();
463         else
464             m_dirtyArea[m_currentDirtyAreaIndex].op(dirtyArea, SkRegion::kDifference_Op);
465 
466         if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty())
467             m_dirty = true;
468 
469         // Now we can swap the dirty areas
470         // TODO: For surface texture in Async mode, the index will be updated
471         // according to the current buffer just dequeued.
472         m_currentDirtyAreaIndex = (m_currentDirtyAreaIndex+1) % m_maxBufferNumber;
473 
474         if (!m_dirtyArea[m_currentDirtyAreaIndex].isEmpty())
475             m_dirty = true;
476 
477         XLOG("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty);
478 
479         validatePaint();
480     } else {
481         XLOG("tile %p no longer owns texture %p, m_state %d. ft %p bt %p",
482              this, texture, m_state, m_frontTexture, m_backTexture);
483     }
484 
485     m_atomicSync.unlock();
486 }
487 
discardTextures()488 void BaseTile::discardTextures() {
489     android::AutoMutex lock(m_atomicSync);
490     XLOG("%p discarding bt %p, ft %p",
491          this, m_backTexture, m_frontTexture);
492     if (m_frontTexture) {
493         m_frontTexture->release(this);
494         m_frontTexture = 0;
495     }
496     if (m_backTexture) {
497         m_backTexture->release(this);
498         m_backTexture = 0;
499     }
500     for (int i = 0; i < m_maxBufferNumber; i++) {
501         m_dirtyArea[i].setEmpty();
502         m_fullRepaint[i] = true;
503     }
504     m_dirty = true;
505     m_state = Unpainted;
506 }
507 
discardBackTexture()508 void BaseTile::discardBackTexture() {
509     android::AutoMutex lock(m_atomicSync);
510     if (m_backTexture) {
511         m_backTexture->release(this);
512         m_backTexture = 0;
513     }
514     m_state = Unpainted;
515     m_dirty = true;
516 }
517 
swapTexturesIfNeeded()518 bool BaseTile::swapTexturesIfNeeded() {
519     android::AutoMutex lock(m_atomicSync);
520     if (m_state == ReadyToSwap) {
521         // discard old texture and swap the new one in its place
522         if (m_frontTexture)
523             m_frontTexture->release(this);
524 
525         m_frontTexture = m_backTexture;
526         m_backTexture = 0;
527         m_state = UpToDate;
528         XLOG("display texture for %p at %d, %d front is now %p, back is %p",
529              this, m_x, m_y, m_frontTexture, m_backTexture);
530 
531         return true;
532     }
533     return false;
534 }
535 
backTextureTransfer()536 void BaseTile::backTextureTransfer() {
537     android::AutoMutex lock(m_atomicSync);
538     if (m_state == PaintingStarted)
539         m_state = TransferredUnvalidated;
540     else if (m_state == ValidatedUntransferred)
541         m_state = ReadyToSwap;
542     else {
543         // shouldn't have transferred a tile in any other state, log
544         XLOG("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d",
545              this, m_x, m_y, m_state);
546     }
547 }
548 
backTextureTransferFail()549 void BaseTile::backTextureTransferFail() {
550     // transfer failed for some reason, mark dirty so it will (repaint and) be
551     // retransferred.
552     android::AutoMutex lock(m_atomicSync);
553     m_state = Unpainted;
554     m_dirty = true;
555     // whether validatePaint is called before or after, it won't do anything
556 }
557 
validatePaint()558 void BaseTile::validatePaint() {
559     // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap())
560 
561     if (!m_dirty) {
562         // since after the paint, the tile isn't dirty, 'validate' it - this
563         // may happed before or after the transfer queue operation. Only
564         // when both have happened, mark as 'ReadyToSwap'
565         if (m_state == PaintingStarted)
566             m_state = ValidatedUntransferred;
567         else if (m_state == TransferredUnvalidated)
568             m_state = ReadyToSwap;
569         else {
570             XLOG("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d",
571                   this, m_x, m_y, m_state);
572             // failed transferring, in which case mark dirty (since
573             // paintBitmap() may have cleared m_dirty)
574             m_dirty = true;
575         }
576 
577         if (m_deferredDirty) {
578             XLOG("Note: deferred dirty flag set, possibly a missed paint on tile %p", this);
579             m_deferredDirty = false;
580         }
581     } else {
582         XLOG("Note: paint was unsuccessful.");
583         m_state = Unpainted;
584     }
585 
586 }
587 
588 } // namespace WebCore
589 
590 #endif // USE(ACCELERATED_COMPOSITING)
591