io_surface.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/gfx/mac/io_surface.h"
  5. #include <Availability.h>
  6. #include <CoreGraphics/CoreGraphics.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include "base/bits.h"
  10. #include "base/command_line.h"
  11. #include "base/feature_list.h"
  12. #include "base/logging.h"
  13. #include "base/mac/mac_util.h"
  14. #include "base/mac/mach_logging.h"
  15. #include "base/metrics/histogram_macros.h"
  16. #include "base/trace_event/trace_event.h"
  17. #include "ui/gfx/buffer_format_util.h"
  18. #include "ui/gfx/color_space.h"
  19. #include "ui/gfx/icc_profile.h"
  20. namespace gfx {
  21. namespace {
  22. const base::Feature kIOSurfaceUseNamedSRGBForREC709{
  23. "IOSurfaceUseNamedSRGBForREC709", base::FEATURE_ENABLED_BY_DEFAULT};
  24. void AddIntegerValue(CFMutableDictionaryRef dictionary,
  25. const CFStringRef key,
  26. int32_t value) {
  27. base::ScopedCFTypeRef<CFNumberRef> number(
  28. CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
  29. CFDictionaryAddValue(dictionary, key, number.get());
  30. }
  31. int32_t BytesPerElement(gfx::BufferFormat format, int plane) {
  32. switch (format) {
  33. case gfx::BufferFormat::R_8:
  34. DCHECK_EQ(plane, 0);
  35. return 1;
  36. case gfx::BufferFormat::R_16:
  37. DCHECK_EQ(plane, 0);
  38. return 2;
  39. case gfx::BufferFormat::RG_88:
  40. DCHECK_EQ(plane, 0);
  41. return 2;
  42. case gfx::BufferFormat::RG_1616:
  43. DCHECK_EQ(plane, 0);
  44. return 4;
  45. case gfx::BufferFormat::BGRA_8888:
  46. case gfx::BufferFormat::BGRX_8888:
  47. case gfx::BufferFormat::RGBA_8888:
  48. case gfx::BufferFormat::RGBX_8888:
  49. case gfx::BufferFormat::BGRA_1010102:
  50. DCHECK_EQ(plane, 0);
  51. return 4;
  52. case gfx::BufferFormat::RGBA_F16:
  53. DCHECK_EQ(plane, 0);
  54. return 8;
  55. case gfx::BufferFormat::YUV_420_BIPLANAR: {
  56. constexpr int32_t bytes_per_element[] = {1, 2};
  57. DCHECK_LT(static_cast<size_t>(plane), std::size(bytes_per_element));
  58. return bytes_per_element[plane];
  59. }
  60. case gfx::BufferFormat::P010: {
  61. constexpr int32_t bytes_per_element[] = {2, 4};
  62. DCHECK_LT(static_cast<size_t>(plane), std::size(bytes_per_element));
  63. return bytes_per_element[plane];
  64. }
  65. case gfx::BufferFormat::BGR_565:
  66. case gfx::BufferFormat::RGBA_4444:
  67. case gfx::BufferFormat::RGBA_1010102:
  68. case gfx::BufferFormat::YVU_420:
  69. NOTREACHED();
  70. return 0;
  71. }
  72. NOTREACHED();
  73. return 0;
  74. }
  75. } // namespace
  76. uint32_t BufferFormatToIOSurfacePixelFormat(gfx::BufferFormat format) {
  77. switch (format) {
  78. case gfx::BufferFormat::R_8:
  79. return 'L008';
  80. case gfx::BufferFormat::RG_88:
  81. return '2C08';
  82. case gfx::BufferFormat::R_16:
  83. return 'L016';
  84. case gfx::BufferFormat::RG_1616:
  85. return '2C16';
  86. case gfx::BufferFormat::BGRA_1010102:
  87. return 'l10r'; // little-endian ARGB2101010 full-range ARGB
  88. case gfx::BufferFormat::BGRA_8888:
  89. case gfx::BufferFormat::BGRX_8888:
  90. case gfx::BufferFormat::RGBA_8888:
  91. case gfx::BufferFormat::RGBX_8888:
  92. return 'BGRA';
  93. case gfx::BufferFormat::RGBA_F16:
  94. return 'RGhA';
  95. case gfx::BufferFormat::YUV_420_BIPLANAR:
  96. return '420v';
  97. case gfx::BufferFormat::P010:
  98. return 'x420';
  99. case gfx::BufferFormat::BGR_565:
  100. case gfx::BufferFormat::RGBA_4444:
  101. case gfx::BufferFormat::RGBA_1010102:
  102. // Technically RGBA_1010102 should be accepted as 'R10k', but then it won't
  103. // be supported by CGLTexImageIOSurface2D(), so it's best to reject it here.
  104. case gfx::BufferFormat::YVU_420:
  105. return 0;
  106. }
  107. NOTREACHED();
  108. return 0;
  109. }
  110. namespace internal {
  111. // static
  112. mach_port_t IOSurfaceMachPortTraits::Retain(mach_port_t port) {
  113. kern_return_t kr =
  114. mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
  115. MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
  116. << "IOSurfaceMachPortTraits::Retain mach_port_mod_refs";
  117. return port;
  118. }
  119. // static
  120. void IOSurfaceMachPortTraits::Release(mach_port_t port) {
  121. kern_return_t kr =
  122. mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1);
  123. MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
  124. << "IOSurfaceMachPortTraits::Release mach_port_mod_refs";
  125. }
  126. // Common method used by IOSurfaceSetColorSpace and IOSurfaceCanSetColorSpace.
  127. bool IOSurfaceSetColorSpace(IOSurfaceRef io_surface,
  128. const ColorSpace& color_space) {
  129. // Allow but ignore invalid color spaces.
  130. if (!color_space.IsValid())
  131. return true;
  132. // Prefer using named spaces.
  133. CFStringRef color_space_name = nullptr;
  134. if (color_space == ColorSpace::CreateSRGB() ||
  135. (base::FeatureList::IsEnabled(kIOSurfaceUseNamedSRGBForREC709) &&
  136. color_space == ColorSpace::CreateREC709())) {
  137. color_space_name = kCGColorSpaceSRGB;
  138. } else if (color_space == ColorSpace::CreateDisplayP3D65()) {
  139. color_space_name = kCGColorSpaceDisplayP3;
  140. } else if (color_space == ColorSpace::CreateExtendedSRGB()) {
  141. color_space_name = kCGColorSpaceExtendedSRGB;
  142. } else if (color_space == ColorSpace::CreateSRGBLinear()) {
  143. color_space_name = kCGColorSpaceExtendedLinearSRGB;
  144. }
  145. // The symbols kCGColorSpaceITUR_2020_PQ_EOTF and kCGColorSpaceITUR_2020_HLG
  146. // have been deprecated. Claim that we were able to set the color space,
  147. // because the path that will render these color spaces will use the
  148. // HDRCopier, which will manually convert them to a non-deprecated format.
  149. // https://crbug.com/1108627: Bug wherein these symbols are deprecated and
  150. // also not available in some SDK versions.
  151. // https://crbug.com/1101041: Introduces the HDR copier.
  152. // https://crbug.com/1061723: Discussion of issues related to HLG.
  153. if (__builtin_available(macos 10.15, *)) {
  154. if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
  155. ColorSpace::TransferID::PQ,
  156. ColorSpace::MatrixID::BT2020_NCL,
  157. ColorSpace::RangeID::LIMITED)) {
  158. if (__builtin_available(macos 11.0, *)) {
  159. color_space_name = kCGColorSpaceITUR_2100_PQ;
  160. } else {
  161. return true;
  162. }
  163. } else if (color_space == ColorSpace(ColorSpace::PrimaryID::BT2020,
  164. ColorSpace::TransferID::HLG,
  165. ColorSpace::MatrixID::BT2020_NCL,
  166. ColorSpace::RangeID::LIMITED)) {
  167. if (__builtin_available(macos 11.0, *)) {
  168. color_space_name = kCGColorSpaceITUR_2100_HLG;
  169. } else {
  170. return true;
  171. }
  172. }
  173. }
  174. if (color_space_name) {
  175. if (io_surface) {
  176. IOSurfaceSetValue(io_surface, CFSTR("IOSurfaceColorSpace"),
  177. color_space_name);
  178. }
  179. return true;
  180. }
  181. gfx::ColorSpace as_rgb = color_space.GetAsRGB();
  182. gfx::ColorSpace as_full_range_rgb = color_space.GetAsFullRangeRGB();
  183. // IOSurfaces do not support full-range YUV video. Fortunately, the hardware
  184. // decoders never produce full-range video.
  185. // https://crbug.com/882627
  186. if (color_space != as_rgb && as_rgb == as_full_range_rgb)
  187. return false;
  188. // Generate an ICCProfile from the parametric color space.
  189. ICCProfile icc_profile = ICCProfile::FromColorSpace(as_full_range_rgb);
  190. if (!icc_profile.IsValid())
  191. return false;
  192. // Package it as a CFDataRef and send it to the IOSurface.
  193. std::vector<char> icc_profile_data = icc_profile.GetData();
  194. base::ScopedCFTypeRef<CFDataRef> cf_data_icc_profile(CFDataCreate(
  195. nullptr, reinterpret_cast<const UInt8*>(icc_profile_data.data()),
  196. icc_profile_data.size()));
  197. IOSurfaceSetValue(io_surface, CFSTR("IOSurfaceColorSpace"),
  198. cf_data_icc_profile);
  199. return true;
  200. }
  201. } // namespace internal
  202. IOSurfaceRef CreateIOSurface(const gfx::Size& size,
  203. gfx::BufferFormat format,
  204. bool should_clear) {
  205. TRACE_EVENT0("ui", "CreateIOSurface");
  206. base::TimeTicks start_time = base::TimeTicks::Now();
  207. base::ScopedCFTypeRef<CFMutableDictionaryRef> properties(
  208. CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
  209. &kCFTypeDictionaryKeyCallBacks,
  210. &kCFTypeDictionaryValueCallBacks));
  211. AddIntegerValue(properties, kIOSurfaceWidth, size.width());
  212. AddIntegerValue(properties, kIOSurfaceHeight, size.height());
  213. AddIntegerValue(properties, kIOSurfacePixelFormat,
  214. BufferFormatToIOSurfacePixelFormat(format));
  215. // Don't specify plane information unless there are indeed multiple planes
  216. // because DisplayLink drivers do not support this.
  217. // http://crbug.com/527556
  218. size_t num_planes = gfx::NumberOfPlanesForLinearBufferFormat(format);
  219. if (num_planes > 1) {
  220. base::ScopedCFTypeRef<CFMutableArrayRef> planes(CFArrayCreateMutable(
  221. kCFAllocatorDefault, num_planes, &kCFTypeArrayCallBacks));
  222. size_t total_bytes_alloc = 0;
  223. for (size_t plane = 0; plane < num_planes; ++plane) {
  224. const size_t factor =
  225. gfx::SubsamplingFactorForBufferFormat(format, plane);
  226. const size_t plane_width = (size.width() + factor - 1) / factor;
  227. const size_t plane_height = (size.height() + factor - 1) / factor;
  228. const size_t plane_bytes_per_element = BytesPerElement(format, plane);
  229. const size_t plane_bytes_per_row =
  230. IOSurfaceAlignProperty(kIOSurfacePlaneBytesPerRow,
  231. base::bits::AlignUp(plane_width, size_t{2}) *
  232. plane_bytes_per_element);
  233. const size_t plane_bytes_alloc = IOSurfaceAlignProperty(
  234. kIOSurfacePlaneSize,
  235. base::bits::AlignUp(plane_height, size_t{2}) * plane_bytes_per_row);
  236. const size_t plane_offset =
  237. IOSurfaceAlignProperty(kIOSurfacePlaneOffset, total_bytes_alloc);
  238. base::ScopedCFTypeRef<CFMutableDictionaryRef> plane_info(
  239. CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
  240. &kCFTypeDictionaryKeyCallBacks,
  241. &kCFTypeDictionaryValueCallBacks));
  242. AddIntegerValue(plane_info, kIOSurfacePlaneWidth, plane_width);
  243. AddIntegerValue(plane_info, kIOSurfacePlaneHeight, plane_height);
  244. AddIntegerValue(plane_info, kIOSurfacePlaneBytesPerElement,
  245. plane_bytes_per_element);
  246. AddIntegerValue(plane_info, kIOSurfacePlaneBytesPerRow,
  247. plane_bytes_per_row);
  248. AddIntegerValue(plane_info, kIOSurfacePlaneSize, plane_bytes_alloc);
  249. AddIntegerValue(plane_info, kIOSurfacePlaneOffset, plane_offset);
  250. CFArrayAppendValue(planes, plane_info);
  251. total_bytes_alloc = plane_offset + plane_bytes_alloc;
  252. }
  253. CFDictionaryAddValue(properties, kIOSurfacePlaneInfo, planes);
  254. total_bytes_alloc =
  255. IOSurfaceAlignProperty(kIOSurfaceAllocSize, total_bytes_alloc);
  256. AddIntegerValue(properties, kIOSurfaceAllocSize, total_bytes_alloc);
  257. } else {
  258. const size_t bytes_per_element = BytesPerElement(format, 0);
  259. const size_t bytes_per_row = IOSurfaceAlignProperty(
  260. kIOSurfaceBytesPerRow,
  261. base::bits::AlignUp(size.width(), 2) * bytes_per_element);
  262. const size_t bytes_alloc = IOSurfaceAlignProperty(
  263. kIOSurfaceAllocSize,
  264. base::bits::AlignUp(size.height(), 2) * bytes_per_row);
  265. AddIntegerValue(properties, kIOSurfaceBytesPerElement, bytes_per_element);
  266. AddIntegerValue(properties, kIOSurfaceBytesPerRow, bytes_per_row);
  267. AddIntegerValue(properties, kIOSurfaceAllocSize, bytes_alloc);
  268. }
  269. IOSurfaceRef surface = IOSurfaceCreate(properties);
  270. if (!surface) {
  271. LOG(ERROR) << "Failed to allocate IOSurface of size " << size.ToString()
  272. << ".";
  273. return nullptr;
  274. }
  275. if (should_clear) {
  276. // Zero-initialize the IOSurface. Calling IOSurfaceLock/IOSurfaceUnlock
  277. // appears to be sufficient. https://crbug.com/584760#c17
  278. IOReturn r = IOSurfaceLock(surface, 0, nullptr);
  279. DCHECK_EQ(kIOReturnSuccess, r);
  280. r = IOSurfaceUnlock(surface, 0, nullptr);
  281. DCHECK_EQ(kIOReturnSuccess, r);
  282. }
  283. // Ensure that all IOSurfaces start as sRGB.
  284. IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), kCGColorSpaceSRGB);
  285. UMA_HISTOGRAM_TIMES("GPU.IOSurface.CreateTime",
  286. base::TimeTicks::Now() - start_time);
  287. return surface;
  288. }
  289. bool IOSurfaceCanSetColorSpace(const ColorSpace& color_space) {
  290. return internal::IOSurfaceSetColorSpace(nullptr, color_space);
  291. }
  292. void IOSurfaceSetColorSpace(IOSurfaceRef io_surface,
  293. const ColorSpace& color_space) {
  294. if (!internal::IOSurfaceSetColorSpace(io_surface, color_space)) {
  295. DLOG(ERROR) << "Failed to set color space for IOSurface: "
  296. << color_space.ToString();
  297. }
  298. }
  299. GFX_EXPORT base::ScopedCFTypeRef<IOSurfaceRef> IOSurfaceMachPortToIOSurface(
  300. ScopedRefCountedIOSurfaceMachPort io_surface_mach_port) {
  301. base::ScopedCFTypeRef<IOSurfaceRef> io_surface;
  302. if (!io_surface_mach_port) {
  303. DLOG(ERROR) << "Invalid mach port.";
  304. return io_surface;
  305. }
  306. io_surface.reset(IOSurfaceLookupFromMachPort(io_surface_mach_port));
  307. if (!io_surface) {
  308. DLOG(ERROR) << "Unable to lookup IOSurface.";
  309. return io_surface;
  310. }
  311. return io_surface;
  312. }
  313. } // namespace gfx