1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "MacIOSurface.h"
8 #include <OpenGL/gl.h>
9 #include <OpenGL/CGLIOSurface.h>
10 #include <QuartzCore/QuartzCore.h>
11 #include "GLConsts.h"
12 #include "GLContextCGL.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/RefPtr.h"
15
16 using namespace mozilla;
17
MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,double aContentsScaleFactor,bool aHasAlpha,gfx::YUVColorSpace aColorSpace)18 MacIOSurface::MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,
19 double aContentsScaleFactor, bool aHasAlpha,
20 gfx::YUVColorSpace aColorSpace)
21 : mIOSurfaceRef(std::move(aIOSurfaceRef)),
22 mContentsScaleFactor(aContentsScaleFactor),
23 mHasAlpha(aHasAlpha),
24 mColorSpace(aColorSpace) {
25 IncrementUseCount();
26 }
27
~MacIOSurface()28 MacIOSurface::~MacIOSurface() {
29 MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface");
30 DecrementUseCount();
31 }
32
33 /* static */
CreateIOSurface(int aWidth,int aHeight,double aContentsScaleFactor,bool aHasAlpha)34 already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(
35 int aWidth, int aHeight, double aContentsScaleFactor, bool aHasAlpha) {
36 if (aContentsScaleFactor <= 0) return nullptr;
37
38 CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
39 kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks,
40 &kCFTypeDictionaryValueCallBacks);
41 if (!props) return nullptr;
42
43 MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
44 MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
45
46 int32_t bytesPerElem = 4;
47 size_t intScaleFactor = ceil(aContentsScaleFactor);
48 aWidth *= intScaleFactor;
49 aHeight *= intScaleFactor;
50 CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
51 CFNumberRef cfHeight =
52 ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
53 CFNumberRef cfBytesPerElem =
54 ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
55 ::CFDictionaryAddValue(props, kIOSurfaceWidth, cfWidth);
56 ::CFRelease(cfWidth);
57 ::CFDictionaryAddValue(props, kIOSurfaceHeight, cfHeight);
58 ::CFRelease(cfHeight);
59 ::CFDictionaryAddValue(props, kIOSurfaceBytesPerElement, cfBytesPerElem);
60 ::CFRelease(cfBytesPerElem);
61 ::CFDictionaryAddValue(props, kIOSurfaceIsGlobal, kCFBooleanTrue);
62
63 CFTypeRefPtr<IOSurfaceRef> surfaceRef =
64 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(::IOSurfaceCreate(props));
65 ::CFRelease(props);
66
67 if (!surfaceRef) {
68 return nullptr;
69 }
70
71 RefPtr<MacIOSurface> ioSurface =
72 new MacIOSurface(std::move(surfaceRef), aContentsScaleFactor, aHasAlpha);
73
74 return ioSurface.forget();
75 }
76
77 /* static */
LookupSurface(IOSurfaceID aIOSurfaceID,double aContentsScaleFactor,bool aHasAlpha,gfx::YUVColorSpace aColorSpace)78 already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
79 IOSurfaceID aIOSurfaceID, double aContentsScaleFactor, bool aHasAlpha,
80 gfx::YUVColorSpace aColorSpace) {
81 if (aContentsScaleFactor <= 0) return nullptr;
82
83 CFTypeRefPtr<IOSurfaceRef> surfaceRef =
84 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
85 ::IOSurfaceLookup(aIOSurfaceID));
86 if (!surfaceRef) return nullptr;
87
88 RefPtr<MacIOSurface> ioSurface = new MacIOSurface(
89 std::move(surfaceRef), aContentsScaleFactor, aHasAlpha, aColorSpace);
90
91 return ioSurface.forget();
92 }
93
GetIOSurfaceID() const94 IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
95 return ::IOSurfaceGetID(mIOSurfaceRef.get());
96 }
97
GetBaseAddress() const98 void* MacIOSurface::GetBaseAddress() const {
99 return ::IOSurfaceGetBaseAddress(mIOSurfaceRef.get());
100 }
101
GetBaseAddressOfPlane(size_t aPlaneIndex) const102 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
103 return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef.get(), aPlaneIndex);
104 }
105
GetWidth(size_t plane) const106 size_t MacIOSurface::GetWidth(size_t plane) const {
107 size_t intScaleFactor = ceil(mContentsScaleFactor);
108 return GetDevicePixelWidth(plane) / intScaleFactor;
109 }
110
GetHeight(size_t plane) const111 size_t MacIOSurface::GetHeight(size_t plane) const {
112 size_t intScaleFactor = ceil(mContentsScaleFactor);
113 return GetDevicePixelHeight(plane) / intScaleFactor;
114 }
115
GetPlaneCount() const116 size_t MacIOSurface::GetPlaneCount() const {
117 return ::IOSurfaceGetPlaneCount(mIOSurfaceRef.get());
118 }
119
120 /*static*/
GetMaxWidth()121 size_t MacIOSurface::GetMaxWidth() {
122 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
123 }
124
125 /*static*/
GetMaxHeight()126 size_t MacIOSurface::GetMaxHeight() {
127 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
128 }
129
GetDevicePixelWidth(size_t plane) const130 size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
131 return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef.get(), plane);
132 }
133
GetDevicePixelHeight(size_t plane) const134 size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
135 return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef.get(), plane);
136 }
137
GetBytesPerRow(size_t plane) const138 size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
139 return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef.get(), plane);
140 }
141
GetPixelFormat() const142 OSType MacIOSurface::GetPixelFormat() const {
143 return ::IOSurfaceGetPixelFormat(mIOSurfaceRef.get());
144 }
145
IncrementUseCount()146 void MacIOSurface::IncrementUseCount() {
147 ::IOSurfaceIncrementUseCount(mIOSurfaceRef.get());
148 }
149
DecrementUseCount()150 void MacIOSurface::DecrementUseCount() {
151 ::IOSurfaceDecrementUseCount(mIOSurfaceRef.get());
152 }
153
Lock(bool aReadOnly)154 void MacIOSurface::Lock(bool aReadOnly) {
155 MOZ_RELEASE_ASSERT(!mIsLocked, "double MacIOSurface lock");
156 ::IOSurfaceLock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
157 nullptr);
158 mIsLocked = true;
159 }
160
Unlock(bool aReadOnly)161 void MacIOSurface::Unlock(bool aReadOnly) {
162 MOZ_RELEASE_ASSERT(mIsLocked, "MacIOSurface unlock without being locked");
163 ::IOSurfaceUnlock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
164 nullptr);
165 mIsLocked = false;
166 }
167
168 using mozilla::gfx::IntSize;
169 using mozilla::gfx::SourceSurface;
170 using mozilla::gfx::SurfaceFormat;
171
MacIOSurfaceBufferDeallocator(void * aClosure)172 static void MacIOSurfaceBufferDeallocator(void* aClosure) {
173 MOZ_ASSERT(aClosure);
174
175 delete[] static_cast<unsigned char*>(aClosure);
176 }
177
GetAsSurface()178 already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() {
179 Lock();
180 size_t bytesPerRow = GetBytesPerRow();
181 size_t ioWidth = GetDevicePixelWidth();
182 size_t ioHeight = GetDevicePixelHeight();
183
184 unsigned char* ioData = (unsigned char*)GetBaseAddress();
185 auto* dataCpy =
186 new unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
187 for (size_t i = 0; i < ioHeight; i++) {
188 memcpy(dataCpy + i * bytesPerRow, ioData + i * bytesPerRow, ioWidth * 4);
189 }
190
191 Unlock();
192
193 SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8
194 : mozilla::gfx::SurfaceFormat::B8G8R8X8;
195
196 RefPtr<mozilla::gfx::DataSourceSurface> surf =
197 mozilla::gfx::Factory::CreateWrappingDataSourceSurface(
198 dataCpy, bytesPerRow, IntSize(ioWidth, ioHeight), format,
199 &MacIOSurfaceBufferDeallocator, static_cast<void*>(dataCpy));
200
201 return surf.forget();
202 }
203
GetAsDrawTargetLocked(mozilla::gfx::BackendType aBackendType)204 already_AddRefed<mozilla::gfx::DrawTarget> MacIOSurface::GetAsDrawTargetLocked(
205 mozilla::gfx::BackendType aBackendType) {
206 MOZ_RELEASE_ASSERT(
207 IsLocked(),
208 "Only call GetAsDrawTargetLocked while the surface is locked.");
209
210 size_t bytesPerRow = GetBytesPerRow();
211 size_t ioWidth = GetDevicePixelWidth();
212 size_t ioHeight = GetDevicePixelHeight();
213 unsigned char* ioData = (unsigned char*)GetBaseAddress();
214 SurfaceFormat format = GetFormat();
215 return mozilla::gfx::Factory::CreateDrawTargetForData(
216 aBackendType, ioData, IntSize(ioWidth, ioHeight), bytesPerRow, format);
217 }
218
GetFormat() const219 SurfaceFormat MacIOSurface::GetFormat() const {
220 switch (GetPixelFormat()) {
221 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
222 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
223 return SurfaceFormat::NV12;
224 case kCVPixelFormatType_422YpCbCr8:
225 return SurfaceFormat::YUV422;
226 case kCVPixelFormatType_32BGRA:
227 return HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
228 default:
229 return HasAlpha() ? SurfaceFormat::R8G8B8A8 : SurfaceFormat::R8G8B8X8;
230 }
231 }
232
GetReadFormat() const233 SurfaceFormat MacIOSurface::GetReadFormat() const {
234 SurfaceFormat format = GetFormat();
235 if (format == SurfaceFormat::YUV422) {
236 return SurfaceFormat::R8G8B8X8;
237 }
238 return format;
239 }
240
CGLTexImageIOSurface2D(CGLContextObj ctx,GLenum target,GLenum internalFormat,GLsizei width,GLsizei height,GLenum format,GLenum type,GLuint plane) const241 CGLError MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, GLenum target,
242 GLenum internalFormat,
243 GLsizei width, GLsizei height,
244 GLenum format, GLenum type,
245 GLuint plane) const {
246 return ::CGLTexImageIOSurface2D(ctx, target, internalFormat, width, height,
247 format, type, mIOSurfaceRef.get(), plane);
248 }
249
CGLTexImageIOSurface2D(mozilla::gl::GLContext * aGL,CGLContextObj ctx,size_t plane,mozilla::gfx::SurfaceFormat * aOutReadFormat)250 CGLError MacIOSurface::CGLTexImageIOSurface2D(
251 mozilla::gl::GLContext* aGL, CGLContextObj ctx, size_t plane,
252 mozilla::gfx::SurfaceFormat* aOutReadFormat) {
253 MOZ_ASSERT(plane >= 0);
254 bool isCompatibilityProfile = aGL->IsCompatibilityProfile();
255 OSType pixelFormat = GetPixelFormat();
256
257 GLenum internalFormat;
258 GLenum format;
259 GLenum type;
260 if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
261 pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
262 MOZ_ASSERT(GetPlaneCount() == 2);
263 MOZ_ASSERT(plane < 2);
264
265 // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
266 // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
267 // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
268 if (plane == 0) {
269 internalFormat = format =
270 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
271 } else {
272 internalFormat = format =
273 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
274 }
275 type = LOCAL_GL_UNSIGNED_BYTE;
276 if (aOutReadFormat) {
277 *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV12;
278 }
279 } else if (pixelFormat == kCVPixelFormatType_422YpCbCr8) {
280 MOZ_ASSERT(plane == 0);
281 // The YCBCR_422_APPLE ext is only available in compatibility profile. So,
282 // we should use RGB_422_APPLE for core profile. The difference between
283 // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts
284 // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE
285 // doesn't contain color conversion. You should do the color conversion by
286 // yourself for RGB_422_APPLE.
287 //
288 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt
289 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
290 if (isCompatibilityProfile) {
291 format = LOCAL_GL_YCBCR_422_APPLE;
292 if (aOutReadFormat) {
293 *aOutReadFormat = mozilla::gfx::SurfaceFormat::R8G8B8X8;
294 }
295 } else {
296 format = LOCAL_GL_RGB_422_APPLE;
297 if (aOutReadFormat) {
298 *aOutReadFormat = mozilla::gfx::SurfaceFormat::YUV422;
299 }
300 }
301 internalFormat = LOCAL_GL_RGB;
302 type = LOCAL_GL_UNSIGNED_SHORT_8_8_APPLE;
303 } else {
304 MOZ_ASSERT(plane == 0);
305
306 internalFormat = HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
307 format = LOCAL_GL_BGRA;
308 type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
309 if (aOutReadFormat) {
310 *aOutReadFormat = HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8
311 : mozilla::gfx::SurfaceFormat::R8G8B8X8;
312 }
313 }
314
315 return CGLTexImageIOSurface2D(ctx, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
316 internalFormat, GetDevicePixelWidth(plane),
317 GetDevicePixelHeight(plane), format, type,
318 plane);
319 }
320