NativeLayerCA.h (21558B)
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/IOSurfaceRef.h> 10 11 #include <deque> 12 #include <ostream> 13 14 #include "mozilla/Mutex.h" 15 #include "mozilla/TimeStamp.h" 16 17 #include "mozilla/gfx/MacIOSurface.h" 18 #include "mozilla/layers/NativeLayer.h" 19 #include "mozilla/layers/NativeLayerMacSurfaceHandler.h" 20 #include "mozilla/webrender/RenderMacIOSurfaceTextureHost.h" 21 #include "CFTypeRefPtr.h" 22 #include "nsRegion.h" 23 #include "nsISupportsImpl.h" 24 25 #ifdef __OBJC__ 26 @class CALayer; 27 @class CARenderer; 28 #else 29 typedef void CALayer; 30 typedef void CARenderer; 31 #endif 32 33 namespace mozilla { 34 35 namespace gl { 36 class GLContextCGL; 37 class MozFramebuffer; 38 } // namespace gl 39 namespace wr { 40 class RenderMacIOSurfaceTextureHost; 41 } // namespace wr 42 43 namespace layers { 44 45 class NativeLayerRootSnapshotterCA; 46 47 enum class VideoLowPowerType { 48 // These must be kept synchronized with the telemetry histogram enums. 49 NotVideo, // Never emitted as telemetry. No video is visible. 50 LowPower, // As best we can tell, we are in the "detached", 51 // low-power compositing mode. We don't use "Success" 52 // because of name collision with telemetry generation. 53 FailMultipleVideo, // There is more than one video visible. 54 FailWindowed, // The window is not fullscreen. 55 FailOverlaid, // Something is on top of the video (likely captions). 56 FailBacking, // The layer behind the video is not full-coverage black. 57 FailMacOSVersion, // macOS version does not meet requirements. 58 FailPref, // Pref is not set. 59 FailSurface, // Surface is not eligible. 60 FailEnqueue, // Enqueueing the video didn't work. 61 }; 62 63 // The type of update needed to apply the pending changes to a NativeLayerCA 64 // representation. 65 // 66 // Order is important. Each enum must fully encompass the work implied by the 67 // previous enums. 68 enum class NativeLayerCAUpdateType { 69 None, 70 OnlyVideo, 71 All, 72 }; 73 74 class SnapshotterCADelegate { 75 public: 76 virtual ~SnapshotterCADelegate(); 77 78 virtual float BackingScale() const { return 1.0f; } 79 virtual bool DoCustomReadbackForReftestsIfDesired( 80 const gfx::IntSize& aReadbackSize, gfx::SurfaceFormat aReadbackFormat, 81 const Range<uint8_t>& aReadbackBuffer) { 82 return false; 83 } 84 virtual void UpdateSnapshotterLayers(CALayer* aRootCALayer) = 0; 85 virtual void OnSnapshotterDestroyed( 86 NativeLayerRootSnapshotterCA* aSnapshotter) {} 87 }; 88 89 // NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot 90 // interface. A NativeLayerRootCA is created by the widget around an existing 91 // CALayer with a call to CreateForCALayer - this CALayer is the root of the 92 // "onscreen" representation of this layer tree. 93 // All methods can be called from any thread, there is internal locking. 94 // All effects from mutating methods are buffered locally and don't modify the 95 // underlying CoreAnimation layers until CommitToScreen() is called. This 96 // ensures that the modifications happen on the right thread. 97 // 98 // More specifically: During normal operation, screen updates are driven from a 99 // compositing thread. On this thread, the layers are created / destroyed, their 100 // contents are painted, and the result is committed to the screen. However, 101 // there are some scenarios that need to involve the main thread, most notably 102 // window resizing: During a window resize, we still need the drawing part to 103 // happen on the compositing thread, but the modifications to the underlying 104 // CALayers need to happen on the main thread, once compositing is done. 105 // 106 // NativeLayerRootCA + NativeLayerCA create and maintain *two* CALayer tree 107 // representations: An "onscreen" representation and an "offscreen" 108 // representation. These representations are updated via calls to 109 // CommitToScreen() and CommitOffscreen(), respectively. The reason for having 110 // two representations is the following: Our implementation of the snapshotter 111 // API uses CARenderer, which lets us render the composited result of our layer 112 // tree into a GPU buffer. But CARenderer requires "ownership" of the rendered 113 // CALayers in the sense that it associates the CALayers with a local 114 // "CAContext". A CALayer can only be associated with one CAContext at any time. 115 // If we wanted te render our *onscreen* CALayers with CARenderer, we would need 116 // to remove them from the window, reparent them to the CARenderer, render them, 117 // and then put them back into the window. This would lead to a visible flashing 118 // effect. To solve this problem, we build two CALayer representations, so that 119 // one representation can stay inside the window and the other can stay attached 120 // to the CARenderer. 121 class NativeLayerRootCA final : public NativeLayerRoot { 122 public: 123 static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer); 124 125 virtual NativeLayerRootCA* AsNativeLayerRootCA() override { return this; } 126 127 // Can be called on any thread at any point. Returns whether comitting was 128 // successful. Will return false if called off the main thread while 129 // off-main-thread commits are suspended. 130 bool CommitToScreen() override; 131 132 void CommitOffscreen(CALayer* aRootCALayer); 133 void OnNativeLayerRootSnapshotterDestroyed( 134 NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter); 135 136 // Enters a mode during which CommitToScreen(), when called on a non-main 137 // thread, will not apply any updates to the CALayer tree. 138 void SuspendOffMainThreadCommits(); 139 140 // Exits the mode entered by SuspendOffMainThreadCommits(). 141 // Returns true if the last CommitToScreen() was canceled due to suspension, 142 // indicating that another call to CommitToScreen() is needed. 143 bool UnsuspendOffMainThreadCommits(); 144 145 bool AreOffMainThreadCommitsSuspended(); 146 147 void DumpLayerTreeToFile(const char* aPath, 148 const MutexAutoLock& aProofOfLock); 149 150 enum class WhichRepresentation : uint8_t { ONSCREEN, OFFSCREEN }; 151 152 // Overridden methods 153 already_AddRefed<NativeLayer> CreateLayer( 154 const gfx::IntSize& aSize, bool aIsOpaque, 155 SurfacePoolHandle* aSurfacePoolHandle) override; 156 void AppendLayer(NativeLayer* aLayer) override; 157 void RemoveLayer(NativeLayer* aLayer) override; 158 void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) override; 159 UniquePtr<NativeLayerRootSnapshotter> CreateSnapshotter() override; 160 161 void SetBackingScale(float aBackingScale); 162 float BackingScale(); 163 164 already_AddRefed<NativeLayer> CreateLayerForExternalTexture( 165 bool aIsOpaque) override; 166 already_AddRefed<NativeLayer> CreateLayerForColor( 167 gfx::DeviceColor aColor) override; 168 169 // A macOS-specific layer creation method, which uses no 170 // SurfacePoolHandle, because it will be handed surfaces directly. 171 already_AddRefed<NativeLayerCA> CreateLayerForSurfacePresentation( 172 const gfx::IntSize& aSize, bool aIsOpaque); 173 174 void SetWindowIsFullscreen(bool aFullscreen); 175 176 VideoLowPowerType CheckVideoLowPower(const MutexAutoLock& aProofOfLock); 177 178 protected: 179 explicit NativeLayerRootCA(CALayer* aLayer); 180 ~NativeLayerRootCA() override; 181 182 // Do a commit of the pending changes to the CALayers. This is used for both 183 // onscreen commits (modifying the CALayers attached to the NSView), and for 184 // "offscreen" commits. 185 // "Offscreen" commits are updating a separate copy of the CALayer tree for 186 // use with CARenderer by the snapshotter. 187 void CommitRepresentation(WhichRepresentation aRepresentation, 188 CALayer* aRootCALayer, 189 const nsTArray<RefPtr<NativeLayerCA>>& aSublayers, 190 bool aMutatedLayerStructure, 191 bool aWindowIsFullscreen); 192 193 void SetMutatedLayerStructure(); 194 195 using UpdateType = NativeLayerCAUpdateType; 196 UpdateType GetMaxUpdateRequired( 197 WhichRepresentation aRepresentation, 198 const nsTArray<RefPtr<NativeLayerCA>>& aSublayers, 199 bool aMutatedLayerStructure) const; 200 201 // An implementation of SnapshotterCADelegate, backed by a NativeLayerRootCA. 202 struct SnapshotterDelegate final : public SnapshotterCADelegate { 203 explicit SnapshotterDelegate(NativeLayerRootCA* aLayerRoot); 204 virtual ~SnapshotterDelegate() override; 205 virtual float BackingScale() const override { 206 return mLayerRoot->BackingScale(); 207 } 208 virtual void UpdateSnapshotterLayers(CALayer* aRootCALayer) override { 209 mLayerRoot->CommitOffscreen(aRootCALayer); 210 } 211 virtual void OnSnapshotterDestroyed( 212 NativeLayerRootSnapshotterCA* aSnapshotter) override { 213 mLayerRoot->OnNativeLayerRootSnapshotterDestroyed(aSnapshotter); 214 } 215 RefPtr<NativeLayerRootCA> mLayerRoot; 216 }; 217 218 Mutex mMutex MOZ_UNANNOTATED; // protects all other fields 219 CALayer* mOnscreenRootCALayer = nullptr; // strong 220 CALayer* mOffscreenRootCALayer = nullptr; // strong 221 NativeLayerRootSnapshotterCA* mWeakSnapshotter = nullptr; 222 nsTArray<RefPtr<NativeLayerCA>> mSublayers; // in z-order 223 float mBackingScale = 1.0f; 224 bool mMutated = false; 225 226 // While mOffMainThreadCommitsSuspended is true, no commits 227 // should happen on a non-main thread, because they might race with 228 // main-thread driven updates such as window shape changes, and cause 229 // glitches. 230 bool mOffMainThreadCommitsSuspended = false; 231 232 // Set to true if CommitToScreen() was aborted because of commit suspension. 233 // Set to false when CommitToScreen() completes successfully. When true, 234 // indicates that CommitToScreen() needs to be called at the next available 235 // opportunity. 236 bool mCommitPending = false; 237 238 // Updated by the layer's view's window to match the fullscreen state 239 // of that window. 240 bool mWindowIsFullscreen = false; 241 242 // Whether mSublayers has changed since the last onscreen / offscreen commit. 243 bool mMutatedOnscreenLayerStructure = false; 244 bool mMutatedOffscreenLayerStructure = false; 245 246 // How many times have we committed since the last time we emitted 247 // telemetry? 248 unsigned int mTelemetryCommitCount = 0; 249 }; 250 251 class RenderSourceNLRS; 252 253 #ifdef XP_MACOSX 254 class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter { 255 public: 256 static UniquePtr<NativeLayerRootSnapshotterCA> Create( 257 UniquePtr<SnapshotterCADelegate>&& aDelegate); 258 virtual ~NativeLayerRootSnapshotterCA(); 259 260 bool ReadbackPixels(const gfx::IntSize& aReadbackSize, 261 gfx::SurfaceFormat aReadbackFormat, 262 const Range<uint8_t>& aReadbackBuffer) override; 263 already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents( 264 const gfx::IntSize& aWindowSize) override; 265 already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget( 266 const gfx::IntSize& aSize) override; 267 already_AddRefed<profiler_screenshots::AsyncReadbackBuffer> 268 CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override; 269 270 protected: 271 NativeLayerRootSnapshotterCA(UniquePtr<SnapshotterCADelegate>&& aDelegate, 272 RefPtr<gl::GLContext>&& aGL); 273 void UpdateSnapshot(const gfx::IntSize& aSize); 274 275 UniquePtr<SnapshotterCADelegate> mDelegate; 276 RefPtr<gl::GLContext> mGL; 277 278 // Can be null. Created and updated in UpdateSnapshot. 279 RefPtr<RenderSourceNLRS> mSnapshot; 280 CARenderer* mRenderer = nullptr; // strong 281 }; 282 #endif 283 284 // Wraps one CALayer representation of NativeLayerCA. 285 struct NativeLayerCARepresentation { 286 using UpdateType = NativeLayerCAUpdateType; 287 288 NativeLayerCARepresentation(); 289 ~NativeLayerCARepresentation(); 290 291 // Returns null if the layer is currently completely clipped out. 292 CALayer* UnderlyingCALayer() { 293 return mWrappingCALayerHasExtent ? mWrappingCALayer : nullptr; 294 } 295 296 bool EnqueueSurface(IOSurfaceRef aSurfaceRef); 297 298 // Applies buffered changes to the native CALayers. The contract with the 299 // caller is as follows: If any of these values have changed since the last 300 // call to ApplyChanges, mMutated[Field] needs to have been set to true 301 // before the call. If aUpdate is not All, then a partial update will be 302 // applied. In such a case, ApplyChanges may not make any changes that 303 // require a CATransacation, because no transaction will be created. In a 304 // a partial update, the return value will indicate if all the needed 305 // changes were able to be applied under these restrictions. A false return 306 // value indicates an All update is necessary. 307 bool ApplyChanges(NativeLayerCAUpdateType aUpdate, const gfx::IntSize& aSize, 308 bool aIsOpaque, const gfx::IntPoint& aPosition, 309 const gfx::Matrix4x4& aTransform, 310 const gfx::IntRect& aDisplayRect, 311 const Maybe<gfx::IntRect>& aClipRect, 312 const Maybe<gfx::RoundedRect>& aRoundedClip, 313 float aBackingScale, bool aSurfaceIsFlipped, 314 gfx::SamplingFilter aSamplingFilter, bool aSpecializeVideo, 315 const CFTypeRefPtr<IOSurfaceRef>& aFrontSurface, 316 const Maybe<gfx::DeviceColor>& aColor, bool aIsDRM, 317 bool aIsVideo); 318 319 // Return whether any aspects of this layer representation have been mutated 320 // since the last call to ApplyChanges, i.e. whether ApplyChanges needs to 321 // be called. 322 // This is used to optimize away a CATransaction commit if no layers have 323 // changed. 324 NativeLayerCAUpdateType HasUpdate(bool aIsVideo); 325 326 // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the 327 // layer that applies the intersection of mDisplayRect and mClipRect (if 328 // set), and mContentCALayer is the layer that hosts the IOSurface. We do 329 // not share clip layers between consecutive NativeLayerCA objects with the 330 // same clip rect. 331 CALayer* mWrappingCALayer = nullptr; // strong 332 CALayer* mRoundedClipCALayer = nullptr; // strong 333 CALayer* mContentCALayer = nullptr; // strong 334 CALayer* mOpaquenessTintLayer = nullptr; // strong 335 336 #ifdef NIGHTLY_BUILD 337 bool mLogNextVideoSurface = false; 338 #endif 339 340 bool mWrappingCALayerHasExtent : 1; 341 342 // These are all initialized to true by the constructor. 343 bool mMutatedPosition : 1; 344 bool mMutatedTransform : 1; 345 bool mMutatedDisplayRect : 1; 346 bool mMutatedClipRect : 1; 347 bool mMutatedRoundedClipRect : 1; 348 bool mMutatedBackingScale : 1; 349 bool mMutatedSize : 1; 350 bool mMutatedSurfaceIsFlipped : 1; 351 bool mMutatedFrontSurface : 1; 352 bool mMutatedSamplingFilter : 1; 353 bool mMutatedSpecializeVideo : 1; 354 bool mMutatedIsDRM : 1; 355 // Don't forget to update the constructor when you add a field here. 356 }; 357 358 // NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only 359 // fully-drawn frames make their way to the screen, by maintaining a swap chain 360 // of IOSurfaces. 361 // All calls to mutating methods are buffered, and don't take effect on the 362 // underlying CoreAnimation layers until ApplyChanges() is called. 363 // The two most important methods are NextSurface and NotifySurfaceReady: 364 // NextSurface takes an available surface from the swap chain or creates a new 365 // surface if necessary. This surface can then be drawn to. Once drawing is 366 // finished, NotifySurfaceReady marks the surface as ready. This surface is 367 // committed to the layer during the next call to ApplyChanges(). 368 // The swap chain keeps track of invalid areas within the surfaces. 369 class NativeLayerCA : public NativeLayer { 370 public: 371 virtual NativeLayerCA* AsNativeLayerCA() override { return this; } 372 373 // Overridden methods 374 gfx::IntSize GetSize() override; 375 void SetPosition(const gfx::IntPoint& aPosition) override; 376 gfx::IntPoint GetPosition() override; 377 void SetTransform(const gfx::Matrix4x4& aTransform) override; 378 gfx::Matrix4x4 GetTransform() override; 379 gfx::IntRect GetRect() override; 380 void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) override; 381 RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget( 382 const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, 383 gfx::BackendType aBackendType) override; 384 Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect, 385 const gfx::IntRegion& aUpdateRegion, 386 bool aNeedsDepth) override; 387 void NotifySurfaceReady() override; 388 void DiscardBackbuffers() override; 389 bool IsOpaque() override; 390 void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) override; 391 Maybe<gfx::IntRect> ClipRect() override; 392 void SetRoundedClipRect(const Maybe<gfx::RoundedRect>& aClip) override; 393 Maybe<gfx::RoundedRect> RoundedClipRect() override; 394 gfx::IntRect CurrentSurfaceDisplayRect() override; 395 void SetDisplayRect(const gfx::IntRect& aDisplayRect); 396 void SetSurfaceIsFlipped(bool aIsFlipped) override; 397 bool SurfaceIsFlipped() override; 398 399 // Used to force a specific IOSurfaceRef to be used. 400 void SetSurfaceToPresent(CFTypeRefPtr<IOSurfaceRef> aSurfaceRef, 401 gfx::IntSize& aSize, bool aIsDRM, bool aIsHDR); 402 403 void DumpLayer(std::ostream& aOutputStream); 404 405 void AttachExternalImage(wr::RenderTextureHost* aExternalImage) override; 406 GpuFence* GetGpuFence() override; 407 408 void SetRootWindowIsFullscreen(bool aFullscreen); 409 410 protected: 411 friend class NativeLayerRootCA; 412 friend struct NativeLayerCARepresentation; 413 using UpdateType = NativeLayerCAUpdateType; 414 using WhichRepresentation = NativeLayerRootCA::WhichRepresentation; 415 using Representation = NativeLayerCARepresentation; 416 417 NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque, 418 SurfacePoolHandleCA* aSurfacePoolHandle); 419 explicit NativeLayerCA(bool aIsOpaque); 420 explicit NativeLayerCA(gfx::DeviceColor aColor); 421 // This constructor is used for surfaces being directly supplied 422 // to the layer. 423 explicit NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque); 424 425 ~NativeLayerCA() override; 426 427 // To be called by NativeLayerRootCA: 428 CALayer* UnderlyingCALayer(WhichRepresentation aRepresentation); 429 430 NativeLayerCAUpdateType HasUpdate(WhichRepresentation aRepresentation); 431 432 // Apply pending updates to the underlaying CALayer. Sets *aMustRebuild to 433 // true if the update requires changing which set of CALayers should be in the 434 // parent. 435 bool ApplyChanges(WhichRepresentation aRepresentation, UpdateType aUpdate, 436 bool* aMustRebuild); 437 438 void SetBackingScale(float aBackingScale); 439 440 // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any 441 // valid content from the previous surface outside of aUpdateRegion, so that 442 // only aUpdateRegion needs to be drawn. If content needs to be copied, 443 // aCopyFn is called to do the copying. 444 // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface, 445 // const gfx::IntRegion& aCopyRegion) -> void 446 template <typename F> 447 void HandlePartialUpdate(const MutexAutoLock& aProofOfLock, 448 const gfx::IntRect& aDisplayRect, 449 const gfx::IntRegion& aUpdateRegion, F&& aCopyFn); 450 451 bool IsVideo(const MutexAutoLock& aProofOfLock); 452 bool ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock); 453 454 // This function returns a CGRect if a clip should be applied to the layer. 455 // If set, the CGRect has the scaled position of the clip relative to the 456 // surface origin and the scaled size of the clip rect. 457 static Maybe<CGRect> CalculateClipGeometry( 458 const gfx::IntSize& aSize, const gfx::IntPoint& aPosition, 459 const gfx::Matrix4x4& aTransform, const gfx::IntRect& aDisplayRect, 460 const Maybe<gfx::IntRect>& aClipRect, float aBackingScale); 461 462 Representation& GetRepresentation(WhichRepresentation aRepresentation); 463 template <typename F> 464 void ForAllRepresentations(F aFn); 465 466 // Controls access to all fields of this class. 467 Mutex mMutex MOZ_UNANNOTATED; 468 469 // The IOSurface to use for this layer. Optional. 470 // If not present, we get use a surface from mSurfaceHandler or mTextureHost 471 // instead. We mark the surface as "in use" as long as it is in 472 // mSurfaceToPresent, to prevent other processes from recycling it. 473 CFTypeRefPtr<IOSurfaceRef> mSurfaceToPresent; 474 475 Maybe<NativeLayerMacSurfaceHandler> mSurfaceHandler; 476 477 RefPtr<wr::RenderMacIOSurfaceTextureHost> mTextureHost; 478 bool mTextureHostIsVideo = false; 479 480 Representation mOnscreenRepresentation; 481 Representation mOffscreenRepresentation; 482 483 gfx::IntPoint mPosition; 484 gfx::Matrix4x4 mTransform; 485 gfx::IntRect mDisplayRect; 486 gfx::IntSize mSize; 487 Maybe<gfx::IntRect> mClipRect; 488 Maybe<gfx::RoundedRect> mRoundedClipRect; 489 Maybe<gfx::DeviceColor> mColor; 490 gfx::SamplingFilter mSamplingFilter = gfx::SamplingFilter::POINT; 491 float mBackingScale = 1.0f; 492 bool mSurfaceIsFlipped = false; 493 const bool mIsOpaque = false; 494 bool mRootWindowIsFullscreen = false; 495 bool mSpecializeVideo = false; 496 bool mIsDRM = false; 497 bool mIsHDR = false; 498 499 #ifdef NIGHTLY_BUILD 500 // Track the consistency of our caller's API usage. Layers that are drawn 501 // should only ever be called with NotifySurfaceReady. Layers that are 502 // external should only ever be called with AttachExternalImage. 503 bool mHasEverAttachExternalImage = false; 504 bool mHasEverNotifySurfaceReady = false; 505 #endif 506 }; 507 508 } // namespace layers 509 } // namespace mozilla 510 511 #endif // mozilla_layers_NativeLayerCA_h