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