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 = ▭
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