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 "GLWebViewState.h" 28 29 #if USE(ACCELERATED_COMPOSITING) 30 31 #include "BaseLayerAndroid.h" 32 #include "ClassTracker.h" 33 #include "GLUtils.h" 34 #include "ImagesManager.h" 35 #include "LayerAndroid.h" 36 #include "ScrollableLayerAndroid.h" 37 #include "SkPath.h" 38 #include "TilesManager.h" 39 #include "TilesTracker.h" 40 #include "TreeManager.h" 41 #include <wtf/CurrentTime.h> 42 43 #include <pthread.h> 44 45 #include <cutils/log.h> 46 #include <wtf/text/CString.h> 47 48 #undef XLOGC 49 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__) 50 51 #ifdef DEBUG 52 53 #undef XLOG 54 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "GLWebViewState", __VA_ARGS__) 55 56 #else 57 58 #undef XLOG 59 #define XLOG(...) 60 61 #endif // DEBUG 62 63 #define FIRST_TILED_PAGE_ID 1 64 #define SECOND_TILED_PAGE_ID 2 65 66 #define FRAMERATE_CAP 0.01666 // We cap at 60 fps 67 68 // log warnings if scale goes outside this range 69 #define MIN_SCALE_WARNING 0.1 70 #define MAX_SCALE_WARNING 10 71 72 namespace WebCore { 73 74 using namespace android; 75 76 GLWebViewState::GLWebViewState() 77 : m_zoomManager(this) 78 , m_currentPictureCounter(0) 79 , m_usePageA(true) 80 , m_frameworkInval(0, 0, 0, 0) 81 , m_frameworkLayersInval(0, 0, 0, 0) 82 , m_isScrolling(false) 83 , m_goingDown(true) 84 , m_goingLeft(false) 85 , m_expandedTileBoundsX(0) 86 , m_expandedTileBoundsY(0) 87 , m_highEndGfx(false) 88 , m_scale(1) 89 , m_layersRenderingMode(kAllTextures) 90 { 91 m_viewport.setEmpty(); 92 m_futureViewportTileBounds.setEmpty(); 93 m_viewportTileBounds.setEmpty(); 94 m_preZoomBounds.setEmpty(); 95 96 m_tiledPageA = new TiledPage(FIRST_TILED_PAGE_ID, this); 97 m_tiledPageB = new TiledPage(SECOND_TILED_PAGE_ID, this); 98 99 #ifdef DEBUG_COUNT 100 ClassTracker::instance()->increment("GLWebViewState"); 101 #endif 102 #ifdef MEASURES_PERF 103 m_timeCounter = 0; 104 m_totalTimeCounter = 0; 105 m_measurePerfs = false; 106 #endif 107 } 108 109 GLWebViewState::~GLWebViewState() 110 { 111 // Take care of the transfer queue such that Tex Gen thread will not stuck 112 TilesManager::instance()->unregisterGLWebViewState(this); 113 114 // We have to destroy the two tiled pages first as their destructor 115 // may depend on the existence of this GLWebViewState and some of its 116 // instance variables in order to complete. 117 // Explicitely, currently we need to have the m_paintingBaseLayer around 118 // in order to complete any pending paint operations (the tiled pages 119 // will remove any pending operations, and wait if one is underway). 120 delete m_tiledPageA; 121 delete m_tiledPageB; 122 #ifdef DEBUG_COUNT 123 ClassTracker::instance()->decrement("GLWebViewState"); 124 #endif 125 126 } 127 128 void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval, 129 bool showVisualIndicator, bool isPictureAfterFirstLayout) 130 { 131 if (!layer || isPictureAfterFirstLayout) { 132 // TODO: move this into TreeManager 133 m_zoomManager.swapPages(); // reset zoom state 134 m_tiledPageA->discardTextures(); 135 m_tiledPageB->discardTextures(); 136 m_layersRenderingMode = kAllTextures; 137 } 138 if (layer) { 139 XLOG("new base layer %p, (inval region empty %d) with child %p", layer, inval.isEmpty(), layer->getChild(0)); 140 layer->setState(this); 141 layer->markAsDirty(inval); // TODO: set in webview.cpp 142 } 143 m_treeManager.updateWithTree(layer, isPictureAfterFirstLayout); 144 m_glExtras.setDrawExtra(0); 145 146 #ifdef MEASURES_PERF 147 if (m_measurePerfs && !showVisualIndicator) 148 dumpMeasures(); 149 m_measurePerfs = showVisualIndicator; 150 #endif 151 152 TilesManager::instance()->setShowVisualIndicator(showVisualIndicator); 153 } 154 155 void GLWebViewState::scrollLayer(int layerId, int x, int y) 156 { 157 m_treeManager.updateScrollableLayer(layerId, x, y); 158 159 // TODO: only inval the area of the scrolled layer instead of 160 // doing a fullInval() 161 if (m_layersRenderingMode == kSingleSurfaceRendering) 162 fullInval(); 163 } 164 165 void GLWebViewState::invalRegion(const SkRegion& region) 166 { 167 if (m_layersRenderingMode == kSingleSurfaceRendering) { 168 // TODO: do the union of both layers tree to compute 169 //the minimum inval instead of doing a fullInval() 170 fullInval(); 171 return; 172 } 173 SkRegion::Iterator iterator(region); 174 while (!iterator.done()) { 175 SkIRect r = iterator.rect(); 176 IntRect ir(r.fLeft, r.fTop, r.width(), r.height()); 177 inval(ir); 178 iterator.next(); 179 } 180 } 181 182 void GLWebViewState::inval(const IntRect& rect) 183 { 184 m_currentPictureCounter++; 185 if (!rect.isEmpty()) { 186 // find which tiles fall within the invalRect and mark them as dirty 187 m_tiledPageA->invalidateRect(rect, m_currentPictureCounter); 188 m_tiledPageB->invalidateRect(rect, m_currentPictureCounter); 189 if (m_frameworkInval.isEmpty()) 190 m_frameworkInval = rect; 191 else 192 m_frameworkInval.unite(rect); 193 XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(), 194 m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(), 195 rect.x(), rect.y(), rect.width(), rect.height()); 196 } 197 TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale()); 198 } 199 200 unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas) 201 { 202 m_treeManager.drawCanvas(canvas, m_layersRenderingMode == kSingleSurfaceRendering); 203 return m_currentPictureCounter; 204 } 205 206 TiledPage* GLWebViewState::sibling(TiledPage* page) 207 { 208 return (page == m_tiledPageA) ? m_tiledPageB : m_tiledPageA; 209 } 210 211 TiledPage* GLWebViewState::frontPage() 212 { 213 android::Mutex::Autolock lock(m_tiledPageLock); 214 return m_usePageA ? m_tiledPageA : m_tiledPageB; 215 } 216 217 TiledPage* GLWebViewState::backPage() 218 { 219 android::Mutex::Autolock lock(m_tiledPageLock); 220 return m_usePageA ? m_tiledPageB : m_tiledPageA; 221 } 222 223 void GLWebViewState::swapPages() 224 { 225 android::Mutex::Autolock lock(m_tiledPageLock); 226 m_usePageA ^= true; 227 TiledPage* oldPage = m_usePageA ? m_tiledPageB : m_tiledPageA; 228 zoomManager()->swapPages(); 229 oldPage->discardTextures(); 230 } 231 232 int GLWebViewState::baseContentWidth() 233 { 234 return m_treeManager.baseContentWidth(); 235 } 236 int GLWebViewState::baseContentHeight() 237 { 238 return m_treeManager.baseContentHeight(); 239 } 240 241 void GLWebViewState::setViewport(SkRect& viewport, float scale) 242 { 243 if ((m_viewport == viewport) && 244 (zoomManager()->futureScale() == scale)) 245 return; 246 247 m_goingDown = m_viewport.fTop - viewport.fTop <= 0; 248 m_goingLeft = m_viewport.fLeft - viewport.fLeft >= 0; 249 m_viewport = viewport; 250 251 XLOG("New VIEWPORT %.2f - %.2f %.2f - %.2f (w: %2.f h: %.2f scale: %.2f currentScale: %.2f futureScale: %.2f)", 252 m_viewport.fLeft, m_viewport.fTop, m_viewport.fRight, m_viewport.fBottom, 253 m_viewport.width(), m_viewport.height(), scale, 254 zoomManager()->currentScale(), zoomManager()->futureScale()); 255 256 const float invTileContentWidth = scale / TilesManager::tileWidth(); 257 const float invTileContentHeight = scale / TilesManager::tileHeight(); 258 259 m_viewportTileBounds.set( 260 static_cast<int>(floorf(viewport.fLeft * invTileContentWidth)), 261 static_cast<int>(floorf(viewport.fTop * invTileContentHeight)), 262 static_cast<int>(ceilf(viewport.fRight * invTileContentWidth)), 263 static_cast<int>(ceilf(viewport.fBottom * invTileContentHeight))); 264 265 // allocate max possible number of tiles visible with this viewport 266 int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1; 267 int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1; 268 269 int maxTextureCount = (viewMaxTileX + m_expandedTileBoundsX * 2) * 270 (viewMaxTileY + m_expandedTileBoundsY * 2) * (m_highEndGfx ? 4 : 2); 271 272 TilesManager::instance()->setMaxTextureCount(maxTextureCount); 273 m_tiledPageA->updateBaseTileSize(); 274 m_tiledPageB->updateBaseTileSize(); 275 } 276 277 #ifdef MEASURES_PERF 278 void GLWebViewState::dumpMeasures() 279 { 280 for (int i = 0; i < m_timeCounter; i++) { 281 XLOGC("%d delay: %d ms", m_totalTimeCounter + i, 282 static_cast<int>(m_delayTimes[i]*1000)); 283 m_delayTimes[i] = 0; 284 } 285 m_totalTimeCounter += m_timeCounter; 286 m_timeCounter = 0; 287 } 288 #endif // MEASURES_PERF 289 290 void GLWebViewState::resetFrameworkInval() 291 { 292 m_frameworkInval.setX(0); 293 m_frameworkInval.setY(0); 294 m_frameworkInval.setWidth(0); 295 m_frameworkInval.setHeight(0); 296 } 297 298 void GLWebViewState::addDirtyArea(const IntRect& rect) 299 { 300 if (rect.isEmpty()) 301 return; 302 303 IntRect inflatedRect = rect; 304 inflatedRect.inflate(8); 305 if (m_frameworkLayersInval.isEmpty()) 306 m_frameworkLayersInval = inflatedRect; 307 else 308 m_frameworkLayersInval.unite(inflatedRect); 309 } 310 311 void GLWebViewState::resetLayersDirtyArea() 312 { 313 m_frameworkLayersInval.setX(0); 314 m_frameworkLayersInval.setY(0); 315 m_frameworkLayersInval.setWidth(0); 316 m_frameworkLayersInval.setHeight(0); 317 } 318 319 void GLWebViewState::drawBackground(Color& backgroundColor) 320 { 321 if (TilesManager::instance()->invertedScreen()) { 322 float color = 1.0 - ((((float) backgroundColor.red() / 255.0) + 323 ((float) backgroundColor.green() / 255.0) + 324 ((float) backgroundColor.blue() / 255.0)) / 3.0); 325 glClearColor(color, color, color, 1); 326 } else { 327 glClearColor((float)backgroundColor.red() / 255.0, 328 (float)backgroundColor.green() / 255.0, 329 (float)backgroundColor.blue() / 255.0, 1); 330 } 331 glClear(GL_COLOR_BUFFER_BIT); 332 } 333 334 double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect, 335 IntRect& webViewRect, int titleBarHeight, 336 IntRect& screenClip, float scale) 337 { 338 int left = viewRect.x(); 339 int top = viewRect.y(); 340 int width = viewRect.width(); 341 int height = viewRect.height(); 342 343 ShaderProgram* shader = TilesManager::instance()->shader(); 344 if (shader->program() == -1) { 345 XLOG("Reinit shader"); 346 shader->init(); 347 } 348 shader->setViewport(visibleRect, scale); 349 shader->setViewRect(viewRect); 350 shader->setWebViewRect(webViewRect); 351 shader->setTitleBarHeight(titleBarHeight); 352 shader->setScreenClip(screenClip); 353 shader->resetBlending(); 354 355 shader->calculateAnimationDelta(); 356 357 glViewport(left + shader->getAnimationDeltaX(), 358 top - shader->getAnimationDeltaY(), 359 width, height); 360 361 double currentTime = WTF::currentTime(); 362 363 setViewport(visibleRect, scale); 364 m_zoomManager.processNewScale(currentTime, scale); 365 366 return currentTime; 367 } 368 369 bool GLWebViewState::setLayersRenderingMode(TexturesResult& nbTexturesNeeded) 370 { 371 bool invalBase = false; 372 373 if (!nbTexturesNeeded.full) 374 TilesManager::instance()->setMaxLayerTextureCount(0); 375 else 376 TilesManager::instance()->setMaxLayerTextureCount((2*nbTexturesNeeded.full)+1); 377 378 int maxTextures = TilesManager::instance()->maxLayerTextureCount(); 379 LayersRenderingMode layersRenderingMode = m_layersRenderingMode; 380 381 if (m_layersRenderingMode == kSingleSurfaceRendering) { 382 // only switch out of SingleSurface mode, if we have 2x needed textures 383 // to avoid changing too often 384 maxTextures /= 2; 385 } 386 387 m_layersRenderingMode = kSingleSurfaceRendering; 388 if (nbTexturesNeeded.fixed < maxTextures) 389 m_layersRenderingMode = kFixedLayers; 390 if (nbTexturesNeeded.scrollable < maxTextures) 391 m_layersRenderingMode = kScrollableAndFixedLayers; 392 if (nbTexturesNeeded.clipped < maxTextures) 393 m_layersRenderingMode = kClippedTextures; 394 if (nbTexturesNeeded.full < maxTextures) 395 m_layersRenderingMode = kAllTextures; 396 397 if (!maxTextures && !nbTexturesNeeded.full) 398 m_layersRenderingMode = kAllTextures; 399 400 if (m_layersRenderingMode < layersRenderingMode 401 && m_layersRenderingMode != kAllTextures) 402 invalBase = true; 403 404 if (m_layersRenderingMode > layersRenderingMode 405 && m_layersRenderingMode != kClippedTextures) 406 invalBase = true; 407 408 #ifdef DEBUG 409 if (m_layersRenderingMode != layersRenderingMode) { 410 char* mode[] = { "kAllTextures", "kClippedTextures", 411 "kScrollableAndFixedLayers", "kFixedLayers", "kSingleSurfaceRendering" }; 412 XLOGC("Change from mode %s to %s -- We need textures: fixed: %d," 413 " scrollable: %d, clipped: %d, full: %d, max textures: %d", 414 static_cast<char*>(mode[layersRenderingMode]), 415 static_cast<char*>(mode[m_layersRenderingMode]), 416 nbTexturesNeeded.fixed, 417 nbTexturesNeeded.scrollable, 418 nbTexturesNeeded.clipped, 419 nbTexturesNeeded.full, maxTextures); 420 } 421 #endif 422 423 // For now, anything below kClippedTextures is equivalent 424 // to kSingleSurfaceRendering 425 // TODO: implement the other rendering modes 426 if (m_layersRenderingMode > kClippedTextures) 427 m_layersRenderingMode = kSingleSurfaceRendering; 428 429 // update the base surface if needed 430 if (m_layersRenderingMode != layersRenderingMode 431 && invalBase) { 432 m_tiledPageA->discardTextures(); 433 m_tiledPageB->discardTextures(); 434 fullInval(); 435 return true; 436 } 437 return false; 438 } 439 440 void GLWebViewState::fullInval() 441 { 442 // TODO -- use base layer's size. 443 IntRect ir(0, 0, 1E6, 1E6); 444 inval(ir); 445 } 446 447 bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect, 448 IntRect& webViewRect, int titleBarHeight, 449 IntRect& clip, float scale, 450 bool* treesSwappedPtr, bool* newTreeHasAnimPtr) 451 { 452 m_scale = scale; 453 TilesManager::instance()->getProfiler()->nextFrame(viewport.fLeft, 454 viewport.fTop, 455 viewport.fRight, 456 viewport.fBottom, 457 scale); 458 TilesManager::instance()->incDrawGLCount(); 459 460 #ifdef DEBUG 461 TilesManager::instance()->getTilesTracker()->clear(); 462 #endif 463 464 float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO; 465 float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO; 466 bool useMinimalMemory = TilesManager::instance()->useMinimalMemory(); 467 bool useHorzPrefetch = useMinimalMemory ? 0 : viewWidth < baseContentWidth(); 468 bool useVertPrefetch = useMinimalMemory ? 0 : viewHeight < baseContentHeight(); 469 m_expandedTileBoundsX = (useHorzPrefetch) ? TILE_PREFETCH_DISTANCE : 0; 470 m_expandedTileBoundsY = (useVertPrefetch) ? TILE_PREFETCH_DISTANCE : 0; 471 472 XLOG("drawGL, rect(%d, %d, %d, %d), viewport(%.2f, %.2f, %.2f, %.2f)", 473 rect.x(), rect.y(), rect.width(), rect.height(), 474 viewport.fLeft, viewport.fTop, viewport.fRight, viewport.fBottom); 475 476 resetLayersDirtyArea(); 477 478 // when adding or removing layers, use the the paintingBaseLayer's tree so 479 // that content that moves to the base layer from a layer is synchronized 480 481 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) 482 XLOGC("WARNING, scale seems corrupted before update: %e", scale); 483 484 // Here before we draw, update the BaseTile which has updated content. 485 // Inside this function, just do GPU blits from the transfer queue into 486 // the BaseTiles' texture. 487 TilesManager::instance()->transferQueue()->updateDirtyBaseTiles(); 488 489 // Upload any pending ImageTexture 490 // Return true if we still have some images to upload. 491 // TODO: upload as many textures as possible within a certain time limit 492 bool ret = ImagesManager::instance()->prepareTextures(this); 493 494 if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) 495 XLOGC("WARNING, scale seems corrupted after update: %e", scale); 496 497 // gather the textures we can use 498 TilesManager::instance()->gatherLayerTextures(); 499 500 double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale); 501 502 503 TexturesResult nbTexturesNeeded; 504 bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering; 505 ret |= m_treeManager.drawGL(currentTime, rect, viewport, 506 scale, fastSwap, 507 treesSwappedPtr, newTreeHasAnimPtr, 508 &nbTexturesNeeded); 509 if (!ret) 510 resetFrameworkInval(); 511 512 int nbTexturesForImages = ImagesManager::instance()->nbTextures(); 513 XLOG("*** We have %d textures for images, %d full, %d clipped, total %d / %d", 514 nbTexturesForImages, nbTexturesNeeded.full, nbTexturesNeeded.clipped, 515 nbTexturesNeeded.full + nbTexturesForImages, 516 nbTexturesNeeded.clipped + nbTexturesForImages); 517 nbTexturesNeeded.full += nbTexturesForImages; 518 nbTexturesNeeded.clipped += nbTexturesForImages; 519 ret |= setLayersRenderingMode(nbTexturesNeeded); 520 521 FloatRect extrasclip(0, 0, rect.width(), rect.height()); 522 TilesManager::instance()->shader()->clip(extrasclip); 523 524 m_glExtras.drawGL(webViewRect, viewport, titleBarHeight); 525 526 glBindBuffer(GL_ARRAY_BUFFER, 0); 527 528 // Clean up GL textures for video layer. 529 TilesManager::instance()->videoLayerManager()->deleteUnusedTextures(); 530 ret |= TilesManager::instance()->invertedScreenSwitch(); 531 532 if (ret) { 533 // ret==true && empty inval region means we've inval'd everything, 534 // but don't have new content. Keep redrawing full view (0,0,0,0) 535 // until tile generation catches up and we swap pages. 536 bool fullScreenInval = m_frameworkInval.isEmpty(); 537 538 if (TilesManager::instance()->invertedScreenSwitch()) { 539 fullScreenInval = true; 540 TilesManager::instance()->setInvertedScreenSwitch(false); 541 } 542 543 if (!fullScreenInval) { 544 FloatRect frameworkInval = TilesManager::instance()->shader()->rectInInvScreenCoord( 545 m_frameworkInval); 546 // Inflate the invalidate rect to avoid precision lost. 547 frameworkInval.inflate(1); 548 IntRect inval(frameworkInval.x(), frameworkInval.y(), 549 frameworkInval.width(), frameworkInval.height()); 550 551 inval.unite(m_frameworkLayersInval); 552 553 invalRect->setX(inval.x()); 554 invalRect->setY(inval.y()); 555 invalRect->setWidth(inval.width()); 556 invalRect->setHeight(inval.height()); 557 558 XLOG("invalRect(%d, %d, %d, %d)", inval.x(), 559 inval.y(), inval.width(), inval.height()); 560 561 if (!invalRect->intersects(rect)) { 562 // invalidate is occurring offscreen, do full inval to guarantee redraw 563 fullScreenInval = true; 564 } 565 } 566 567 if (fullScreenInval) { 568 invalRect->setX(0); 569 invalRect->setY(0); 570 invalRect->setWidth(0); 571 invalRect->setHeight(0); 572 } 573 } else { 574 resetFrameworkInval(); 575 } 576 577 #ifdef MEASURES_PERF 578 if (m_measurePerfs) { 579 m_delayTimes[m_timeCounter++] = delta; 580 if (m_timeCounter >= MAX_MEASURES_PERF) 581 dumpMeasures(); 582 } 583 #endif 584 585 #ifdef DEBUG 586 TilesManager::instance()->getTilesTracker()->showTrackTextures(); 587 ImagesManager::instance()->showImages(); 588 #endif 589 590 return ret; 591 } 592 593 } // namespace WebCore 594 595 #endif // USE(ACCELERATED_COMPOSITING) 596