tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }