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