tor-browser

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

NativeLayerMacSurfaceHandler.mm (9760B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "mozilla/gfx/2D.h"
      7 #include "mozilla/gfx/Logging.h"
      8 #include "mozilla/gfx/MacIOSurface.h"
      9 #include "mozilla/layers/NativeLayerMacSurfaceHandler.h"
     10 #include "mozilla/layers/SurfacePoolCA.h"
     11 #include "GLBlitHelper.h"
     12 #ifdef XP_MACOSX
     13 #  include "GLContextCGL.h"
     14 #else
     15 #  include "GLContextEAGL.h"
     16 #endif
     17 #include "nsRegion.h"
     18 
     19 namespace mozilla {
     20 namespace layers {
     21 
     22 using gfx::DataSourceSurface;
     23 using gfx::IntPoint;
     24 using gfx::IntRect;
     25 using gfx::IntRegion;
     26 using gfx::IntSize;
     27 using gfx::Matrix4x4;
     28 using gfx::SurfaceFormat;
     29 using gl::GLContext;
     30 #ifdef XP_MACOSX
     31 using gl::GLContextCGL;
     32 #endif
     33 
     34 NativeLayerMacSurfaceHandler::NativeLayerMacSurfaceHandler(
     35    const gfx::IntSize& aSize, SurfacePoolHandleCA* aSurfacePoolHandle)
     36    : mSize(aSize), mSurfacePoolHandle(aSurfacePoolHandle) {
     37  MOZ_RELEASE_ASSERT(mSurfacePoolHandle,
     38                     "Need a non-null surface pool handle.");
     39 }
     40 
     41 NativeLayerMacSurfaceHandler::~NativeLayerMacSurfaceHandler() {
     42  if (mInProgressLockedIOSurface) {
     43    mInProgressLockedIOSurface->Unlock(false);
     44    mInProgressLockedIOSurface = nullptr;
     45  }
     46  if (mInProgressSurface) {
     47    IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
     48    mSurfacePoolHandle->ReturnSurfaceToPool(mInProgressSurface->mSurface);
     49  }
     50  if (mFrontSurface) {
     51    mSurfacePoolHandle->ReturnSurfaceToPool(mFrontSurface->mSurface);
     52  }
     53  for (const auto& surf : mSurfaces) {
     54    mSurfacePoolHandle->ReturnSurfaceToPool(surf.mEntry.mSurface);
     55  }
     56 }
     57 
     58 bool NativeLayerMacSurfaceHandler::NextSurface() {
     59  if (mSize.IsEmpty()) {
     60    gfxCriticalError()
     61        << "NextSurface returning false because of invalid mSize ("
     62        << mSize.width << ", " << mSize.height << ").";
     63    return false;
     64  }
     65 
     66  MOZ_RELEASE_ASSERT(!mInProgressSurface,
     67                     "ERROR: Do not call NextSurface twice in sequence. Call "
     68                     "NotifySurfaceReady before the "
     69                     "next call to NextSurface.");
     70 
     71  Maybe<SurfaceWithInvalidRegion> surf = GetUnusedSurfaceAndCleanUp();
     72  if (!surf) {
     73    CFTypeRefPtr<IOSurfaceRef> newSurf =
     74        mSurfacePoolHandle->ObtainSurfaceFromPool(mSize);
     75    MOZ_RELEASE_ASSERT(
     76        newSurf, "NextSurface IOSurfaceCreate failed to create the surface.");
     77    surf = Some(SurfaceWithInvalidRegion{newSurf, IntRect({}, mSize)});
     78  }
     79 
     80  mInProgressSurface = std::move(surf);
     81  IOSurfaceIncrementUseCount(mInProgressSurface->mSurface.get());
     82  return true;
     83 }
     84 
     85 void NativeLayerMacSurfaceHandler::InvalidateRegionThroughoutSwapchain(
     86    const IntRegion& aRegion) {
     87  IntRegion r = aRegion;
     88  if (mInProgressSurface) {
     89    mInProgressSurface->mInvalidRegion.OrWith(r);
     90  }
     91  if (mFrontSurface) {
     92    mFrontSurface->mInvalidRegion.OrWith(r);
     93  }
     94  for (auto& surf : mSurfaces) {
     95    surf.mEntry.mInvalidRegion.OrWith(r);
     96  }
     97 }
     98 
     99 template <typename F>
    100 void NativeLayerMacSurfaceHandler::HandlePartialUpdate(
    101    const IntRect& aDisplayRect, const IntRegion& aUpdateRegion, F&& aCopyFn) {
    102  MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aUpdateRegion.GetBounds()),
    103                     "The update region should be within the surface bounds.");
    104  MOZ_RELEASE_ASSERT(IntRect({}, mSize).Contains(aDisplayRect),
    105                     "The display rect should be within the surface bounds.");
    106 
    107  MOZ_RELEASE_ASSERT(!mInProgressUpdateRegion);
    108  MOZ_RELEASE_ASSERT(!mInProgressDisplayRect);
    109 
    110  mInProgressUpdateRegion = Some(aUpdateRegion);
    111  mInProgressDisplayRect = Some(aDisplayRect);
    112 
    113  if (mFrontSurface) {
    114    // Copy not-overwritten valid content from mFrontSurface so that valid
    115    // content never gets lost.
    116    gfx::IntRegion copyRegion;
    117    copyRegion.Sub(mInProgressSurface->mInvalidRegion, aUpdateRegion);
    118    copyRegion.SubOut(mFrontSurface->mInvalidRegion);
    119 
    120    if (!copyRegion.IsEmpty()) {
    121      // Now copy the valid content, using a caller-provided copy function.
    122      aCopyFn(mFrontSurface->mSurface, copyRegion);
    123      mInProgressSurface->mInvalidRegion.SubOut(copyRegion);
    124    }
    125  }
    126 
    127  InvalidateRegionThroughoutSwapchain(aUpdateRegion);
    128 }
    129 
    130 Maybe<SurfaceWithInvalidRegion>
    131 NativeLayerMacSurfaceHandler::GetUnusedSurfaceAndCleanUp() {
    132  std::vector<SurfaceWithInvalidRegionAndCheckCount> usedSurfaces;
    133  Maybe<SurfaceWithInvalidRegion> unusedSurface;
    134 
    135  // Separate mSurfaces into used and unused surfaces.
    136  for (auto& surf : mSurfaces) {
    137    if (IOSurfaceIsInUse(surf.mEntry.mSurface.get())) {
    138      surf.mCheckCount++;
    139      if (surf.mCheckCount < 10) {
    140        usedSurfaces.push_back(std::move(surf));
    141      } else {
    142        // The window server has been holding on to this surface for an
    143        // unreasonably long time. This is known to happen sometimes, for
    144        // example in occluded windows or after a GPU switch. In that case,
    145        // release our references to the surface so that it doesn't look like
    146        // we're trying to keep it alive.
    147        mSurfacePoolHandle->ReturnSurfaceToPool(
    148            std::move(surf.mEntry.mSurface));
    149      }
    150    } else {
    151      if (unusedSurface) {
    152        // Multiple surfaces are unused. Keep the most recent one and release
    153        // any earlier ones. The most recent one requires the least amount of
    154        // copying during partial repaints.
    155        mSurfacePoolHandle->ReturnSurfaceToPool(
    156            std::move(unusedSurface->mSurface));
    157      }
    158      unusedSurface = Some(std::move(surf.mEntry));
    159    }
    160  }
    161 
    162  // Put the used surfaces back into mSurfaces.
    163  mSurfaces = std::move(usedSurfaces);
    164 
    165  return unusedSurface;
    166 }
    167 
    168 RefPtr<gfx::DrawTarget> NativeLayerMacSurfaceHandler::NextSurfaceAsDrawTarget(
    169    const IntRect& aDisplayRect, const IntRegion& aUpdateRegion,
    170    gfx::BackendType aBackendType) {
    171  if (!NextSurface()) {
    172    return nullptr;
    173  }
    174 
    175  auto surf = MakeRefPtr<MacIOSurface>(mInProgressSurface->mSurface);
    176  if (NS_WARN_IF(!surf->Lock(false))) {
    177    gfxCriticalError() << "NextSurfaceAsDrawTarget lock surface failed.";
    178    return nullptr;
    179  }
    180 
    181  mInProgressLockedIOSurface = std::move(surf);
    182  RefPtr<gfx::DrawTarget> dt =
    183      mInProgressLockedIOSurface->GetAsDrawTargetLocked(aBackendType);
    184 
    185  HandlePartialUpdate(
    186      aDisplayRect, aUpdateRegion,
    187      [&](CFTypeRefPtr<IOSurfaceRef> validSource,
    188          const gfx::IntRegion& copyRegion) {
    189        RefPtr<MacIOSurface> source = new MacIOSurface(validSource);
    190        if (source->Lock(true)) {
    191          RefPtr<gfx::DrawTarget> sourceDT =
    192              source->GetAsDrawTargetLocked(aBackendType);
    193          RefPtr<gfx::SourceSurface> sourceSurface = sourceDT->Snapshot();
    194 
    195          for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
    196            const gfx::IntRect& r = iter.Get();
    197            dt->CopySurface(sourceSurface, r, r.TopLeft());
    198          }
    199          source->Unlock(true);
    200        } else {
    201          gfxCriticalError() << "HandlePartialUpdate lock surface failed.";
    202        }
    203      });
    204 
    205  return dt;
    206 }
    207 
    208 Maybe<GLuint> NativeLayerMacSurfaceHandler::NextSurfaceAsFramebuffer(
    209    const IntRect& aDisplayRect, const IntRegion& aUpdateRegion,
    210    bool aNeedsDepth) {
    211  bool gotNextSurface = NextSurface();
    212  MOZ_RELEASE_ASSERT(gotNextSurface,
    213                     "NextSurfaceAsFramebuffer needs a surface.");
    214 
    215  Maybe<GLuint> fbo = mSurfacePoolHandle->GetFramebufferForSurface(
    216      mInProgressSurface->mSurface, aNeedsDepth);
    217  MOZ_RELEASE_ASSERT(fbo, "GetFramebufferForSurface failed.");
    218 
    219  HandlePartialUpdate(
    220      aDisplayRect, aUpdateRegion,
    221      [&](CFTypeRefPtr<IOSurfaceRef> validSource,
    222          const gfx::IntRegion& copyRegion) {
    223        // Copy copyRegion from validSource to fbo.
    224        MOZ_RELEASE_ASSERT(mSurfacePoolHandle->gl());
    225        mSurfacePoolHandle->gl()->MakeCurrent();
    226        Maybe<GLuint> sourceFBO =
    227            mSurfacePoolHandle->GetFramebufferForSurface(validSource, false);
    228        MOZ_RELEASE_ASSERT(
    229            sourceFBO,
    230            "GetFramebufferForSurface failed during HandlePartialUpdate.");
    231        for (auto iter = copyRegion.RectIter(); !iter.Done(); iter.Next()) {
    232          gfx::IntRect r = iter.Get();
    233          if (mSurfaceIsFlipped) {
    234            r.y = mSize.height - r.YMost();
    235          }
    236          mSurfacePoolHandle->gl()->BlitHelper()->BlitFramebufferToFramebuffer(
    237              *sourceFBO, *fbo, r, r, LOCAL_GL_NEAREST);
    238        }
    239      });
    240 
    241  return fbo;
    242 }
    243 
    244 bool NativeLayerMacSurfaceHandler::NotifySurfaceReady() {
    245  MOZ_RELEASE_ASSERT(
    246      mInProgressSurface,
    247      "NotifySurfaceReady called without preceding call to NextSurface");
    248 
    249  if (mInProgressLockedIOSurface) {
    250    mInProgressLockedIOSurface->Unlock(false);
    251    mInProgressLockedIOSurface = nullptr;
    252  }
    253 
    254  if (mFrontSurface) {
    255    mSurfaces.push_back({*mFrontSurface, 0});
    256    mFrontSurface = Nothing();
    257  }
    258 
    259  MOZ_RELEASE_ASSERT(mInProgressUpdateRegion);
    260  IOSurfaceDecrementUseCount(mInProgressSurface->mSurface.get());
    261  mFrontSurface = std::move(mInProgressSurface);
    262  mFrontSurface->mInvalidRegion.SubOut(mInProgressUpdateRegion.extract());
    263 
    264  bool mutatedDisplayRect = false;
    265  MOZ_RELEASE_ASSERT(mInProgressDisplayRect);
    266  if (!mDisplayRect.IsEqualInterior(*mInProgressDisplayRect)) {
    267    mutatedDisplayRect = true;
    268    mDisplayRect = *mInProgressDisplayRect;
    269  }
    270  mInProgressDisplayRect = Nothing();
    271  return mutatedDisplayRect;
    272 }
    273 
    274 void NativeLayerMacSurfaceHandler::DiscardBackbuffers() {
    275  for (const auto& surf : mSurfaces) {
    276    mSurfacePoolHandle->ReturnSurfaceToPool(surf.mEntry.mSurface);
    277  }
    278  mSurfaces.clear();
    279 }
    280 
    281 }  // namespace layers
    282 }  // namespace mozilla