[iOS][WebKit2] Mark layer contents as being opaque if they are
[WebKit-https.git] / Source / WebKit2 / Shared / mac / RemoteLayerBackingStore.mm
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 "ShareableBitmap.h"
33 #import "WebCoreArgumentCoders.h"
34 #import <QuartzCore/QuartzCore.h>
35 #import <WebCore/GraphicsContextCG.h>
36 #import <WebCore/WebLayer.h>
37
38 #if USE(IOSURFACE)
39 #import <mach/mach_port.h>
40 #endif
41
42 #if __has_include(<ApplicationServices/ApplicationServicesPriv.h>)
43 #import <ApplicationServices/ApplicationServicesPriv.h>
44 #endif
45
46 extern "C" {
47 #if USE(IOSURFACE)
48 CGContextRef CGIOSurfaceContextCreate(IOSurfaceRef, size_t, size_t, size_t, size_t, CGColorSpaceRef, CGBitmapInfo);
49 #endif
50 CGImageRef CGIOSurfaceContextCreateImage(CGContextRef);
51 }
52
53 #if __has_include(<QuartzCore/CALayerPrivate.h>)
54 #import <QuartzCore/CALayerPrivate.h>
55 #endif
56
57 @interface CALayer (Details)
58 @property BOOL contentsOpaque;
59 @end
60
61 using namespace WebCore;
62 using namespace WebKit;
63
64 RemoteLayerBackingStore::RemoteLayerBackingStore()
65     : m_layer(nullptr)
66     , m_isOpaque(false)
67 {
68 }
69
70 void RemoteLayerBackingStore::ensureBackingStore(PlatformCALayerRemote* layer, IntSize size, float scale, bool acceleratesDrawing, bool isOpaque)
71 {
72     if (m_layer == layer && m_size == size && m_scale == scale && m_acceleratesDrawing == acceleratesDrawing && m_isOpaque == isOpaque)
73         return;
74
75     m_layer = layer;
76     m_size = size;
77     m_scale = scale;
78     m_acceleratesDrawing = acceleratesDrawing;
79     m_isOpaque = isOpaque;
80
81 #if USE(IOSURFACE)
82     m_frontSurface = nullptr;
83 #endif
84     m_frontBuffer = nullptr;
85 }
86
87 void RemoteLayerBackingStore::encode(IPC::ArgumentEncoder& encoder) const
88 {
89     encoder << m_size;
90     encoder << m_scale;
91     encoder << m_acceleratesDrawing;
92     encoder << m_isOpaque;
93
94 #if USE(IOSURFACE)
95     if (m_acceleratesDrawing) {
96         mach_port_t port = IOSurfaceCreateMachPort(m_frontSurface.get());
97         encoder << IPC::MachPort(port, MACH_MSG_TYPE_MOVE_SEND);
98         return;
99     }
100 #else
101     ASSERT(!m_acceleratesDrawing);
102 #endif
103
104     ShareableBitmap::Handle handle;
105     m_frontBuffer->createHandle(handle);
106     encoder << handle;
107 }
108
109 bool RemoteLayerBackingStore::decode(IPC::ArgumentDecoder& decoder, RemoteLayerBackingStore& result)
110 {
111     if (!decoder.decode(result.m_size))
112         return false;
113
114     if (!decoder.decode(result.m_scale))
115         return false;
116
117     if (!decoder.decode(result.m_acceleratesDrawing))
118         return false;
119
120     if (!decoder.decode(result.m_isOpaque))
121         return false;
122
123 #if USE(IOSURFACE)
124     if (result.m_acceleratesDrawing) {
125         IPC::MachPort machPort;
126         if (!decoder.decode(machPort))
127             return false;
128         result.m_frontSurface = adoptCF(IOSurfaceLookupFromMachPort(machPort.port()));
129         mach_port_deallocate(mach_task_self(), machPort.port());
130         return true;
131     }
132 #else
133     ASSERT(!result.m_acceleratesDrawing);
134 #endif
135
136     ShareableBitmap::Handle handle;
137     if (!decoder.decode(handle))
138         return false;
139     result.m_frontBuffer = ShareableBitmap::create(handle);
140
141     return true;
142 }
143
144 void RemoteLayerBackingStore::setNeedsDisplay(const IntRect rect)
145 {
146     m_dirtyRegion.unite(rect);
147 }
148
149 void RemoteLayerBackingStore::setNeedsDisplay()
150 {
151     setNeedsDisplay(IntRect(IntPoint(), m_size));
152 }
153
154 #if USE(IOSURFACE)
155 static RetainPtr<CGContextRef> createIOSurfaceContext(IOSurfaceRef surface, IntSize size, CGColorSpaceRef colorSpace)
156 {
157     if (!surface)
158         return nullptr;
159
160     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
161     size_t bitsPerComponent = 8;
162     size_t bitsPerPixel = 32;
163     return adoptCF(CGIOSurfaceContextCreate(surface, size.width(), size.height(), bitsPerComponent, bitsPerPixel, colorSpace, bitmapInfo));
164 }
165
166 static RetainPtr<IOSurfaceRef> createIOSurface(IntSize size)
167 {
168     unsigned pixelFormat = 'BGRA';
169     unsigned bytesPerElement = 4;
170     int width = size.width();
171     int height = size.height();
172
173     size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
174     ASSERT(bytesPerRow);
175
176     size_t allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
177     ASSERT(allocSize);
178
179     NSDictionary *dict = @{
180         (id)kIOSurfaceWidth: @(width),
181         (id)kIOSurfaceHeight: @(height),
182         (id)kIOSurfacePixelFormat: @(pixelFormat),
183         (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
184         (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
185         (id)kIOSurfaceAllocSize: @(allocSize)
186     };
187
188     return adoptCF(IOSurfaceCreate((CFDictionaryRef)dict));
189 }
190 #endif // USE(IOSURFACE)
191
192 RetainPtr<CGImageRef> RemoteLayerBackingStore::createImageForFrontBuffer() const
193 {
194     if (!m_frontBuffer || m_acceleratesDrawing)
195         return nullptr;
196
197     // FIXME: Do we need Copy?
198     return m_frontBuffer->makeCGImageCopy();
199 }
200
201 bool RemoteLayerBackingStore::display()
202 {
203     if (!m_layer)
204         return false;
205
206     // If we previously were drawsContent=YES, and now are not, we need
207     // to note that our backing store has been cleared.
208     if (!m_layer->owner() || !m_layer->owner()->platformCALayerDrawsContent()) {
209         bool previouslyDrewContents = hasFrontBuffer();
210
211         m_frontBuffer = nullptr;
212 #if USE(IOSURFACE)
213         m_frontSurface = nullptr;
214 #endif
215
216         return previouslyDrewContents;
217     }
218
219     if (m_dirtyRegion.isEmpty() || m_size.isEmpty())
220         return false;
221
222     if (!hasFrontBuffer())
223         m_dirtyRegion.unite(IntRect(IntPoint(), m_size));
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 = expandedIntSize(scaledSize);
233
234 #if USE(IOSURFACE)
235     if (m_acceleratesDrawing) {
236         RetainPtr<IOSurfaceRef> backSurface = createIOSurface(expandedScaledSize);
237         RetainPtr<CGContextRef> cgContext = createIOSurfaceContext(backSurface.get(), expandedScaledSize, sRGBColorSpaceRef());
238         GraphicsContext context(cgContext.get());
239         context.clearRect(FloatRect(FloatPoint(), expandedScaledSize));
240         context.scale(FloatSize(1, -1));
241         context.translate(0, -expandedScaledSize.height());
242
243         RetainPtr<CGContextRef> frontContext;
244         RetainPtr<CGImageRef> frontImage;
245         if (m_frontSurface) {
246             frontContext = createIOSurfaceContext(m_frontSurface.get(), expandedIntSize(m_size * m_scale), sRGBColorSpaceRef());
247             frontImage = adoptCF(CGIOSurfaceContextCreateImage(frontContext.get()));
248         }
249
250         drawInContext(context, frontImage.get());
251         m_frontSurface = backSurface;
252
253         // If our frontImage is derived from an IOSurface, we need to
254         // destroy the image before the CGContext it's derived from,
255         // so that the context doesn't make a CPU copy of the surface data.
256         if (frontImage) {
257             frontImage = nullptr;
258             ASSERT(frontContext);
259         }
260
261         return true;
262     }
263 #else
264     ASSERT(!m_acceleratesDrawing);
265 #endif
266
267     RetainPtr<CGImageRef> frontImage = createImageForFrontBuffer();
268     m_frontBuffer = ShareableBitmap::createShareable(expandedScaledSize, m_isOpaque ? ShareableBitmap::NoFlags : ShareableBitmap::SupportsAlpha);
269     std::unique_ptr<GraphicsContext> context = m_frontBuffer->createGraphicsContext();
270     drawInContext(*context, frontImage.get());
271     
272     return true;
273 }
274
275 void RemoteLayerBackingStore::drawInContext(GraphicsContext& context, CGImageRef frontImage)
276 {
277     IntRect layerBounds(IntPoint(), m_size);
278     IntRect scaledLayerBounds(IntPoint(), expandedIntSize(m_size * m_scale));
279
280     CGContextRef cgContext = context.platformContext();
281
282     // If we have less than webLayerMaxRectsToPaint rects to paint and they cover less
283     // than webLayerWastedSpaceThreshold of the total dirty area, we'll repaint each rect separately.
284     // Otherwise, repaint the entire bounding box of the dirty region.
285     IntRect dirtyBounds = m_dirtyRegion.bounds();
286     Vector<IntRect> dirtyRects = m_dirtyRegion.rects();
287     if (dirtyRects.size() > webLayerMaxRectsToPaint || m_dirtyRegion.totalArea() > webLayerWastedSpaceThreshold * dirtyBounds.width() * dirtyBounds.height()) {
288         dirtyRects.clear();
289         dirtyRects.append(dirtyBounds);
290     }
291
292     for (const auto& rect : dirtyRects) {
293         FloatRect scaledRect(rect);
294         scaledRect.scale(m_scale, m_scale);
295         scaledRect = enclosingIntRect(scaledRect);
296         scaledRect.scale(1 / m_scale, 1 / m_scale);
297         m_paintingRects.append(scaledRect);
298     }
299
300     CGRect cgPaintingRects[webLayerMaxRectsToPaint];
301     for (size_t i = 0, dirtyRectCount = m_paintingRects.size(); i < dirtyRectCount; ++i) {
302         FloatRect scaledPaintingRect = m_paintingRects[i];
303         scaledPaintingRect.scale(m_scale);
304         cgPaintingRects[i] = enclosingIntRect(scaledPaintingRect);
305     }
306
307     if (frontImage) {
308         CGContextSaveGState(cgContext);
309         CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
310
311         CGContextAddRect(cgContext, CGRectInfinite);
312         CGContextAddRects(cgContext, cgPaintingRects, m_paintingRects.size());
313         CGContextEOClip(cgContext);
314
315         CGContextTranslateCTM(cgContext, 0, scaledLayerBounds.height());
316         CGContextScaleCTM(cgContext, 1, -1);
317         CGContextDrawImage(cgContext, scaledLayerBounds, frontImage);
318         CGContextRestoreGState(cgContext);
319     }
320
321     CGContextClipToRects(cgContext, cgPaintingRects, m_paintingRects.size());
322
323     context.scale(FloatSize(m_scale, m_scale));
324
325     switch (m_layer->layerType()) {
326     case PlatformCALayer::LayerTypeSimpleLayer:
327     case PlatformCALayer::LayerTypeTiledBackingTileLayer:
328         m_layer->owner()->platformCALayerPaintContents(m_layer, context, dirtyBounds);
329         break;
330     case PlatformCALayer::LayerTypeWebLayer:
331         drawLayerContents(cgContext, m_layer, m_paintingRects);
332         break;
333     case PlatformCALayer::LayerTypeLayer:
334     case PlatformCALayer::LayerTypeTransformLayer:
335     case PlatformCALayer::LayerTypeWebTiledLayer:
336     case PlatformCALayer::LayerTypeTiledBackingLayer:
337     case PlatformCALayer::LayerTypePageTiledBackingLayer:
338     case PlatformCALayer::LayerTypeRootLayer:
339     case PlatformCALayer::LayerTypeAVPlayerLayer:
340     case PlatformCALayer::LayerTypeCustom:
341         ASSERT_NOT_REACHED();
342         break;
343     };
344
345     m_dirtyRegion = Region();
346     m_paintingRects.clear();
347
348     CGContextFlush(context.platformContext());
349 }
350
351 void RemoteLayerBackingStore::enumerateRectsBeingDrawn(CGContextRef context, void (^block)(CGRect))
352 {
353     CGAffineTransform inverseTransform = CGAffineTransformInvert(CGContextGetCTM(context));
354
355     // We don't want to un-apply the flipping or contentsScale,
356     // because they're not applied to repaint rects.
357     inverseTransform = CGAffineTransformScale(inverseTransform, m_scale, -m_scale);
358     inverseTransform = CGAffineTransformTranslate(inverseTransform, 0, -m_size.height());
359
360     for (const auto& rect : m_paintingRects) {
361         CGRect rectToDraw = CGRectApplyAffineTransform(rect, inverseTransform);
362         block(rectToDraw);
363     }
364 }
365
366 void RemoteLayerBackingStore::applyBackingStoreToLayer(CALayer *layer)
367 {
368 #if USE(IOSURFACE)
369     if (acceleratesDrawing())
370         layer.contents = (id)m_frontSurface.get();
371     else
372         layer.contents = (id)createImageForFrontBuffer().get();
373 #else
374     ASSERT(!acceleratesDrawing());
375     layer.contents = (id)createImageForFrontBuffer().get();
376 #endif
377
378     layer.contentsOpaque = m_isOpaque;
379 }