9fa6f230939536b39d483c3228e7c7b92b3debb6
[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     return IntSize(IOSurfaceGetPropertyMaximum(kIOSurfaceWidth), IOSurfaceGetPropertyMaximum(kIOSurfaceHeight));
243 }
244
245 MachSendRight IOSurface::createSendRight() const
246 {
247     return MachSendRight::adopt(IOSurfaceCreateMachPort(m_surface.get()));
248 }
249
250 RetainPtr<CGImageRef> IOSurface::createImage()
251 {
252     return adoptCF(CGIOSurfaceContextCreateImage(ensurePlatformContext()));
253 }
254
255 RetainPtr<CGImageRef> IOSurface::sinkIntoImage(std::unique_ptr<IOSurface> surface)
256 {
257 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
258     return adoptCF(CGIOSurfaceContextCreateImageReference(surface->ensurePlatformContext()));
259 #else
260     return surface->createImage();
261 #endif
262 }
263
264 void IOSurface::setContextSize(IntSize contextSize)
265 {
266     if (contextSize == m_contextSize)
267         return;
268
269     // Release the graphics context and update the context size. Next time the graphics context is
270     // accessed, we will construct it again with the right size.
271     releaseGraphicsContext();
272     m_contextSize = contextSize;
273 }
274
275 CGContextRef IOSurface::ensurePlatformContext()
276 {
277     if (m_cgContext)
278         return m_cgContext.get();
279
280     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
281
282     size_t bitsPerComponent = 8;
283     size_t bitsPerPixel = 32;
284     
285     switch (format()) {
286     case Format::RGBA:
287         break;
288     case Format::RGB10:
289     case Format::RGB10A8:
290         // A half-float format will be used if CG needs to read back the IOSurface contents,
291         // but for an IOSurface-to-IOSurface copy, there shoud be no conversion.
292         bitsPerComponent = 16;
293         bitsPerPixel = 64;
294         bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Host | kCGBitmapFloatComponents;
295         break;
296     case Format::YUV422:
297         ASSERT_NOT_REACHED();
298         break;
299     }
300     
301     m_cgContext = adoptCF(CGIOSurfaceContextCreate(m_surface.get(), m_contextSize.width(), m_contextSize.height(), bitsPerComponent, bitsPerPixel, cachedCGColorSpace(m_colorSpace), bitmapInfo));
302
303     return m_cgContext.get();
304 }
305
306 GraphicsContext& IOSurface::ensureGraphicsContext()
307 {
308     if (m_graphicsContext)
309         return *m_graphicsContext;
310
311     m_graphicsContext = std::make_unique<GraphicsContext>(ensurePlatformContext());
312     m_graphicsContext->setIsAcceleratedContext(true);
313
314     return *m_graphicsContext;
315 }
316
317 IOSurface::SurfaceState IOSurface::state() const
318 {
319     uint32_t previousState = 0;
320     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
321     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
322     return previousState == kIOSurfacePurgeableEmpty ? IOSurface::SurfaceState::Empty : IOSurface::SurfaceState::Valid;
323 }
324
325 bool IOSurface::isVolatile() const
326 {
327     uint32_t previousState = 0;
328     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
329     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
330     return previousState != kIOSurfacePurgeableNonVolatile;
331 }
332
333 IOSurface::SurfaceState IOSurface::setIsVolatile(bool isVolatile)
334 {
335     uint32_t previousState = 0;
336     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), isVolatile ? kIOSurfacePurgeableVolatile : kIOSurfacePurgeableNonVolatile, &previousState);
337     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
338
339     if (previousState == kIOSurfacePurgeableEmpty)
340         return IOSurface::SurfaceState::Empty;
341
342     return IOSurface::SurfaceState::Valid;
343 }
344
345 IOSurface::Format IOSurface::format() const
346 {
347     unsigned pixelFormat = IOSurfaceGetPixelFormat(m_surface.get());
348     if (pixelFormat == 'BGRA')
349         return Format::RGBA;
350
351     if (pixelFormat == 'w30r')
352         return Format::RGB10;
353
354     if (pixelFormat == 'b3a8')
355         return Format::RGB10A8;
356
357     if (pixelFormat == 'yuvf')
358         return Format::YUV422;
359
360     ASSERT_NOT_REACHED();
361     return Format::RGBA;
362 }
363
364 bool IOSurface::isInUse() const
365 {
366     return IOSurfaceIsInUse(m_surface.get());
367 }
368
369 void IOSurface::releaseGraphicsContext()
370 {
371     m_graphicsContext = nullptr;
372     m_cgContext = nullptr;
373 }
374
375 #if PLATFORM(IOS)
376 bool IOSurface::allowConversionFromFormatToFormat(Format sourceFormat, Format destFormat)
377 {
378     if ((sourceFormat == Format::RGB10 || sourceFormat == Format::RGB10A8) && destFormat == Format::YUV422)
379         return false;
380
381     return true;
382 }
383
384 void IOSurface::convertToFormat(std::unique_ptr<WebCore::IOSurface>&& inSurface, Format format, std::function<void(std::unique_ptr<WebCore::IOSurface>)> callback)
385 {
386     static IOSurfaceAcceleratorRef accelerator;
387     if (!accelerator) {
388         IOSurfaceAcceleratorCreate(nullptr, nullptr, &accelerator);
389
390         auto runLoopSource = IOSurfaceAcceleratorGetRunLoopSource(accelerator);
391         CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
392     }
393
394     if (inSurface->format() == format) {
395         callback(WTFMove(inSurface));
396         return;
397     }
398
399     auto destinationSurface = IOSurface::create(inSurface->size(), inSurface->colorSpace(), format);
400     IOSurfaceRef destinationIOSurfaceRef = destinationSurface->surface();
401
402     IOSurfaceAcceleratorCompletion completion;
403     completion.completionRefCon = new std::function<void(std::unique_ptr<IOSurface>)> (WTFMove(callback));
404     completion.completionRefCon2 = destinationSurface.release();
405     completion.completionCallback = [](void *completionRefCon, IOReturn, void * completionRefCon2) {
406         auto* callback = static_cast<std::function<void(std::unique_ptr<WebCore::IOSurface>)>*>(completionRefCon);
407         auto destinationSurface = std::unique_ptr<IOSurface>(static_cast<IOSurface*>(completionRefCon2));
408         
409         (*callback)(WTFMove(destinationSurface));
410         delete callback;
411     };
412
413     NSDictionary *options = @{ WebIOSurfaceAcceleratorUnwireSurfaceKey : @YES };
414
415     IOReturn ret = IOSurfaceAcceleratorTransformSurface(accelerator, inSurface->surface(), destinationIOSurfaceRef, (CFDictionaryRef)options, nullptr, &completion, nullptr, nullptr);
416     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
417 }
418 #endif // PLATFORM(IOS)
419
420 #endif // USE(IOSURFACE)