[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / IOSurface.mm
1 /*
2  * Copyright (C) 2014-2018 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 HAVE(IOSURFACE)
30
31 #import "GraphicsContext3D.h"
32 #import "GraphicsContextCG.h"
33 #import "HostWindow.h"
34 #import "IOSurfacePool.h"
35 #import "ImageBuffer.h"
36 #import "ImageBufferDataCG.h"
37 #import "Logging.h"
38 #import "PlatformScreen.h"
39 #import <pal/spi/cg/CoreGraphicsSPI.h>
40 #import <pal/spi/cocoa/IOSurfaceSPI.h>
41 #import <wtf/Assertions.h>
42 #import <wtf/MachSendRight.h>
43 #import <wtf/MathExtras.h>
44 #import <wtf/text/TextStream.h>
45
46 namespace WebCore {
47
48 inline std::unique_ptr<IOSurface> IOSurface::surfaceFromPool(IntSize size, IntSize contextSize, CGColorSpaceRef colorSpace, Format pixelFormat)
49 {
50     auto cachedSurface = IOSurfacePool::sharedPool().takeSurface(size, colorSpace, pixelFormat);
51     if (!cachedSurface)
52         return nullptr;
53
54     cachedSurface->setContextSize(contextSize);
55     return cachedSurface;
56 }
57
58 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, CGColorSpaceRef colorSpace, Format pixelFormat)
59 {
60     return IOSurface::create(size, size, colorSpace, pixelFormat);
61 }
62
63 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, IntSize contextSize, CGColorSpaceRef colorSpace, Format pixelFormat)
64 {
65     if (auto cachedSurface = surfaceFromPool(size, contextSize, colorSpace, pixelFormat)) {
66         LOG_WITH_STREAM(IOSurface, stream << "IOSurface::create took from pool: " << *cachedSurface);
67         return cachedSurface;
68     }
69     bool success = false;
70     auto surface = std::unique_ptr<IOSurface>(new IOSurface(size, contextSize, colorSpace, pixelFormat, success));
71     if (!success) {
72         LOG(IOSurface, "IOSurface::create failed to create %dx%d surface", size.width(), size.height());
73         return nullptr;
74     }
75
76     LOG_WITH_STREAM(IOSurface, stream << "IOSurface::create created " << *surface);
77     return surface;
78 }
79
80 std::unique_ptr<IOSurface> IOSurface::createFromSendRight(const MachSendRight&& sendRight, CGColorSpaceRef colorSpace)
81 {
82     auto surface = adoptCF(IOSurfaceLookupFromMachPort(sendRight.sendRight()));
83     return IOSurface::createFromSurface(surface.get(), colorSpace);
84 }
85
86 std::unique_ptr<IOSurface> IOSurface::createFromSurface(IOSurfaceRef surface, CGColorSpaceRef colorSpace)
87 {
88     return std::unique_ptr<IOSurface>(new IOSurface(surface, colorSpace));
89 }
90
91 std::unique_ptr<IOSurface> IOSurface::createFromImage(CGImageRef image)
92 {
93     if (!image)
94         return nullptr;
95
96     size_t width = CGImageGetWidth(image);
97     size_t height = CGImageGetHeight(image);
98
99     auto surface = IOSurface::create(IntSize(width, height), CGImageGetColorSpace(image));
100     if (!surface)
101         return nullptr;
102     auto surfaceContext = surface->ensurePlatformContext();
103     CGContextDrawImage(surfaceContext, CGRectMake(0, 0, width, height), image);
104     CGContextFlush(surfaceContext);
105     return surface;
106 }
107
108 void IOSurface::moveToPool(std::unique_ptr<IOSurface>&& surface)
109 {
110     IOSurfacePool::sharedPool().addSurface(WTFMove(surface));
111 }
112
113 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
114 std::unique_ptr<IOSurface> IOSurface::createFromImageBuffer(std::unique_ptr<ImageBuffer> imageBuffer)
115 {
116     return WTFMove(imageBuffer->m_data.surface);
117 }
118 #endif
119
120 static NSDictionary *optionsForBiplanarSurface(IntSize size, unsigned pixelFormat, size_t firstPlaneBytesPerPixel, size_t secondPlaneBytesPerPixel)
121 {
122     int width = size.width();
123     int height = size.height();
124
125     size_t firstPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * firstPlaneBytesPerPixel);
126     size_t firstPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * firstPlaneBytesPerRow);
127
128     size_t secondPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * secondPlaneBytesPerPixel);
129     size_t secondPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * secondPlaneBytesPerRow);
130     
131     size_t totalBytes = firstPlaneTotalBytes + secondPlaneTotalBytes;
132     ASSERT(totalBytes);
133
134     NSArray *planeInfo = @[
135         @{
136             (id)kIOSurfacePlaneWidth: @(width),
137             (id)kIOSurfacePlaneHeight: @(height),
138             (id)kIOSurfacePlaneBytesPerRow: @(firstPlaneBytesPerRow),
139             (id)kIOSurfacePlaneOffset: @(0),
140             (id)kIOSurfacePlaneSize: @(firstPlaneTotalBytes)
141         },
142         @{
143             (id)kIOSurfacePlaneWidth: @(width),
144             (id)kIOSurfacePlaneHeight: @(height),
145             (id)kIOSurfacePlaneBytesPerRow: @(secondPlaneBytesPerRow),
146             (id)kIOSurfacePlaneOffset: @(firstPlaneTotalBytes),
147             (id)kIOSurfacePlaneSize: @(secondPlaneTotalBytes)
148         }
149     ];
150
151     return @{
152         (id)kIOSurfaceWidth: @(width),
153         (id)kIOSurfaceHeight: @(height),
154         (id)kIOSurfacePixelFormat: @(pixelFormat),
155         (id)kIOSurfaceAllocSize: @(totalBytes),
156 #if PLATFORM(IOS_FAMILY)
157         (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
158 #endif
159         (id)kIOSurfacePlaneInfo: planeInfo,
160     };
161 }
162
163 static NSDictionary *optionsFor32BitSurface(IntSize size, unsigned pixelFormat)
164 {
165     int width = size.width();
166     int height = size.height();
167
168     unsigned bytesPerElement = 4;
169     unsigned bytesPerPixel = 4;
170
171     size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerPixel);
172     ASSERT(bytesPerRow);
173
174     size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
175     ASSERT(totalBytes);
176
177     return @{
178         (id)kIOSurfaceWidth: @(width),
179         (id)kIOSurfaceHeight: @(height),
180         (id)kIOSurfacePixelFormat: @(pixelFormat),
181         (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
182         (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
183         (id)kIOSurfaceAllocSize: @(totalBytes),
184 #if PLATFORM(IOS_FAMILY)
185         (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
186 #endif
187         (id)kIOSurfaceElementHeight: @(1)
188     };
189
190 }
191
192 IOSurface::IOSurface(IntSize size, IntSize contextSize, CGColorSpaceRef colorSpace, Format format, bool& success)
193     : m_colorSpace(colorSpace)
194     , m_size(size)
195     , m_contextSize(contextSize)
196 {
197     ASSERT(!success);
198     ASSERT(contextSize.width() <= size.width());
199     ASSERT(contextSize.height() <= size.height());
200
201     NSDictionary *options;
202
203     switch (format) {
204     case Format::RGBA:
205         options = optionsFor32BitSurface(size, 'BGRA');
206         break;
207 #if HAVE(IOSURFACE_RGB10)
208     case Format::RGB10:
209         options = optionsFor32BitSurface(size, 'w30r');
210         break;
211     case Format::RGB10A8:
212         options = optionsForBiplanarSurface(size, 'b3a8', 4, 1);
213         break;
214 #endif
215     case Format::YUV422:
216         options = optionsForBiplanarSurface(size, '422f', 1, 1);
217         break;
218     }
219     m_surface = adoptCF(IOSurfaceCreate((CFDictionaryRef)options));
220     success = !!m_surface;
221     if (success)
222         m_totalBytes = IOSurfaceGetAllocSize(m_surface.get());
223     else
224         RELEASE_LOG_ERROR(Layers, "Surface creation failed for size: (%d %d) and format: (%d)", size.width(), size.height(), format);
225 }
226
227 IOSurface::IOSurface(IOSurfaceRef surface, CGColorSpaceRef colorSpace)
228     : m_colorSpace(colorSpace)
229     , m_surface(surface)
230 {
231     m_size = IntSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
232     m_totalBytes = IOSurfaceGetAllocSize(surface);
233 }
234
235 IntSize IOSurface::maximumSize()
236 {
237     IntSize maxSize(clampToInteger(IOSurfaceGetPropertyMaximum(kIOSurfaceWidth)), clampToInteger(IOSurfaceGetPropertyMaximum(kIOSurfaceHeight)));
238
239     // Protect against maxSize being { 0, 0 }.
240     const int iOSMaxSurfaceDimensionLowerBound = 1024;
241
242 #if PLATFORM(IOS_FAMILY)
243     // Match limits imposed by Core Animation. FIXME: should have API for this <rdar://problem/25454148>
244     const int iOSMaxSurfaceDimension = 8 * 1024;
245 #else
246     // IOSurface::maximumSize() can return { INT_MAX, INT_MAX } when hardware acceleration is unavailable.
247     const int iOSMaxSurfaceDimension = 32 * 1024;
248 #endif
249
250     return maxSize.constrainedBetween({ iOSMaxSurfaceDimensionLowerBound, iOSMaxSurfaceDimensionLowerBound }, { iOSMaxSurfaceDimension, iOSMaxSurfaceDimension });
251 }
252
253 MachSendRight IOSurface::createSendRight() const
254 {
255     return MachSendRight::adopt(IOSurfaceCreateMachPort(m_surface.get()));
256 }
257
258 RetainPtr<CGImageRef> IOSurface::createImage()
259 {
260     return adoptCF(CGIOSurfaceContextCreateImage(ensurePlatformContext()));
261 }
262
263 RetainPtr<CGImageRef> IOSurface::sinkIntoImage(std::unique_ptr<IOSurface> surface)
264 {
265     return adoptCF(CGIOSurfaceContextCreateImageReference(surface->ensurePlatformContext()));
266 }
267
268 void IOSurface::setContextSize(IntSize contextSize)
269 {
270     if (contextSize == m_contextSize)
271         return;
272
273     // Release the graphics context and update the context size. Next time the graphics context is
274     // accessed, we will construct it again with the right size.
275     releaseGraphicsContext();
276     m_contextSize = contextSize;
277 }
278
279 CGContextRef IOSurface::ensurePlatformContext(const HostWindow* hostWindow)
280 {
281     if (m_cgContext)
282         return m_cgContext.get();
283
284     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
285
286     size_t bitsPerComponent = 8;
287     size_t bitsPerPixel = 32;
288     
289     switch (format()) {
290     case Format::RGBA:
291         break;
292 #if HAVE(IOSURFACE_RGB10)
293     case Format::RGB10:
294     case Format::RGB10A8:
295         // A half-float format will be used if CG needs to read back the IOSurface contents,
296         // but for an IOSurface-to-IOSurface copy, there should be no conversion.
297         bitsPerComponent = 16;
298         bitsPerPixel = 64;
299         bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Host | kCGBitmapFloatComponents;
300         break;
301 #endif
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, m_colorSpace.get(), bitmapInfo));
308
309 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
310     if (auto displayMask = primaryOpenGLDisplayMask()) {
311         if (hostWindow && hostWindow->displayID())
312             displayMask = displayMaskForDisplay(hostWindow->displayID());
313 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
314         CGIOSurfaceContextSetDisplayMask(m_cgContext.get(), displayMask);
315 ALLOW_DEPRECATED_DECLARATIONS_END
316     }
317 #else
318     UNUSED_PARAM(hostWindow);
319 #endif
320
321     return m_cgContext.get();
322 }
323
324 GraphicsContext& IOSurface::ensureGraphicsContext()
325 {
326     if (m_graphicsContext)
327         return *m_graphicsContext;
328
329     m_graphicsContext = makeUnique<GraphicsContext>(ensurePlatformContext());
330     m_graphicsContext->setIsAcceleratedContext(true);
331
332     return *m_graphicsContext;
333 }
334
335 IOSurface::SurfaceState IOSurface::state() const
336 {
337     uint32_t previousState = 0;
338     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
339     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
340     return previousState == kIOSurfacePurgeableEmpty ? IOSurface::SurfaceState::Empty : IOSurface::SurfaceState::Valid;
341 }
342
343 bool IOSurface::isVolatile() const
344 {
345     uint32_t previousState = 0;
346     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
347     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
348     return previousState != kIOSurfacePurgeableNonVolatile;
349 }
350
351 IOSurface::SurfaceState IOSurface::setIsVolatile(bool isVolatile)
352 {
353     uint32_t previousState = 0;
354     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), isVolatile ? kIOSurfacePurgeableVolatile : kIOSurfacePurgeableNonVolatile, &previousState);
355     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
356
357     if (previousState == kIOSurfacePurgeableEmpty)
358         return IOSurface::SurfaceState::Empty;
359
360     return IOSurface::SurfaceState::Valid;
361 }
362
363 IOSurface::Format IOSurface::format() const
364 {
365     unsigned pixelFormat = IOSurfaceGetPixelFormat(m_surface.get());
366     if (pixelFormat == 'BGRA')
367         return Format::RGBA;
368
369 #if HAVE(IOSURFACE_RGB10)
370     if (pixelFormat == 'w30r')
371         return Format::RGB10;
372
373     if (pixelFormat == 'b3a8')
374         return Format::RGB10A8;
375 #endif
376
377     if (pixelFormat == '422f')
378         return Format::YUV422;
379
380     ASSERT_NOT_REACHED();
381     return Format::RGBA;
382 }
383
384 IOSurfaceID IOSurface::surfaceID() const
385 {
386 #if PLATFORM(IOS_FAMILY) && (!USE(APPLE_INTERNAL_SDK) || __IPHONE_OS_VERSION_MIN_REQUIRED < 110000)
387     return 0;
388 #else
389     return IOSurfaceGetID(m_surface.get());
390 #endif
391 }
392
393 size_t IOSurface::bytesPerRow() const
394 {
395     return IOSurfaceGetBytesPerRow(m_surface.get());
396 }
397
398 bool IOSurface::isInUse() const
399 {
400     return IOSurfaceIsInUse(m_surface.get());
401 }
402
403 void IOSurface::releaseGraphicsContext()
404 {
405     m_graphicsContext = nullptr;
406     m_cgContext = nullptr;
407 }
408
409 #if HAVE(IOSURFACE_ACCELERATOR)
410
411 bool IOSurface::allowConversionFromFormatToFormat(Format sourceFormat, Format destFormat)
412 {
413 #if HAVE(IOSURFACE_RGB10)
414     if ((sourceFormat == Format::RGB10 || sourceFormat == Format::RGB10A8) && destFormat == Format::YUV422)
415         return false;
416 #endif
417
418     return true;
419 }
420
421 void IOSurface::convertToFormat(std::unique_ptr<IOSurface>&& inSurface, Format format, WTF::Function<void(std::unique_ptr<IOSurface>)>&& callback)
422 {
423     static IOSurfaceAcceleratorRef accelerator;
424     if (!accelerator) {
425         IOSurfaceAcceleratorCreate(nullptr, nullptr, &accelerator);
426
427         auto runLoopSource = IOSurfaceAcceleratorGetRunLoopSource(accelerator);
428         CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
429     }
430
431     if (inSurface->format() == format) {
432         callback(WTFMove(inSurface));
433         return;
434     }
435
436     auto destinationSurface = IOSurface::create(inSurface->size(), inSurface->colorSpace(), format);
437     if (!destinationSurface) {
438         callback(nullptr);
439         return;
440     }
441
442     IOSurfaceRef destinationIOSurfaceRef = destinationSurface->surface();
443     IOSurfaceAcceleratorCompletion completion;
444     completion.completionRefCon = new WTF::Function<void(std::unique_ptr<IOSurface>)> (WTFMove(callback));
445     completion.completionRefCon2 = destinationSurface.release();
446     completion.completionCallback = [](void *completionRefCon, IOReturn, void * completionRefCon2) {
447         auto* callback = static_cast<WTF::Function<void(std::unique_ptr<IOSurface>)>*>(completionRefCon);
448         auto destinationSurface = std::unique_ptr<IOSurface>(static_cast<IOSurface*>(completionRefCon2));
449         
450         (*callback)(WTFMove(destinationSurface));
451         delete callback;
452     };
453
454     NSDictionary *options = @{ (id)kIOSurfaceAcceleratorUnwireSurfaceKey : @YES };
455
456     IOReturn ret = IOSurfaceAcceleratorTransformSurface(accelerator, inSurface->surface(), destinationIOSurfaceRef, (CFDictionaryRef)options, nullptr, &completion, nullptr, nullptr);
457     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
458 }
459
460 #endif // HAVE(IOSURFACE_ACCELERATOR)
461
462 void IOSurface::migrateColorSpaceToProperties()
463 {
464     auto colorSpaceProperties = adoptCF(CGColorSpaceCopyPropertyList(m_colorSpace.get()));
465     IOSurfaceSetValue(m_surface.get(), kIOSurfaceColorSpace, colorSpaceProperties.get());
466 }
467
468 static TextStream& operator<<(TextStream& ts, IOSurface::Format format)
469 {
470     switch (format) {
471     case IOSurface::Format::RGBA:
472         ts << "RGBA";
473         break;
474     case IOSurface::Format::YUV422:
475         ts << "YUV422";
476         break;
477 #if HAVE(IOSURFACE_RGB10)
478     case IOSurface::Format::RGB10:
479         ts << "RGB10";
480         break;
481     case IOSurface::Format::RGB10A8:
482         ts << "RGB10A8";
483         break;
484 #endif
485     }
486     return ts;
487 }
488
489 static TextStream& operator<<(TextStream& ts, IOSurface::SurfaceState state)
490 {
491     switch (state) {
492     case IOSurface::SurfaceState::Valid:
493         ts << "valid";
494         break;
495     case IOSurface::SurfaceState::Empty:
496         ts << "empty";
497         break;
498     }
499     return ts;
500 }
501
502 TextStream& operator<<(TextStream& ts, const IOSurface& surface)
503 {
504     return ts << "IOSurface " << surface.surfaceID() << " size " << surface.size() << " format " << surface.format() << " state " << surface.state();
505 }
506
507 } // namespace WebCore
508
509 #endif // HAVE(IOSURFACE)