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