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