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