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