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