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