MacIOSurface.cpp (27711B)
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 #ifdef XP_MACOSX 9 # include <OpenGL/gl.h> 10 # include <OpenGL/CGLIOSurface.h> 11 #endif 12 #include <QuartzCore/QuartzCore.h> 13 #include "GLConsts.h" 14 #ifdef XP_MACOSX 15 # include "GLContextCGL.h" 16 #else 17 # include "GLContextEAGL.h" 18 #endif 19 #include "gfxMacUtils.h" 20 #include "nsPrintfCString.h" 21 #include "mozilla/Assertions.h" 22 #include "mozilla/RefPtr.h" 23 #include "mozilla/gfx/Logging.h" 24 #include "mozilla/StaticPrefs_gfx.h" 25 26 using namespace mozilla; 27 28 MacIOSurface::MacIOSurface(CFTypeRefPtr<IOSurfaceRef> aIOSurfaceRef, 29 bool aHasAlpha, gfx::YUVColorSpace aColorSpace) 30 : mIOSurfaceRef(std::move(aIOSurfaceRef)), 31 mHasAlpha(aHasAlpha), 32 mColorSpace(aColorSpace) { 33 IncrementUseCount(); 34 } 35 36 MacIOSurface::~MacIOSurface() { 37 MOZ_RELEASE_ASSERT(!IsLocked(), "Destroying locked surface"); 38 DecrementUseCount(); 39 } 40 41 void AddDictionaryInt(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict, 42 const void* aType, uint32_t aValue) { 43 auto cfValue = CFTypeRefPtr<CFNumberRef>::WrapUnderCreateRule( 44 ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aValue)); 45 ::CFDictionaryAddValue(aDict.get(), aType, cfValue.get()); 46 } 47 48 void SetSizeProperties(const CFTypeRefPtr<CFMutableDictionaryRef>& aDict, 49 int aWidth, int aHeight, int aBytesPerPixel) { 50 AddDictionaryInt(aDict, kIOSurfaceWidth, aWidth); 51 AddDictionaryInt(aDict, kIOSurfaceHeight, aHeight); 52 ::CFDictionaryAddValue(aDict.get(), kIOSurfaceIsGlobal, kCFBooleanTrue); 53 AddDictionaryInt(aDict, kIOSurfaceBytesPerElement, aBytesPerPixel); 54 55 size_t bytesPerRow = 56 IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, aWidth * aBytesPerPixel); 57 AddDictionaryInt(aDict, kIOSurfaceBytesPerRow, bytesPerRow); 58 59 // Add a SIMD register worth of extra bytes to the end of the allocation for 60 // SWGL. 61 size_t totalBytes = 62 IOSurfaceAlignProperty(kIOSurfaceAllocSize, aHeight * bytesPerRow + 16); 63 AddDictionaryInt(aDict, kIOSurfaceAllocSize, totalBytes); 64 } 65 66 /* static */ 67 already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, 68 int aHeight, 69 bool aHasAlpha) { 70 auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule( 71 ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4, 72 &kCFTypeDictionaryKeyCallBacks, 73 &kCFTypeDictionaryValueCallBacks)); 74 if (!props) return nullptr; 75 76 MOZ_ASSERT((size_t)aWidth <= GetMaxWidth()); 77 MOZ_ASSERT((size_t)aHeight <= GetMaxHeight()); 78 79 int32_t bytesPerElem = 4; 80 SetSizeProperties(props, aWidth, aHeight, bytesPerElem); 81 82 AddDictionaryInt(props, kIOSurfacePixelFormat, 83 (uint32_t)kCVPixelFormatType_32BGRA); 84 85 CFTypeRefPtr<IOSurfaceRef> surfaceRef = 86 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule( 87 ::IOSurfaceCreate(props.get())); 88 89 if (StaticPrefs::gfx_color_management_native_srgb()) { 90 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"), 91 kCGColorSpaceSRGB); 92 } 93 94 if (!surfaceRef) { 95 return nullptr; 96 } 97 98 RefPtr<MacIOSurface> ioSurface = 99 new MacIOSurface(std::move(surfaceRef), aHasAlpha); 100 101 return ioSurface.forget(); 102 } 103 104 size_t CreatePlaneDictionary(CFTypeRefPtr<CFMutableDictionaryRef>& aDict, 105 const gfx::IntSize& aSize, size_t aOffset, 106 size_t aBytesPerPixel) { 107 size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow, 108 aSize.width * aBytesPerPixel); 109 // Add a SIMD register worth of extra bytes to the end of the allocation for 110 // SWGL. 111 size_t totalBytes = IOSurfaceAlignProperty(kIOSurfacePlaneSize, 112 aSize.height * bytesPerRow + 16); 113 114 aDict = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule( 115 ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4, 116 &kCFTypeDictionaryKeyCallBacks, 117 &kCFTypeDictionaryValueCallBacks)); 118 119 AddDictionaryInt(aDict, kIOSurfacePlaneWidth, aSize.width); 120 AddDictionaryInt(aDict, kIOSurfacePlaneHeight, aSize.height); 121 AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerRow, bytesPerRow); 122 AddDictionaryInt(aDict, kIOSurfacePlaneOffset, aOffset); 123 AddDictionaryInt(aDict, kIOSurfacePlaneSize, totalBytes); 124 AddDictionaryInt(aDict, kIOSurfacePlaneBytesPerElement, aBytesPerPixel); 125 126 return totalBytes; 127 } 128 129 // Helper function to set common color IOSurface properties. 130 static void SetIOSurfaceCommonProperties( 131 CFTypeRefPtr<IOSurfaceRef> surfaceRef, 132 MacIOSurface::YUVColorSpace aColorSpace, 133 MacIOSurface::TransferFunction aTransferFunction) { 134 // Setup the correct YCbCr conversion matrix, color primaries, and transfer 135 // functions on the IOSurface, in case we pass this directly to CoreAnimation. 136 // For keys and values, we'd like to use values specified by the API, but 137 // those are only defined for CVImageBuffers. Luckily, when an image buffer is 138 // converted into an IOSurface, the keys are transformed but the values are 139 // the same. Since we are creating the IOSurface directly, we use hard-coded 140 // keys derived from inspecting the extracted IOSurfaces in the copying case, 141 // but we use the API-defined values from CVImageBuffer. 142 if (aColorSpace == MacIOSurface::YUVColorSpace::BT601) { 143 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"), 144 kCVImageBufferYCbCrMatrix_ITU_R_601_4); 145 } else if (aColorSpace == MacIOSurface::YUVColorSpace::BT709) { 146 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"), 147 kCVImageBufferYCbCrMatrix_ITU_R_709_2); 148 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"), 149 kCVImageBufferColorPrimaries_ITU_R_709_2); 150 } else { 151 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceYCbCrMatrix"), 152 kCVImageBufferYCbCrMatrix_ITU_R_2020); 153 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorPrimaries"), 154 kCVImageBufferColorPrimaries_ITU_R_2020); 155 } 156 157 // Transfer function is applied independently from the colorSpace. 158 IOSurfaceSetValue( 159 surfaceRef.get(), CFSTR("IOSurfaceTransferFunction"), 160 gfxMacUtils::CFStringForTransferFunction(aTransferFunction)); 161 162 #ifdef XP_MACOSX 163 // Override the color space to be the same as the main display, so that 164 // CoreAnimation won't try to do any color correction (from the IOSurface 165 // space, to the display). In the future we may want to try specifying this 166 // correctly, but probably only once we do the same for videos drawn through 167 // our gfx code. 168 auto colorSpace = CFTypeRefPtr<CGColorSpaceRef>::WrapUnderCreateRule( 169 CGDisplayCopyColorSpace(CGMainDisplayID())); 170 auto colorData = CFTypeRefPtr<CFDataRef>::WrapUnderCreateRule( 171 CGColorSpaceCopyICCData(colorSpace.get())); 172 IOSurfaceSetValue(surfaceRef.get(), CFSTR("IOSurfaceColorSpace"), 173 colorData.get()); 174 #endif 175 } 176 177 /* static */ 178 already_AddRefed<MacIOSurface> MacIOSurface::CreateBiPlanarSurface( 179 const IntSize& aYSize, const IntSize& aCbCrSize, 180 ChromaSubsampling aChromaSubsampling, YUVColorSpace aColorSpace, 181 TransferFunction aTransferFunction, ColorRange aColorRange, 182 ColorDepth aColorDepth) { 183 MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 || 184 aColorSpace == YUVColorSpace::BT709 || 185 aColorSpace == YUVColorSpace::BT2020); 186 MOZ_ASSERT(aColorRange == ColorRange::LIMITED || 187 aColorRange == ColorRange::FULL); 188 MOZ_ASSERT(aColorDepth == ColorDepth::COLOR_8 || 189 aColorDepth == ColorDepth::COLOR_10); 190 191 auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule( 192 ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4, 193 &kCFTypeDictionaryKeyCallBacks, 194 &kCFTypeDictionaryValueCallBacks)); 195 if (!props) return nullptr; 196 197 MOZ_ASSERT((size_t)aYSize.width <= GetMaxWidth()); 198 MOZ_ASSERT((size_t)aYSize.height <= GetMaxHeight()); 199 200 AddDictionaryInt(props, kIOSurfaceWidth, aYSize.width); 201 AddDictionaryInt(props, kIOSurfaceHeight, aYSize.height); 202 ::CFDictionaryAddValue(props.get(), kIOSurfaceIsGlobal, kCFBooleanTrue); 203 204 if (aChromaSubsampling == ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) { 205 // 4:2:0 subsampling. 206 if (aColorDepth == ColorDepth::COLOR_8) { 207 if (aColorRange == ColorRange::LIMITED) { 208 AddDictionaryInt( 209 props, kIOSurfacePixelFormat, 210 (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); 211 } else { 212 AddDictionaryInt( 213 props, kIOSurfacePixelFormat, 214 (uint32_t)kCVPixelFormatType_420YpCbCr8BiPlanarFullRange); 215 } 216 } else { 217 if (aColorRange == ColorRange::LIMITED) { 218 AddDictionaryInt( 219 props, kIOSurfacePixelFormat, 220 (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); 221 } else { 222 AddDictionaryInt( 223 props, kIOSurfacePixelFormat, 224 (uint32_t)kCVPixelFormatType_420YpCbCr10BiPlanarFullRange); 225 } 226 } 227 } else { 228 // 4:2:2 subsampling. We can only handle 10-bit color. 229 MOZ_ASSERT(aColorDepth == ColorDepth::COLOR_10, 230 "macOS bi-planar 4:2:2 formats must be 10-bit color."); 231 if (aColorRange == ColorRange::LIMITED) { 232 AddDictionaryInt( 233 props, kIOSurfacePixelFormat, 234 (uint32_t)kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange); 235 } else { 236 AddDictionaryInt( 237 props, kIOSurfacePixelFormat, 238 (uint32_t)kCVPixelFormatType_422YpCbCr10BiPlanarFullRange); 239 } 240 } 241 242 size_t bytesPerPixel = (aColorDepth == ColorDepth::COLOR_8) ? 1 : 2; 243 244 CFTypeRefPtr<CFMutableDictionaryRef> planeProps[2]; 245 size_t yPlaneBytes = 246 CreatePlaneDictionary(planeProps[0], aYSize, 0, bytesPerPixel); 247 size_t cbCrOffset = 248 IOSurfaceAlignProperty(kIOSurfacePlaneOffset, yPlaneBytes); 249 size_t cbCrPlaneBytes = CreatePlaneDictionary(planeProps[1], aCbCrSize, 250 cbCrOffset, bytesPerPixel * 2); 251 size_t totalBytes = 252 IOSurfaceAlignProperty(kIOSurfaceAllocSize, cbCrOffset + cbCrPlaneBytes); 253 254 AddDictionaryInt(props, kIOSurfaceAllocSize, totalBytes); 255 256 auto array = CFTypeRefPtr<CFArrayRef>::WrapUnderCreateRule( 257 CFArrayCreate(kCFAllocatorDefault, (const void**)planeProps, 2, 258 &kCFTypeArrayCallBacks)); 259 ::CFDictionaryAddValue(props.get(), kIOSurfacePlaneInfo, array.get()); 260 261 CFTypeRefPtr<IOSurfaceRef> surfaceRef = 262 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule( 263 ::IOSurfaceCreate(props.get())); 264 265 if (!surfaceRef) { 266 return nullptr; 267 } 268 269 SetIOSurfaceCommonProperties(surfaceRef, aColorSpace, aTransferFunction); 270 271 RefPtr<MacIOSurface> ioSurface = 272 new MacIOSurface(std::move(surfaceRef), false, aColorSpace); 273 274 return ioSurface.forget(); 275 } 276 277 /* static */ 278 already_AddRefed<MacIOSurface> MacIOSurface::CreateSinglePlanarSurface( 279 const IntSize& aSize, YUVColorSpace aColorSpace, 280 TransferFunction aTransferFunction, ColorRange aColorRange) { 281 MOZ_ASSERT(aColorSpace == YUVColorSpace::BT601 || 282 aColorSpace == YUVColorSpace::BT709); 283 MOZ_ASSERT(aColorRange == ColorRange::LIMITED || 284 aColorRange == ColorRange::FULL); 285 286 auto props = CFTypeRefPtr<CFMutableDictionaryRef>::WrapUnderCreateRule( 287 ::CFDictionaryCreateMutable(kCFAllocatorDefault, 4, 288 &kCFTypeDictionaryKeyCallBacks, 289 &kCFTypeDictionaryValueCallBacks)); 290 if (!props) return nullptr; 291 292 MOZ_ASSERT((size_t)aSize.width <= GetMaxWidth()); 293 MOZ_ASSERT((size_t)aSize.height <= GetMaxHeight()); 294 295 SetSizeProperties(props, aSize.width, aSize.height, 2); 296 297 if (aColorRange == ColorRange::LIMITED) { 298 AddDictionaryInt(props, kIOSurfacePixelFormat, 299 (uint32_t)kCVPixelFormatType_422YpCbCr8_yuvs); 300 } else { 301 AddDictionaryInt(props, kIOSurfacePixelFormat, 302 (uint32_t)kCVPixelFormatType_422YpCbCr8FullRange); 303 } 304 305 CFTypeRefPtr<IOSurfaceRef> surfaceRef = 306 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule( 307 ::IOSurfaceCreate(props.get())); 308 309 if (!surfaceRef) { 310 return nullptr; 311 } 312 313 SetIOSurfaceCommonProperties(surfaceRef, aColorSpace, aTransferFunction); 314 315 RefPtr<MacIOSurface> ioSurface = 316 new MacIOSurface(std::move(surfaceRef), false, aColorSpace); 317 318 return ioSurface.forget(); 319 } 320 321 /* static */ 322 already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface( 323 IOSurfaceID aIOSurfaceID, bool aHasAlpha, gfx::YUVColorSpace aColorSpace) { 324 CFTypeRefPtr<IOSurfaceRef> surfaceRef = 325 CFTypeRefPtr<IOSurfaceRef>::WrapUnderCreateRule( 326 ::IOSurfaceLookup(aIOSurfaceID)); 327 if (!surfaceRef) return nullptr; 328 329 RefPtr<MacIOSurface> ioSurface = 330 new MacIOSurface(std::move(surfaceRef), aHasAlpha, aColorSpace); 331 332 return ioSurface.forget(); 333 } 334 335 /* static */ 336 mozilla::gfx::SurfaceFormat MacIOSurface::SurfaceFormatForPixelFormat( 337 OSType aPixelFormat, bool aHasAlpha) { 338 switch (aPixelFormat) { 339 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: 340 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: 341 return mozilla::gfx::SurfaceFormat::NV12; 342 case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: 343 case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: 344 return mozilla::gfx::SurfaceFormat::P010; 345 case kCVPixelFormatType_422YpCbCr8_yuvs: 346 case kCVPixelFormatType_422YpCbCr8FullRange: 347 return mozilla::gfx::SurfaceFormat::YUY2; 348 case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange: 349 case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange: 350 return mozilla::gfx::SurfaceFormat::NV16; 351 case kCVPixelFormatType_32BGRA: 352 return aHasAlpha ? mozilla::gfx::SurfaceFormat::B8G8R8A8 353 : mozilla::gfx::SurfaceFormat::B8G8R8X8; 354 default: 355 MOZ_ASSERT_UNREACHABLE("Unknown format"); 356 return mozilla::gfx::SurfaceFormat::B8G8R8A8; 357 } 358 } 359 360 IOSurfaceID MacIOSurface::GetIOSurfaceID() const { 361 return ::IOSurfaceGetID(mIOSurfaceRef.get()); 362 } 363 364 void* MacIOSurface::GetBaseAddress() const { 365 return ::IOSurfaceGetBaseAddress(mIOSurfaceRef.get()); 366 } 367 368 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const { 369 return ::IOSurfaceGetBaseAddressOfPlane(mIOSurfaceRef.get(), aPlaneIndex); 370 } 371 372 size_t MacIOSurface::GetWidth(size_t plane) const { 373 return GetDevicePixelWidth(plane); 374 } 375 376 size_t MacIOSurface::GetHeight(size_t plane) const { 377 return GetDevicePixelHeight(plane); 378 } 379 380 size_t MacIOSurface::GetPlaneCount() const { 381 return ::IOSurfaceGetPlaneCount(mIOSurfaceRef.get()); 382 } 383 384 /*static*/ 385 size_t MacIOSurface::GetMaxWidth() { 386 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth); 387 } 388 389 /*static*/ 390 size_t MacIOSurface::GetMaxHeight() { 391 return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight); 392 } 393 394 size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const { 395 return ::IOSurfaceGetWidthOfPlane(mIOSurfaceRef.get(), plane); 396 } 397 398 size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const { 399 return ::IOSurfaceGetHeightOfPlane(mIOSurfaceRef.get(), plane); 400 } 401 402 size_t MacIOSurface::GetBytesPerRow(size_t plane) const { 403 return ::IOSurfaceGetBytesPerRowOfPlane(mIOSurfaceRef.get(), plane); 404 } 405 406 size_t MacIOSurface::GetAllocSize() const { 407 return ::IOSurfaceGetAllocSize(mIOSurfaceRef.get()); 408 } 409 410 OSType MacIOSurface::GetPixelFormat() const { 411 return ::IOSurfaceGetPixelFormat(mIOSurfaceRef.get()); 412 } 413 414 void MacIOSurface::IncrementUseCount() { 415 ::IOSurfaceIncrementUseCount(mIOSurfaceRef.get()); 416 } 417 418 void MacIOSurface::DecrementUseCount() { 419 ::IOSurfaceDecrementUseCount(mIOSurfaceRef.get()); 420 } 421 422 bool MacIOSurface::Lock(bool aReadOnly) { 423 MOZ_RELEASE_ASSERT(!mIsLocked, "double MacIOSurface lock"); 424 kern_return_t rv = ::IOSurfaceLock( 425 mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr); 426 if (NS_WARN_IF(rv != KERN_SUCCESS)) { 427 gfxCriticalNoteOnce << "MacIOSurface::Lock failed " << gfx::hexa(rv); 428 return false; 429 } 430 mIsLocked = true; 431 return true; 432 } 433 434 void MacIOSurface::Unlock(bool aReadOnly) { 435 MOZ_RELEASE_ASSERT(mIsLocked, "MacIOSurface unlock without being locked"); 436 ::IOSurfaceUnlock(mIOSurfaceRef.get(), aReadOnly ? kIOSurfaceLockReadOnly : 0, 437 nullptr); 438 mIsLocked = false; 439 } 440 441 using mozilla::gfx::ColorDepth; 442 using mozilla::gfx::IntSize; 443 using mozilla::gfx::SourceSurface; 444 using mozilla::gfx::SurfaceFormat; 445 446 static void MacIOSurfaceBufferDeallocator(void* aClosure) { 447 MOZ_ASSERT(aClosure); 448 449 delete[] static_cast<unsigned char*>(aClosure); 450 } 451 452 already_AddRefed<SourceSurface> MacIOSurface::GetAsSurface() { 453 if (NS_WARN_IF(!Lock())) { 454 return nullptr; 455 } 456 457 size_t bytesPerRow = GetBytesPerRow(); 458 size_t ioWidth = GetDevicePixelWidth(); 459 size_t ioHeight = GetDevicePixelHeight(); 460 461 unsigned char* ioData = (unsigned char*)GetBaseAddress(); 462 auto* dataCpy = new ( 463 fallible) unsigned char[bytesPerRow * ioHeight / sizeof(unsigned char)]; 464 if (NS_WARN_IF(!dataCpy)) { 465 Unlock(); 466 return nullptr; 467 } 468 469 for (size_t i = 0; i < ioHeight; i++) { 470 memcpy(dataCpy + i * bytesPerRow, ioData + i * bytesPerRow, ioWidth * 4); 471 } 472 473 Unlock(); 474 475 SurfaceFormat format = HasAlpha() ? mozilla::gfx::SurfaceFormat::B8G8R8A8 476 : mozilla::gfx::SurfaceFormat::B8G8R8X8; 477 478 RefPtr<mozilla::gfx::DataSourceSurface> surf = 479 mozilla::gfx::Factory::CreateWrappingDataSourceSurface( 480 dataCpy, bytesPerRow, IntSize(ioWidth, ioHeight), format, 481 &MacIOSurfaceBufferDeallocator, static_cast<void*>(dataCpy)); 482 483 return surf.forget(); 484 } 485 486 already_AddRefed<mozilla::gfx::DrawTarget> MacIOSurface::GetAsDrawTargetLocked( 487 mozilla::gfx::BackendType aBackendType) { 488 MOZ_RELEASE_ASSERT( 489 IsLocked(), 490 "Only call GetAsDrawTargetLocked while the surface is locked."); 491 492 size_t bytesPerRow = GetBytesPerRow(); 493 size_t ioWidth = GetDevicePixelWidth(); 494 size_t ioHeight = GetDevicePixelHeight(); 495 unsigned char* ioData = (unsigned char*)GetBaseAddress(); 496 SurfaceFormat format = GetFormat(); 497 return mozilla::gfx::Factory::CreateDrawTargetForData( 498 aBackendType, ioData, IntSize(ioWidth, ioHeight), bytesPerRow, format); 499 } 500 501 SurfaceFormat MacIOSurface::GetFormat() const { 502 return SurfaceFormatForPixelFormat(GetPixelFormat(), HasAlpha()); 503 } 504 505 SurfaceFormat MacIOSurface::GetReadFormat() const { 506 SurfaceFormat format = GetFormat(); 507 if (format == SurfaceFormat::YUY2) { 508 return SurfaceFormat::R8G8B8X8; 509 } 510 return format; 511 } 512 513 ColorDepth MacIOSurface::GetColorDepth() const { 514 switch (GetPixelFormat()) { 515 case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: 516 case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: 517 case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange: 518 case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange: 519 return ColorDepth::COLOR_10; 520 default: 521 return ColorDepth::COLOR_8; 522 } 523 } 524 525 #ifdef DEBUG 526 /* static */ Maybe<OSType> MacIOSurface::ChoosePixelFormat( 527 ChromaSubsampling aChromaSubsampling, ColorRange aColorRange, 528 ColorDepth aColorDepth) { 529 switch (aChromaSubsampling) { 530 case ChromaSubsampling::FULL: 531 if (aColorDepth == ColorDepth::COLOR_10) { 532 switch (aColorRange) { 533 case ColorRange::LIMITED: 534 return Some(kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange); 535 case ColorRange::FULL: 536 return Some(kCVPixelFormatType_422YpCbCr10BiPlanarFullRange); 537 } 538 } 539 break; 540 case ChromaSubsampling::HALF_WIDTH: 541 switch (aColorDepth) { 542 case ColorDepth::COLOR_8: 543 switch (aColorRange) { 544 case ColorRange::LIMITED: 545 return Some(kCVPixelFormatType_422YpCbCr8_yuvs); 546 case ColorRange::FULL: 547 return Some(kCVPixelFormatType_422YpCbCr8FullRange); 548 } 549 break; 550 case ColorDepth::COLOR_10: 551 switch (aColorRange) { 552 case ColorRange::LIMITED: 553 return Some(kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange); 554 case ColorRange::FULL: 555 return Some(kCVPixelFormatType_422YpCbCr10BiPlanarFullRange); 556 } 557 break; 558 default: 559 break; 560 } 561 break; 562 case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT: 563 switch (aColorDepth) { 564 case ColorDepth::COLOR_8: 565 switch (aColorRange) { 566 case ColorRange::LIMITED: 567 return Some(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); 568 case ColorRange::FULL: 569 return Some(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange); 570 } 571 break; 572 case ColorDepth::COLOR_10: 573 case ColorDepth::COLOR_12: 574 case ColorDepth::COLOR_16: 575 switch (aColorRange) { 576 case ColorRange::LIMITED: 577 return Some(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); 578 case ColorRange::FULL: 579 return Some(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange); 580 } 581 break; 582 } 583 break; 584 } 585 return Nothing(); 586 } 587 #endif 588 589 bool MacIOSurface::BindTexImage(mozilla::gl::GLContext* aGL, size_t aPlane, 590 mozilla::gfx::SurfaceFormat* aOutReadFormat) { 591 #ifdef XP_MACOSX 592 MOZ_ASSERT(aPlane >= 0); 593 bool isCompatibilityProfile = aGL->IsCompatibilityProfile(); 594 OSType pixelFormat = GetPixelFormat(); 595 596 GLenum internalFormat; 597 GLenum format; 598 GLenum type; 599 if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange || 600 pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { 601 MOZ_ASSERT(GetPlaneCount() == 2); 602 MOZ_ASSERT(aPlane < 2); 603 604 // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated 605 // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile. 606 // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats 607 if (aPlane == 0) { 608 internalFormat = format = 609 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED); 610 } else { 611 internalFormat = format = 612 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG); 613 } 614 type = LOCAL_GL_UNSIGNED_BYTE; 615 if (aOutReadFormat) { 616 *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV12; 617 } 618 } else if (pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange || 619 pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarFullRange) { 620 MOZ_ASSERT(GetPlaneCount() == 2); 621 MOZ_ASSERT(aPlane < 2); 622 623 // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated 624 // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile. 625 // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats 626 if (aPlane == 0) { 627 internalFormat = format = 628 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED); 629 } else { 630 internalFormat = format = 631 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG); 632 } 633 type = LOCAL_GL_UNSIGNED_SHORT; 634 if (aOutReadFormat) { 635 *aOutReadFormat = mozilla::gfx::SurfaceFormat::P010; 636 } 637 } else if (pixelFormat == kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange || 638 pixelFormat == kCVPixelFormatType_422YpCbCr10BiPlanarFullRange) { 639 MOZ_ASSERT(GetPlaneCount() == 2); 640 MOZ_ASSERT(aPlane < 2); 641 642 // The LOCAL_GL_LUMINANCE and LOCAL_GL_LUMINANCE_ALPHA are the deprecated 643 // format. So, use LOCAL_GL_RED and LOCAL_GL_RB if we use core profile. 644 // https://www.khronos.org/opengl/wiki/Image_Format#Legacy_Image_Formats 645 if (aPlane == 0) { 646 internalFormat = format = 647 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE) : (LOCAL_GL_RED); 648 } else { 649 internalFormat = format = 650 (isCompatibilityProfile) ? (LOCAL_GL_LUMINANCE_ALPHA) : (LOCAL_GL_RG); 651 } 652 type = LOCAL_GL_UNSIGNED_SHORT; 653 if (aOutReadFormat) { 654 *aOutReadFormat = mozilla::gfx::SurfaceFormat::NV16; 655 } 656 } else if (pixelFormat == kCVPixelFormatType_422YpCbCr8_yuvs || 657 pixelFormat == kCVPixelFormatType_422YpCbCr8FullRange) { 658 MOZ_ASSERT(aPlane == 0); 659 // The YCBCR_422_APPLE ext is only available in compatibility profile. So, 660 // we should use RGB_422_APPLE for core profile. The difference between 661 // YCBCR_422_APPLE and RGB_422_APPLE is that the YCBCR_422_APPLE converts 662 // the YCbCr value to RGB with REC 601 conversion. But the RGB_422_APPLE 663 // doesn't contain color conversion. You should do the color conversion by 664 // yourself for RGB_422_APPLE. 665 // 666 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_ycbcr_422.txt 667 // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt 668 if (isCompatibilityProfile) { 669 format = LOCAL_GL_YCBCR_422_APPLE; 670 if (aOutReadFormat) { 671 *aOutReadFormat = mozilla::gfx::SurfaceFormat::R8G8B8X8; 672 } 673 } else { 674 format = LOCAL_GL_RGB_422_APPLE; 675 if (aOutReadFormat) { 676 *aOutReadFormat = mozilla::gfx::SurfaceFormat::YUY2; 677 } 678 } 679 internalFormat = LOCAL_GL_RGB; 680 type = LOCAL_GL_UNSIGNED_SHORT_8_8_REV_APPLE; 681 } else { 682 MOZ_ASSERT(aPlane == 0); 683 684 internalFormat = HasAlpha() ? LOCAL_GL_RGBA : LOCAL_GL_RGB; 685 format = LOCAL_GL_BGRA; 686 type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; 687 if (aOutReadFormat) { 688 *aOutReadFormat = HasAlpha() ? mozilla::gfx::SurfaceFormat::R8G8B8A8 689 : mozilla::gfx::SurfaceFormat::R8G8B8X8; 690 } 691 } 692 693 size_t width = GetDevicePixelWidth(aPlane); 694 size_t height = GetDevicePixelHeight(aPlane); 695 696 auto err = ::CGLTexImageIOSurface2D( 697 gl::GLContextCGL::Cast(aGL)->GetCGLContext(), 698 LOCAL_GL_TEXTURE_RECTANGLE_ARB, internalFormat, width, height, format, 699 type, mIOSurfaceRef.get(), aPlane); 700 if (err) { 701 const auto formatChars = (const char*)&pixelFormat; 702 const char formatStr[] = {formatChars[3], formatChars[2], formatChars[1], 703 formatChars[0], 0}; 704 const nsPrintfCString errStr( 705 "CGLTexImageIOSurface2D(context, target, 0x%04x," 706 " %u, %u, 0x%04x, 0x%04x, iosurfPtr, %u) -> %i", 707 internalFormat, uint32_t(width), uint32_t(height), format, type, 708 (unsigned int)aPlane, err); 709 gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr 710 << ")"; 711 } 712 return !err; 713 #else 714 MOZ_CRASH("unimplemented"); 715 #endif 716 } 717 718 void MacIOSurface::SetColorSpace(const mozilla::gfx::ColorSpace2 cs) const { 719 Maybe<CFStringRef> str; 720 switch (cs) { 721 case gfx::ColorSpace2::UNKNOWN: 722 break; 723 case gfx::ColorSpace2::SRGB: 724 str = Some(kCGColorSpaceSRGB); 725 break; 726 case gfx::ColorSpace2::DISPLAY_P3: 727 str = Some(kCGColorSpaceDisplayP3); 728 break; 729 case gfx::ColorSpace2::BT601_525: // Doesn't really have a better option. 730 case gfx::ColorSpace2::BT709: 731 str = Some(kCGColorSpaceITUR_709); 732 break; 733 case gfx::ColorSpace2::BT2020: 734 str = Some(kCGColorSpaceITUR_2020); 735 break; 736 } 737 if (str) { 738 IOSurfaceSetValue(mIOSurfaceRef.get(), CFSTR("IOSurfaceColorSpace"), *str); 739 } 740 }