1 /* 2 * Copyright 2011, 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 "SurfaceCollectionManager" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "SurfaceCollectionManager.h" 31 32 #include "AndroidLog.h" 33 #include "private/hwui/DrawGlInfo.h" 34 #include "TilesManager.h" 35 #include "SurfaceCollection.h" 36 37 namespace WebCore { 38 39 using namespace android::uirenderer; 40 41 // Tag used to display current number of SurfaceCollections. 42 // Note: this will only work if one webview is actively drawing at a time. 43 static const char* COLLECTION_COUNT_TAG = "CollectionCount"; 44 45 SurfaceCollectionManager::SurfaceCollectionManager() 46 : m_drawingCollection(0) 47 , m_paintingCollection(0) 48 , m_queuedCollection(0) 49 , m_fastSwapMode(false) 50 , m_previouslyScrolling(false) 51 , m_newPaintingCollection(false) 52 { 53 } 54 55 SurfaceCollectionManager::~SurfaceCollectionManager() 56 { 57 clearCollections(); 58 } 59 60 // the painting collection has finished painting: 61 // discard the drawing collection 62 // swap the painting collection in place of the drawing collection 63 // and start painting the queued collection 64 void SurfaceCollectionManager::swap() 65 { 66 // swap can't be called unless painting just finished 67 ASSERT(m_paintingCollection); 68 69 ALOGV("SWAPPING, D %p, P %p, Q %p", 70 m_drawingCollection, m_paintingCollection, m_queuedCollection); 71 72 // if we have a drawing collection, discard it since the painting collection is done 73 if (m_drawingCollection) { 74 ALOGV("destroying drawing collection %p", m_drawingCollection); 75 m_drawingCollection->addFrameworkInvals(); 76 m_drawingCollection->removePainterOperations(); 77 SkSafeUnref(m_drawingCollection); 78 } 79 80 // painting collection becomes the drawing collection 81 ALOGV("drawing collection %p", m_paintingCollection); 82 m_paintingCollection->setIsDrawing(); // initialize animations 83 m_paintingCollection->addFrameworkInvals(); 84 85 if (m_queuedCollection) { 86 // start painting with the queued collection 87 ALOGV("now painting collection %p", m_queuedCollection); 88 m_queuedCollection->setIsPainting(m_paintingCollection); 89 } 90 m_drawingCollection = m_paintingCollection; 91 m_paintingCollection = m_queuedCollection; 92 m_queuedCollection = 0; 93 94 if (ATRACE_ENABLED()) { 95 ATRACE_INT(COLLECTION_COUNT_TAG, 96 (m_drawingCollection ? 1 : 0) 97 + (m_paintingCollection ? 1 : 0)); 98 } 99 100 ALOGV("SWAPPING COMPLETE, D %p, P %p, Q %p", 101 m_drawingCollection, m_paintingCollection, m_queuedCollection); 102 } 103 104 // clear all of the content in the three collections held by the collection manager 105 void SurfaceCollectionManager::clearCollections() 106 { 107 // remove all painting operations, since they're no longer relevant 108 if (m_drawingCollection) 109 m_drawingCollection->removePainterOperations(); 110 if (m_paintingCollection) 111 m_paintingCollection->removePainterOperations(); 112 113 SkSafeUnref(m_drawingCollection); 114 m_drawingCollection = 0; 115 SkSafeUnref(m_paintingCollection); 116 m_paintingCollection = 0; 117 SkSafeUnref(m_queuedCollection); 118 m_queuedCollection = 0; 119 120 ATRACE_INT(COLLECTION_COUNT_TAG, 0); 121 } 122 123 void SurfaceCollectionManager::updatePaintingCollection(SurfaceCollection* newCollection) 124 { 125 m_paintingCollection = newCollection; 126 m_paintingCollection->setIsPainting(m_drawingCollection); 127 m_newPaintingCollection = true; 128 } 129 130 // a new layer collection has arrived, queue it if we're painting something already, 131 // or start painting it if we aren't. Returns true if the manager has two collections 132 // already queued. 133 bool SurfaceCollectionManager::updateWithSurfaceCollection(SurfaceCollection* newCollection, 134 bool brandNew) 135 { 136 // can't have a queued collection unless have a painting collection too 137 ASSERT(m_paintingCollection || !m_queuedCollection); 138 139 if (!newCollection || brandNew) { 140 clearCollections(); 141 if (brandNew) { 142 updatePaintingCollection(newCollection); 143 ATRACE_INT(COLLECTION_COUNT_TAG, 1); 144 } 145 return false; 146 } 147 148 ALOGV("updateWithSurfaceCollection - %p, has children %d, has animations %d", 149 newCollection, newCollection->hasCompositedLayers(), 150 newCollection->hasCompositedAnimations()); 151 152 if (m_queuedCollection || m_paintingCollection) { 153 // currently painting, so defer this new collection 154 if (m_queuedCollection) { 155 // already have a queued collection, copy over invals so the regions are 156 // eventually repainted and let the old queued collection be discarded 157 m_queuedCollection->mergeInvalsInto(newCollection); 158 159 if (!TilesManager::instance()->useDoubleBuffering()) { 160 // not double buffering, count discarded collection/webkit paint as an update 161 TilesManager::instance()->incContentUpdates(); 162 } 163 164 ALOGV("DISCARDING collection - %p, has children %d, has animations %d", 165 newCollection, newCollection->hasCompositedLayers(), 166 newCollection->hasCompositedAnimations()); 167 } 168 SkSafeUnref(m_queuedCollection); 169 m_queuedCollection = newCollection; 170 } else { 171 // don't have painting collection, paint this one! 172 updatePaintingCollection(newCollection); 173 } 174 175 if (ATRACE_ENABLED()) { 176 ATRACE_INT(COLLECTION_COUNT_TAG, 177 (m_drawingCollection ? 1 : 0) 178 + (m_paintingCollection ? 1 : 0) 179 + (m_queuedCollection ? 1 : 0)); 180 } 181 return m_drawingCollection && TilesManager::instance()->useDoubleBuffering(); 182 } 183 184 void SurfaceCollectionManager::updateScrollableLayer(int layerId, int x, int y) 185 { 186 if (m_queuedCollection) 187 m_queuedCollection->updateScrollableLayer(layerId, x, y); 188 if (m_paintingCollection) 189 m_paintingCollection->updateScrollableLayer(layerId, x, y); 190 if (m_drawingCollection) 191 m_drawingCollection->updateScrollableLayer(layerId, x, y); 192 } 193 194 195 int SurfaceCollectionManager::singleSurfaceModeInvalidation(bool hasRunningAnimation, 196 bool scrolling, 197 bool shouldDraw) 198 { 199 int returnFlags = 0; 200 // In single surface mode, we need to dirty all the tiles when we are finishing 201 // scrolling or have an incoming painting tree. 202 bool requireDirtyAll = (m_previouslyScrolling && !scrolling) 203 || m_newPaintingCollection; 204 205 // We also need to tell the framework to continue to invoke until 206 // the base layer is ready. 207 bool drawingBaseSurfaceReady = m_drawingCollection 208 && m_drawingCollection->isReady(); 209 210 // When the base layer is ready, we can ask the framework to draw. And if 211 // animation is running, dirty all the tiles, otherwise the animation will 212 // be paused. 213 if (drawingBaseSurfaceReady) { 214 if (!shouldDraw) 215 returnFlags |= DrawGlInfo::kStatusDraw; 216 else 217 requireDirtyAll |= hasRunningAnimation; 218 } 219 if (requireDirtyAll) 220 TilesManager::instance()->dirtyAllTiles(); 221 222 bool requireInvoke = requireDirtyAll || !drawingBaseSurfaceReady; 223 if (requireInvoke) 224 returnFlags |= DrawGlInfo::kStatusInvoke; 225 226 m_newPaintingCollection = false; 227 m_previouslyScrolling = scrolling; 228 229 return returnFlags; 230 } 231 232 int SurfaceCollectionManager::drawGL(double currentTime, IntRect& viewRect, 233 SkRect& visibleContentRect, float scale, 234 bool scrolling, bool singleSurfaceMode, 235 bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr, 236 TexturesResult* texturesResultPtr, bool shouldDraw) 237 { 238 m_fastSwapMode |= scrolling || singleSurfaceMode; 239 240 ALOGV("drawGL, D %p, P %p, Q %p, fastSwap %d shouldDraw %d", 241 m_drawingCollection, m_paintingCollection, 242 m_queuedCollection, m_fastSwapMode, shouldDraw); 243 244 // ask for kStatusInvoke while painting, kStatusDraw if we have content to be redrawn next frame 245 // returning 0 indicates all painting complete, no framework inval needed. 246 int returnFlags = 0; 247 248 bool didCollectionSwap = false; 249 bool tryFastBlit = !m_fastSwapMode; 250 if (m_paintingCollection) { 251 ALOGV("preparing painting collection %p", m_paintingCollection); 252 253 m_paintingCollection->evaluateAnimations(currentTime); 254 255 m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit); 256 m_paintingCollection->computeTexturesAmount(texturesResultPtr); 257 258 if (!TilesManager::instance()->useDoubleBuffering() || m_paintingCollection->isReady()) { 259 ALOGV("have painting collection %p ready, swapping!", m_paintingCollection); 260 didCollectionSwap = true; 261 m_fastSwapMode = false; 262 TilesManager::instance()->incContentUpdates(); 263 if (collectionsSwappedPtr) 264 *collectionsSwappedPtr = true; 265 if (newCollectionHasAnimPtr) 266 *newCollectionHasAnimPtr = m_paintingCollection->hasCompositedAnimations(); 267 swap(); 268 returnFlags |= uirenderer::DrawGlInfo::kStatusDraw; 269 } 270 } else if (m_drawingCollection) { 271 ALOGV("preparing drawing collection %p", m_drawingCollection); 272 m_drawingCollection->prepareGL(visibleContentRect); 273 m_drawingCollection->computeTexturesAmount(texturesResultPtr); 274 } 275 276 if (m_paintingCollection) 277 returnFlags |= DrawGlInfo::kStatusInvoke; 278 279 if (!shouldDraw) { 280 if (didCollectionSwap 281 || (!m_paintingCollection 282 && m_drawingCollection 283 && m_drawingCollection->isReady())) { 284 // either a swap just occurred, or there is no more work to be done: do a full draw 285 m_drawingCollection->swapTiles(); 286 287 if (didCollectionSwap && m_paintingCollection) 288 m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit); 289 returnFlags |= DrawGlInfo::kStatusDraw; 290 } else { 291 // current collection not ready - invoke functor in process mode 292 // until either drawing or painting collection is ready 293 returnFlags |= DrawGlInfo::kStatusInvoke; 294 } 295 296 return returnFlags; 297 } 298 299 // =========================================================================== 300 // Don't have a drawing collection, draw white background 301 Color background = Color::white; 302 bool drawBackground = true; 303 bool hasRunningAnimation = false; 304 if (m_drawingCollection) { 305 bool drawingReady = didCollectionSwap || m_drawingCollection->isReady(); 306 307 if (didCollectionSwap || m_fastSwapMode || (drawingReady && !m_paintingCollection)) 308 m_drawingCollection->swapTiles(); 309 310 if (didCollectionSwap && m_paintingCollection) 311 m_paintingCollection->prepareGL(visibleContentRect, tryFastBlit); 312 313 if (drawingReady) { 314 // exit fast swap mode, as content is up to date 315 m_fastSwapMode = false; 316 } else { 317 // drawing isn't ready, must redraw 318 returnFlags |= DrawGlInfo::kStatusInvoke; 319 } 320 321 hasRunningAnimation = m_drawingCollection->evaluateAnimations(currentTime); 322 323 ALOGV("drawing collection %p", m_drawingCollection); 324 background = m_drawingCollection->getBackgroundColor(); 325 drawBackground = m_drawingCollection->isMissingBackgroundContent(); 326 } else if (m_paintingCollection) { 327 // Use paintingCollection background color while tiles are not done painting. 328 background = m_paintingCollection->getBackgroundColor(); 329 } 330 331 if (singleSurfaceMode) 332 returnFlags |= singleSurfaceModeInvalidation(hasRunningAnimation, 333 scrolling, shouldDraw); 334 // Start doing the actual GL drawing. 335 if (drawBackground) { 336 ALOGV("background is %x", background.rgb()); 337 // If background is opaque, we can safely and efficiently clear it here. 338 // Otherwise, we have to calculate all the missing tiles and blend the background. 339 GLUtils::clearBackgroundIfOpaque(&background); 340 } 341 342 #ifdef DEBUG 343 ALOGV("Drawing %d / %d surfaces", 344 m_drawingCollection ? m_drawingCollection->backedSize() : -1, 345 m_drawingCollection ? m_drawingCollection->size() : -1); 346 #endif 347 348 if (m_drawingCollection && m_drawingCollection->drawGL(visibleContentRect)) 349 returnFlags |= DrawGlInfo::kStatusDraw; 350 351 ALOGV("returnFlags %d, m_paintingCollection %d ", returnFlags, m_paintingCollection); 352 return returnFlags; 353 } 354 355 } // namespace WebCore 356