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/. */
7 #include "MacIOSurfaceHelpers.h"
8 #include "MacIOSurfaceImage.h"
9 #include "gfxPlatform.h"
10 #include "mozilla/layers/CompositableClient.h"
11 #include "mozilla/layers/CompositableForwarder.h"
12 #include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
13 #include "mozilla/layers/TextureForwarder.h"
14 #include "mozilla/StaticPrefs_layers.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/Unused.h"
17 #include "YCbCrUtils.h"
19 using namespace mozilla::layers
;
20 using namespace mozilla::gfx
;
22 TextureClient
* MacIOSurfaceImage::GetTextureClient(
23 KnowsCompositor
* aKnowsCompositor
) {
24 if (!mTextureClient
) {
25 BackendType backend
= BackendType::NONE
;
27 IsDRM() ? TextureFlags::DRM_SOURCE
: TextureFlags::DEFAULT
;
28 mTextureClient
= TextureClient::CreateWithData(
29 MacIOSurfaceTextureData::Create(mSurface
, backend
), flags
,
30 aKnowsCompositor
->GetTextureForwarder());
32 return mTextureClient
;
35 ColorDepth
MacIOSurfaceImage::GetColorDepth() const {
37 return gfx::ColorDepth::COLOR_8
;
39 return mSurface
->GetColorDepth();
42 already_AddRefed
<SourceSurface
> MacIOSurfaceImage::GetAsSourceSurface() {
43 return CreateSourceSurfaceFromMacIOSurface(mSurface
);
46 static inline uint16_t safeShift10BitBy6(const uint16_t& a10BitLSB
) {
47 // a10BitLSB is a 10-bit value packed into the least significant bits of
48 // a 16 bit value. This function asserts that the 6 MSBs are zero, then
49 // shifts the 10 LSBs by 6 to become the MSBs.
50 MOZ_ASSERT((a10BitLSB
& 0b1111'1100'0000'0000) == 0);
51 return a10BitLSB
<< 6;
54 bool MacIOSurfaceImage::SetData(ImageContainer
* aContainer
,
55 const PlanarYCbCrData
& aData
) {
56 MOZ_ASSERT(!mSurface
);
58 if (aData
.mYSkip
!= 0 || aData
.mCbSkip
!= 0 || aData
.mCrSkip
!= 0 ||
59 !(aData
.mYUVColorSpace
== YUVColorSpace::BT601
||
60 aData
.mYUVColorSpace
== YUVColorSpace::BT709
||
61 aData
.mYUVColorSpace
== YUVColorSpace::BT2020
) ||
62 !(aData
.mColorRange
== ColorRange::FULL
||
63 aData
.mColorRange
== ColorRange::LIMITED
) ||
64 !(aData
.mColorDepth
== ColorDepth::COLOR_8
||
65 aData
.mColorDepth
== ColorDepth::COLOR_10
)) {
69 // We can only support 4:2:2 and 4:2:0 formats currently.
70 switch (aData
.mChromaSubsampling
) {
71 case ChromaSubsampling::HALF_WIDTH
:
72 case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT
:
78 RefPtr
<MacIOSurfaceRecycleAllocator
> allocator
=
79 aContainer
->GetMacIOSurfaceRecycleAllocator();
81 auto ySize
= aData
.YDataSize();
82 auto cbcrSize
= aData
.CbCrDataSize();
83 RefPtr
<MacIOSurface
> surf
= allocator
->Allocate(
84 ySize
, cbcrSize
, aData
.mYUVColorSpace
, aData
.mTransferFunction
,
85 aData
.mColorRange
, aData
.mColorDepth
);
89 if (surf
->GetFormat() == SurfaceFormat::YUV422
) {
90 // If the CbCrSize's height is half of the YSize's height, then we'll
91 // need to duplicate the CbCr data on every second row.
92 size_t heightScale
= ySize
.height
/ cbcrSize
.height
;
94 // The underlying IOSurface has format
95 // kCVPixelFormatType_422YpCbCr8FullRange or
96 // kCVPixelFormatType_422YpCbCr8_yuvs, which uses a 4:2:2 Y`0 Cb Y`1 Cr
97 // layout. See CVPixelBuffer.h for the full list of format descriptions.
98 MOZ_ASSERT(ySize
.height
> 0);
99 uint8_t* dst
= (uint8_t*)surf
->GetBaseAddressOfPlane(0);
100 size_t stride
= surf
->GetBytesPerRow(0);
101 for (size_t i
= 0; i
< (size_t)ySize
.height
; i
++) {
102 // Compute the row addresses. If the input was 4:2:0, then
103 // we divide i by 2, so that each source row of CbCr maps to
105 uint8_t* rowYSrc
= aData
.mYChannel
+ aData
.mYStride
* i
;
107 aData
.mCbChannel
+ aData
.mCbCrStride
* (i
/ heightScale
);
109 aData
.mCrChannel
+ aData
.mCbCrStride
* (i
/ heightScale
);
110 uint8_t* rowDst
= dst
+ stride
* i
;
112 // Iterate across the CbCr width (which we have guaranteed to be half of
113 // the surface width), and write two 16bit pixels each time.
114 for (size_t j
= 0; j
< (size_t)cbcrSize
.width
; j
++) {
132 } else if (surf
->GetFormat() == SurfaceFormat::NV12
) {
133 MOZ_ASSERT(ySize
.height
> 0);
134 uint8_t* dst
= (uint8_t*)surf
->GetBaseAddressOfPlane(0);
135 size_t stride
= surf
->GetBytesPerRow(0);
136 for (size_t i
= 0; i
< (size_t)ySize
.height
; i
++) {
137 uint8_t* rowSrc
= aData
.mYChannel
+ aData
.mYStride
* i
;
138 uint8_t* rowDst
= dst
+ stride
* i
;
139 memcpy(rowDst
, rowSrc
, ySize
.width
);
142 // Copy and interleave the Cb and Cr channels.
143 MOZ_ASSERT(cbcrSize
.height
> 0);
144 dst
= (uint8_t*)surf
->GetBaseAddressOfPlane(1);
145 stride
= surf
->GetBytesPerRow(1);
146 for (size_t i
= 0; i
< (size_t)cbcrSize
.height
; i
++) {
147 uint8_t* rowCbSrc
= aData
.mCbChannel
+ aData
.mCbCrStride
* i
;
148 uint8_t* rowCrSrc
= aData
.mCrChannel
+ aData
.mCbCrStride
* i
;
149 uint8_t* rowDst
= dst
+ stride
* i
;
151 for (size_t j
= 0; j
< (size_t)cbcrSize
.width
; j
++) {
161 } else if (surf
->GetFormat() == SurfaceFormat::P010
) {
162 MOZ_ASSERT(ySize
.height
> 0);
163 auto dst
= reinterpret_cast<uint16_t*>(surf
->GetBaseAddressOfPlane(0));
164 size_t stride
= surf
->GetBytesPerRow(0) / 2;
165 for (size_t i
= 0; i
< (size_t)ySize
.height
; i
++) {
166 auto rowSrc
= reinterpret_cast<const uint16_t*>(aData
.mYChannel
+
168 auto rowDst
= dst
+ stride
* i
;
170 for (const auto j
: IntegerRange(ySize
.width
)) {
173 *rowDst
= safeShift10BitBy6(*rowSrc
);
179 // Copy and interleave the Cb and Cr channels.
180 MOZ_ASSERT(cbcrSize
.height
> 0);
181 dst
= (uint16_t*)surf
->GetBaseAddressOfPlane(1);
182 stride
= surf
->GetBytesPerRow(1) / 2;
183 for (size_t i
= 0; i
< (size_t)cbcrSize
.height
; i
++) {
185 (uint16_t*)(aData
.mCbChannel
+ aData
.mCbCrStride
* i
);
187 (uint16_t*)(aData
.mCrChannel
+ aData
.mCbCrStride
* i
);
188 uint16_t* rowDst
= dst
+ stride
* i
;
190 for (const auto j
: IntegerRange(cbcrSize
.width
)) {
193 *rowDst
= safeShift10BitBy6(*rowCbSrc
);
197 *rowDst
= safeShift10BitBy6(*rowCrSrc
);
206 mPictureRect
= aData
.mPictureRect
;
210 already_AddRefed
<MacIOSurface
> MacIOSurfaceRecycleAllocator::Allocate(
211 const gfx::IntSize aYSize
, const gfx::IntSize
& aCbCrSize
,
212 gfx::YUVColorSpace aYUVColorSpace
, gfx::TransferFunction aTransferFunction
,
213 gfx::ColorRange aColorRange
, gfx::ColorDepth aColorDepth
) {
214 nsTArray
<CFTypeRefPtr
<IOSurfaceRef
>> surfaces
= std::move(mSurfaces
);
215 RefPtr
<MacIOSurface
> result
;
216 for (auto& surf
: surfaces
) {
217 // If the surface size has changed, then discard any surfaces of the old
219 if (::IOSurfaceGetWidthOfPlane(surf
.get(), 0) != (size_t)aYSize
.width
||
220 ::IOSurfaceGetHeightOfPlane(surf
.get(), 0) != (size_t)aYSize
.height
) {
224 // Only construct a MacIOSurface object when we find one that isn't
225 // in-use, since the constructor adds a usage ref.
226 if (!result
&& !::IOSurfaceIsInUse(surf
.get())) {
227 result
= new MacIOSurface(surf
, false, aYUVColorSpace
);
230 mSurfaces
.AppendElement(surf
);
234 if (StaticPrefs::layers_iosurfaceimage_use_nv12_AtStartup()) {
235 result
= MacIOSurface::CreateNV12OrP010Surface(
236 aYSize
, aCbCrSize
, aYUVColorSpace
, aTransferFunction
, aColorRange
,
239 result
= MacIOSurface::CreateYUV422Surface(aYSize
, aYUVColorSpace
,
243 if (mSurfaces
.Length() <
244 StaticPrefs::layers_iosurfaceimage_recycle_limit()) {
245 mSurfaces
.AppendElement(result
->GetIOSurfaceRef());
249 return result
.forget();