[iOS WK2] Avoid creating tiles that are too large for rendering
[WebKit.git] / Source / WebCore / platform / graphics / cocoa / IOSurface.mm
1 /*
2  * Copyright (C) 2014 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 "IOSurface.h"
28
29 #if USE(IOSURFACE)
30
31 #import "CoreGraphicsSPI.h"
32 #import "GraphicsContextCG.h"
33 #import "IOSurfacePool.h"
34 #import "IOSurfaceSPI.h"
35 #import "ImageBuffer.h"
36 #import "ImageBufferDataCG.h"
37 #import "MachSendRight.h"
38 #import <wtf/Assertions.h>
39
40 #if PLATFORM(IOS)
41 // Move this into the SPI header once it's possible to put inside the APPLE_INTERNAL_SDK block.
42 NSString * const WebIOSurfaceAcceleratorUnwireSurfaceKey = @"UnwireSurface";
43 #endif
44
45 using namespace WebCore;
46
47 inline std::unique_ptr<IOSurface> IOSurface::surfaceFromPool(IntSize size, IntSize contextSize, ColorSpace colorSpace, Format pixelFormat)
48 {
49     auto cachedSurface = IOSurfacePool::sharedPool().takeSurface(size, colorSpace, pixelFormat);
50     if (!cachedSurface)
51         return nullptr;
52
53     cachedSurface->setContextSize(contextSize);
54     return cachedSurface;
55 }
56
57 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, ColorSpace colorSpace, Format pixelFormat)
58 {
59     if (auto cachedSurface = surfaceFromPool(size, size, colorSpace, pixelFormat))
60         return cachedSurface;
61
62     return std::unique_ptr<IOSurface>(new IOSurface(size, colorSpace, pixelFormat));
63 }
64
65 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, IntSize contextSize, ColorSpace colorSpace, Format pixelFormat)
66 {
67     if (auto cachedSurface = surfaceFromPool(size, contextSize, colorSpace, pixelFormat))
68         return cachedSurface;
69     return std::unique_ptr<IOSurface>(new IOSurface(size, contextSize, colorSpace, pixelFormat));
70 }
71
72 std::unique_ptr<IOSurface> IOSurface::createFromSendRight(const MachSendRight& sendRight, ColorSpace colorSpace)
73 {
74     auto surface = adoptCF(IOSurfaceLookupFromMachPort(sendRight.sendRight()));
75     return IOSurface::createFromSurface(surface.get(), colorSpace);
76 }
77
78 std::unique_ptr<IOSurface> IOSurface::createFromSurface(IOSurfaceRef surface, ColorSpace colorSpace)
79 {
80     return std::unique_ptr<IOSurface>(new IOSurface(surface, colorSpace));
81 }
82
83 std::unique_ptr<IOSurface> IOSurface::createFromImage(CGImageRef image)
84 {
85     if (!image)
86         return nullptr;
87
88     size_t width = CGImageGetWidth(image);
89     size_t height = CGImageGetHeight(image);
90
91     auto surface = IOSurface::create(IntSize(width, height), ColorSpaceSRGB);
92     auto surfaceContext = surface->ensurePlatformContext();
93     CGContextDrawImage(surfaceContext, CGRectMake(0, 0, width, height), image);
94     CGContextFlush(surfaceContext);
95
96     return surface;
97 }
98
99 void IOSurface::moveToPool(std::unique_ptr<IOSurface>&& surface)
100 {
101     IOSurfacePool::sharedPool().addSurface(WTFMove(surface));
102 }
103
104 std::unique_ptr<IOSurface> IOSurface::createFromImageBuffer(std::unique_ptr<ImageBuffer> imageBuffer)
105 {
106     return WTFMove(imageBuffer->m_data.surface);
107 }
108
109 IOSurface::IOSurface(IntSize size, ColorSpace colorSpace, Format format)
110     : m_colorSpace(colorSpace)
111     , m_size(size)
112     , m_contextSize(size)
113 {
114     unsigned pixelFormat;
115     unsigned bytesPerPixel;
116     unsigned bytesPerElement;
117
118     int width = size.width();
119     int height = size.height();
120
121     NSDictionary *options;
122     
123     if (format == Format::RGB10A8) {
124         pixelFormat = 'b3a8';
125         
126         // RGB plane (10-10-10)
127         bytesPerPixel = 4;
128         bytesPerElement = 4;
129
130         size_t rgbPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
131         size_t rgbPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * rgbPlaneBytesPerRow);
132
133         // Alpha plane (8)
134         bytesPerElement = 1;
135         size_t alphaPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
136         size_t alphaPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * alphaPlaneBytesPerRow);
137         
138         m_totalBytes = rgbPlaneTotalBytes + alphaPlaneTotalBytes;
139
140         NSArray *planeInfo = @[
141             @{
142                 (id)kIOSurfacePlaneWidth: @(width),
143                 (id)kIOSurfacePlaneHeight: @(height),
144                 (id)kIOSurfacePlaneBytesPerRow: @(rgbPlaneBytesPerRow),
145                 (id)kIOSurfacePlaneOffset: @(0),
146                 (id)kIOSurfacePlaneSize: @(rgbPlaneTotalBytes)
147             },
148             @{
149                 (id)kIOSurfacePlaneWidth: @(width),
150                 (id)kIOSurfacePlaneHeight: @(height),
151                 (id)kIOSurfacePlaneBytesPerRow: @(alphaPlaneBytesPerRow),
152                 (id)kIOSurfacePlaneOffset: @(rgbPlaneTotalBytes),
153                 (id)kIOSurfacePlaneSize: @(alphaPlaneTotalBytes)
154             }
155         ];
156
157         options = @{
158             (id)kIOSurfaceWidth: @(width),
159             (id)kIOSurfaceHeight: @(height),
160             (id)kIOSurfacePixelFormat: @(pixelFormat),
161             (id)kIOSurfaceAllocSize: @(m_totalBytes),
162 #if PLATFORM(IOS)
163             (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
164 #endif
165             (id)kIOSurfacePlaneInfo: planeInfo,
166         };
167     } else {
168         unsigned elementWidth;
169
170         switch (format) {
171         case Format::RGBA:
172             pixelFormat = 'BGRA';
173             bytesPerPixel = 4;
174             bytesPerElement = 4;
175             elementWidth = 1;
176             break;
177         case Format::YUV422:
178             pixelFormat = 'yuvf';
179             bytesPerPixel = 2;
180             bytesPerElement = 4;
181             elementWidth = 2;
182             break;
183         case Format::RGB10:
184             pixelFormat = 'w30r';
185             bytesPerPixel = 4;
186             bytesPerElement = 4;
187             elementWidth = 1;
188             break;
189         case Format::RGB10A8:
190             ASSERT_NOT_REACHED();
191             pixelFormat = 'b3a8';
192             bytesPerPixel = 1;
193             bytesPerElement = 1;
194             elementWidth = 1;
195             break;
196         }
197
198         size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerPixel);
199         ASSERT(bytesPerRow);
200
201         m_totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
202         ASSERT(m_totalBytes);
203
204         options = @{
205             (id)kIOSurfaceWidth: @(width),
206             (id)kIOSurfaceHeight: @(height),
207             (id)kIOSurfacePixelFormat: @(pixelFormat),
208             (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
209             (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
210             (id)kIOSurfaceAllocSize: @(m_totalBytes),
211 #if PLATFORM(IOS)
212             (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
213 #endif
214             (id)kIOSurfaceElementWidth: @(elementWidth),
215             (id)kIOSurfaceElementHeight: @(1)
216         };
217     }
218     
219     m_surface = adoptCF(IOSurfaceCreate((CFDictionaryRef)options));
220     if (!m_surface)
221         NSLog(@"Surface creation failed for options %@", options);
222 }
223
224 IOSurface::IOSurface(IntSize size, IntSize contextSize, ColorSpace colorSpace, Format pixelFormat)
225     : IOSurface(size, colorSpace, pixelFormat)
226 {
227     ASSERT(contextSize.width() <= size.width());
228     ASSERT(contextSize.height() <= size.height());
229     m_contextSize = contextSize;
230 }
231
232 IOSurface::IOSurface(IOSurfaceRef surface, ColorSpace colorSpace)
233     : m_colorSpace(colorSpace)
234     , m_surface(surface)
235 {
236     m_size = IntSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
237     m_totalBytes = IOSurfaceGetAllocSize(surface);
238 }
239
240 IntSize IOSurface::maximumSize()
241 {
242     IntSize maxSize(IOSurfaceGetPropertyMaximum(kIOSurfaceWidth), IOSurfaceGetPropertyMaximum(kIOSurfaceHeight));
243 #if PLATFORM(IOS)
244     // Match limits imposed by CA. FIXME: should have API for this <rdar://problem/25454148>
245     const int iOSMaxSurfaceDimension = 8 * 1024;
246     maxSize = maxSize.shrunkTo({ iOSMaxSurfaceDimension, iOSMaxSurfaceDimension });
247 #endif
248     return maxSize;
249 }
250
251 MachSendRight IOSurface::createSendRight() const
252 {
253     return MachSendRight::adopt(IOSurfaceCreateMachPort(m_surface.get()));
254 }
255
256 RetainPtr<CGImageRef> IOSurface::createImage()
257 {
258     return adoptCF(CGIOSurfaceContextCreateImage(ensurePlatformContext()));
259 }
260
261 RetainPtr<CGImageRef> IOSurface::sinkIntoImage(std::unique_ptr<IOSurface> surface)
262 {
263 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
264     return adoptCF(CGIOSurfaceContextCreateImageReference(surface->ensurePlatformContext()));
265 #else
266     return surface->createImage();
267 #endif
268 }
269
270 void IOSurface::setContextSize(IntSize contextSize)
271 {
272     if (contextSize == m_contextSize)
273         return;
274
275     // Release the graphics context and update the context size. Next time the graphics context is
276     // accessed, we will construct it again with the right size.
277     releaseGraphicsContext();
278     m_contextSize = contextSize;
279 }
280
281 CGContextRef IOSurface::ensurePlatformContext()
282 {
283     if (m_cgContext)
284         return m_cgContext.get();
285
286     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
287
288     size_t bitsPerComponent = 8;
289     size_t bitsPerPixel = 32;
290     
291     switch (format()) {
292     case Format::RGBA:
293         break;
294     case Format::RGB10:
295     case Format::RGB10A8:
296         // A half-float format will be used if CG needs to read back the IOSurface contents,
297         // but for an IOSurface-to-IOSurface copy, there should be no conversion.
298         bitsPerComponent = 16;
299         bitsPerPixel = 64;
300         bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Host | kCGBitmapFloatComponents;
301         break;
302     case Format::YUV422:
303         ASSERT_NOT_REACHED();
304         break;
305     }
306     
307     m_cgContext = adoptCF(CGIOSurfaceContextCreate(m_surface.get(), m_contextSize.width(), m_contextSize.height(), bitsPerComponent, bitsPerPixel, cachedCGColorSpace(m_colorSpace), bitmapInfo));
308
309     return m_cgContext.get();
310 }
311
312 GraphicsContext& IOSurface::ensureGraphicsContext()
313 {
314     if (m_graphicsContext)
315         return *m_graphicsContext;
316
317     m_graphicsContext = std::make_unique<GraphicsContext>(ensurePlatformContext());
318     m_graphicsContext->setIsAcceleratedContext(true);
319
320     return *m_graphicsContext;
321 }
322
323 IOSurface::SurfaceState IOSurface::state() const
324 {
325     uint32_t previousState = 0;
326     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
327     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
328     return previousState == kIOSurfacePurgeableEmpty ? IOSurface::SurfaceState::Empty : IOSurface::SurfaceState::Valid;
329 }
330
331 bool IOSurface::isVolatile() const
332 {
333     uint32_t previousState = 0;
334     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
335     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
336     return previousState != kIOSurfacePurgeableNonVolatile;
337 }
338
339 IOSurface::SurfaceState IOSurface::setIsVolatile(bool isVolatile)
340 {
341     uint32_t previousState = 0;
342     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), isVolatile ? kIOSurfacePurgeableVolatile : kIOSurfacePurgeableNonVolatile, &previousState);
343     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
344
345     if (previousState == kIOSurfacePurgeableEmpty)
346         return IOSurface::SurfaceState::Empty;
347
348     return IOSurface::SurfaceState::Valid;
349 }
350
351 IOSurface::Format IOSurface::format() const
352 {
353     unsigned pixelFormat = IOSurfaceGetPixelFormat(m_surface.get());
354     if (pixelFormat == 'BGRA')
355         return Format::RGBA;
356
357     if (pixelFormat == 'w30r')
358         return Format::RGB10;
359
360     if (pixelFormat == 'b3a8')
361         return Format::RGB10A8;
362
363     if (pixelFormat == 'yuvf')
364         return Format::YUV422;
365
366     ASSERT_NOT_REACHED();
367     return Format::RGBA;
368 }
369
370 bool IOSurface::isInUse() const
371 {
372     return IOSurfaceIsInUse(m_surface.get());
373 }
374
375 void IOSurface::releaseGraphicsContext()
376 {
377     m_graphicsContext = nullptr;
378     m_cgContext = nullptr;
379 }
380
381 #if PLATFORM(IOS)
382 bool IOSurface::allowConversionFromFormatToFormat(Format sourceFormat, Format destFormat)
383 {
384     if ((sourceFormat == Format::RGB10 || sourceFormat == Format::RGB10A8) && destFormat == Format::YUV422)
385         return false;
386
387     return true;
388 }
389
390 void IOSurface::convertToFormat(std::unique_ptr<WebCore::IOSurface>&& inSurface, Format format, std::function<void(std::unique_ptr<WebCore::IOSurface>)> callback)
391 {
392     static IOSurfaceAcceleratorRef accelerator;
393     if (!accelerator) {
394         IOSurfaceAcceleratorCreate(nullptr, nullptr, &accelerator);
395
396         auto runLoopSource = IOSurfaceAcceleratorGetRunLoopSource(accelerator);
397         CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
398     }
399
400     if (inSurface->format() == format) {
401         callback(WTFMove(inSurface));
402         return;
403     }
404
405     auto destinationSurface = IOSurface::create(inSurface->size(), inSurface->colorSpace(), format);
406     IOSurfaceRef destinationIOSurfaceRef = destinationSurface->surface();
407
408     IOSurfaceAcceleratorCompletion completion;
409     completion.completionRefCon = new std::function<void(std::unique_ptr<IOSurface>)> (WTFMove(callback));
410     completion.completionRefCon2 = destinationSurface.release();
411     completion.completionCallback = [](void *completionRefCon, IOReturn, void * completionRefCon2) {
412         auto* callback = static_cast<std::function<void(std::unique_ptr<WebCore::IOSurface>)>*>(completionRefCon);
413         auto destinationSurface = std::unique_ptr<IOSurface>(static_cast<IOSurface*>(completionRefCon2));
414         
415         (*callback)(WTFMove(destinationSurface));
416         delete callback;
417     };
418
419     NSDictionary *options = @{ WebIOSurfaceAcceleratorUnwireSurfaceKey : @YES };
420
421     IOReturn ret = IOSurfaceAcceleratorTransformSurface(accelerator, inSurface->surface(), destinationIOSurfaceRef, (CFDictionaryRef)options, nullptr, &completion, nullptr, nullptr);
422     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
423 }
424 #endif // PLATFORM(IOS)
425
426 #endif // USE(IOSURFACE)