Backed out changeset 62ed9ebaa426 (bug 1933152) for causing bc failures @browser_ext_...
[gecko.git] / gfx / layers / MacIOSurfaceImage.cpp
blobed1a7413a8a6dddc7123aa7b18cd3fbba8630f66
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;
26 TextureFlags flags =
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 {
36 if (!mSurface) {
37 return gfx::ColorDepth::COLOR_8;
39 return mSurface->GetColorDepth();
42 already_AddRefed<SourceSurface> MacIOSurfaceImage::GetAsSourceSurface() {
43 return CreateSourceSurfaceFromMacIOSurface(mSurface);
46 nsresult MacIOSurfaceImage::BuildSurfaceDescriptorBuffer(
47 SurfaceDescriptorBuffer& aSdBuffer, BuildSdbFlags aFlags,
48 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) {
49 return CreateSurfaceDescriptorBufferFromMacIOSurface(mSurface, aSdBuffer,
50 aFlags, aAllocate);
53 static inline uint16_t safeShift10BitBy6(const uint16_t& a10BitLSB) {
54 // a10BitLSB is a 10-bit value packed into the least significant bits of
55 // a 16 bit value. This function asserts that the 6 MSBs are zero, then
56 // shifts the 10 LSBs by 6 to become the MSBs.
57 MOZ_ASSERT((a10BitLSB & 0b1111'1100'0000'0000) == 0);
58 return a10BitLSB << 6;
61 bool MacIOSurfaceImage::SetData(ImageContainer* aContainer,
62 const PlanarYCbCrData& aData) {
63 MOZ_ASSERT(!mSurface);
65 if (aData.mYSkip != 0 || aData.mCbSkip != 0 || aData.mCrSkip != 0 ||
66 !(aData.mYUVColorSpace == YUVColorSpace::BT601 ||
67 aData.mYUVColorSpace == YUVColorSpace::BT709 ||
68 aData.mYUVColorSpace == YUVColorSpace::BT2020) ||
69 !(aData.mColorRange == ColorRange::FULL ||
70 aData.mColorRange == ColorRange::LIMITED) ||
71 !(aData.mColorDepth == ColorDepth::COLOR_8 ||
72 aData.mColorDepth == ColorDepth::COLOR_10)) {
73 return false;
76 // We can only support 4:2:2 and 4:2:0 formats currently.
77 switch (aData.mChromaSubsampling) {
78 case ChromaSubsampling::HALF_WIDTH:
79 case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
80 break;
81 default:
82 return false;
85 RefPtr<MacIOSurfaceRecycleAllocator> allocator =
86 aContainer->GetMacIOSurfaceRecycleAllocator();
88 auto ySize = aData.YDataSize();
89 auto cbcrSize = aData.CbCrDataSize();
90 RefPtr<MacIOSurface> surf = allocator->Allocate(
91 ySize, cbcrSize, aData.mChromaSubsampling, aData.mYUVColorSpace,
92 aData.mTransferFunction, aData.mColorRange, aData.mColorDepth);
94 if (NS_WARN_IF(!surf) || NS_WARN_IF(!surf->Lock(false))) {
95 return false;
98 if (surf->GetFormat() == SurfaceFormat::YUY2) {
99 // If the CbCrSize's height is half of the YSize's height, then we'll
100 // need to duplicate the CbCr data on every second row.
101 size_t heightScale = ySize.height / cbcrSize.height;
103 // The underlying IOSurface has format
104 // kCVPixelFormatType_422YpCbCr8FullRange or
105 // kCVPixelFormatType_422YpCbCr8_yuvs, which uses a 4:2:2 Y`0 Cb Y`1 Cr
106 // layout. See CVPixelBuffer.h for the full list of format descriptions.
107 MOZ_ASSERT(ySize.height > 0);
108 uint8_t* dst = (uint8_t*)surf->GetBaseAddressOfPlane(0);
109 size_t stride = surf->GetBytesPerRow(0);
110 for (size_t i = 0; i < (size_t)ySize.height; i++) {
111 // Compute the row addresses. If the input was 4:2:0, then
112 // we divide i by 2, so that each source row of CbCr maps to
113 // two dest rows.
114 uint8_t* rowYSrc = aData.mYChannel + aData.mYStride * i;
115 uint8_t* rowCbSrc =
116 aData.mCbChannel + aData.mCbCrStride * (i / heightScale);
117 uint8_t* rowCrSrc =
118 aData.mCrChannel + aData.mCbCrStride * (i / heightScale);
119 uint8_t* rowDst = dst + stride * i;
121 // Iterate across the CbCr width (which we have guaranteed to be half of
122 // the surface width), and write two 16bit pixels each time.
123 for (size_t j = 0; j < (size_t)cbcrSize.width; j++) {
124 *rowDst = *rowYSrc;
125 rowDst++;
126 rowYSrc++;
128 *rowDst = *rowCbSrc;
129 rowDst++;
130 rowCbSrc++;
132 *rowDst = *rowYSrc;
133 rowDst++;
134 rowYSrc++;
136 *rowDst = *rowCrSrc;
137 rowDst++;
138 rowCrSrc++;
141 } else if (surf->GetFormat() == SurfaceFormat::NV12) {
142 MOZ_ASSERT(ySize.height > 0);
143 uint8_t* dst = (uint8_t*)surf->GetBaseAddressOfPlane(0);
144 size_t stride = surf->GetBytesPerRow(0);
145 for (size_t i = 0; i < (size_t)ySize.height; i++) {
146 uint8_t* rowSrc = aData.mYChannel + aData.mYStride * i;
147 uint8_t* rowDst = dst + stride * i;
148 memcpy(rowDst, rowSrc, ySize.width);
151 // Copy and interleave the Cb and Cr channels.
152 MOZ_ASSERT(cbcrSize.height > 0);
153 dst = (uint8_t*)surf->GetBaseAddressOfPlane(1);
154 stride = surf->GetBytesPerRow(1);
155 for (size_t i = 0; i < (size_t)cbcrSize.height; i++) {
156 uint8_t* rowCbSrc = aData.mCbChannel + aData.mCbCrStride * i;
157 uint8_t* rowCrSrc = aData.mCrChannel + aData.mCbCrStride * i;
158 uint8_t* rowDst = dst + stride * i;
160 for (size_t j = 0; j < (size_t)cbcrSize.width; j++) {
161 *rowDst = *rowCbSrc;
162 rowDst++;
163 rowCbSrc++;
165 *rowDst = *rowCrSrc;
166 rowDst++;
167 rowCrSrc++;
170 } else if (surf->GetFormat() == SurfaceFormat::P010) {
171 MOZ_ASSERT(ySize.height > 0);
172 auto dst = reinterpret_cast<uint16_t*>(surf->GetBaseAddressOfPlane(0));
173 size_t stride = surf->GetBytesPerRow(0) / 2;
174 for (size_t i = 0; i < (size_t)ySize.height; i++) {
175 auto rowSrc = reinterpret_cast<const uint16_t*>(aData.mYChannel +
176 aData.mYStride * i);
177 auto rowDst = dst + stride * i;
179 for (const auto j : IntegerRange(ySize.width)) {
180 Unused << j;
182 *rowDst = safeShift10BitBy6(*rowSrc);
183 rowDst++;
184 rowSrc++;
188 // Copy and interleave the Cb and Cr channels.
189 MOZ_ASSERT(cbcrSize.height > 0);
190 dst = (uint16_t*)surf->GetBaseAddressOfPlane(1);
191 stride = surf->GetBytesPerRow(1) / 2;
192 for (size_t i = 0; i < (size_t)cbcrSize.height; i++) {
193 uint16_t* rowCbSrc =
194 (uint16_t*)(aData.mCbChannel + aData.mCbCrStride * i);
195 uint16_t* rowCrSrc =
196 (uint16_t*)(aData.mCrChannel + aData.mCbCrStride * i);
197 uint16_t* rowDst = dst + stride * i;
199 for (const auto j : IntegerRange(cbcrSize.width)) {
200 Unused << j;
202 *rowDst = safeShift10BitBy6(*rowCbSrc);
203 rowDst++;
204 rowCbSrc++;
206 *rowDst = safeShift10BitBy6(*rowCrSrc);
207 rowDst++;
208 rowCrSrc++;
211 } else if (surf->GetFormat() == SurfaceFormat::NV16) {
212 MOZ_ASSERT(aData.mColorDepth == ColorDepth::COLOR_10,
213 "Currently NV16 only supports 10-bit color.");
214 MOZ_ASSERT(ySize.height > 0);
215 auto dst = reinterpret_cast<uint16_t*>(surf->GetBaseAddressOfPlane(0));
216 size_t stride = surf->GetBytesPerRow(0) / 2;
217 for (size_t i = 0; i < (size_t)ySize.height; i++) {
218 auto rowSrc = reinterpret_cast<const uint16_t*>(aData.mYChannel +
219 aData.mYStride * i);
220 auto rowDst = dst + stride * i;
222 for (const auto j : IntegerRange(ySize.width)) {
223 Unused << j;
225 *rowDst = safeShift10BitBy6(*rowSrc);
226 rowDst++;
227 rowSrc++;
231 // Copy and interleave the Cb and Cr channels.
232 MOZ_ASSERT(cbcrSize.height > 0);
233 MOZ_ASSERT(cbcrSize.height == ySize.height,
234 "4:2:2 CbCr should have same height as Y.");
235 dst = (uint16_t*)surf->GetBaseAddressOfPlane(1);
236 stride = surf->GetBytesPerRow(1) / 2;
237 for (size_t i = 0; i < (size_t)cbcrSize.height; i++) {
238 uint16_t* rowCbSrc =
239 (uint16_t*)(aData.mCbChannel + aData.mCbCrStride * i);
240 uint16_t* rowCrSrc =
241 (uint16_t*)(aData.mCrChannel + aData.mCbCrStride * i);
242 uint16_t* rowDst = dst + stride * i;
244 for (const auto j : IntegerRange(cbcrSize.width)) {
245 Unused << j;
247 *rowDst = safeShift10BitBy6(*rowCbSrc);
248 rowDst++;
249 rowCbSrc++;
251 *rowDst = safeShift10BitBy6(*rowCrSrc);
252 rowDst++;
253 rowCrSrc++;
258 surf->Unlock(false);
259 mSurface = surf;
260 mPictureRect = aData.mPictureRect;
261 return true;
264 already_AddRefed<MacIOSurface> MacIOSurfaceRecycleAllocator::Allocate(
265 const gfx::IntSize aYSize, const gfx::IntSize& aCbCrSize,
266 gfx::ChromaSubsampling aChromaSubsampling,
267 gfx::YUVColorSpace aYUVColorSpace, gfx::TransferFunction aTransferFunction,
268 gfx::ColorRange aColorRange, gfx::ColorDepth aColorDepth) {
269 // To avoid checking every property of every surface, we just cache the
270 // parameters used during the last allocation. If any of these have changed,
271 // dump the cached surfaces and update our cached parameters.
272 if (mYSize != aYSize || mCbCrSize != aCbCrSize ||
273 mChromaSubsampling != aChromaSubsampling ||
274 mYUVColorSpace != aYUVColorSpace ||
275 mTransferFunction != aTransferFunction || mColorRange != aColorRange ||
276 mColorDepth != aColorDepth) {
277 mSurfaces.Clear();
278 mYSize = aYSize;
279 mCbCrSize = aCbCrSize;
280 mChromaSubsampling = aChromaSubsampling;
281 mYUVColorSpace = aYUVColorSpace;
282 mTransferFunction = aTransferFunction;
283 mColorRange = aColorRange;
284 mColorDepth = aColorDepth;
287 // Scan for an unused surface, and reuse that if one is available.
288 for (auto& surf : mSurfaces) {
289 if (::IOSurfaceIsInUse(surf.get())) {
290 continue;
293 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
294 Maybe<OSType> pixelFormat = MacIOSurface::ChoosePixelFormat(
295 aChromaSubsampling, aColorRange, aColorDepth);
296 MOZ_DIAGNOSTIC_ASSERT(pixelFormat.isSome());
297 MOZ_DIAGNOSTIC_ASSERT(::IOSurfaceGetPixelFormat(surf.get()) ==
298 *pixelFormat);
299 MOZ_DIAGNOSTIC_ASSERT(::IOSurfaceGetWidthOfPlane(surf.get(), 0) ==
300 (size_t)aYSize.width);
301 MOZ_DIAGNOSTIC_ASSERT(::IOSurfaceGetHeightOfPlane(surf.get(), 0) ==
302 (size_t)aYSize.height);
303 if (*pixelFormat != kCVPixelFormatType_422YpCbCr8_yuvs &&
304 *pixelFormat != kCVPixelFormatType_422YpCbCr8FullRange) {
305 MOZ_DIAGNOSTIC_ASSERT(::IOSurfaceGetWidthOfPlane(surf.get(), 1) ==
306 (size_t)aCbCrSize.width);
307 MOZ_DIAGNOSTIC_ASSERT(::IOSurfaceGetHeightOfPlane(surf.get(), 1) ==
308 (size_t)aCbCrSize.height);
310 #endif
312 return MakeAndAddRef<MacIOSurface>(surf, false, aYUVColorSpace);
315 // Time to decide if we are creating a single planar or bi-planar surface.
316 // We limit ourselves to macOS's single planar and bi-planar formats for
317 // simplicity reasons, possibly gaining some small memory or performance
318 // benefit relative to the tri-planar formats. We try and use as few
319 // planes as possible.
320 // 4:2:0 formats are always bi-planar, because there is no 4:2:0 single
321 // planar format.
322 // 4:2:2 formats with 8 bit color are single planar, otherwise bi-planar.
324 RefPtr<MacIOSurface> result;
325 if (aChromaSubsampling == gfx::ChromaSubsampling::HALF_WIDTH &&
326 aColorDepth == gfx::ColorDepth::COLOR_8) {
327 result = MacIOSurface::CreateSinglePlanarSurface(
328 aYSize, aYUVColorSpace, aTransferFunction, aColorRange);
329 } else {
330 result = MacIOSurface::CreateBiPlanarSurface(
331 aYSize, aCbCrSize, aChromaSubsampling, aYUVColorSpace,
332 aTransferFunction, aColorRange, aColorDepth);
335 if (result &&
336 mSurfaces.Length() < StaticPrefs::layers_iosurfaceimage_recycle_limit()) {
337 mSurfaces.AppendElement(result->GetIOSurfaceRef());
340 return result.forget();