Make it possible to detach GraphicsLayerCA backing store
[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 "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/MachSendRight.h>
41 #import <WebCore/QuartzCoreSPI.h>
42 #import <WebCore/WebLayer.h>
43
44 #if USE(IOSURFACE)
45 #import <mach/mach_port.h>
46 #endif
47
48 using namespace WebCore;
49
50 namespace WebKit {
51
52 RemoteLayerBackingStore::RemoteLayerBackingStore(PlatformCALayerRemote* layer)
53     : m_layer(layer)
54     , m_isOpaque(false)
55     , m_lastDisplayTime(std::chrono::steady_clock::time_point::min())
56 {
57     if (!m_layer)
58         return;
59     if (RemoteLayerTreeContext* context = m_layer->context())
60         context->backingStoreWasCreated(*this);
61 }
62
63 RemoteLayerBackingStore::~RemoteLayerBackingStore()
64 {
65     clearBackingStore();
66
67     if (!m_layer)
68         return;
69
70     if (RemoteLayerTreeContext* context = m_layer->context())
71         context->backingStoreWillBeDestroyed(*this);
72 }
73
74 void RemoteLayerBackingStore::ensureBackingStore(FloatSize size, float scale, bool acceleratesDrawing, bool isOpaque)
75 {
76     if (m_size == size && m_scale == scale && m_acceleratesDrawing == acceleratesDrawing && m_isOpaque == isOpaque)
77         return;
78
79     m_size = size;
80     m_scale = scale;
81     m_acceleratesDrawing = acceleratesDrawing;
82     m_isOpaque = isOpaque;
83
84     if (m_frontBuffer) {
85         // If we have a valid backing store, we need to ensure that it gets completely
86         // repainted the next time display() is called.
87         setNeedsDisplay();
88     }
89
90     clearBackingStore();
91 }
92
93 void RemoteLayerBackingStore::clearBackingStore()
94 {
95     m_frontBuffer.discard();
96     m_backBuffer.discard();
97 #if USE(IOSURFACE)
98     m_secondaryBackBuffer.discard();
99 #endif
100 }
101
102 void RemoteLayerBackingStore::encode(IPC::ArgumentEncoder& encoder) const
103 {
104     encoder << m_size;
105     encoder << m_scale;
106     encoder << m_acceleratesDrawing;
107     encoder << m_isOpaque;
108
109 #if USE(IOSURFACE)
110     if (m_acceleratesDrawing) {
111         encoder << m_frontBuffer.surface->createSendRight();
112         return;
113     }
114 #endif
115
116     ASSERT(!m_acceleratesDrawing);
117
118     ShareableBitmap::Handle handle;
119     m_frontBuffer.bitmap->createHandle(handle);
120     encoder << handle;
121 }
122
123 bool RemoteLayerBackingStore::decode(IPC::ArgumentDecoder& decoder, RemoteLayerBackingStore& result)
124 {
125     if (!decoder.decode(result.m_size))
126         return false;
127
128     if (!decoder.decode(result.m_scale))
129         return false;
130
131     if (!decoder.decode(result.m_acceleratesDrawing))
132         return false;
133
134     if (!decoder.decode(result.m_isOpaque))
135         return false;
136
137 #if USE(IOSURFACE)
138     if (result.m_acceleratesDrawing) {
139         MachSendRight sendRight;
140         if (!decoder.decode(sendRight))
141             return false;
142         result.m_frontBuffer.surface = IOSurface::createFromSendRight(sendRight, ColorSpaceDeviceRGB);
143         return true;
144     }
145 #endif
146
147     ASSERT(!result.m_acceleratesDrawing);
148
149     ShareableBitmap::Handle handle;
150     if (!decoder.decode(handle))
151         return false;
152     result.m_frontBuffer.bitmap = ShareableBitmap::create(handle);
153
154     return true;
155 }
156
157 void RemoteLayerBackingStore::setNeedsDisplay(const IntRect rect)
158 {
159     m_dirtyRegion.unite(rect);
160 }
161
162 void RemoteLayerBackingStore::setNeedsDisplay()
163 {
164     setNeedsDisplay(IntRect(IntPoint(), expandedIntSize(m_size)));
165 }
166
167 IntSize RemoteLayerBackingStore::backingStoreSize() const
168 {
169     FloatSize scaledSize = m_size;
170     scaledSize.scale(m_scale);
171     return roundedIntSize(scaledSize);
172 }
173
174 void RemoteLayerBackingStore::swapToValidFrontBuffer()
175 {
176     IntSize expandedScaledSize = backingStoreSize();
177
178 #if USE(IOSURFACE)
179     if (m_acceleratesDrawing) {
180         if (!m_backBuffer.surface || m_backBuffer.surface->isInUse()) {
181             std::swap(m_backBuffer, m_secondaryBackBuffer);
182             if (m_backBuffer.surface && m_backBuffer.surface->isInUse())
183                 m_backBuffer.discard();
184         }
185
186         std::swap(m_frontBuffer, m_backBuffer);
187
188         if (!m_frontBuffer.surface)
189             m_frontBuffer.surface = IOSurface::create(expandedScaledSize, ColorSpaceDeviceRGB);
190
191         setBufferVolatility(BufferType::Front, false);
192
193         return;
194     }
195 #endif
196
197     ASSERT(!m_acceleratesDrawing);
198     std::swap(m_frontBuffer, m_backBuffer);
199
200     if (!m_frontBuffer.bitmap)
201         m_frontBuffer.bitmap = ShareableBitmap::createShareable(expandedScaledSize, m_isOpaque ? ShareableBitmap::NoFlags : ShareableBitmap::SupportsAlpha);
202 }
203
204 bool RemoteLayerBackingStore::display()
205 {
206     ASSERT(!m_frontContextPendingFlush);
207
208     m_lastDisplayTime = std::chrono::steady_clock::now();
209
210     bool needToEncodeBackingStore = false;
211     if (RemoteLayerTreeContext* context = m_layer->context())
212         needToEncodeBackingStore = context->backingStoreWillBeDisplayed(*this);
213
214     // Make the previous front buffer non-volatile early, so that we can dirty the whole layer if it comes back empty.
215     setBufferVolatility(BufferType::Front, false);
216
217     IntSize expandedScaledSize = backingStoreSize();
218
219     if (m_dirtyRegion.isEmpty() || expandedScaledSize.isEmpty())
220         return needToEncodeBackingStore;
221
222     IntRect layerBounds(IntPoint(), expandedIntSize(m_size));
223     if (!hasFrontBuffer())
224         m_dirtyRegion.unite(layerBounds);
225
226     if (m_layer->owner()->platformCALayerShowRepaintCounter(m_layer)) {
227         IntRect indicatorRect(0, 0, 52, 27);
228         m_dirtyRegion.unite(indicatorRect);
229     }
230
231     IntRect expandedScaledLayerBounds(IntPoint(), expandedScaledSize);
232     bool willPaintEntireBackingStore = m_dirtyRegion.contains(layerBounds);
233
234     swapToValidFrontBuffer();
235
236 #if USE(IOSURFACE)
237     if (m_acceleratesDrawing) {
238         RetainPtr<CGImageRef> backImage;
239         if (m_backBuffer.surface && !willPaintEntireBackingStore)
240             backImage = m_backBuffer.surface->createImage();
241
242         GraphicsContext& context = m_frontBuffer.surface->ensureGraphicsContext();
243
244         context.scale(FloatSize(1, -1));
245         context.translate(0, -expandedScaledSize.height());
246         drawInContext(context, backImage.get());
247
248         m_frontBuffer.surface->releaseGraphicsContext();
249     } else
250 #endif
251     {
252         ASSERT(!m_acceleratesDrawing);
253         std::unique_ptr<GraphicsContext> context = m_frontBuffer.bitmap->createGraphicsContext();
254
255         RetainPtr<CGImageRef> backImage;
256         if (m_backBuffer.bitmap && !willPaintEntireBackingStore)
257             backImage = m_backBuffer.bitmap->makeCGImage();
258
259         drawInContext(*context, backImage.get());
260     }
261     
262     m_layer->owner()->platformCALayerLayerDidDisplay(m_layer);
263     
264     return true;
265 }
266
267 void RemoteLayerBackingStore::drawInContext(GraphicsContext& context, CGImageRef backImage)
268 {
269     FloatSize scaledSize = m_size;
270     scaledSize.scale(m_scale);
271     IntRect scaledLayerBounds(IntPoint(), roundedIntSize(scaledSize));
272
273     if (!m_isOpaque)
274         context.clearRect(scaledLayerBounds);
275
276 #ifndef NDEBUG
277     if (m_isOpaque)
278         context.fillRect(scaledLayerBounds, Color(255, 0, 0), ColorSpaceDeviceRGB);
279 #endif
280
281     CGContextRef cgContext = context.platformContext();
282
283     // If we have less than webLayerMaxRectsToPaint rects to paint and they cover less
284     // than webLayerWastedSpaceThreshold of the total dirty area, we'll repaint each rect separately.
285     // Otherwise, repaint the entire bounding box of the dirty region.
286     IntRect dirtyBounds = m_dirtyRegion.bounds();
287
288     Vector<IntRect> dirtyRects = m_dirtyRegion.rects();
289     if (dirtyRects.size() > PlatformCALayer::webLayerMaxRectsToPaint || m_dirtyRegion.totalArea() > PlatformCALayer::webLayerWastedSpaceThreshold * dirtyBounds.width() * dirtyBounds.height()) {
290         dirtyRects.clear();
291         dirtyRects.append(dirtyBounds);
292     }
293
294     // FIXME: find a consistent way to scale and snap dirty and CG clip rects.
295     for (const auto& rect : dirtyRects) {
296         FloatRect scaledRect(rect);
297         scaledRect.scale(m_scale);
298         scaledRect = enclosingIntRect(scaledRect);
299         scaledRect.scale(1 / m_scale);
300         m_paintingRects.append(scaledRect);
301     }
302
303     CGRect cgPaintingRects[PlatformCALayer::webLayerMaxRectsToPaint];
304     for (size_t i = 0, dirtyRectCount = m_paintingRects.size(); i < dirtyRectCount; ++i) {
305         FloatRect scaledPaintingRect = m_paintingRects[i];
306         scaledPaintingRect.scale(m_scale);
307         cgPaintingRects[i] = scaledPaintingRect;
308     }
309
310     if (backImage) {
311         CGContextSaveGState(cgContext);
312         CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
313
314         CGContextAddRect(cgContext, CGRectInfinite);
315         CGContextAddRects(cgContext, cgPaintingRects, m_paintingRects.size());
316         CGContextEOClip(cgContext);
317
318         CGContextTranslateCTM(cgContext, 0, scaledLayerBounds.height());
319         CGContextScaleCTM(cgContext, 1, -1);
320         CGContextDrawImage(cgContext, scaledLayerBounds, backImage);
321         CGContextRestoreGState(cgContext);
322     }
323
324     CGContextClipToRects(cgContext, cgPaintingRects, m_paintingRects.size());
325
326     context.scale(FloatSize(m_scale, m_scale));
327
328     // FIXME: This should be moved to PlatformCALayerRemote for better layering.
329     switch (m_layer->layerType()) {
330     case PlatformCALayer::LayerTypeSimpleLayer:
331     case PlatformCALayer::LayerTypeTiledBackingTileLayer:
332         m_layer->owner()->platformCALayerPaintContents(m_layer, context, dirtyBounds);
333         break;
334     case PlatformCALayer::LayerTypeWebLayer:
335     case PlatformCALayer::LayerTypeBackdropLayer:
336         PlatformCALayer::drawLayerContents(cgContext, m_layer, m_paintingRects);
337         break;
338     case PlatformCALayer::LayerTypeDarkSystemBackdropLayer:
339     case PlatformCALayer::LayerTypeLightSystemBackdropLayer:
340         // FIXME: These have a more complicated layer hierarchy. We need to paint into
341         // a child layer in order to see the rendered results.
342         PlatformCALayer::drawLayerContents(cgContext, m_layer, m_paintingRects);
343         break;
344     case PlatformCALayer::LayerTypeLayer:
345     case PlatformCALayer::LayerTypeTransformLayer:
346     case PlatformCALayer::LayerTypeWebTiledLayer:
347     case PlatformCALayer::LayerTypeTiledBackingLayer:
348     case PlatformCALayer::LayerTypePageTiledBackingLayer:
349     case PlatformCALayer::LayerTypeRootLayer:
350     case PlatformCALayer::LayerTypeAVPlayerLayer:
351     case PlatformCALayer::LayerTypeWebGLLayer:
352     case PlatformCALayer::LayerTypeShapeLayer:
353     case PlatformCALayer::LayerTypeScrollingLayer:
354     case PlatformCALayer::LayerTypeCustom:
355         ASSERT_NOT_REACHED();
356         break;
357     };
358
359     m_dirtyRegion = Region();
360     m_paintingRects.clear();
361
362     m_frontContextPendingFlush = context.platformContext();
363 }
364
365 void RemoteLayerBackingStore::enumerateRectsBeingDrawn(CGContextRef context, void (^block)(CGRect))
366 {
367     CGAffineTransform inverseTransform = CGAffineTransformInvert(CGContextGetCTM(context));
368
369     // We don't want to un-apply the flipping or contentsScale,
370     // because they're not applied to repaint rects.
371     inverseTransform = CGAffineTransformScale(inverseTransform, m_scale, -m_scale);
372     inverseTransform = CGAffineTransformTranslate(inverseTransform, 0, -m_size.height());
373
374     for (const auto& rect : m_paintingRects) {
375         CGRect rectToDraw = CGRectApplyAffineTransform(rect, inverseTransform);
376         block(rectToDraw);
377     }
378 }
379
380 void RemoteLayerBackingStore::applyBackingStoreToLayer(CALayer *layer)
381 {
382     layer.contentsOpaque = m_isOpaque;
383
384 #if USE(IOSURFACE)
385     if (acceleratesDrawing()) {
386         layer.contents = (id)m_frontBuffer.surface->surface();
387         return;
388     }
389 #endif
390
391     ASSERT(!acceleratesDrawing());
392     layer.contents = (id)m_frontBuffer.bitmap->makeCGImageCopy().get();
393 }
394
395 RetainPtr<CGContextRef> RemoteLayerBackingStore::takeFrontContextPendingFlush()
396 {
397     return WTF::move(m_frontContextPendingFlush);
398 }
399
400 #if USE(IOSURFACE)
401 bool RemoteLayerBackingStore::setBufferVolatility(BufferType type, bool isVolatile)
402 {
403     switch(type) {
404     case BufferType::Front:
405         if (m_frontBuffer.surface && m_frontBuffer.isVolatile != isVolatile) {
406             if (isVolatile)
407                 m_frontBuffer.surface->releaseGraphicsContext();
408             if (!isVolatile || !m_frontBuffer.surface->isInUse()) {
409                 IOSurface::SurfaceState previousState = m_frontBuffer.surface->setIsVolatile(isVolatile);
410                 m_frontBuffer.isVolatile = isVolatile;
411
412                 // Becoming non-volatile and the front buffer was purged, so we need to repaint.
413                 if (!isVolatile && (previousState == IOSurface::SurfaceState::Empty))
414                     setNeedsDisplay();
415             } else
416                 return false;
417         }
418         break;
419     case BufferType::Back:
420         if (m_backBuffer.surface && m_backBuffer.isVolatile != isVolatile) {
421             if (isVolatile)
422                 m_backBuffer.surface->releaseGraphicsContext();
423             if (!isVolatile || !m_backBuffer.surface->isInUse()) {
424                 m_backBuffer.surface->setIsVolatile(isVolatile);
425                 m_backBuffer.isVolatile = isVolatile;
426             } else
427                 return false;
428         }
429         break;
430     case BufferType::SecondaryBack:
431         if (m_secondaryBackBuffer.surface && m_secondaryBackBuffer.isVolatile != isVolatile) {
432             if (isVolatile)
433                 m_secondaryBackBuffer.surface->releaseGraphicsContext();
434             if (!isVolatile || !m_secondaryBackBuffer.surface->isInUse()) {
435                 m_secondaryBackBuffer.surface->setIsVolatile(isVolatile);
436                 m_secondaryBackBuffer.isVolatile = isVolatile;
437             } else
438                 return false;
439         }
440         break;
441     }
442     return true;
443 }
444 #else
445 bool RemoteLayerBackingStore::setBufferVolatility(BufferType, bool)
446 {
447     return true;
448 }
449 #endif
450
451 void RemoteLayerBackingStore::Buffer::discard()
452 {
453 #if USE(IOSURFACE)
454     if (surface)
455         IOSurfacePool::sharedPool().addSurface(WTF::move(surface));
456     isVolatile = false;
457 #endif
458     bitmap = nullptr;
459 }
460
461 } // namespace WebKit