/* * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "RemoteLayerBackingStore.h" #import "ArgumentCoders.h" #import "MachPort.h" #import "PlatformCALayerRemote.h" #import "RemoteLayerBackingStoreCollection.h" #import "RemoteLayerTreeContext.h" #import "ShareableBitmap.h" #import "WebCoreArgumentCoders.h" #import #import #import #import #import #import #if USE(IOSURFACE) #import #endif using namespace WebCore; namespace WebKit { RemoteLayerBackingStore::RemoteLayerBackingStore(PlatformCALayerRemote* layer) : m_layer(layer) , m_isOpaque(false) , m_lastDisplayTime(std::chrono::steady_clock::time_point::min()) { if (!m_layer) return; if (RemoteLayerTreeContext* context = m_layer->context()) context->backingStoreWasCreated(*this); } RemoteLayerBackingStore::~RemoteLayerBackingStore() { clearBackingStore(); if (!m_layer) return; if (RemoteLayerTreeContext* context = m_layer->context()) context->backingStoreWillBeDestroyed(*this); } void RemoteLayerBackingStore::ensureBackingStore(FloatSize size, float scale, bool acceleratesDrawing, bool deepColor, bool isOpaque) { #if !USE(IOSURFACE) acceleratesDrawing = false; #endif if (m_size == size && m_scale == scale && m_deepColor == deepColor && m_acceleratesDrawing == acceleratesDrawing && m_isOpaque == isOpaque) return; m_size = size; m_scale = scale; m_acceleratesDrawing = acceleratesDrawing; m_deepColor = deepColor; m_isOpaque = isOpaque; if (m_frontBuffer) { // If we have a valid backing store, we need to ensure that it gets completely // repainted the next time display() is called. setNeedsDisplay(); } clearBackingStore(); } void RemoteLayerBackingStore::clearBackingStore() { m_frontBuffer.discard(); m_backBuffer.discard(); #if USE(IOSURFACE) m_secondaryBackBuffer.discard(); #endif } void RemoteLayerBackingStore::encode(IPC::Encoder& encoder) const { encoder << m_size; encoder << m_scale; encoder << m_acceleratesDrawing; encoder << m_isOpaque; #if USE(IOSURFACE) if (m_acceleratesDrawing) { if (m_frontBuffer.surface) encoder << m_frontBuffer.surface->createSendRight(); else encoder << WebCore::MachSendRight(); return; } #endif ASSERT(!m_acceleratesDrawing); ShareableBitmap::Handle handle; m_frontBuffer.bitmap->createHandle(handle); encoder << handle; } bool RemoteLayerBackingStore::decode(IPC::Decoder& decoder, RemoteLayerBackingStore& result) { if (!decoder.decode(result.m_size)) return false; if (!decoder.decode(result.m_scale)) return false; if (!decoder.decode(result.m_acceleratesDrawing)) return false; if (!decoder.decode(result.m_isOpaque)) return false; #if USE(IOSURFACE) if (result.m_acceleratesDrawing) { MachSendRight sendRight; if (!decoder.decode(sendRight)) return false; result.m_frontBufferSendRight = WTFMove(sendRight); return true; } #endif ASSERT(!result.m_acceleratesDrawing); ShareableBitmap::Handle handle; if (!decoder.decode(handle)) return false; result.m_frontBuffer.bitmap = ShareableBitmap::create(handle); return true; } void RemoteLayerBackingStore::setNeedsDisplay(const IntRect rect) { m_dirtyRegion.unite(rect); } void RemoteLayerBackingStore::setNeedsDisplay() { setNeedsDisplay(IntRect(IntPoint(), expandedIntSize(m_size))); } IntSize RemoteLayerBackingStore::backingStoreSize() const { FloatSize scaledSize = m_size; scaledSize.scale(m_scale); return roundedIntSize(scaledSize); } unsigned RemoteLayerBackingStore::bytesPerPixel() const { #if USE(IOSURFACE) switch (surfaceBufferFormat()) { case WebCore::IOSurface::Format::RGBA: return 4; case WebCore::IOSurface::Format::YUV422: return 2; case WebCore::IOSurface::Format::RGB10: return 4; case WebCore::IOSurface::Format::RGB10A8: return 5; } #endif return 4; } void RemoteLayerBackingStore::swapToValidFrontBuffer() { IntSize expandedScaledSize = backingStoreSize(); #if USE(IOSURFACE) if (m_acceleratesDrawing) { if (!m_backBuffer.surface || m_backBuffer.surface->isInUse()) { std::swap(m_backBuffer, m_secondaryBackBuffer); if (m_backBuffer.surface && m_backBuffer.surface->isInUse()) m_backBuffer.discard(); } std::swap(m_frontBuffer, m_backBuffer); if (!m_frontBuffer.surface) m_frontBuffer.surface = WebCore::IOSurface::create(expandedScaledSize, sRGBColorSpaceRef(), surfaceBufferFormat()); setBufferVolatility(BufferType::Front, false); return; } #endif ASSERT(!m_acceleratesDrawing); std::swap(m_frontBuffer, m_backBuffer); if (!m_frontBuffer.bitmap) { ShareableBitmap::Configuration bitmapConfiguration; bitmapConfiguration.isOpaque = m_isOpaque; m_frontBuffer.bitmap = ShareableBitmap::createShareable(expandedScaledSize, bitmapConfiguration); } } bool RemoteLayerBackingStore::display() { ASSERT(!m_frontContextPendingFlush); m_lastDisplayTime = std::chrono::steady_clock::now(); bool needToEncodeBackingStore = false; if (RemoteLayerTreeContext* context = m_layer->context()) needToEncodeBackingStore = context->backingStoreWillBeDisplayed(*this); // Make the previous front buffer non-volatile early, so that we can dirty the whole layer if it comes back empty. setBufferVolatility(BufferType::Front, false); IntSize expandedScaledSize = backingStoreSize(); if (m_dirtyRegion.isEmpty() || expandedScaledSize.isEmpty()) return needToEncodeBackingStore; IntRect layerBounds(IntPoint(), expandedIntSize(m_size)); if (!hasFrontBuffer()) m_dirtyRegion.unite(layerBounds); if (m_layer->owner()->platformCALayerShowRepaintCounter(m_layer)) { IntRect indicatorRect(0, 0, 52, 27); m_dirtyRegion.unite(indicatorRect); } IntRect expandedScaledLayerBounds(IntPoint(), expandedScaledSize); bool willPaintEntireBackingStore = m_dirtyRegion.contains(layerBounds); swapToValidFrontBuffer(); #if USE(IOSURFACE) if (m_acceleratesDrawing) { RetainPtr backImage; if (m_backBuffer.surface && !willPaintEntireBackingStore) backImage = m_backBuffer.surface->createImage(); if (m_frontBuffer.surface) { GraphicsContext& context = m_frontBuffer.surface->ensureGraphicsContext(); context.scale(FloatSize(1, -1)); context.translate(0, -expandedScaledSize.height()); drawInContext(context, backImage.get()); m_frontBuffer.surface->releaseGraphicsContext(); } } else #endif { ASSERT(!m_acceleratesDrawing); std::unique_ptr context = m_frontBuffer.bitmap->createGraphicsContext(); RetainPtr backImage; if (m_backBuffer.bitmap && !willPaintEntireBackingStore) backImage = m_backBuffer.bitmap->makeCGImage(); drawInContext(*context, backImage.get()); } m_layer->owner()->platformCALayerLayerDidDisplay(m_layer); return true; } void RemoteLayerBackingStore::drawInContext(GraphicsContext& context, CGImageRef backImage) { FloatSize scaledSize = m_size; scaledSize.scale(m_scale); IntRect scaledLayerBounds(IntPoint(), roundedIntSize(scaledSize)); // If we have less than webLayerMaxRectsToPaint rects to paint and they cover less // than webLayerWastedSpaceThreshold of the total dirty area, we'll repaint each rect separately. // Otherwise, repaint the entire bounding box of the dirty region. IntRect dirtyBounds = m_dirtyRegion.bounds(); Vector dirtyRects = m_dirtyRegion.rects(); if (dirtyRects.size() > PlatformCALayer::webLayerMaxRectsToPaint || m_dirtyRegion.totalArea() > PlatformCALayer::webLayerWastedSpaceThreshold * dirtyBounds.width() * dirtyBounds.height()) { dirtyRects.clear(); dirtyRects.append(dirtyBounds); } // FIXME: find a consistent way to scale and snap dirty and CG clip rects. for (const auto& rect : dirtyRects) { FloatRect scaledRect(rect); scaledRect.scale(m_scale); scaledRect = enclosingIntRect(scaledRect); scaledRect.scale(1 / m_scale); m_paintingRects.append(scaledRect); } CGRect cgPaintingRects[PlatformCALayer::webLayerMaxRectsToPaint]; for (size_t i = 0, dirtyRectCount = m_paintingRects.size(); i < dirtyRectCount; ++i) { FloatRect scaledPaintingRect = m_paintingRects[i]; scaledPaintingRect.scale(m_scale); cgPaintingRects[i] = scaledPaintingRect; } CGContextRef cgContext = context.platformContext(); if (backImage) { CGContextStateSaver stateSaver(cgContext); CGContextSetBlendMode(cgContext, kCGBlendModeCopy); CGContextTranslateCTM(cgContext, 0, scaledLayerBounds.height()); CGContextScaleCTM(cgContext, 1, -1); CGContextDrawImage(cgContext, scaledLayerBounds, backImage); } CGContextClipToRects(cgContext, cgPaintingRects, m_paintingRects.size()); if (!m_isOpaque) context.clearRect(scaledLayerBounds); #ifndef NDEBUG if (m_isOpaque) context.fillRect(scaledLayerBounds, Color(255, 0, 0)); #endif context.scale(m_scale); auto flags = m_layer->context() && m_layer->context()->nextFlushIsForImmediatePaint() ? WebCore::GraphicsLayerPaintSnapshotting : WebCore::GraphicsLayerPaintNormal; // FIXME: This should be moved to PlatformCALayerRemote for better layering. switch (m_layer->layerType()) { case PlatformCALayer::LayerTypeSimpleLayer: case PlatformCALayer::LayerTypeTiledBackingTileLayer: m_layer->owner()->platformCALayerPaintContents(m_layer, context, dirtyBounds, flags); break; case PlatformCALayer::LayerTypeWebLayer: case PlatformCALayer::LayerTypeBackdropLayer: PlatformCALayer::drawLayerContents(cgContext, m_layer, m_paintingRects, flags); break; case PlatformCALayer::LayerTypeDarkSystemBackdropLayer: case PlatformCALayer::LayerTypeLightSystemBackdropLayer: // FIXME: These have a more complicated layer hierarchy. We need to paint into // a child layer in order to see the rendered results. PlatformCALayer::drawLayerContents(cgContext, m_layer, m_paintingRects, flags); break; case PlatformCALayer::LayerTypeLayer: case PlatformCALayer::LayerTypeTransformLayer: case PlatformCALayer::LayerTypeTiledBackingLayer: case PlatformCALayer::LayerTypePageTiledBackingLayer: case PlatformCALayer::LayerTypeRootLayer: case PlatformCALayer::LayerTypeAVPlayerLayer: case PlatformCALayer::LayerTypeContentsProvidedLayer: case PlatformCALayer::LayerTypeShapeLayer: case PlatformCALayer::LayerTypeScrollingLayer: case PlatformCALayer::LayerTypeCustom: ASSERT_NOT_REACHED(); break; }; m_dirtyRegion = Region(); m_paintingRects.clear(); m_frontContextPendingFlush = context.platformContext(); } void RemoteLayerBackingStore::enumerateRectsBeingDrawn(CGContextRef context, void (^block)(CGRect)) { CGAffineTransform inverseTransform = CGAffineTransformInvert(CGContextGetCTM(context)); // We don't want to un-apply the flipping or contentsScale, // because they're not applied to repaint rects. inverseTransform = CGAffineTransformScale(inverseTransform, m_scale, -m_scale); inverseTransform = CGAffineTransformTranslate(inverseTransform, 0, -m_size.height()); for (const auto& rect : m_paintingRects) { CGRect rectToDraw = CGRectApplyAffineTransform(rect, inverseTransform); block(rectToDraw); } } void RemoteLayerBackingStore::applyBackingStoreToLayer(CALayer *layer, LayerContentsType contentsType) { layer.contentsOpaque = m_isOpaque; #if USE(IOSURFACE) if (acceleratesDrawing()) { switch (contentsType) { case LayerContentsType::IOSurface: if (!m_frontBuffer.surface) { ASSERT(m_frontBufferSendRight); m_frontBuffer.surface = WebCore::IOSurface::createFromSendRight(WTFMove(m_frontBufferSendRight), sRGBColorSpaceRef()); } layer.contents = m_frontBuffer.surface ? m_frontBuffer.surface->asLayerContents() : nil; break; case LayerContentsType::CAMachPort: ASSERT(m_frontBufferSendRight); layer.contents = (id)adoptCF(CAMachPortCreate(m_frontBufferSendRight.leakSendRight())).get(); break; } return; } #endif ASSERT(!acceleratesDrawing()); layer.contents = (id)m_frontBuffer.bitmap->makeCGImageCopy().get(); } RetainPtr RemoteLayerBackingStore::takeFrontContextPendingFlush() { return WTFMove(m_frontContextPendingFlush); } #if USE(IOSURFACE) bool RemoteLayerBackingStore::setBufferVolatility(BufferType type, bool isVolatile) { switch (type) { case BufferType::Front: if (m_frontBuffer.surface && m_frontBuffer.isVolatile != isVolatile) { if (isVolatile) m_frontBuffer.surface->releaseGraphicsContext(); if (!isVolatile || !m_frontBuffer.surface->isInUse()) { auto previousState = m_frontBuffer.surface->setIsVolatile(isVolatile); m_frontBuffer.isVolatile = isVolatile; // Becoming non-volatile and the front buffer was purged, so we need to repaint. if (!isVolatile && (previousState == WebCore::IOSurface::SurfaceState::Empty)) setNeedsDisplay(); } else return false; } break; case BufferType::Back: if (m_backBuffer.surface && m_backBuffer.isVolatile != isVolatile) { if (isVolatile) m_backBuffer.surface->releaseGraphicsContext(); if (!isVolatile || !m_backBuffer.surface->isInUse()) { m_backBuffer.surface->setIsVolatile(isVolatile); m_backBuffer.isVolatile = isVolatile; } else return false; } break; case BufferType::SecondaryBack: if (m_secondaryBackBuffer.surface && m_secondaryBackBuffer.isVolatile != isVolatile) { if (isVolatile) m_secondaryBackBuffer.surface->releaseGraphicsContext(); if (!isVolatile || !m_secondaryBackBuffer.surface->isInUse()) { m_secondaryBackBuffer.surface->setIsVolatile(isVolatile); m_secondaryBackBuffer.isVolatile = isVolatile; } else return false; } break; } return true; } #else bool RemoteLayerBackingStore::setBufferVolatility(BufferType, bool) { return true; } #endif void RemoteLayerBackingStore::Buffer::discard() { #if USE(IOSURFACE) if (surface) WebCore::IOSurface::moveToPool(WTFMove(surface)); isVolatile = false; #endif bitmap = nullptr; } #if USE(IOSURFACE) WebCore::IOSurface::Format RemoteLayerBackingStore::surfaceBufferFormat() const { if (m_deepColor) return m_isOpaque ? WebCore::IOSurface::Format::RGB10 : WebCore::IOSurface::Format::RGB10A8; return WebCore::IOSurface::Format::RGBA; } #endif // USE(IOSURFACE) } // namespace WebKit