summaryrefslogtreecommitdiffstats
path: root/gfx/2d/MacIOSurface.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/2d/MacIOSurface.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/2d/MacIOSurface.cpp')
-rw-r--r--gfx/2d/MacIOSurface.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/gfx/2d/MacIOSurface.cpp b/gfx/2d/MacIOSurface.cpp
new file mode 100644
index 0000000000..a90d7dfd50
--- /dev/null
+++ b/gfx/2d/MacIOSurface.cpp
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacIOSurface.h"
+#include <OpenGL/gl.h>
+#include <OpenGL/CGLIOSurface.h>
+#include <QuartzCore/QuartzCore.h>
+#include "GLConsts.h"
+#include "GLContextCGL.h"
+#include "gfxMacUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/StaticPrefs_gfx.h"
+
+using namespace mozilla;
+
+MacIOSurface::MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef,
+ bool aHasAlpha, gfx::YUVColorSpace aColorSpace)
+ : mIOSurfaceRef(std::move(aIOSurfaceRef)),
+ mHasAlpha(aHasAlpha),
+ mColorSpace(aColorSpace) {
+ IncrementUseCount();
+}
+
+MacIOSurface::~MacIOSurface() {
+ MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface");
+ DecrementUseCount();
+}
+
+void AddDictionaryInt(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
+ const void* aType, uint32_t aValue) {
+ auto cfValue = CFTypeRefPtr<CFNumberRef>::WrapUnderCreateRule(
+ ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aValue));
+ ::CFDictionaryAddValue(aDict.get(), aType, cfValue.get());
+}
+
+void SetSizeProperties(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
+ int aWidth, int aHeight, int aBytesPerPixel) {
+ AddDictionaryInt(aDict, kIOSurfaceWidth, aWidth);
+ AddDictionaryInt(aDict, kIOSurfaceHeight, aHeight);
+ ::CFDictionaryAddValue(aDict.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
+ AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel);
+
+ size_t bytesPerRow =
+ IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, aWidth * aBytesPerPixel);
+ AddDictionaryInt(aDict, kIOSurfaceBytesPerRow, bytesPerRow);
+
+ // Add a SIMD register worth of extra bytes to the end of the allocation for
+ // SWGL.
+ size_t totalBytes =
+ IOSurfaceAlignProperty(kIOSurfaceAllocSize, aHeight * bytesPerRow + 16);
+ AddDictionaryInt(aDict, kIOSurfaceAllocSize, totalBytes);
+}
+
+/* static */
+already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth,
+ int aHeight,
+ bool aHasAlpha) {
+ auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
+ ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ if (!props) return nullptr;
+
+ MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
+ MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
+
+ int32_t bytesPerElem = 4;
+ SetSizeProperties(props, aWidth, aHeight, bytesPerElem);
+
+ AddDictionaryInt(props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_32BGRA);
+
+ CFTypeRefPtr<IOSurfaceRef> surfaceRef =
+ CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
+ ::IOSurfaceCreate(props.get()));
+
+ if (StaticPrefs::gfx_color_management_native_srgb()) {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
+ kCGColorSpaceSRGB);
+ }
+
+ if (!surfaceRef) {
+ return nullptr;
+ }
+
+ RefPtr<MacIOSurface> ioSurface =
+ new MacIOSurface(std::move(surfaceRef), aHasAlpha);
+
+ return ioSurface.forget();
+}
+
+size_t CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef>& aDict,
+ const gfx::IntSize& aSize, size_t aOffset,
+ size_t aBytesPerPixel) {
+ size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow,
+ aSize.width * aBytesPerPixel);
+ // Add a SIMD register worth of extra bytes to the end of the allocation for
+ // SWGL.
+ size_t totalBytes = IOSurfaceAlignProperty(kIOSurfacePlaneSize,
+ aSize.height * bytesPerRow + 16);
+
+ aDict = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
+ ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ AddDictionaryInt(aDict, kIOSurfacePlaneWidth, aSize.width);
+ AddDictionaryInt(aDict, kIOSurfacePlaneHeight, aSize.height);
+ AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerRow, bytesPerRow);
+ AddDictionaryInt(aDict, kIOSurfacePlaneOffset, aOffset);
+ AddDictionaryInt(aDict, kIOSurfacePlaneSize, totalBytes);
+ AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerElement, aBytesPerPixel);
+
+ return totalBytes;
+}
+
+/* static */
+already_AddRefed<MacIOSurface> MacIOSurface::CreateNV12OrP010Surface(
+ const IntSize& aYSize, const IntSize& aCbCrSize, YUVColorSpace aColorSpace,
+ TransferFunction aTransferFunction, ColorRange aColorRange,
+ ColorDepth aColorDepth) {
+ MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
+ aColorSpace == YUVColorSpace::BT709 ||
+ aColorSpace == YUVColorSpace::BT2020);
+ MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
+ aColorRange == ColorRange::FULL);
+ MOZ_ASSERT(aColorDepth == ColorDepth::COLOR_8 ||
+ aColorDepth == ColorDepth::COLOR_10);
+
+ auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
+ ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ if (!props) return nullptr;
+
+ MOZ_ASSERT((size_t)aYSize.width <= GetMaxWidth());
+ MOZ_ASSERT((size_t)aYSize.height <= GetMaxHeight());
+
+ AddDictionaryInt(props, kIOSurfaceWidth, aYSize.width);
+ AddDictionaryInt(props, kIOSurfaceHeight, aYSize.height);
+ ::CFDictionaryAddValue(props.get(), kIOSurfaceIsGlobal, kCFBooleanTrue);
+
+ if (aColorRange == ColorRange::LIMITED) {
+ if (aColorDepth == ColorDepth::COLOR_8) {
+ AddDictionaryInt(
+ props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
+ } else {
+ AddDictionaryInt(
+ props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange);
+ }
+ } else {
+ if (aColorDepth == ColorDepth::COLOR_8) {
+ AddDictionaryInt(
+ props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarFullRange);
+ } else {
+ AddDictionaryInt(
+ props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarFullRange);
+ }
+ }
+
+ size_t bytesPerPixel = (aColorDepth == ColorDepth::COLOR_8) ? 1 : 2;
+
+ CFTypeRefPtr<CFMutableDictionaryRef> planeProps[2];
+ size_t yPlaneBytes =
+ CreatePlaneDictionary(planeProps[0], aYSize, 0, bytesPerPixel);
+ size_t cbCrOffset =
+ IOSurfaceAlignProperty(kIOSurfacePlaneOffset, yPlaneBytes);
+ size_t cbCrPlaneBytes = CreatePlaneDictionary(planeProps[1], aCbCrSize,
+ cbCrOffset, bytesPerPixel * 2);
+ size_t totalBytes =
+ IOSurfaceAlignProperty(kIOSurfaceAllocSize, cbCrOffset + cbCrPlaneBytes);
+
+ AddDictionaryInt(props, kIOSurfaceAllocSize, totalBytes);
+
+ auto array = CFTypeRefPtr<CFArrayRef>::WrapUnderCreateRule(
+ CFArrayCreate(kCFAllocatorDefault, (const void**)planeProps, 2,
+ &kCFTypeArrayCallBacks));
+ ::CFDictionaryAddValue(props.get(), kIOSurfacePlaneInfo, array.get());
+
+ CFTypeRefPtr<IOSurfaceRef> surfaceRef =
+ CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
+ ::IOSurfaceCreate(props.get()));
+
+ if (!surfaceRef) {
+ return nullptr;
+ }
+
+ // Setup the correct YCbCr conversion matrix, color primaries, and transfer
+ // functions on the IOSurface, in case we pass this directly to CoreAnimation.
+ // For keys and values, we'd like to use values specified by the API, but
+ // those are only defined for CVImageBuffers. Luckily, when an image buffer is
+ // converted into an IOSurface, the keys are transformed but the values are
+ // the same. Since we are creating the IOSurface directly, we use hard-coded
+ // keys derived from inspecting the extracted IOSurfaces in the copying case,
+ // but we use the API-defined values from CVImageBuffer.
+ if (aColorSpace == YUVColorSpace::BT601) {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
+ kCVImageBufferYCbCrMatrix_ITU_R_601_4);
+ } else if (aColorSpace == YUVColorSpace::BT709) {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
+ kCVImageBufferYCbCrMatrix_ITU_R_709_2);
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"),
+ kCVImageBufferColorPrimaries_ITU_R_709_2);
+ } else {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
+ kCVImageBufferYCbCrMatrix_ITU_R_2020);
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"),
+ kCVImageBufferColorPrimaries_ITU_R_2020);
+ }
+
+ // Transfer function is applied independently from the colorSpace.
+ IOSurfaceSetValue(
+ surfaceRef.get(), CFSTR("IOSurfaceTransferFunction"),
+ gfxMacUtils::CFStringForTransferFunction(aTransferFunction));
+
+ // Override the color space to be the same as the main display, so that
+ // CoreAnimation won't try to do any color correction (from the IOSurface
+ // space, to the display). In the future we may want to try specifying this
+ // correctly, but probably only once we do the same for videos drawn through
+ // our gfx code.
+ auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule(
+ CGDisplayCopyColorSpace(CGMainDisplayID()));
+ auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule(
+ CGColorSpaceCopyICCProfile(colorSpace.get()));
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
+ colorData.get());
+
+ RefPtr<MacIOSurface> ioSurface =
+ new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
+
+ return ioSurface.forget();
+}
+
+/* static */
+already_AddRefed<MacIOSurface> MacIOSurface::CreateYUV422Surface(
+ const IntSize& aSize, YUVColorSpace aColorSpace, ColorRange aColorRange) {
+ MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 ||
+ aColorSpace == YUVColorSpace::BT709);
+ MOZ_ASSERT(aColorRange == ColorRange::LIMITED ||
+ aColorRange == ColorRange::FULL);
+
+ auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule(
+ ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ if (!props) return nullptr;
+
+ MOZ_ASSERT((size_t)aSize.width <= GetMaxWidth());
+ MOZ_ASSERT((size_t)aSize.height <= GetMaxHeight());
+
+ SetSizeProperties(props, aSize.width, aSize.height, 2);
+
+ if (aColorRange == ColorRange::LIMITED) {
+ AddDictionaryInt(props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_422YpCbCr8_yuvs);
+ } else {
+ AddDictionaryInt(props, kIOSurfacePixelFormat,
+ (uint32_t)kCVPixelFormatType_422YpCbCr8FullRange);
+ }
+
+ CFTypeRefPtr<IOSurfaceRef> surfaceRef =
+ CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
+ ::IOSurfaceCreate(props.get()));
+
+ if (!surfaceRef) {
+ return nullptr;
+ }
+
+ // Setup the correct YCbCr conversion matrix on the IOSurface, in case we pass
+ // this directly to CoreAnimation.
+ if (aColorSpace == YUVColorSpace::BT601) {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
+ CFSTR("ITU_R_601_4"));
+ } else {
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"),
+ CFSTR("ITU_R_709_2"));
+ }
+ // Override the color space to be the same as the main display, so that
+ // CoreAnimation won't try to do any color correction (from the IOSurface
+ // space, to the display). In the future we may want to try specifying this
+ // correctly, but probably only once we do the same for videos drawn through
+ // our gfx code.
+ auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule(
+ CGDisplayCopyColorSpace(CGMainDisplayID()));
+ auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule(
+ CGColorSpaceCopyICCProfile(colorSpace.get()));
+ IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"),
+ colorData.get());
+
+ RefPtr<MacIOSurface> ioSurface =
+ new MacIOSurface(std::move(surfaceRef), false, aColorSpace);
+
+ return ioSurface.forget();
+}
+
+/* static */
+already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
+ IOSurfaceID aIOSurfaceID, bool aHasAlpha, gfx::YUVColorSpace aColorSpace) {
+ CFTypeRefPtr<IOSurfaceRef> surfaceRef =
+ CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule(
+ ::IOSurfaceLookup(aIOSurfaceID));
+ if (!surfaceRef) return nullptr;
+
+ RefPtr<MacIOSurface> ioSurface =
+ new MacIOSurface(std::move(surfaceRef), aHasAlpha, aColorSpace);
+
+ return ioSurface.forget();
+}
+
+IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
+ return ::IOSurfaceGetID(mIOSurfaceRef.get());
+}
+
+void* MacIOSurface::GetBaseAddress() const {
+ return ::IOSurfaceGetBaseAddress(mIOSurfaceRef.get());
+}
+
+void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
+ return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef.get(), aPlaneIndex);
+}
+
+size_t MacIOSurface::GetWidth(size_t plane) const {
+ return GetDevicePixelWidth(plane);
+}
+
+size_t MacIOSurface::GetHeight(size_t plane) const {
+ return GetDevicePixelHeight(plane);
+}
+
+size_t MacIOSurface::GetPlaneCount() const {
+ return ::IOSurfaceGetPlaneCount(mIOSurfaceRef.get());
+}
+
+/*static*/
+size_t MacIOSurface::GetMaxWidth() {
+ return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
+}
+
+/*static*/
+size_t MacIOSurface::GetMaxHeight() {
+ return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
+}
+
+size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
+ return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef.get(), plane);
+}
+
+size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
+ return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef.get(), plane);
+}
+
+size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
+ return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef.get(), plane);
+}
+
+size_t MacIOSurface::GetAllocSize() const {
+ return ::IOSurfaceGetAllocSize(mIOSurfaceRef.get());
+}
+
+OSType MacIOSurface::GetPixelFormat() const {
+ return ::IOSurfaceGetPixelFormat(mIOSurfaceRef.get());
+}
+
+void MacIOSurface::IncrementUseCount() {
+ ::IOSurfaceIncrementUseCount(mIOSurfaceRef.get());
+}
+
+void MacIOSurface::DecrementUseCount() {
+ ::IOSurfaceDecrementUseCount(mIOSurfaceRef.get());
+}
+
+void MacIOSurface::Lock(bool aReadOnly) {
+ MOZ_RELEASE_ASSERT(!mIsLocked, "double MacIOSurface lock");
+ ::IOSurfaceLock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
+ nullptr);
+ mIsLocked = true;
+}
+
+void MacIOSurface::Unlock(bool aReadOnly) {
+ MOZ_RELEASE_ASSERT(mIsLocked, "MacIOSurface unlock without being locked");
+ ::IOSurfaceUnlock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0,
+ nullptr);
+ mIsLocked = false;
+}
+
+using mozilla::gfx::ColorDepth;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::SourceSurface;
+using mozilla::gfx::SurfaceFormat;
+
+static void MacIOSurfaceBufferDeallocator(void* aClosure) {
+ MOZ_ASSERT(aClosure);
+
+ delete[] static_cast<unsigned char*>(aClosure);
+}
+
+already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() {
+ Lock();
+ size_t bytesPerRow = GetBytesPerRow();
+ size_t ioWidth = GetDevicePixelWidth();
+ size_t ioHeight = GetDevicePixelHeight();
+
+ unsigned char* ioData = (unsigned char*)GetBaseAddress();
+ auto* dataCpy =
+ new unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)];
+ for (size_t i = 0; i < ioHeight; i++) {
+ memcpy(dataCpy + i * bytesPerRow, ioData + i * bytesPerRow, ioWidth * 4);
+ }
+
+ Unlock();
+
+ SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8
+ : mozilla::gfx::SurfaceFormat::B8G8R8X8;
+
+ RefPtr<mozilla::gfx::DataSourceSurface> surf =
+ mozilla::gfx::Factory::CreateWrappingDataSourceSurface(
+ dataCpy, bytesPerRow, IntSize(ioWidth, ioHeight), format,
+ &MacIOSurfaceBufferDeallocator, static_cast<void*>(dataCpy));
+
+ return surf.forget();
+}
+
+already_AddRefed<mozilla::gfx::DrawTarget> MacIOSurface::GetAsDrawTargetLocked(
+ mozilla::gfx::BackendType aBackendType) {
+ MOZ_RELEASE_ASSERT(
+ IsLocked(),
+ "Only call GetAsDrawTargetLocked while the surface is locked.");
+
+ size_t bytesPerRow = GetBytesPerRow();
+ size_t ioWidth = GetDevicePixelWidth();
+ size_t ioHeight = GetDevicePixelHeight();
+ unsigned char* ioData = (unsigned char*)GetBaseAddress();
+ SurfaceFormat format = GetFormat();
+ return mozilla::gfx::Factory::CreateDrawTargetForData(
+ aBackendType, ioData, IntSize(ioWidth, ioHeight), bytesPerRow, format);
+}
+
+SurfaceFormat MacIOSurface::GetFormat() const {
+ switch (GetPixelFormat()) {
+ case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
+ return SurfaceFormat::NV12;
+ case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
+ return SurfaceFormat::P010;
+ case kCVPixelFormatType_422YpCbCr8_yuvs:
+ case kCVPixelFormatType_422YpCbCr8FullRange:
+ return SurfaceFormat::YUV422;
+ case kCVPixelFormatType_32BGRA:
+ return HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown format");
+ return SurfaceFormat::B8G8R8A8;
+ }
+}
+
+SurfaceFormat MacIOSurface::GetReadFormat() const {
+ SurfaceFormat format = GetFormat();
+ if (format == SurfaceFormat::YUV422) {
+ return SurfaceFormat::R8G8B8X8;
+ }
+ return format;
+}
+
+ColorDepth MacIOSurface::GetColorDepth() const {
+ switch (GetPixelFormat()) {
+ case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
+ case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
+ return ColorDepth::COLOR_10;
+ default:
+ return ColorDepth::COLOR_8;
+ }
+}
+
+CGLError MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, GLenum target,
+ GLenum internalFormat,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type,
+ GLuint plane) const {
+ return ::CGLTexImageIOSurface2D(ctx, target, internalFormat, width, height,
+ format, type, mIOSurfaceRef.get(), plane);
+}
+
+CGLError MacIOSurface::CGLTexImageIOSurface2D(
+ mozilla::gl::GLContext* aGL, CGLContextObj ctx, size_t plane,
+ mozilla::gfx::SurfaceFormat* aOutReadFormat) {
+ MOZ_ASSERT(plane >= 0);
+ bool isCompatibilityProfile = aGL->IsCompatibilityProfile();
+ OSType pixelFormat = GetPixelFormat();
+
+ GLenum internalFormat;
+ GLenum format;
+ GLenum type;
+ if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
+ pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
+ MOZ_ASSERT(GetPlaneCount() == 2);
+ MOZ_ASSERT(plane < 2);
+
+ // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
+ // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
+ // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
+ if (plane == 0) {
+ internalFormat = format =
+ (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
+ } else {
+ internalFormat = format =
+ (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
+ }
+ type = LOCAL_GL_UNSIGNED_BYTE;
+ if (aOutReadFormat) {
+ *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV12;
+ }
+ } else if (pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange ||
+ pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarFullRange) {
+ MOZ_ASSERT(GetPlaneCount() == 2);
+ MOZ_ASSERT(plane < 2);
+
+ // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated
+ // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile.
+ // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats
+ if (plane == 0) {
+ internalFormat = format =
+ (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED);
+ } else {
+ internalFormat = format =
+ (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG);
+ }
+ type = LOCAL_GL_UNSIGNED_SHORT;
+ if (aOutReadFormat) {
+ *aOutReadFormat = mozilla::gfx::SurfaceFormat::P010;
+ }
+ } else if (pixelFormat == kCVPixelFormatType_422YpCbCr8_yuvs ||
+ pixelFormat == kCVPixelFormatType_422YpCbCr8FullRange) {
+ MOZ_ASSERT(plane == 0);
+ // The YCBCR_422_APPLE ext is only available in compatibility profile. So,
+ // we should use RGB_422_APPLE for core profile. The difference between
+ // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts
+ // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE
+ // doesn't contain color conversion. You should do the color conversion by
+ // yourself for RGB_422_APPLE.
+ //
+ // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt
+ // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
+ if (isCompatibilityProfile) {
+ format = LOCAL_GL_YCBCR_422_APPLE;
+ if (aOutReadFormat) {
+ *aOutReadFormat = mozilla::gfx::SurfaceFormat::R8G8B8X8;
+ }
+ } else {
+ format = LOCAL_GL_RGB_422_APPLE;
+ if (aOutReadFormat) {
+ *aOutReadFormat = mozilla::gfx::SurfaceFormat::YUV422;
+ }
+ }
+ internalFormat = LOCAL_GL_RGB;
+ type = LOCAL_GL_UNSIGNED_SHORT_8_8_REV_APPLE;
+ } else {
+ MOZ_ASSERT(plane == 0);
+
+ internalFormat = HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
+ format = LOCAL_GL_BGRA;
+ type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+ if (aOutReadFormat) {
+ *aOutReadFormat = HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8
+ : mozilla::gfx::SurfaceFormat::R8G8B8X8;
+ }
+ }
+
+ auto err =
+ CGLTexImageIOSurface2D(ctx, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ internalFormat, GetDevicePixelWidth(plane),
+ GetDevicePixelHeight(plane), format, type, plane);
+ if (err) {
+ const auto formatChars = (const char*)&pixelFormat;
+ const char formatStr[] = {formatChars[3], formatChars[2], formatChars[1],
+ formatChars[0], 0};
+ const nsPrintfCString errStr(
+ "CGLTexImageIOSurface2D(context, target, 0x%04x,"
+ " %u, %u, 0x%04x, 0x%04x, iosurfPtr, %u) -> %i",
+ internalFormat, uint32_t(GetDevicePixelWidth(plane)),
+ uint32_t(GetDevicePixelHeight(plane)), format, type,
+ (unsigned int)plane, err);
+ gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr
+ << ")";
+ }
+ return err;
+}
+
+void MacIOSurface::SetColorSpace(const mozilla::gfx::ColorSpace2 cs) const {
+ Maybe<CFStringRef> str;
+ switch (cs) {
+ case gfx::ColorSpace2::UNKNOWN:
+ break;
+ case gfx::ColorSpace2::SRGB:
+ str = Some(kCGColorSpaceSRGB);
+ break;
+ case gfx::ColorSpace2::DISPLAY_P3:
+ str = Some(kCGColorSpaceDisplayP3);
+ break;
+ case gfx::ColorSpace2::BT601_525: // Doesn't really have a better option.
+ case gfx::ColorSpace2::BT709:
+ str = Some(kCGColorSpaceITUR_709);
+ break;
+ case gfx::ColorSpace2::BT2020:
+ str = Some(kCGColorSpaceITUR_2020);
+ break;
+ }
+ if (str) {
+ IOSurfaceSetValue(mIOSurfaceRef.get(), CFSTR("IOSurfaceColorSpace"), *str);
+ }
+}