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