1 /* -*- Mode: C++; tab-width: 2; 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 #ifndef mozilla_layers_NativeLayerCA_h 7 #define mozilla_layers_NativeLayerCA_h 8 9 #include <IOSurface/IOSurface.h> 10 11 #include <deque> 12 #include <unordered_map> 13 14 #include "mozilla/Mutex.h" 15 16 #include "mozilla/gfx/MacIOSurface.h" 17 #include "mozilla/layers/NativeLayer.h" 18 #include "CFTypeRefPtr.h" 19 #include "nsRegion.h" 20 #include "nsISupportsImpl.h" 21 22 #ifdef __OBJC__ 23 @class CALayer; 24 #else 25 typedef void CALayer; 26 #endif 27 28 namespace mozilla { 29 30 namespace gl { 31 class GLContextCGL; 32 class MozFramebuffer; 33 } // namespace gl 34 namespace wr { 35 class RenderMacIOSurfaceTextureHost; 36 } // namespace wr 37 38 namespace layers { 39 40 class NativeLayerRootSnapshotterCA; 41 class SurfacePoolHandleCA; 42 43 // NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot 44 // interface. A NativeLayerRootCA is created by the widget around an existing 45 // CALayer with a call to CreateForCALayer - this CALayer is the root of the 46 // "onscreen" representation of this layer tree. 47 // All methods can be called from any thread, there is internal locking. 48 // All effects from mutating methods are buffered locally and don't modify the 49 // underlying CoreAnimation layers until CommitToScreen() is called. This 50 // ensures that the modifications happen on the right thread. 51 // 52 // More specifically: During normal operation, screen updates are driven from a 53 // compositing thread. On this thread, the layers are created / destroyed, their 54 // contents are painted, and the result is committed to the screen. However, 55 // there are some scenarios that need to involve the main thread, most notably 56 // window resizing: During a window resize, we still need the drawing part to 57 // happen on the compositing thread, but the modifications to the underlying 58 // CALayers need to happen on the main thread, once compositing is done. 59 // 60 // NativeLayerRootCA + NativeLayerCA create and maintain *two* CALayer tree 61 // representations: An "onscreen" representation and an "offscreen" 62 // representation. These representations are updated via calls to 63 // CommitToScreen() and CommitOffscreen(), respectively. The reason for having 64 // two representations is the following: Our implementation of the snapshotter 65 // API uses CARenderer, which lets us render the composited result of our layer 66 // tree into a GPU buffer. But CARenderer requires "ownership" of the rendered 67 // CALayers in the sense that it associates the CALayers with a local 68 // "CAContext". A CALayer can only be associated with one CAContext at any time. 69 // If we wanted te render our *onscreen* CALayers with CARenderer, we would need 70 // to remove them from the window, reparent them to the CARenderer, render them, 71 // and then put them back into the window. This would lead to a visible flashing 72 // effect. To solve this problem, we build two CALayer representations, so that 73 // one representation can stay inside the window and the other can stay attached 74 // to the CARenderer. 75 class NativeLayerRootCA : public NativeLayerRoot { 76 public: 77 static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer); 78 AsNativeLayerRootCA()79 virtual NativeLayerRootCA* AsNativeLayerRootCA() override { return this; } 80 81 // Can be called on any thread at any point. Returns whether comitting was 82 // successful. Will return false if called off the main thread while 83 // off-main-thread commits are suspended. 84 bool CommitToScreen() override; 85 86 void CommitOffscreen(); 87 void OnNativeLayerRootSnapshotterDestroyed( 88 NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter); 89 90 // Enters a mode during which CommitToScreen(), when called on a non-main 91 // thread, will not apply any updates to the CALayer tree. 92 void SuspendOffMainThreadCommits(); 93 94 // Exits the mode entered by SuspendOffMainThreadCommits(). 95 // Returns true if the last CommitToScreen() was canceled due to suspension, 96 // indicating that another call to CommitToScreen() is needed. 97 bool UnsuspendOffMainThreadCommits(); 98 99 bool AreOffMainThreadCommitsSuspended(); 100 101 enum class WhichRepresentation : uint8_t { ONSCREEN, OFFSCREEN }; 102 103 // Overridden methods 104 already_AddRefed<NativeLayer> CreateLayer( 105 const gfx::IntSize& aSize, bool aIsOpaque, 106 SurfacePoolHandle* aSurfacePoolHandle) override; 107 void AppendLayer(NativeLayer* aLayer) override; 108 void RemoveLayer(NativeLayer* aLayer) override; 109 void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) override; 110 UniquePtr<NativeLayerRootSnapshotter> CreateSnapshotter() override; 111 112 void SetBackingScale(float aBackingScale); 113 float BackingScale(); 114 115 already_AddRefed<NativeLayer> CreateLayerForExternalTexture( 116 bool aIsOpaque) override; 117 118 protected: 119 explicit NativeLayerRootCA(CALayer* aLayer); 120 ~NativeLayerRootCA() override; 121 122 struct Representation { 123 explicit Representation(CALayer* aRootCALayer); 124 ~Representation(); 125 void Commit(WhichRepresentation aRepresentation, 126 const nsTArray<RefPtr<NativeLayerCA>>& aSublayers); 127 CALayer* mRootCALayer = nullptr; // strong 128 bool mMutated = false; 129 }; 130 131 template <typename F> 132 void ForAllRepresentations(F aFn); 133 134 Mutex mMutex; // protects all other fields 135 Representation mOnscreenRepresentation; 136 Representation mOffscreenRepresentation; 137 NativeLayerRootSnapshotterCA* mWeakSnapshotter = nullptr; 138 nsTArray<RefPtr<NativeLayerCA>> mSublayers; // in z-order 139 float mBackingScale = 1.0f; 140 bool mMutated = false; 141 142 // While mOffMainThreadCommitsSuspended is true, no commits 143 // should happen on a non-main thread, because they might race with 144 // main-thread driven updates such as window shape changes, and cause 145 // glitches. 146 bool mOffMainThreadCommitsSuspended = false; 147 148 // Set to true if CommitToScreen() was aborted because of commit suspension. 149 // Set to false when CommitToScreen() completes successfully. When true, 150 // indicates that CommitToScreen() needs to be called at the next available 151 // opportunity. 152 bool mCommitPending = false; 153 }; 154 155 class RenderSourceNLRS; 156 157 class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter { 158 public: 159 static UniquePtr<NativeLayerRootSnapshotterCA> Create( 160 NativeLayerRootCA* aLayerRoot, CALayer* aRootCALayer); 161 virtual ~NativeLayerRootSnapshotterCA(); 162 163 bool ReadbackPixels(const gfx::IntSize& aReadbackSize, 164 gfx::SurfaceFormat aReadbackFormat, 165 const Range<uint8_t>& aReadbackBuffer) override; 166 already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents( 167 const gfx::IntSize& aWindowSize) override; 168 already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget( 169 const gfx::IntSize& aSize) override; 170 already_AddRefed<profiler_screenshots::AsyncReadbackBuffer> 171 CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override; 172 173 protected: 174 NativeLayerRootSnapshotterCA(NativeLayerRootCA* aLayerRoot, 175 RefPtr<gl::GLContext>&& aGL, 176 CALayer* aRootCALayer); 177 void UpdateSnapshot(const gfx::IntSize& aSize); 178 179 RefPtr<NativeLayerRootCA> mLayerRoot; 180 RefPtr<gl::GLContext> mGL; 181 182 // Can be null. Created and updated in UpdateSnapshot. 183 RefPtr<RenderSourceNLRS> mSnapshot; 184 CARenderer* mRenderer = nullptr; // strong 185 }; 186 187 // NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only 188 // fully-drawn frames make their way to the screen, by maintaining a swap chain 189 // of IOSurfaces. 190 // All calls to mutating methods are buffered, and don't take effect on the 191 // underlying CoreAnimation layers until ApplyChanges() is called. 192 // The two most important methods are NextSurface and NotifySurfaceReady: 193 // NextSurface takes an available surface from the swap chain or creates a new 194 // surface if necessary. This surface can then be drawn to. Once drawing is 195 // finished, NotifySurfaceReady marks the surface as ready. This surface is 196 // committed to the layer during the next call to ApplyChanges(). 197 // The swap chain keeps track of invalid areas within the surfaces. 198 class NativeLayerCA : public NativeLayer { 199 public: AsNativeLayerCA()200 virtual NativeLayerCA* AsNativeLayerCA() override { return this; } 201 202 // Overridden methods 203 gfx::IntSize GetSize() override; 204 void SetPosition(const gfx::IntPoint& aPosition) override; 205 gfx::IntPoint GetPosition() override; 206 void SetTransform(const gfx::Matrix4x4& aTransform) override; 207 gfx::Matrix4x4 GetTransform() override; 208 gfx::IntRect GetRect() override; 209 void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) override; 210 RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget( 211 const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, 212 gfx::BackendType aBackendType) override; 213 Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect, 214 const gfx::IntRegion& aUpdateRegion, 215 bool aNeedsDepth) override; 216 void NotifySurfaceReady() override; 217 void DiscardBackbuffers() override; 218 bool IsOpaque() override; 219 void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) override; 220 Maybe<gfx::IntRect> ClipRect() override; 221 gfx::IntRect CurrentSurfaceDisplayRect() override; 222 void SetSurfaceIsFlipped(bool aIsFlipped) override; 223 bool SurfaceIsFlipped() override; 224 225 void AttachExternalImage(wr::RenderTextureHost* aExternalImage) override; 226 227 protected: 228 friend class NativeLayerRootCA; 229 230 NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque, 231 SurfacePoolHandleCA* aSurfacePoolHandle); 232 explicit NativeLayerCA(bool aIsOpaque); 233 ~NativeLayerCA() override; 234 235 // Gets the next surface for drawing from our swap chain and stores it in 236 // mInProgressSurface. Returns whether this was successful. 237 // mInProgressSurface is guaranteed to be not in use by the window server. 238 // After a call to NextSurface, NextSurface must not be called again until 239 // after NotifySurfaceReady has been called. Can be called on any thread. When 240 // used from multiple threads, callers need to make sure that they still only 241 // call NextSurface and NotifySurfaceReady alternatingly and not in any other 242 // order. 243 bool NextSurface(const MutexAutoLock& aProofOfLock); 244 245 // To be called by NativeLayerRootCA: 246 typedef NativeLayerRootCA::WhichRepresentation WhichRepresentation; 247 CALayer* UnderlyingCALayer(WhichRepresentation aRepresentation); 248 void ApplyChanges(WhichRepresentation aRepresentation); 249 bool HasUpdate(WhichRepresentation aRepresentation); 250 void SetBackingScale(float aBackingScale); 251 252 // Invalidates the specified region in all surfaces that are tracked by this 253 // layer. 254 void InvalidateRegionThroughoutSwapchain(const MutexAutoLock& aProofOfLock, 255 const gfx::IntRegion& aRegion); 256 257 // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any 258 // valid content from the previous surface outside of aUpdateRegion, so that 259 // only aUpdateRegion needs to be drawn. If content needs to be copied, 260 // aCopyFn is called to do the copying. 261 // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface, 262 // const gfx::IntRegion& aCopyRegion) -> void 263 template <typename F> 264 void HandlePartialUpdate(const MutexAutoLock& aProofOfLock, 265 const gfx::IntRect& aDisplayRect, 266 const gfx::IntRegion& aUpdateRegion, F&& aCopyFn); 267 268 struct SurfaceWithInvalidRegion { 269 CFTypeRefPtr<IOSurfaceRef> mSurface; 270 gfx::IntRegion mInvalidRegion; 271 }; 272 273 struct SurfaceWithInvalidRegionAndCheckCount { 274 SurfaceWithInvalidRegion mEntry; 275 uint32_t mCheckCount; // The number of calls to IOSurfaceIsInUse 276 }; 277 278 Maybe<SurfaceWithInvalidRegion> GetUnusedSurfaceAndCleanUp( 279 const MutexAutoLock& aProofOfLock); 280 281 // Wraps one CALayer representation of this NativeLayer. 282 struct Representation { 283 ~Representation(); 284 UnderlyingCALayerRepresentation285 CALayer* UnderlyingCALayer() { return mWrappingCALayer; } 286 287 // Applies buffered changes to the native CALayers. The contract with the 288 // caller is as follows: If any of these values have changed since the last 289 // call to ApplyChanges, mMutated[Field] needs to have been set to true 290 // before the call. 291 void ApplyChanges(const gfx::IntSize& aSize, bool aIsOpaque, 292 const gfx::IntPoint& aPosition, 293 const gfx::Matrix4x4& aTransform, 294 const gfx::IntRect& aDisplayRect, 295 const Maybe<gfx::IntRect>& aClipRect, float aBackingScale, 296 bool aSurfaceIsFlipped, 297 gfx::SamplingFilter aSamplingFilter, 298 CFTypeRefPtr<IOSurfaceRef> aFrontSurface); 299 300 // Return whether any aspects of this layer representation have been mutated 301 // since the last call to ApplyChanges, i.e. whether ApplyChanges needs to 302 // be called. 303 // This is used to optimize away a CATransaction commit if no layers have 304 // changed. 305 bool HasUpdate(); 306 307 // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the 308 // layer that applies the intersection of mDisplayRect and mClipRect (if 309 // set), and mContentCALayer is the layer that hosts the IOSurface. We do 310 // not share clip layers between consecutive NativeLayerCA objects with the 311 // same clip rect. 312 CALayer* mWrappingCALayer = nullptr; // strong 313 CALayer* mContentCALayer = nullptr; // strong 314 CALayer* mOpaquenessTintLayer = nullptr; // strong 315 316 bool mMutatedPosition = true; 317 bool mMutatedTransform = true; 318 bool mMutatedDisplayRect = true; 319 bool mMutatedClipRect = true; 320 bool mMutatedBackingScale = true; 321 bool mMutatedSize = true; 322 bool mMutatedSurfaceIsFlipped = true; 323 bool mMutatedFrontSurface = true; 324 bool mMutatedSamplingFilter = true; 325 }; 326 327 Representation& GetRepresentation(WhichRepresentation aRepresentation); 328 template <typename F> 329 void ForAllRepresentations(F aFn); 330 331 // Controls access to all fields of this class. 332 Mutex mMutex; 333 334 // Each IOSurface is initially created inside NextSurface. 335 // The surface stays alive until the recycling mechanism in NextSurface 336 // determines it is no longer needed (because the swap chain has grown too 337 // long) or until DiscardBackbuffers() is called or the layer is destroyed. 338 // During the surface's lifetime, it will continuously move through the fields 339 // mInProgressSurface, mFrontSurface, and back to front through the mSurfaces 340 // queue: 341 // 342 // mSurfaces.front() 343 // ------[NextSurface()]-----> mInProgressSurface 344 // --[NotifySurfaceReady()]--> mFrontSurface 345 // --[NotifySurfaceReady()]--> mSurfaces.back() --> .... --> 346 // mSurfaces.front() 347 // 348 // We mark an IOSurface as "in use" as long as it is either in 349 // mInProgressSurface. When it is in mFrontSurface or in the mSurfaces queue, 350 // it is not marked as "in use" by us - but it can be "in use" by the window 351 // server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects 352 // whether the window server is still reading from the surface, and we can use 353 // this indicator to decide when to recycle the surface. 354 // 355 // Users of NativeLayerCA normally proceed in this order: 356 // 1. Begin a frame by calling NextSurface to get the surface. 357 // 2. Draw to the surface. 358 // 3. Mark the surface as done by calling NotifySurfaceReady. 359 // 4. Call NativeLayerRoot::CommitToScreen(), which calls ApplyChanges() 360 // during a CATransaction. 361 362 // The surface we returned from the most recent call to NextSurface, before 363 // the matching call to NotifySurfaceReady. 364 // Will only be Some() between calls to NextSurface and NotifySurfaceReady. 365 Maybe<SurfaceWithInvalidRegion> mInProgressSurface; 366 Maybe<gfx::IntRegion> mInProgressUpdateRegion; 367 Maybe<gfx::IntRect> mInProgressDisplayRect; 368 369 // The surface that the most recent call to NotifySurfaceReady was for. 370 // Will be Some() after the first call to NotifySurfaceReady, for the rest of 371 // the layer's life time. 372 Maybe<SurfaceWithInvalidRegion> mFrontSurface; 373 374 // The queue of surfaces which make up the rest of our "swap chain". 375 // mSurfaces.front() is the next surface we'll attempt to use. 376 // mSurfaces.back() is the one that was used most recently. 377 std::vector<SurfaceWithInvalidRegionAndCheckCount> mSurfaces; 378 379 // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady. 380 RefPtr<MacIOSurface> mInProgressLockedIOSurface; 381 382 RefPtr<SurfacePoolHandleCA> mSurfacePoolHandle; 383 RefPtr<wr::RenderMacIOSurfaceTextureHost> mTextureHost; 384 385 Representation mOnscreenRepresentation; 386 Representation mOffscreenRepresentation; 387 388 gfx::IntPoint mPosition; 389 gfx::Matrix4x4 mTransform; 390 gfx::IntRect mDisplayRect; 391 gfx::IntSize mSize; 392 Maybe<gfx::IntRect> mClipRect; 393 gfx::SamplingFilter mSamplingFilter = gfx::SamplingFilter::POINT; 394 float mBackingScale = 1.0f; 395 bool mSurfaceIsFlipped = false; 396 const bool mIsOpaque = false; 397 }; 398 399 } // namespace layers 400 } // namespace mozilla 401 402 #endif // mozilla_layers_NativeLayerCA_h 403