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