// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/mac/io_surface.h" #include #include #include "base/command_line.h" #include "base/logging.h" #include "base/mac/mac_util.h" #include "base/mac/mach_logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/buffer_format_util.h" #include "ui/gfx/icc_profile.h" namespace gfx { namespace { void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) { base::ScopedCFTypeRef number( CFNumberCreate(NULL, kCFNumberSInt32Type, &value)); CFDictionaryAddValue(dictionary, key, number.get()); } int32_t BytesPerElement(gfx::BufferFormat format, int plane) { switch (format) { case gfx::BufferFormat::R_8: DCHECK_EQ(plane, 0); return 1; case gfx::BufferFormat::BGRA_8888: case gfx::BufferFormat::BGRX_8888: case gfx::BufferFormat::RGBA_8888: case gfx::BufferFormat::BGRX_1010102: DCHECK_EQ(plane, 0); return 4; case gfx::BufferFormat::RGBA_F16: DCHECK_EQ(plane, 0); return 8; case gfx::BufferFormat::YUV_420_BIPLANAR: static int32_t bytes_per_element[] = {1, 2}; DCHECK_LT(static_cast(plane), arraysize(bytes_per_element)); return bytes_per_element[plane]; case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: case gfx::BufferFormat::UYVY_422: DCHECK_EQ(plane, 0); return 2; case gfx::BufferFormat::ATC: case gfx::BufferFormat::ATCIA: case gfx::BufferFormat::DXT1: case gfx::BufferFormat::DXT5: case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: case gfx::BufferFormat::RGBX_1010102: case gfx::BufferFormat::YVU_420: NOTREACHED(); return 0; } NOTREACHED(); return 0; } int32_t PixelFormat(gfx::BufferFormat format) { switch (format) { case gfx::BufferFormat::R_8: return 'L008'; case gfx::BufferFormat::BGRX_1010102: return 'l10r'; // little-endian ARGB2101010 full-range ARGB case gfx::BufferFormat::BGRA_8888: case gfx::BufferFormat::BGRX_8888: case gfx::BufferFormat::RGBA_8888: return 'BGRA'; case gfx::BufferFormat::RGBA_F16: return 'RGhA'; case gfx::BufferFormat::YUV_420_BIPLANAR: return '420v'; case gfx::BufferFormat::UYVY_422: return '2vuy'; case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: case gfx::BufferFormat::ATC: case gfx::BufferFormat::ATCIA: case gfx::BufferFormat::DXT1: case gfx::BufferFormat::DXT5: case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: case gfx::BufferFormat::RGBX_1010102: // Technically RGBX_1010102 should be accepted as 'R10k', but then it won't // be supported by CGLTexImageIOSurface2D(), so it's best to reject it here. case gfx::BufferFormat::YVU_420: NOTREACHED(); return 0; } NOTREACHED(); return 0; } } // namespace namespace internal { // static mach_port_t IOSurfaceMachPortTraits::Retain(mach_port_t port) { kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1); MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "IOSurfaceMachPortTraits::Retain mach_port_mod_refs"; return port; } // static void IOSurfaceMachPortTraits::Release(mach_port_t port) { kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1); MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "IOSurfaceMachPortTraits::Release mach_port_mod_refs"; } } // namespace internal IOSurfaceRef CreateIOSurface(const gfx::Size& size, gfx::BufferFormat format, bool should_clear) { TRACE_EVENT0("ui", "CreateIOSurface"); base::TimeTicks start_time = base::TimeTicks::Now(); size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); base::ScopedCFTypeRef planes(CFArrayCreateMutable( kCFAllocatorDefault, num_planes, &kCFTypeArrayCallBacks)); // Don't specify plane information unless there are indeed multiple planes // because DisplayLink drivers do not support this. // http://crbug.com/527556 if (num_planes > 1) { for (size_t plane = 0; plane < num_planes; ++plane) { size_t factor = gfx::SubsamplingFactorForBufferFormat(format, plane); base::ScopedCFTypeRef plane_info( CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); AddIntegerValue(plane_info, kIOSurfacePlaneWidth, size.width() / factor); AddIntegerValue(plane_info, kIOSurfacePlaneHeight, size.height() / factor); AddIntegerValue(plane_info, kIOSurfacePlaneBytesPerElement, BytesPerElement(format, plane)); CFArrayAppendValue(planes, plane_info); } } base::ScopedCFTypeRef properties( CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); AddIntegerValue(properties, kIOSurfaceWidth, size.width()); AddIntegerValue(properties, kIOSurfaceHeight, size.height()); AddIntegerValue(properties, kIOSurfacePixelFormat, PixelFormat(format)); if (num_planes > 1) { CFDictionaryAddValue(properties, kIOSurfacePlaneInfo, planes); } else { AddIntegerValue(properties, kIOSurfaceBytesPerElement, BytesPerElement(format, 0)); } IOSurfaceRef surface = IOSurfaceCreate(properties); if (!surface) { LOG(ERROR) << "Failed to allocate IOSurface of size " << size.ToString() << "."; return nullptr; } // For unknown reasons, triggering this lock on OS X 10.9, on certain GPUs, // causes PDFs to render incorrectly. Hopefully this check can be removed once // pdfium switches to a Skia backend on Mac. // https://crbug.com/594343. // IOSurface clearing causes significant performance regression on about half // of all devices running Yosemite. https://crbug.com/606850#c22. if (base::mac::IsOS10_9() || base::mac::IsOS10_10()) should_clear = false; if (should_clear) { // Zero-initialize the IOSurface. Calling IOSurfaceLock/IOSurfaceUnlock // appears to be sufficient. https://crbug.com/584760#c17 IOReturn r = IOSurfaceLock(surface, 0, nullptr); DCHECK_EQ(kIOReturnSuccess, r); r = IOSurfaceUnlock(surface, 0, nullptr); DCHECK_EQ(kIOReturnSuccess, r); } // Ensure that all IOSurfaces start as sRGB. CGColorSpaceRef color_space = base::mac::GetSRGBColorSpace(); base::ScopedCFTypeRef color_space_icc( CGColorSpaceCopyICCProfile(color_space)); IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), color_space_icc); UMA_HISTOGRAM_TIMES("GPU.IOSurface.CreateTime", base::TimeTicks::Now() - start_time); return surface; } void IOSurfaceSetColorSpace(IOSurfaceRef io_surface, const ColorSpace& color_space) { // Retrieve the ICC profile data that created this profile, if it exists. ICCProfile icc_profile = ICCProfile::FromCacheMac(color_space); // If that fails, generate parametric data. if (!icc_profile.IsValid()) { icc_profile = ICCProfile::FromParametricColorSpace(color_space.GetAsFullRangeRGB()); } if (!icc_profile.IsValid()) { if (base::mac::IsAtLeastOS10_12()) { static const ColorSpace kBt2020(ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::SMPTEST2084, ColorSpace::MatrixID::BT2020_NCL, ColorSpace::RangeID::LIMITED); if (color_space == kBt2020) { base::ScopedCFTypeRef cg_color_space( CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020)); DCHECK(cg_color_space); base::ScopedCFTypeRef cf_data_icc_profile( CGColorSpaceCopyICCData(cg_color_space)); DCHECK(cf_data_icc_profile); IOSurfaceSetValue(io_surface, CFSTR("IOSurfaceColorSpace"), cf_data_icc_profile); return; } } DLOG(ERROR) << "Failed to set color space for IOSurface: no ICC profile: " << color_space.ToString(); return; } std::vector icc_profile_data = icc_profile.GetData(); // Package it as a CFDataRef and send it to the IOSurface. base::ScopedCFTypeRef cf_data_icc_profile(CFDataCreate( nullptr, reinterpret_cast(icc_profile_data.data()), icc_profile_data.size())); IOSurfaceSetValue(io_surface, CFSTR("IOSurfaceColorSpace"), cf_data_icc_profile); } } // namespace gfx