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 "nsPrintfCString.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/gfx/Logging.h"
17 #include "mozilla/StaticPrefs_gfx.h"
18 
19 using namespace mozilla;
20 
MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,bool aHasAlpha,gfx::YUVColorSpace aColorSpace)21 MacIOSurface::MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,
22                            bool aHasAlpha, gfx::YUVColorSpace aColorSpace)
23     : mIOSurfaceRef(std::move(aIOSurfaceRef)),
24       mHasAlpha(aHasAlpha),
25       mColorSpace(aColorSpace) {
26   IncrementUseCount();
27 }
28 
~MacIOSurface()29 MacIOSurface::~MacIOSurface() {
30   MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface");
31   DecrementUseCount();
32 }
33 
AddDictionaryInt(const CFTypeRefPtr<CFMutableDictionaryRef> & aDict,const void * aType,uint32_t aValue)34 void AddDictionaryInt(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
35                       const void* aType, uint32_t aValue) {
36   auto cfValue = CFTypeRefPtr<CFNumberRef>::WrapUnderCreateRule(
37       ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aValue));
38   ::CFDictionaryAddValue(aDict.get(), aType, cfValue.get());
39 }
40 
SetSizeProperties(const CFTypeRefPtr<CFMutableDictionaryRef> & aDict,int aWidth,int aHeight,int aBytesPerPixel)41 void SetSizeProperties(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
42                        int aWidth, int aHeight, int aBytesPerPixel) {
43   AddDictionaryInt(aDict, kIOSurfaceWidth, aWidth);
44   AddDictionaryInt(aDict, kIOSurfaceHeight, aHeight);
45   ::CFDictionaryAddValue(aDict.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
46   AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel);
47 
48   size_t bytesPerRow =
49       IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, aWidth * aBytesPerPixel);
50   AddDictionaryInt(aDict, kIOSurfaceBytesPerRow, bytesPerRow);
51 
52   // Add a SIMD register worth of extra bytes to the end of the allocation for
53   // SWGL.
54   size_t totalBytes =
55       IOSurfaceAlignProperty(kIOSurfaceAllocSize, aHeight * bytesPerRow + 16);
56   AddDictionaryInt(aDict, kIOSurfaceAllocSize, totalBytes);
57 }
58 
59 /* static */
CreateIOSurface(int aWidth,int aHeight,bool aHasAlpha)60 already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth,
61                                                              int aHeight,
62                                                              bool aHasAlpha) {
63   auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
64       ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
65                                   &kCFTypeDictionaryKeyCallBacks,
66                                   &kCFTypeDictionaryValueCallBacks));
67   if (!props) return nullptr;
68 
69   MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
70   MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
71 
72   int32_t bytesPerElem = 4;
73   SetSizeProperties(props, aWidth, aHeight, bytesPerElem);
74 
75   AddDictionaryInt(props, kIOSurfacePixelFormat,
76                    (uint32_t)kCVPixelFormatType_32BGRA);
77 
78   CFTypeRefPtr<IOSurfaceRef> surfaceRef =
79       CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
80           ::IOSurfaceCreate(props.get()));
81 
82   if (StaticPrefs::gfx_color_management_native_srgb()) {
83     IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
84                       kCGColorSpaceSRGB);
85   }
86 
87   if (!surfaceRef) {
88     return nullptr;
89   }
90 
91   RefPtr<MacIOSurface> ioSurface =
92       new MacIOSurface(std::move(surfaceRef), aHasAlpha);
93 
94   return ioSurface.forget();
95 }
96 
CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef> & aDict,const gfx::IntSize & aSize,size_t aOffset,size_t aBytesPerPixel)97 size_t CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
98                              const gfx::IntSize& aSize, size_t aOffset,
99                              size_t aBytesPerPixel) {
100   size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow,
101                                               aSize.width * aBytesPerPixel);
102   // Add a SIMD register worth of extra bytes to the end of the allocation for
103   // SWGL.
104   size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize,
105                                              aSize.height * bytesPerRow + 16);
106 
107   aDict = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
108       ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
109                                   &kCFTypeDictionaryKeyCallBacks,
110                                   &kCFTypeDictionaryValueCallBacks));
111 
112   AddDictionaryInt(aDict, kIOSurfacePlaneWidth, aSize.width);
113   AddDictionaryInt(aDict, kIOSurfacePlaneHeight, aSize.height);
114   AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerRow, bytesPerRow);
115   AddDictionaryInt(aDict, kIOSurfacePlaneOffset, aOffset);
116   AddDictionaryInt(aDict, kIOSurfacePlaneSize, totalBytes);
117   AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel);
118 
119   return totalBytes;
120 }
121 
122 /* static */
CreateNV12Surface(const IntSize & aYSize,const IntSize & aCbCrSize,YUVColorSpace aColorSpace,ColorRange aColorRange)123 already_AddRefed<MacIOSurface> MacIOSurface::CreateNV12Surface(
124     const IntSize& aYSize, const IntSize& aCbCrSize, YUVColorSpace aColorSpace,
125     ColorRange aColorRange) {
126   MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
127              aColorSpace == YUVColorSpace::BT709);
128   MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
129              aColorRange == ColorRange::FULL);
130 
131   auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
132       ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
133                                   &kCFTypeDictionaryKeyCallBacks,
134                                   &kCFTypeDictionaryValueCallBacks));
135   if (!props) return nullptr;
136 
137   MOZ_ASSERT((size_t)aYSize.width <= GetMaxWidth());
138   MOZ_ASSERT((size_t)aYSize.height <= GetMaxHeight());
139 
140   AddDictionaryInt(props, kIOSurfaceWidth, aYSize.width);
141   AddDictionaryInt(props, kIOSurfaceHeight, aYSize.height);
142   ::CFDictionaryAddValue(props.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
143 
144   if (aColorRange == ColorRange::LIMITED) {
145     AddDictionaryInt(props, kIOSurfacePixelFormat,
146                      (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
147   } else {
148     AddDictionaryInt(props, kIOSurfacePixelFormat,
149                      (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarFullRange);
150   }
151 
152   CFTypeRefPtr<CFMutableDictionaryRef> planeProps[2];
153   size_t planeTotalBytes = CreatePlaneDictionary(planeProps[0], aYSize, 0, 1);
154   planeTotalBytes +=
155       CreatePlaneDictionary(planeProps[1], aCbCrSize, planeTotalBytes, 2);
156 
157   AddDictionaryInt(props, kIOSurfaceAllocSize, planeTotalBytes);
158 
159   auto array = CFTypeRefPtr<CFArrayRef>::WrapUnderCreateRule(
160       CFArrayCreate(kCFAllocatorDefault, (const void**)planeProps, 2,
161                     &kCFTypeArrayCallBacks));
162   ::CFDictionaryAddValue(props.get(), kIOSurfacePlaneInfo, array.get());
163 
164   CFTypeRefPtr<IOSurfaceRef> surfaceRef =
165       CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
166           ::IOSurfaceCreate(props.get()));
167 
168   if (!surfaceRef) {
169     return nullptr;
170   }
171 
172   // Setup the correct YCbCr conversion matrix on the IOSurface, in case we pass
173   // this directly to CoreAnimation.
174   if (aColorSpace == YUVColorSpace::BT601) {
175     IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
176                       CFSTR("ITU_R_601_4"));
177   } else {
178     IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
179                       CFSTR("ITU_R_709_2"));
180   }
181   // Override the color space to be the same as the main display, so that
182   // CoreAnimation won't try to do any color correction (from the IOSurface
183   // space, to the display). In the future we may want to try specifying this
184   // correctly, but probably only once we do the same for videos drawn through
185   // our gfx code.
186   auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule(
187       CGDisplayCopyColorSpace(CGMainDisplayID()));
188   auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule(
189       CGColorSpaceCopyICCProfile(colorSpace.get()));
190   IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
191                     colorData.get());
192 
193   RefPtr<MacIOSurface> ioSurface =
194       new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
195 
196   return ioSurface.forget();
197 }
198 
199 /* static */
CreateYUV422Surface(const IntSize & aSize,YUVColorSpace aColorSpace,ColorRange aColorRange)200 already_AddRefed<MacIOSurface> MacIOSurface::CreateYUV422Surface(
201     const IntSize& aSize, YUVColorSpace aColorSpace, ColorRange aColorRange) {
202   MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
203              aColorSpace == YUVColorSpace::BT709);
204   MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
205              aColorRange == ColorRange::FULL);
206 
207   auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
208       ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
209                                   &kCFTypeDictionaryKeyCallBacks,
210                                   &kCFTypeDictionaryValueCallBacks));
211   if (!props) return nullptr;
212 
213   MOZ_ASSERT((size_t)aSize.width <= GetMaxWidth());
214   MOZ_ASSERT((size_t)aSize.height <= GetMaxHeight());
215 
216   SetSizeProperties(props, aSize.width, aSize.height, 2);
217 
218   if (aColorRange == ColorRange::LIMITED) {
219     AddDictionaryInt(props, kIOSurfacePixelFormat,
220                      (uint32_t)kCVPixelFormatType_422YpCbCr8_yuvs);
221   } else {
222     AddDictionaryInt(props, kIOSurfacePixelFormat,
223                      (uint32_t)kCVPixelFormatType_422YpCbCr8FullRange);
224   }
225 
226   CFTypeRefPtr<IOSurfaceRef> surfaceRef =
227       CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
228           ::IOSurfaceCreate(props.get()));
229 
230   if (!surfaceRef) {
231     return nullptr;
232   }
233 
234   // Setup the correct YCbCr conversion matrix on the IOSurface, in case we pass
235   // this directly to CoreAnimation.
236   if (aColorSpace == YUVColorSpace::BT601) {
237     IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
238                       CFSTR("ITU_R_601_4"));
239   } else {
240     IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
241                       CFSTR("ITU_R_709_2"));
242   }
243   // Override the color space to be the same as the main display, so that
244   // CoreAnimation won't try to do any color correction (from the IOSurface
245   // space, to the display). In the future we may want to try specifying this
246   // correctly, but probably only once we do the same for videos drawn through
247   // our gfx code.
248   auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule(
249       CGDisplayCopyColorSpace(CGMainDisplayID()));
250   auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule(
251       CGColorSpaceCopyICCProfile(colorSpace.get()));
252   IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
253                     colorData.get());
254 
255   RefPtr<MacIOSurface> ioSurface =
256       new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
257 
258   return ioSurface.forget();
259 }
260 
261 /* static */
LookupSurface(IOSurfaceID aIOSurfaceID,bool aHasAlpha,gfx::YUVColorSpace aColorSpace)262 already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
263     IOSurfaceID aIOSurfaceID, bool aHasAlpha, gfx::YUVColorSpace aColorSpace) {
264   CFTypeRefPtr<IOSurfaceRef> surfaceRef =
265       CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
266           ::IOSurfaceLookup(aIOSurfaceID));
267   if (!surfaceRef) return nullptr;
268 
269   RefPtr<MacIOSurface> ioSurface =
270       new MacIOSurface(std::move(surfaceRef), aHasAlpha, aColorSpace);
271 
272   return ioSurface.forget();
273 }
274 
GetIOSurfaceID() const275 IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
276   return ::IOSurfaceGetID(mIOSurfaceRef.get());
277 }
278 
GetBaseAddress() const279 void* MacIOSurface::GetBaseAddress() const {
280   return ::IOSurfaceGetBaseAddress(mIOSurfaceRef.get());
281 }
282 
GetBaseAddressOfPlane(size_t aPlaneIndex) const283 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
284   return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef.get(), aPlaneIndex);
285 }
286 
GetWidth(size_t plane) const287 size_t MacIOSurface::GetWidth(size_t plane) const {
288   return GetDevicePixelWidth(plane);
289 }
290 
GetHeight(size_t plane) const291 size_t MacIOSurface::GetHeight(size_t plane) const {
292   return GetDevicePixelHeight(plane);
293 }
294 
GetPlaneCount() const295 size_t MacIOSurface::GetPlaneCount() const {
296   return ::IOSurfaceGetPlaneCount(mIOSurfaceRef.get());
297 }
298 
299 /*static*/
GetMaxWidth()300 size_t MacIOSurface::GetMaxWidth() {
301   return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
302 }
303 
304 /*static*/
GetMaxHeight()305 size_t MacIOSurface::GetMaxHeight() {
306   return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
307 }
308 
GetDevicePixelWidth(size_t plane) const309 size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
310   return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef.get(), plane);
311 }
312 
GetDevicePixelHeight(size_t plane) const313 size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
314   return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef.get(), plane);
315 }
316 
GetBytesPerRow(size_t plane) const317 size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
318   return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef.get(), plane);
319 }
320 
GetAllocSize() const321 size_t MacIOSurface::GetAllocSize() const {
322   return ::IOSurfaceGetAllocSize(mIOSurfaceRef.get());
323 }
324 
GetPixelFormat() const325 OSType MacIOSurface::GetPixelFormat() const {
326   return ::IOSurfaceGetPixelFormat(mIOSurfaceRef.get());
327 }
328 
IncrementUseCount()329 void MacIOSurface::IncrementUseCount() {
330   ::IOSurfaceIncrementUseCount(mIOSurfaceRef.get());
331 }
332 
DecrementUseCount()333 void MacIOSurface::DecrementUseCount() {
334   ::IOSurfaceDecrementUseCount(mIOSurfaceRef.get());
335 }
336 
Lock(bool aReadOnly)337 void MacIOSurface::Lock(bool aReadOnly) {
338   MOZ_RELEASE_ASSERT(!mIsLocked, "double MacIOSurface lock");
339   ::IOSurfaceLock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
340                   nullptr);
341   mIsLocked = true;
342 }
343 
Unlock(bool aReadOnly)344 void MacIOSurface::Unlock(bool aReadOnly) {
345   MOZ_RELEASE_ASSERT(mIsLocked, "MacIOSurface unlock without being locked");
346   ::IOSurfaceUnlock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
347                     nullptr);
348   mIsLocked = false;
349 }
350 
351 using mozilla::gfx::IntSize;
352 using mozilla::gfx::SourceSurface;
353 using mozilla::gfx::SurfaceFormat;
354 
MacIOSurfaceBufferDeallocator(void * aClosure)355 static void MacIOSurfaceBufferDeallocator(void* aClosure) {
356   MOZ_ASSERT(aClosure);
357 
358   delete[] static_cast<unsigned char*>(aClosure);
359 }
360 
GetAsSurface()361 already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() {
362   Lock();
363   size_t bytesPerRow = GetBytesPerRow();
364   size_t ioWidth = GetDevicePixelWidth();
365   size_t ioHeight = GetDevicePixelHeight();
366 
367   unsigned char* ioData = (unsigned char*)GetBaseAddress();
368   auto* dataCpy =
369       new unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
370   for (size_t i = 0; i < ioHeight; i++) {
371     memcpy(dataCpy + i * bytesPerRow, ioData + i * bytesPerRow, ioWidth * 4);
372   }
373 
374   Unlock();
375 
376   SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8
377                                     : mozilla::gfx::SurfaceFormat::B8G8R8X8;
378 
379   RefPtr<mozilla::gfx::DataSourceSurface> surf =
380       mozilla::gfx::Factory::CreateWrappingDataSourceSurface(
381           dataCpy, bytesPerRow, IntSize(ioWidth, ioHeight), format,
382           &MacIOSurfaceBufferDeallocator, static_cast<void*>(dataCpy));
383 
384   return surf.forget();
385 }
386 
GetAsDrawTargetLocked(mozilla::gfx::BackendType aBackendType)387 already_AddRefed<mozilla::gfx::DrawTarget> MacIOSurface::GetAsDrawTargetLocked(
388     mozilla::gfx::BackendType aBackendType) {
389   MOZ_RELEASE_ASSERT(
390       IsLocked(),
391       "Only call GetAsDrawTargetLocked while the surface is locked.");
392 
393   size_t bytesPerRow = GetBytesPerRow();
394   size_t ioWidth = GetDevicePixelWidth();
395   size_t ioHeight = GetDevicePixelHeight();
396   unsigned char* ioData = (unsigned char*)GetBaseAddress();
397   SurfaceFormat format = GetFormat();
398   return mozilla::gfx::Factory::CreateDrawTargetForData(
399       aBackendType, ioData, IntSize(ioWidth, ioHeight), bytesPerRow, format);
400 }
401 
GetFormat() const402 SurfaceFormat MacIOSurface::GetFormat() const {
403   switch (GetPixelFormat()) {
404     case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
405     case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
406       return SurfaceFormat::NV12;
407     case kCVPixelFormatType_422YpCbCr8_yuvs:
408     case kCVPixelFormatType_422YpCbCr8FullRange:
409       return SurfaceFormat::YUV422;
410     case kCVPixelFormatType_32BGRA:
411       return HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
412     default:
413       MOZ_ASSERT_UNREACHABLE("Unknown format");
414       return SurfaceFormat::B8G8R8A8;
415   }
416 }
417 
GetReadFormat() const418 SurfaceFormat MacIOSurface::GetReadFormat() const {
419   SurfaceFormat format = GetFormat();
420   if (format == SurfaceFormat::YUV422) {
421     return SurfaceFormat::R8G8B8X8;
422   }
423   return format;
424 }
425 
CGLTexImageIOSurface2D(CGLContextObj ctx,GLenum target,GLenum internalFormat,GLsizei width,GLsizei height,GLenum format,GLenum type,GLuint plane) const426 CGLError MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, GLenum target,
427                                               GLenum internalFormat,
428                                               GLsizei width, GLsizei height,
429                                               GLenum format, GLenum type,
430                                               GLuint plane) const {
431   return ::CGLTexImageIOSurface2D(ctx, target, internalFormat, width, height,
432                                   format, type, mIOSurfaceRef.get(), plane);
433 }
434 
CGLTexImageIOSurface2D(mozilla::gl::GLContext * aGL,CGLContextObj ctx,size_t plane,mozilla::gfx::SurfaceFormat * aOutReadFormat)435 CGLError MacIOSurface::CGLTexImageIOSurface2D(
436     mozilla::gl::GLContext* aGL, CGLContextObj ctx, size_t plane,
437     mozilla::gfx::SurfaceFormat* aOutReadFormat) {
438   MOZ_ASSERT(plane >= 0);
439   bool isCompatibilityProfile = aGL->IsCompatibilityProfile();
440   OSType pixelFormat = GetPixelFormat();
441 
442   GLenum internalFormat;
443   GLenum format;
444   GLenum type;
445   if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
446       pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
447     MOZ_ASSERT(GetPlaneCount() == 2);
448     MOZ_ASSERT(plane < 2);
449 
450     // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
451     // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
452     // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
453     if (plane == 0) {
454       internalFormat = format =
455           (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
456     } else {
457       internalFormat = format =
458           (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
459     }
460     type = LOCAL_GL_UNSIGNED_BYTE;
461     if (aOutReadFormat) {
462       *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV12;
463     }
464   } else if (pixelFormat == kCVPixelFormatType_422YpCbCr8_yuvs ||
465              pixelFormat == kCVPixelFormatType_422YpCbCr8FullRange) {
466     MOZ_ASSERT(plane == 0);
467     // The YCBCR_422_APPLE ext is only available in compatibility profile. So,
468     // we should use RGB_422_APPLE for core profile. The difference between
469     // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts
470     // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE
471     // doesn't contain color conversion. You should do the color conversion by
472     // yourself for RGB_422_APPLE.
473     //
474     // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt
475     // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
476     if (isCompatibilityProfile) {
477       format = LOCAL_GL_YCBCR_422_APPLE;
478       if (aOutReadFormat) {
479         *aOutReadFormat = mozilla::gfx::SurfaceFormat::R8G8B8X8;
480       }
481     } else {
482       format = LOCAL_GL_RGB_422_APPLE;
483       if (aOutReadFormat) {
484         *aOutReadFormat = mozilla::gfx::SurfaceFormat::YUV422;
485       }
486     }
487     internalFormat = LOCAL_GL_RGB;
488     type = LOCAL_GL_UNSIGNED_SHORT_8_8_REV_APPLE;
489   } else {
490     MOZ_ASSERT(plane == 0);
491 
492     internalFormat = HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
493     format = LOCAL_GL_BGRA;
494     type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
495     if (aOutReadFormat) {
496       *aOutReadFormat = HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8
497                                    : mozilla::gfx::SurfaceFormat::R8G8B8X8;
498     }
499   }
500 
501   auto err =
502       CGLTexImageIOSurface2D(ctx, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
503                              internalFormat, GetDevicePixelWidth(plane),
504                              GetDevicePixelHeight(plane), format, type, plane);
505   if (err) {
506     const auto formatChars = (const char*)&pixelFormat;
507     const char formatStr[] = {formatChars[3], formatChars[2], formatChars[1],
508                               formatChars[0], 0};
509     const nsPrintfCString errStr(
510         "CGLTexImageIOSurface2D(context, target, 0x%04x,"
511         " %u, %u, 0x%04x, 0x%04x, iosurfPtr, %u) -> %i",
512         internalFormat, uint32_t(GetDevicePixelWidth(plane)),
513         uint32_t(GetDevicePixelHeight(plane)), format, type,
514         (unsigned int)plane, err);
515     gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr
516                        << ")";
517   }
518   return err;
519 }
520