1 /* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE INC. ``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 APPLE INC. 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#import "config.h" 27#import "RemoteLayerBackingStore.h" 28 29#import "ArgumentCoders.h" 30#import "MachPort.h" 31#import "PlatformCALayerRemote.h" 32#import "RemoteLayerBackingStoreCollection.h" 33#import "RemoteLayerTreeContext.h" 34#import "ShareableBitmap.h" 35#import "WebCoreArgumentCoders.h" 36#import <QuartzCore/QuartzCore.h> 37#import <WebCore/GraphicsContextCG.h> 38#import <WebCore/IOSurface.h> 39#import <WebCore/IOSurfacePool.h> 40#import <WebCore/WebLayer.h> 41 42#if USE(IOSURFACE) 43#import <mach/mach_port.h> 44#endif 45 46#if __has_include(<QuartzCore/CALayerPrivate.h>) 47#import <QuartzCore/CALayerPrivate.h> 48#endif 49 50@interface CALayer (Details) 51@property BOOL contentsOpaque; 52@end 53 54using namespace WebCore; 55 56namespace WebKit { 57 58RemoteLayerBackingStore::RemoteLayerBackingStore(PlatformCALayerRemote* layer) 59 : m_layer(layer) 60 , m_isOpaque(false) 61 , m_lastDisplayTime(std::chrono::steady_clock::time_point::min()) 62{ 63 if (!m_layer) 64 return; 65 if (RemoteLayerTreeContext* context = m_layer->context()) 66 context->backingStoreWasCreated(*this); 67} 68 69RemoteLayerBackingStore::~RemoteLayerBackingStore() 70{ 71 clearBackingStore(); 72 73 if (!m_layer) 74 return; 75 if (RemoteLayerTreeContext* context = m_layer->context()) 76 context->backingStoreWillBeDestroyed(*this); 77} 78 79void RemoteLayerBackingStore::ensureBackingStore(FloatSize size, float scale, bool acceleratesDrawing, bool isOpaque) 80{ 81 if (m_size == size && m_scale == scale && m_acceleratesDrawing == acceleratesDrawing && m_isOpaque == isOpaque) 82 return; 83 84 m_size = size; 85 m_scale = scale; 86 m_acceleratesDrawing = acceleratesDrawing; 87 m_isOpaque = isOpaque; 88 89 if (m_frontBuffer) { 90 // If we have a valid backing store, we need to ensure that it gets completely 91 // repainted the next time display() is called. 92 setNeedsDisplay(); 93 } 94 95 clearBackingStore(); 96} 97 98void RemoteLayerBackingStore::clearBackingStore() 99{ 100 m_frontBuffer.discard(); 101 m_backBuffer.discard(); 102#if USE(IOSURFACE) 103 m_secondaryBackBuffer.discard(); 104#endif 105} 106 107void RemoteLayerBackingStore::encode(IPC::ArgumentEncoder& encoder) const 108{ 109 encoder << m_size; 110 encoder << m_scale; 111 encoder << m_acceleratesDrawing; 112 encoder << m_isOpaque; 113 114#if USE(IOSURFACE) 115 if (m_acceleratesDrawing) { 116 mach_port_t port = m_frontBuffer.surface->createMachPort(); 117 encoder << IPC::MachPort(port, MACH_MSG_TYPE_MOVE_SEND); 118 return; 119 } 120#endif 121 122 ASSERT(!m_acceleratesDrawing); 123 124 ShareableBitmap::Handle handle; 125 m_frontBuffer.bitmap->createHandle(handle); 126 encoder << handle; 127} 128 129bool RemoteLayerBackingStore::decode(IPC::ArgumentDecoder& decoder, RemoteLayerBackingStore& result) 130{ 131 if (!decoder.decode(result.m_size)) 132 return false; 133 134 if (!decoder.decode(result.m_scale)) 135 return false; 136 137 if (!decoder.decode(result.m_acceleratesDrawing)) 138 return false; 139 140 if (!decoder.decode(result.m_isOpaque)) 141 return false; 142 143#if USE(IOSURFACE) 144 if (result.m_acceleratesDrawing) { 145 IPC::MachPort machPort; 146 if (!decoder.decode(machPort)) 147 return false; 148 result.m_frontBuffer.surface = IOSurface::createFromMachPort(machPort.port(), ColorSpaceDeviceRGB); 149 mach_port_deallocate(mach_task_self(), machPort.port()); 150 return true; 151 } 152#endif 153 154 ASSERT(!result.m_acceleratesDrawing); 155 156 ShareableBitmap::Handle handle; 157 if (!decoder.decode(handle)) 158 return false; 159 result.m_frontBuffer.bitmap = ShareableBitmap::create(handle); 160 161 return true; 162} 163 164void RemoteLayerBackingStore::setNeedsDisplay(const IntRect rect) 165{ 166 m_dirtyRegion.unite(rect); 167} 168 169void RemoteLayerBackingStore::setNeedsDisplay() 170{ 171 setNeedsDisplay(IntRect(IntPoint(), expandedIntSize(m_size))); 172} 173 174void RemoteLayerBackingStore::swapToValidFrontBuffer() 175{ 176 FloatSize scaledSize = m_size; 177 scaledSize.scale(m_scale); 178 IntSize expandedScaledSize = roundedIntSize(scaledSize); 179 180#if USE(IOSURFACE) 181 if (m_acceleratesDrawing) { 182 if (!m_backBuffer.surface || m_backBuffer.surface->isInUse()) { 183 std::swap(m_backBuffer, m_secondaryBackBuffer); 184 if (m_backBuffer.surface && m_backBuffer.surface->isInUse()) 185 m_backBuffer.discard(); 186 } 187 188 std::swap(m_frontBuffer, m_backBuffer); 189 190 if (!m_frontBuffer.surface) 191 m_frontBuffer.surface = IOSurface::create(expandedScaledSize, ColorSpaceDeviceRGB); 192 193 setBufferVolatility(BufferType::Front, false); 194 195 return; 196 } 197#endif 198 199 ASSERT(!m_acceleratesDrawing); 200 std::swap(m_frontBuffer, m_backBuffer); 201 202 if (!m_frontBuffer.bitmap) 203 m_frontBuffer.bitmap = ShareableBitmap::createShareable(expandedScaledSize, m_isOpaque ? ShareableBitmap::NoFlags : ShareableBitmap::SupportsAlpha); 204} 205 206bool RemoteLayerBackingStore::display() 207{ 208 ASSERT(!m_frontContextPendingFlush); 209 210 m_lastDisplayTime = std::chrono::steady_clock::now(); 211 212 if (RemoteLayerTreeContext* context = m_layer->context()) 213 context->backingStoreWillBeDisplayed(*this); 214 215 // Make the previous front buffer non-volatile early, so that we can dirty the whole layer if it comes back empty. 216 setBufferVolatility(BufferType::Front, false); 217 218 if (m_dirtyRegion.isEmpty() || m_size.isEmpty()) 219 return false; 220 221 IntRect layerBounds(IntPoint(), expandedIntSize(m_size)); 222 if (!hasFrontBuffer()) 223 m_dirtyRegion.unite(layerBounds); 224 225 if (m_layer->owner()->platformCALayerShowRepaintCounter(m_layer)) { 226 IntRect indicatorRect(0, 0, 52, 27); 227 m_dirtyRegion.unite(indicatorRect); 228 } 229 230 FloatSize scaledSize = m_size; 231 scaledSize.scale(m_scale); 232 IntSize expandedScaledSize = roundedIntSize(scaledSize); 233 IntRect expandedScaledLayerBounds(IntPoint(), expandedScaledSize); 234 bool willPaintEntireBackingStore = m_dirtyRegion.contains(layerBounds); 235 236 swapToValidFrontBuffer(); 237 238#if USE(IOSURFACE) 239 if (m_acceleratesDrawing) { 240 RetainPtr<CGImageRef> backImage; 241 if (m_backBuffer.surface && !willPaintEntireBackingStore) 242 backImage = m_backBuffer.surface->createImage(); 243 244 GraphicsContext& context = m_frontBuffer.surface->ensureGraphicsContext(); 245 246 context.scale(FloatSize(1, -1)); 247 context.translate(0, -expandedScaledSize.height()); 248 drawInContext(context, backImage.get()); 249 250 m_frontBuffer.surface->releaseGraphicsContext(); 251 } else 252#endif 253 { 254 ASSERT(!m_acceleratesDrawing); 255 std::unique_ptr<GraphicsContext> context = m_frontBuffer.bitmap->createGraphicsContext(); 256 257 RetainPtr<CGImageRef> backImage; 258 if (m_backBuffer.bitmap && !willPaintEntireBackingStore) 259 backImage = m_backBuffer.bitmap->makeCGImage(); 260 261 drawInContext(*context, backImage.get()); 262 } 263 264 m_layer->owner()->platformCALayerLayerDidDisplay(m_layer); 265 266 return true; 267} 268 269void RemoteLayerBackingStore::drawInContext(GraphicsContext& context, CGImageRef backImage) 270{ 271 FloatSize scaledSize = m_size; 272 scaledSize.scale(m_scale); 273 IntRect scaledLayerBounds(IntPoint(), roundedIntSize(scaledSize)); 274 275 if (!m_isOpaque) 276 context.clearRect(scaledLayerBounds); 277 278#ifndef NDEBUG 279 if (m_isOpaque) 280 context.fillRect(scaledLayerBounds, Color(255, 0, 0), ColorSpaceDeviceRGB); 281#endif 282 283 CGContextRef cgContext = context.platformContext(); 284 285 // If we have less than webLayerMaxRectsToPaint rects to paint and they cover less 286 // than webLayerWastedSpaceThreshold of the total dirty area, we'll repaint each rect separately. 287 // Otherwise, repaint the entire bounding box of the dirty region. 288 IntRect dirtyBounds = m_dirtyRegion.bounds(); 289 290 Vector<IntRect> dirtyRects = m_dirtyRegion.rects(); 291 if (dirtyRects.size() > PlatformCALayer::webLayerMaxRectsToPaint || m_dirtyRegion.totalArea() > PlatformCALayer::webLayerWastedSpaceThreshold * dirtyBounds.width() * dirtyBounds.height()) { 292 dirtyRects.clear(); 293 dirtyRects.append(dirtyBounds); 294 } 295 296 // FIXME: find a consistent way to scale and snap dirty and CG clip rects. 297 for (const auto& rect : dirtyRects) { 298 FloatRect scaledRect(rect); 299 scaledRect.scale(m_scale); 300 scaledRect = enclosingIntRect(scaledRect); 301 scaledRect.scale(1 / m_scale); 302 m_paintingRects.append(scaledRect); 303 } 304 305 CGRect cgPaintingRects[PlatformCALayer::webLayerMaxRectsToPaint]; 306 for (size_t i = 0, dirtyRectCount = m_paintingRects.size(); i < dirtyRectCount; ++i) { 307 FloatRect scaledPaintingRect = m_paintingRects[i]; 308 scaledPaintingRect.scale(m_scale); 309 cgPaintingRects[i] = scaledPaintingRect; 310 } 311 312 if (backImage) { 313 CGContextSaveGState(cgContext); 314 CGContextSetBlendMode(cgContext, kCGBlendModeCopy); 315 316 CGContextAddRect(cgContext, CGRectInfinite); 317 CGContextAddRects(cgContext, cgPaintingRects, m_paintingRects.size()); 318 CGContextEOClip(cgContext); 319 320 CGContextTranslateCTM(cgContext, 0, scaledLayerBounds.height()); 321 CGContextScaleCTM(cgContext, 1, -1); 322 CGContextDrawImage(cgContext, scaledLayerBounds, backImage); 323 CGContextRestoreGState(cgContext); 324 } 325 326 CGContextClipToRects(cgContext, cgPaintingRects, m_paintingRects.size()); 327 328 context.scale(FloatSize(m_scale, m_scale)); 329 330 // FIXME: This should be moved to PlatformCALayerRemote for better layering. 331 switch (m_layer->layerType()) { 332 case PlatformCALayer::LayerTypeSimpleLayer: 333 case PlatformCALayer::LayerTypeTiledBackingTileLayer: 334 m_layer->owner()->platformCALayerPaintContents(m_layer, context, dirtyBounds); 335 break; 336 case PlatformCALayer::LayerTypeWebLayer: 337 PlatformCALayer::drawLayerContents(cgContext, m_layer, m_paintingRects); 338 break; 339 case PlatformCALayer::LayerTypeLayer: 340 case PlatformCALayer::LayerTypeTransformLayer: 341 case PlatformCALayer::LayerTypeWebTiledLayer: 342 case PlatformCALayer::LayerTypeTiledBackingLayer: 343 case PlatformCALayer::LayerTypePageTiledBackingLayer: 344 case PlatformCALayer::LayerTypeRootLayer: 345 case PlatformCALayer::LayerTypeAVPlayerLayer: 346 case PlatformCALayer::LayerTypeWebGLLayer: 347 case PlatformCALayer::LayerTypeCustom: 348 ASSERT_NOT_REACHED(); 349 break; 350 }; 351 352 m_dirtyRegion = Region(); 353 m_paintingRects.clear(); 354 355 m_frontContextPendingFlush = context.platformContext(); 356} 357 358void RemoteLayerBackingStore::enumerateRectsBeingDrawn(CGContextRef context, void (^block)(CGRect)) 359{ 360 CGAffineTransform inverseTransform = CGAffineTransformInvert(CGContextGetCTM(context)); 361 362 // We don't want to un-apply the flipping or contentsScale, 363 // because they're not applied to repaint rects. 364 inverseTransform = CGAffineTransformScale(inverseTransform, m_scale, -m_scale); 365 inverseTransform = CGAffineTransformTranslate(inverseTransform, 0, -m_size.height()); 366 367 for (const auto& rect : m_paintingRects) { 368 CGRect rectToDraw = CGRectApplyAffineTransform(rect, inverseTransform); 369 block(rectToDraw); 370 } 371} 372 373void RemoteLayerBackingStore::applyBackingStoreToLayer(CALayer *layer) 374{ 375 layer.contentsOpaque = m_isOpaque; 376 377#if USE(IOSURFACE) 378 if (acceleratesDrawing()) { 379 layer.contents = (id)m_frontBuffer.surface->surface(); 380 return; 381 } 382#endif 383 384 ASSERT(!acceleratesDrawing()); 385 layer.contents = (id)m_frontBuffer.bitmap->makeCGImageCopy().get(); 386} 387 388RetainPtr<CGContextRef> RemoteLayerBackingStore::takeFrontContextPendingFlush() 389{ 390 return WTF::move(m_frontContextPendingFlush); 391} 392 393#if USE(IOSURFACE) 394bool RemoteLayerBackingStore::setBufferVolatility(BufferType type, bool isVolatile) 395{ 396 switch(type) { 397 case BufferType::Front: 398 if (m_frontBuffer.surface && m_frontBuffer.isVolatile != isVolatile) { 399 if (isVolatile) 400 m_frontBuffer.surface->releaseGraphicsContext(); 401 if (!isVolatile || !m_frontBuffer.surface->isInUse()) { 402 IOSurface::SurfaceState previousState = m_frontBuffer.surface->setIsVolatile(isVolatile); 403 m_frontBuffer.isVolatile = isVolatile; 404 405 // Becoming non-volatile and the front buffer was purged, so we need to repaint. 406 if (!isVolatile && (previousState == IOSurface::SurfaceState::Empty)) 407 setNeedsDisplay(); 408 } else 409 return false; 410 } 411 break; 412 case BufferType::Back: 413 if (m_backBuffer.surface && m_backBuffer.isVolatile != isVolatile) { 414 if (isVolatile) 415 m_backBuffer.surface->releaseGraphicsContext(); 416 if (!isVolatile || !m_backBuffer.surface->isInUse()) { 417 m_backBuffer.surface->setIsVolatile(isVolatile); 418 m_backBuffer.isVolatile = isVolatile; 419 } else 420 return false; 421 } 422 break; 423 case BufferType::SecondaryBack: 424 if (m_secondaryBackBuffer.surface && m_secondaryBackBuffer.isVolatile != isVolatile) { 425 if (isVolatile) 426 m_secondaryBackBuffer.surface->releaseGraphicsContext(); 427 if (!isVolatile || !m_secondaryBackBuffer.surface->isInUse()) { 428 m_secondaryBackBuffer.surface->setIsVolatile(isVolatile); 429 m_secondaryBackBuffer.isVolatile = isVolatile; 430 } else 431 return false; 432 } 433 break; 434 } 435 return true; 436} 437#else 438bool RemoteLayerBackingStore::setBufferVolatility(BufferType, bool) 439{ 440 return true; 441} 442#endif 443 444void RemoteLayerBackingStore::Buffer::discard() 445{ 446#if USE(IOSURFACE) 447 if (surface) 448 IOSurfacePool::sharedPool().addSurface(surface.get()); 449 surface = nullptr; 450 isVolatile = false; 451#endif 452 bitmap = nullptr; 453} 454 455} // namespace WebKit 456