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/. */
6 #ifndef mozilla_layers_NativeLayerCA_h
7 #define mozilla_layers_NativeLayerCA_h
9 #include <IOSurface/IOSurfaceRef.h>
12 #include <unordered_map>
15 #include "mozilla/Mutex.h"
16 #include "mozilla/TimeStamp.h"
18 #include "mozilla/gfx/MacIOSurface.h"
19 #include "mozilla/layers/NativeLayer.h"
20 #include "CFTypeRefPtr.h"
22 #include "nsISupportsImpl.h"
37 class RenderMacIOSurfaceTextureHost
;
43 class NativeLayerRootSnapshotterCA
;
45 class SurfacePoolHandleCA
;
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.
63 // NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot
64 // interface. A NativeLayerRootCA is created by the widget around an existing
65 // CALayer with a call to CreateForCALayer - this CALayer is the root of the
66 // "onscreen" representation of this layer tree.
67 // All methods can be called from any thread, there is internal locking.
68 // All effects from mutating methods are buffered locally and don't modify the
69 // underlying CoreAnimation layers until CommitToScreen() is called. This
70 // ensures that the modifications happen on the right thread.
72 // More specifically: During normal operation, screen updates are driven from a
73 // compositing thread. On this thread, the layers are created / destroyed, their
74 // contents are painted, and the result is committed to the screen. However,
75 // there are some scenarios that need to involve the main thread, most notably
76 // window resizing: During a window resize, we still need the drawing part to
77 // happen on the compositing thread, but the modifications to the underlying
78 // CALayers need to happen on the main thread, once compositing is done.
80 // NativeLayerRootCA + NativeLayerCA create and maintain *two* CALayer tree
81 // representations: An "onscreen" representation and an "offscreen"
82 // representation. These representations are updated via calls to
83 // CommitToScreen() and CommitOffscreen(), respectively. The reason for having
84 // two representations is the following: Our implementation of the snapshotter
85 // API uses CARenderer, which lets us render the composited result of our layer
86 // tree into a GPU buffer. But CARenderer requires "ownership" of the rendered
87 // CALayers in the sense that it associates the CALayers with a local
88 // "CAContext". A CALayer can only be associated with one CAContext at any time.
89 // If we wanted te render our *onscreen* CALayers with CARenderer, we would need
90 // to remove them from the window, reparent them to the CARenderer, render them,
91 // and then put them back into the window. This would lead to a visible flashing
92 // effect. To solve this problem, we build two CALayer representations, so that
93 // one representation can stay inside the window and the other can stay attached
95 class NativeLayerRootCA
: public NativeLayerRoot
{
97 static already_AddRefed
<NativeLayerRootCA
> CreateForCALayer(CALayer
* aLayer
);
99 virtual NativeLayerRootCA
* AsNativeLayerRootCA() override
{ return this; }
101 // Can be called on any thread at any point. Returns whether comitting was
102 // successful. Will return false if called off the main thread while
103 // off-main-thread commits are suspended.
104 bool CommitToScreen() override
;
106 void CommitOffscreen();
108 void OnNativeLayerRootSnapshotterDestroyed(
109 NativeLayerRootSnapshotterCA
* aNativeLayerRootSnapshotter
);
112 // Enters a mode during which CommitToScreen(), when called on a non-main
113 // thread, will not apply any updates to the CALayer tree.
114 void SuspendOffMainThreadCommits();
116 // Exits the mode entered by SuspendOffMainThreadCommits().
117 // Returns true if the last CommitToScreen() was canceled due to suspension,
118 // indicating that another call to CommitToScreen() is needed.
119 bool UnsuspendOffMainThreadCommits();
121 bool AreOffMainThreadCommitsSuspended();
123 void DumpLayerTreeToFile(const char* aPath
);
125 enum class WhichRepresentation
: uint8_t { ONSCREEN
, OFFSCREEN
};
127 // Overridden methods
128 already_AddRefed
<NativeLayer
> CreateLayer(
129 const gfx::IntSize
& aSize
, bool aIsOpaque
,
130 SurfacePoolHandle
* aSurfacePoolHandle
) override
;
131 void AppendLayer(NativeLayer
* aLayer
) override
;
132 void RemoveLayer(NativeLayer
* aLayer
) override
;
133 void SetLayers(const nsTArray
<RefPtr
<NativeLayer
>>& aLayers
) override
;
134 UniquePtr
<NativeLayerRootSnapshotter
> CreateSnapshotter() override
;
136 void SetBackingScale(float aBackingScale
);
137 float BackingScale();
139 already_AddRefed
<NativeLayer
> CreateLayerForExternalTexture(
140 bool aIsOpaque
) override
;
141 already_AddRefed
<NativeLayer
> CreateLayerForColor(
142 gfx::DeviceColor aColor
) override
;
144 void SetWindowIsFullscreen(bool aFullscreen
);
146 VideoLowPowerType
CheckVideoLowPower(const MutexAutoLock
& aProofOfLock
);
149 explicit NativeLayerRootCA(CALayer
* aLayer
);
150 ~NativeLayerRootCA() override
;
152 struct Representation
{
153 explicit Representation(CALayer
* aRootCALayer
);
155 void Commit(WhichRepresentation aRepresentation
,
156 const nsTArray
<RefPtr
<NativeLayerCA
>>& aSublayers
);
157 CALayer
* mRootCALayer
= nullptr; // strong
158 bool mMutatedLayerStructure
= false;
161 template <typename F
>
162 void ForAllRepresentations(F aFn
);
164 Mutex mMutex MOZ_UNANNOTATED
; // protects all other fields
165 Representation mOnscreenRepresentation
;
166 Representation mOffscreenRepresentation
;
168 NativeLayerRootSnapshotterCA
* mWeakSnapshotter
= nullptr;
170 nsTArray
<RefPtr
<NativeLayerCA
>> mSublayers
; // in z-order
171 float mBackingScale
= 1.0f
;
172 bool mMutated
= false;
174 // While mOffMainThreadCommitsSuspended is true, no commits
175 // should happen on a non-main thread, because they might race with
176 // main-thread driven updates such as window shape changes, and cause
178 bool mOffMainThreadCommitsSuspended
= false;
180 // Set to true if CommitToScreen() was aborted because of commit suspension.
181 // Set to false when CommitToScreen() completes successfully. When true,
182 // indicates that CommitToScreen() needs to be called at the next available
184 bool mCommitPending
= false;
186 // Updated by the layer's view's window to match the fullscreen state
188 bool mWindowIsFullscreen
= false;
190 // How many times have we committed since the last time we emitted
192 unsigned int mTelemetryCommitCount
= 0;
195 class RenderSourceNLRS
;
198 class NativeLayerRootSnapshotterCA final
: public NativeLayerRootSnapshotter
{
200 static UniquePtr
<NativeLayerRootSnapshotterCA
> Create(
201 NativeLayerRootCA
* aLayerRoot
, CALayer
* aRootCALayer
);
202 virtual ~NativeLayerRootSnapshotterCA();
204 bool ReadbackPixels(const gfx::IntSize
& aReadbackSize
,
205 gfx::SurfaceFormat aReadbackFormat
,
206 const Range
<uint8_t>& aReadbackBuffer
) override
;
207 already_AddRefed
<profiler_screenshots::RenderSource
> GetWindowContents(
208 const gfx::IntSize
& aWindowSize
) override
;
209 already_AddRefed
<profiler_screenshots::DownscaleTarget
> CreateDownscaleTarget(
210 const gfx::IntSize
& aSize
) override
;
211 already_AddRefed
<profiler_screenshots::AsyncReadbackBuffer
>
212 CreateAsyncReadbackBuffer(const gfx::IntSize
& aSize
) override
;
215 NativeLayerRootSnapshotterCA(NativeLayerRootCA
* aLayerRoot
,
216 RefPtr
<gl::GLContext
>&& aGL
,
217 CALayer
* aRootCALayer
);
218 void UpdateSnapshot(const gfx::IntSize
& aSize
);
220 RefPtr
<NativeLayerRootCA
> mLayerRoot
;
221 RefPtr
<gl::GLContext
> mGL
;
223 // Can be null. Created and updated in UpdateSnapshot.
224 RefPtr
<RenderSourceNLRS
> mSnapshot
;
225 CARenderer
* mRenderer
= nullptr; // strong
229 // NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only
230 // fully-drawn frames make their way to the screen, by maintaining a swap chain
232 // All calls to mutating methods are buffered, and don't take effect on the
233 // underlying CoreAnimation layers until ApplyChanges() is called.
234 // The two most important methods are NextSurface and NotifySurfaceReady:
235 // NextSurface takes an available surface from the swap chain or creates a new
236 // surface if necessary. This surface can then be drawn to. Once drawing is
237 // finished, NotifySurfaceReady marks the surface as ready. This surface is
238 // committed to the layer during the next call to ApplyChanges().
239 // The swap chain keeps track of invalid areas within the surfaces.
240 class NativeLayerCA
: public NativeLayer
{
242 virtual NativeLayerCA
* AsNativeLayerCA() override
{ return this; }
244 // Overridden methods
245 gfx::IntSize
GetSize() override
;
246 void SetPosition(const gfx::IntPoint
& aPosition
) override
;
247 gfx::IntPoint
GetPosition() override
;
248 void SetTransform(const gfx::Matrix4x4
& aTransform
) override
;
249 gfx::Matrix4x4
GetTransform() override
;
250 gfx::IntRect
GetRect() override
;
251 void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter
) override
;
252 RefPtr
<gfx::DrawTarget
> NextSurfaceAsDrawTarget(
253 const gfx::IntRect
& aDisplayRect
, const gfx::IntRegion
& aUpdateRegion
,
254 gfx::BackendType aBackendType
) override
;
255 Maybe
<GLuint
> NextSurfaceAsFramebuffer(const gfx::IntRect
& aDisplayRect
,
256 const gfx::IntRegion
& aUpdateRegion
,
257 bool aNeedsDepth
) override
;
258 void NotifySurfaceReady() override
;
259 void DiscardBackbuffers() override
;
260 bool IsOpaque() override
;
261 void SetClipRect(const Maybe
<gfx::IntRect
>& aClipRect
) override
;
262 Maybe
<gfx::IntRect
> ClipRect() override
;
263 gfx::IntRect
CurrentSurfaceDisplayRect() override
;
264 void SetSurfaceIsFlipped(bool aIsFlipped
) override
;
265 bool SurfaceIsFlipped() override
;
267 void DumpLayer(std::ostream
& aOutputStream
);
269 void AttachExternalImage(wr::RenderTextureHost
* aExternalImage
) override
;
272 friend class NativeLayerRootCA
;
274 NativeLayerCA(const gfx::IntSize
& aSize
, bool aIsOpaque
,
275 SurfacePoolHandleCA
* aSurfacePoolHandle
);
276 explicit NativeLayerCA(bool aIsOpaque
);
277 explicit NativeLayerCA(gfx::DeviceColor aColor
);
278 ~NativeLayerCA() override
;
280 // Gets the next surface for drawing from our swap chain and stores it in
281 // mInProgressSurface. Returns whether this was successful.
282 // mInProgressSurface is guaranteed to be not in use by the window server.
283 // After a call to NextSurface, NextSurface must not be called again until
284 // after NotifySurfaceReady has been called. Can be called on any thread. When
285 // used from multiple threads, callers need to make sure that they still only
286 // call NextSurface and NotifySurfaceReady alternatingly and not in any other
288 bool NextSurface(const MutexAutoLock
& aProofOfLock
);
290 // To be called by NativeLayerRootCA:
291 typedef NativeLayerRootCA::WhichRepresentation WhichRepresentation
;
292 CALayer
* UnderlyingCALayer(WhichRepresentation aRepresentation
);
294 enum class UpdateType
{
295 None
, // Order is important. Each enum must fully encompass the
296 OnlyVideo
, // work implied by the previous enums.
300 UpdateType
HasUpdate(WhichRepresentation aRepresentation
);
301 bool WillUpdateAffectLayers(WhichRepresentation aRepresentation
);
302 bool ApplyChanges(WhichRepresentation aRepresentation
, UpdateType aUpdate
);
304 void SetBackingScale(float aBackingScale
);
306 // Invalidates the specified region in all surfaces that are tracked by this
308 void InvalidateRegionThroughoutSwapchain(const MutexAutoLock
& aProofOfLock
,
309 const gfx::IntRegion
& aRegion
);
311 // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any
312 // valid content from the previous surface outside of aUpdateRegion, so that
313 // only aUpdateRegion needs to be drawn. If content needs to be copied,
314 // aCopyFn is called to do the copying.
315 // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface,
316 // const gfx::IntRegion& aCopyRegion) -> void
317 template <typename F
>
318 void HandlePartialUpdate(const MutexAutoLock
& aProofOfLock
,
319 const gfx::IntRect
& aDisplayRect
,
320 const gfx::IntRegion
& aUpdateRegion
, F
&& aCopyFn
);
322 struct SurfaceWithInvalidRegion
{
323 CFTypeRefPtr
<IOSurfaceRef
> mSurface
;
324 gfx::IntRegion mInvalidRegion
;
327 struct SurfaceWithInvalidRegionAndCheckCount
{
328 SurfaceWithInvalidRegion mEntry
;
329 uint32_t mCheckCount
; // The number of calls to IOSurfaceIsInUse
332 Maybe
<SurfaceWithInvalidRegion
> GetUnusedSurfaceAndCleanUp(
333 const MutexAutoLock
& aProofOfLock
);
335 bool IsVideo(const MutexAutoLock
& aProofOfLock
);
336 bool ShouldSpecializeVideo(const MutexAutoLock
& aProofOfLock
);
337 bool HasExtent() const { return mHasExtent
; }
338 void SetHasExtent(bool aHasExtent
) { mHasExtent
= aHasExtent
; }
340 // This function returns a CGRect if a clip should be applied to the layer.
341 // If set, the CGRect has the scaled position of the clip relative to the
342 // surface origin and the scaled size of the clip rect.
343 static Maybe
<CGRect
> CalculateClipGeometry(
344 const gfx::IntSize
& aSize
, const gfx::IntPoint
& aPosition
,
345 const gfx::Matrix4x4
& aTransform
, const gfx::IntRect
& aDisplayRect
,
346 const Maybe
<gfx::IntRect
>& aClipRect
, float aBackingScale
);
348 // Wraps one CALayer representation of this NativeLayer.
349 struct Representation
{
353 CALayer
* UnderlyingCALayer() { return mWrappingCALayer
; }
355 bool EnqueueSurface(IOSurfaceRef aSurfaceRef
);
357 // Applies buffered changes to the native CALayers. The contract with the
358 // caller is as follows: If any of these values have changed since the last
359 // call to ApplyChanges, mMutated[Field] needs to have been set to true
360 // before the call. If aUpdate is not All, then a partial update will be
361 // applied. In such a case, ApplyChanges may not make any changes that
362 // require a CATransacation, because no transaction will be created. In a
363 // a partial update, the return value will indicate if all the needed
364 // changes were able to be applied under these restrictions. A false return
365 // value indicates an All update is necessary.
367 UpdateType aUpdate
, const gfx::IntSize
& aSize
, bool aIsOpaque
,
368 const gfx::IntPoint
& aPosition
, const gfx::Matrix4x4
& aTransform
,
369 const gfx::IntRect
& aDisplayRect
, const Maybe
<gfx::IntRect
>& aClipRect
,
370 float aBackingScale
, bool aSurfaceIsFlipped
,
371 gfx::SamplingFilter aSamplingFilter
, bool aSpecializeVideo
,
372 CFTypeRefPtr
<IOSurfaceRef
> aFrontSurface
,
373 CFTypeRefPtr
<CGColorRef
> aColor
, bool aIsDRM
, bool aIsVideo
);
375 // Return whether any aspects of this layer representation have been mutated
376 // since the last call to ApplyChanges, i.e. whether ApplyChanges needs to
378 // This is used to optimize away a CATransaction commit if no layers have
380 UpdateType
HasUpdate(bool aIsVideo
);
382 // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the
383 // layer that applies the intersection of mDisplayRect and mClipRect (if
384 // set), and mContentCALayer is the layer that hosts the IOSurface. We do
385 // not share clip layers between consecutive NativeLayerCA objects with the
387 CALayer
* mWrappingCALayer
= nullptr; // strong
388 CALayer
* mContentCALayer
= nullptr; // strong
389 CALayer
* mOpaquenessTintLayer
= nullptr; // strong
392 bool mLogNextVideoSurface
= false;
395 bool mMutatedPosition
: 1;
396 bool mMutatedTransform
: 1;
397 bool mMutatedDisplayRect
: 1;
398 bool mMutatedClipRect
: 1;
399 bool mMutatedBackingScale
: 1;
400 bool mMutatedSize
: 1;
401 bool mMutatedSurfaceIsFlipped
: 1;
402 bool mMutatedFrontSurface
: 1;
403 bool mMutatedSamplingFilter
: 1;
404 bool mMutatedSpecializeVideo
: 1;
405 bool mMutatedIsDRM
: 1;
408 Representation
& GetRepresentation(WhichRepresentation aRepresentation
);
409 template <typename F
>
410 void ForAllRepresentations(F aFn
);
412 // Controls access to all fields of this class.
413 Mutex mMutex MOZ_UNANNOTATED
;
415 // Each IOSurface is initially created inside NextSurface.
416 // The surface stays alive until the recycling mechanism in NextSurface
417 // determines it is no longer needed (because the swap chain has grown too
418 // long) or until DiscardBackbuffers() is called or the layer is destroyed.
419 // During the surface's lifetime, it will continuously move through the fields
420 // mInProgressSurface, mFrontSurface, and back to front through the mSurfaces
424 // ------[NextSurface()]-----> mInProgressSurface
425 // --[NotifySurfaceReady()]--> mFrontSurface
426 // --[NotifySurfaceReady()]--> mSurfaces.back() --> .... -->
429 // We mark an IOSurface as "in use" as long as it is either in
430 // mInProgressSurface. When it is in mFrontSurface or in the mSurfaces queue,
431 // it is not marked as "in use" by us - but it can be "in use" by the window
432 // server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects
433 // whether the window server is still reading from the surface, and we can use
434 // this indicator to decide when to recycle the surface.
436 // Users of NativeLayerCA normally proceed in this order:
437 // 1. Begin a frame by calling NextSurface to get the surface.
438 // 2. Draw to the surface.
439 // 3. Mark the surface as done by calling NotifySurfaceReady.
440 // 4. Call NativeLayerRoot::CommitToScreen(), which calls ApplyChanges()
441 // during a CATransaction.
443 // The surface we returned from the most recent call to NextSurface, before
444 // the matching call to NotifySurfaceReady.
445 // Will only be Some() between calls to NextSurface and NotifySurfaceReady.
446 Maybe
<SurfaceWithInvalidRegion
> mInProgressSurface
;
447 Maybe
<gfx::IntRegion
> mInProgressUpdateRegion
;
448 Maybe
<gfx::IntRect
> mInProgressDisplayRect
;
450 // The surface that the most recent call to NotifySurfaceReady was for.
451 // Will be Some() after the first call to NotifySurfaceReady, for the rest of
452 // the layer's life time.
453 Maybe
<SurfaceWithInvalidRegion
> mFrontSurface
;
455 // The queue of surfaces which make up the rest of our "swap chain".
456 // mSurfaces.front() is the next surface we'll attempt to use.
457 // mSurfaces.back() is the one that was used most recently.
458 std::vector
<SurfaceWithInvalidRegionAndCheckCount
> mSurfaces
;
460 // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady.
461 RefPtr
<MacIOSurface
> mInProgressLockedIOSurface
;
463 RefPtr
<SurfacePoolHandleCA
> mSurfacePoolHandle
;
464 RefPtr
<wr::RenderMacIOSurfaceTextureHost
> mTextureHost
;
466 Representation mOnscreenRepresentation
;
467 Representation mOffscreenRepresentation
;
469 gfx::IntPoint mPosition
;
470 gfx::Matrix4x4 mTransform
;
471 gfx::IntRect mDisplayRect
;
473 Maybe
<gfx::IntRect
> mClipRect
;
474 gfx::SamplingFilter mSamplingFilter
= gfx::SamplingFilter::POINT
;
475 float mBackingScale
= 1.0f
;
476 bool mSurfaceIsFlipped
= false;
477 CFTypeRefPtr
<CGColorRef
> mColor
;
478 const bool mIsOpaque
= false;
479 bool mSpecializeVideo
= false;
480 bool mHasExtent
= false;
482 bool mIsTextureHostVideo
= false;
485 // Track the consistency of our caller's API usage. Layers that are drawn
486 // should only ever be called with NotifySurfaceReady. Layers that are
487 // external should only ever be called with AttachExternalImage.
488 bool mHasEverAttachExternalImage
= false;
489 bool mHasEverNotifySurfaceReady
= false;
493 } // namespace layers
494 } // namespace mozilla
496 #endif // mozilla_layers_NativeLayerCA_h