blob: 7dd52b60971d51ef623a77a4b077373d85430578 [file] [log] [blame]
/*
* 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 COMPUTER, 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"
#if USE(ACCELERATED_COMPOSITING)
#import "ArgumentCoders.h"
#import "MachPort.h"
#import "PlatformCALayerRemote.h"
#import "ShareableBitmap.h"
#import "WebCoreArgumentCoders.h"
#import <WebCore/GraphicsContextCG.h>
#import <WebCore/WebLayer.h>
#if USE(IOSURFACE)
#import <mach/mach_port.h>
#endif
#if defined(__has_include) && __has_include(<ApplicationServices/ApplicationServicesPriv.h>)
#import <ApplicationServices/ApplicationServicesPriv.h>
#endif
extern "C" {
#if USE(IOSURFACE)
CGContextRef CGIOSurfaceContextCreate(IOSurfaceRef, size_t, size_t, size_t, size_t, CGColorSpaceRef, CGBitmapInfo);
#endif
CGImageRef CGIOSurfaceContextCreateImage(CGContextRef);
}
using namespace WebCore;
using namespace WebKit;
RemoteLayerBackingStore::RemoteLayerBackingStore()
: m_layer(nullptr)
{
}
void RemoteLayerBackingStore::ensureBackingStore(PlatformCALayerRemote* layer, IntSize size, float scale, bool acceleratesDrawing)
{
if (m_layer == layer && m_size == size && m_scale == scale && m_acceleratesDrawing == acceleratesDrawing)
return;
m_layer = layer;
m_size = size;
m_scale = scale;
m_acceleratesDrawing = acceleratesDrawing;
#if USE(IOSURFACE)
m_frontSurface = nullptr;
#endif
m_frontBuffer = nullptr;
}
void RemoteLayerBackingStore::encode(IPC::ArgumentEncoder& encoder) const
{
encoder << m_size;
encoder << m_scale;
encoder << m_acceleratesDrawing;
#if USE(IOSURFACE)
if (m_acceleratesDrawing) {
mach_port_t port = IOSurfaceCreateMachPort(m_frontSurface.get());
encoder << IPC::MachPort(port, MACH_MSG_TYPE_MOVE_SEND);
return;
}
#else
ASSERT(!m_acceleratesDrawing);
#endif
ShareableBitmap::Handle handle;
m_frontBuffer->createHandle(handle);
encoder << handle;
}
bool RemoteLayerBackingStore::decode(IPC::ArgumentDecoder& 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 USE(IOSURFACE)
if (result.m_acceleratesDrawing) {
IPC::MachPort machPort;
if (!decoder.decode(machPort))
return false;
result.m_frontSurface = adoptCF(IOSurfaceLookupFromMachPort(machPort.port()));
mach_port_deallocate(mach_task_self(), machPort.port());
return true;
}
#else
ASSERT(!result.m_acceleratesDrawing);
#endif
ShareableBitmap::Handle handle;
if (!decoder.decode(handle))
return false;
result.m_frontBuffer = ShareableBitmap::create(handle);
return true;
}
void RemoteLayerBackingStore::setNeedsDisplay(const IntRect rect)
{
m_dirtyRegion.unite(rect);
}
void RemoteLayerBackingStore::setNeedsDisplay()
{
setNeedsDisplay(IntRect(IntPoint(), m_size));
}
#if USE(IOSURFACE)
static RetainPtr<CGContextRef> createIOSurfaceContext(IOSurfaceRef surface, IntSize size, CGColorSpaceRef colorSpace)
{
if (!surface)
return nullptr;
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
return adoptCF(CGIOSurfaceContextCreate(surface, size.width(), size.height(), bitsPerComponent, bitsPerPixel, colorSpace, bitmapInfo));
}
static RetainPtr<IOSurfaceRef> createIOSurface(IntSize size)
{
unsigned pixelFormat = 'BGRA';
unsigned bytesPerElement = 4;
int width = size.width();
int height = size.height();
size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
ASSERT(bytesPerRow);
size_t allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
ASSERT(allocSize);
NSDictionary *dict = @{
(id)kIOSurfaceWidth: @(width),
(id)kIOSurfaceHeight: @(height),
(id)kIOSurfacePixelFormat: @(pixelFormat),
(id)kIOSurfaceBytesPerElement: @(bytesPerElement),
(id)kIOSurfaceBytesPerRow: @(bytesPerRow),
(id)kIOSurfaceAllocSize: @(allocSize)
};
return adoptCF(IOSurfaceCreate((CFDictionaryRef)dict));
}
#endif // USE(IOSURFACE)
RetainPtr<CGImageRef> RemoteLayerBackingStore::image() const
{
if (!m_frontBuffer || m_acceleratesDrawing)
return nullptr;
// FIXME: Do we need Copy?
return m_frontBuffer->makeCGImageCopy();
}
bool RemoteLayerBackingStore::display()
{
if (!m_layer)
return false;
// If we previously were drawsContent=YES, and now are not, we need
// to note that our backing store has been cleared.
if (!m_layer->owner() || !m_layer->owner()->platformCALayerDrawsContent()) {
bool previouslyDrewContents = hasFrontBuffer();
m_frontBuffer = nullptr;
#if USE(IOSURFACE)
m_frontSurface = nullptr;
#endif
return previouslyDrewContents;
}
if (m_dirtyRegion.isEmpty() || m_size.isEmpty())
return false;
if (!hasFrontBuffer())
m_dirtyRegion.unite(IntRect(IntPoint(), m_size));
if (m_layer->owner()->platformCALayerShowRepaintCounter(m_layer)) {
IntRect indicatorRect(0, 0, 52, 27);
m_dirtyRegion.unite(indicatorRect);
}
FloatSize scaledSize = m_size;
scaledSize.scale(m_scale);
IntSize expandedScaledSize = expandedIntSize(scaledSize);
#if USE(IOSURFACE)
if (m_acceleratesDrawing) {
RetainPtr<IOSurfaceRef> backSurface = createIOSurface(expandedScaledSize);
RetainPtr<CGContextRef> cgContext = createIOSurfaceContext(backSurface.get(), expandedScaledSize, sRGBColorSpaceRef());
GraphicsContext context(cgContext.get());
context.clearRect(FloatRect(FloatPoint(), expandedScaledSize));
context.scale(FloatSize(1, -1));
context.translate(0, -expandedScaledSize.height());
RetainPtr<CGContextRef> frontContext;
RetainPtr<CGImageRef> frontImage;
if (m_frontSurface) {
frontContext = createIOSurfaceContext(m_frontSurface.get(), expandedIntSize(m_size * m_scale), sRGBColorSpaceRef());
frontImage = adoptCF(CGIOSurfaceContextCreateImage(frontContext.get()));
}
drawInContext(context, frontImage.get());
m_frontSurface = backSurface;
// If our frontImage is derived from an IOSurface, we need to
// destroy the image before the CGContext it's derived from,
// so that the context doesn't make a CPU copy of the surface data.
if (frontImage) {
frontImage = nullptr;
ASSERT(frontContext);
}
return true;
}
#else
ASSERT(!m_acceleratesDrawing);
#endif
RetainPtr<CGImageRef> frontImage = image();
m_frontBuffer = ShareableBitmap::createShareable(expandedScaledSize, ShareableBitmap::SupportsAlpha);
std::unique_ptr<GraphicsContext> context = m_frontBuffer->createGraphicsContext();
drawInContext(*context, frontImage.get());
return true;
}
void RemoteLayerBackingStore::drawInContext(GraphicsContext& context, CGImageRef frontImage)
{
IntRect layerBounds(IntPoint(), m_size);
IntRect scaledLayerBounds(IntPoint(), expandedIntSize(m_size * m_scale));
CGContextRef cgContext = context.platformContext();
// 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<IntRect> dirtyRects = m_dirtyRegion.rects();
if (dirtyRects.size() > webLayerMaxRectsToPaint || m_dirtyRegion.totalArea() > webLayerWastedSpaceThreshold * dirtyBounds.width() * dirtyBounds.height()) {
dirtyRects.clear();
dirtyRects.append(dirtyBounds);
}
for (const auto& rect : dirtyRects) {
FloatRect scaledRect(rect);
scaledRect.scale(m_scale, m_scale);
scaledRect = enclosingIntRect(scaledRect);
scaledRect.scale(1 / m_scale, 1 / m_scale);
m_paintingRects.append(scaledRect);
}
CGRect cgPaintingRects[webLayerMaxRectsToPaint];
for (size_t i = 0, dirtyRectCount = m_paintingRects.size(); i < dirtyRectCount; ++i) {
FloatRect scaledPaintingRect = m_paintingRects[i];
scaledPaintingRect.scale(m_scale);
cgPaintingRects[i] = enclosingIntRect(scaledPaintingRect);
}
if (frontImage) {
CGContextSaveGState(cgContext);
CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
CGContextAddRect(cgContext, CGRectInfinite);
CGContextAddRects(cgContext, cgPaintingRects, m_paintingRects.size());
CGContextEOClip(cgContext);
CGContextTranslateCTM(cgContext, 0, scaledLayerBounds.height());
CGContextScaleCTM(cgContext, 1, -1);
CGContextDrawImage(cgContext, scaledLayerBounds, frontImage);
CGContextRestoreGState(cgContext);
}
CGContextClipToRects(cgContext, cgPaintingRects, m_paintingRects.size());
context.scale(FloatSize(m_scale, m_scale));
switch (m_layer->layerType()) {
case PlatformCALayer::LayerTypeSimpleLayer:
case PlatformCALayer::LayerTypeTiledBackingTileLayer:
m_layer->owner()->platformCALayerPaintContents(m_layer, context, dirtyBounds);
break;
case PlatformCALayer::LayerTypeWebLayer:
drawLayerContents(cgContext, m_layer, m_paintingRects);
break;
case PlatformCALayer::LayerTypeLayer:
case PlatformCALayer::LayerTypeTransformLayer:
case PlatformCALayer::LayerTypeWebTiledLayer:
case PlatformCALayer::LayerTypeTiledBackingLayer:
case PlatformCALayer::LayerTypePageTiledBackingLayer:
case PlatformCALayer::LayerTypeRootLayer:
case PlatformCALayer::LayerTypeAVPlayerLayer:
case PlatformCALayer::LayerTypeCustom:
ASSERT_NOT_REACHED();
break;
};
m_dirtyRegion = Region();
m_paintingRects.clear();
CGContextFlush(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 (auto rect : m_paintingRects) {
CGRect rectToDraw = CGRectApplyAffineTransform(rect, inverseTransform);
block(rectToDraw);
}
}
#endif // USE(ACCELERATED_COMPOSITING)