Bug 1891710: part 2) Enable <Element-outerHTML.html> WPT for Trusted Types. r=smaug
[gecko.git] / gfx / layers / NativeLayerCA.h
blob93b6b3a6decefc8118196e24b6897f0c7de929e6
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>
11 #include <deque>
12 #include <unordered_map>
13 #include <ostream>
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"
21 #include "nsRegion.h"
22 #include "nsISupportsImpl.h"
24 #ifdef __OBJC__
25 @class CALayer;
26 #else
27 typedef void CALayer;
28 #endif
30 namespace mozilla {
32 namespace gl {
33 class GLContextCGL;
34 class MozFramebuffer;
35 } // namespace gl
36 namespace wr {
37 class RenderMacIOSurfaceTextureHost;
38 } // namespace wr
40 namespace layers {
42 #ifdef XP_MACOSX
43 class NativeLayerRootSnapshotterCA;
44 #endif
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
94 // to the CARenderer.
95 class NativeLayerRootCA : public NativeLayerRoot {
96 public:
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();
107 #ifdef XP_MACOSX
108 void OnNativeLayerRootSnapshotterDestroyed(
109 NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter);
110 #endif
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);
148 protected:
149 explicit NativeLayerRootCA(CALayer* aLayer);
150 ~NativeLayerRootCA() override;
152 struct Representation {
153 explicit Representation(CALayer* aRootCALayer);
154 ~Representation();
155 void Commit(WhichRepresentation aRepresentation,
156 const nsTArray<RefPtr<NativeLayerCA>>& aSublayers,
157 bool aWindowIsFullscreen);
158 CALayer* mRootCALayer = nullptr; // strong
159 bool mMutatedLayerStructure = false;
162 template <typename F>
163 void ForAllRepresentations(F aFn);
165 Mutex mMutex MOZ_UNANNOTATED; // protects all other fields
166 Representation mOnscreenRepresentation;
167 Representation mOffscreenRepresentation;
168 #ifdef XP_MACOSX
169 NativeLayerRootSnapshotterCA* mWeakSnapshotter = nullptr;
170 #endif
171 nsTArray<RefPtr<NativeLayerCA>> mSublayers; // in z-order
172 float mBackingScale = 1.0f;
173 bool mMutated = false;
175 // While mOffMainThreadCommitsSuspended is true, no commits
176 // should happen on a non-main thread, because they might race with
177 // main-thread driven updates such as window shape changes, and cause
178 // glitches.
179 bool mOffMainThreadCommitsSuspended = false;
181 // Set to true if CommitToScreen() was aborted because of commit suspension.
182 // Set to false when CommitToScreen() completes successfully. When true,
183 // indicates that CommitToScreen() needs to be called at the next available
184 // opportunity.
185 bool mCommitPending = false;
187 // Updated by the layer's view's window to match the fullscreen state
188 // of that window.
189 bool mWindowIsFullscreen = false;
191 // How many times have we committed since the last time we emitted
192 // telemetry?
193 unsigned int mTelemetryCommitCount = 0;
196 class RenderSourceNLRS;
198 #ifdef XP_MACOSX
199 class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter {
200 public:
201 static UniquePtr<NativeLayerRootSnapshotterCA> Create(
202 NativeLayerRootCA* aLayerRoot, CALayer* aRootCALayer);
203 virtual ~NativeLayerRootSnapshotterCA();
205 bool ReadbackPixels(const gfx::IntSize& aReadbackSize,
206 gfx::SurfaceFormat aReadbackFormat,
207 const Range<uint8_t>& aReadbackBuffer) override;
208 already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents(
209 const gfx::IntSize& aWindowSize) override;
210 already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget(
211 const gfx::IntSize& aSize) override;
212 already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
213 CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override;
215 protected:
216 NativeLayerRootSnapshotterCA(NativeLayerRootCA* aLayerRoot,
217 RefPtr<gl::GLContext>&& aGL,
218 CALayer* aRootCALayer);
219 void UpdateSnapshot(const gfx::IntSize& aSize);
221 RefPtr<NativeLayerRootCA> mLayerRoot;
222 RefPtr<gl::GLContext> mGL;
224 // Can be null. Created and updated in UpdateSnapshot.
225 RefPtr<RenderSourceNLRS> mSnapshot;
226 CARenderer* mRenderer = nullptr; // strong
228 #endif
230 // NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only
231 // fully-drawn frames make their way to the screen, by maintaining a swap chain
232 // of IOSurfaces.
233 // All calls to mutating methods are buffered, and don't take effect on the
234 // underlying CoreAnimation layers until ApplyChanges() is called.
235 // The two most important methods are NextSurface and NotifySurfaceReady:
236 // NextSurface takes an available surface from the swap chain or creates a new
237 // surface if necessary. This surface can then be drawn to. Once drawing is
238 // finished, NotifySurfaceReady marks the surface as ready. This surface is
239 // committed to the layer during the next call to ApplyChanges().
240 // The swap chain keeps track of invalid areas within the surfaces.
241 class NativeLayerCA : public NativeLayer {
242 public:
243 virtual NativeLayerCA* AsNativeLayerCA() override { return this; }
245 // Overridden methods
246 gfx::IntSize GetSize() override;
247 void SetPosition(const gfx::IntPoint& aPosition) override;
248 gfx::IntPoint GetPosition() override;
249 void SetTransform(const gfx::Matrix4x4& aTransform) override;
250 gfx::Matrix4x4 GetTransform() override;
251 gfx::IntRect GetRect() override;
252 void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) override;
253 RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
254 const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion,
255 gfx::BackendType aBackendType) override;
256 Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect,
257 const gfx::IntRegion& aUpdateRegion,
258 bool aNeedsDepth) override;
259 void NotifySurfaceReady() override;
260 void DiscardBackbuffers() override;
261 bool IsOpaque() override;
262 void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) override;
263 Maybe<gfx::IntRect> ClipRect() override;
264 gfx::IntRect CurrentSurfaceDisplayRect() override;
265 void SetSurfaceIsFlipped(bool aIsFlipped) override;
266 bool SurfaceIsFlipped() override;
268 void DumpLayer(std::ostream& aOutputStream);
270 void AttachExternalImage(wr::RenderTextureHost* aExternalImage) override;
272 void SetRootWindowIsFullscreen(bool aFullscreen);
274 protected:
275 friend class NativeLayerRootCA;
277 NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque,
278 SurfacePoolHandleCA* aSurfacePoolHandle);
279 explicit NativeLayerCA(bool aIsOpaque);
280 explicit NativeLayerCA(gfx::DeviceColor aColor);
281 ~NativeLayerCA() override;
283 // Gets the next surface for drawing from our swap chain and stores it in
284 // mInProgressSurface. Returns whether this was successful.
285 // mInProgressSurface is guaranteed to be not in use by the window server.
286 // After a call to NextSurface, NextSurface must not be called again until
287 // after NotifySurfaceReady has been called. Can be called on any thread. When
288 // used from multiple threads, callers need to make sure that they still only
289 // call NextSurface and NotifySurfaceReady alternatingly and not in any other
290 // order.
291 bool NextSurface(const MutexAutoLock& aProofOfLock);
293 // To be called by NativeLayerRootCA:
294 typedef NativeLayerRootCA::WhichRepresentation WhichRepresentation;
295 CALayer* UnderlyingCALayer(WhichRepresentation aRepresentation);
297 enum class UpdateType {
298 None, // Order is important. Each enum must fully encompass the
299 OnlyVideo, // work implied by the previous enums.
300 All,
303 UpdateType HasUpdate(WhichRepresentation aRepresentation);
304 bool WillUpdateAffectLayers(WhichRepresentation aRepresentation);
305 bool ApplyChanges(WhichRepresentation aRepresentation, UpdateType aUpdate);
307 void SetBackingScale(float aBackingScale);
309 // Invalidates the specified region in all surfaces that are tracked by this
310 // layer.
311 void InvalidateRegionThroughoutSwapchain(const MutexAutoLock& aProofOfLock,
312 const gfx::IntRegion& aRegion);
314 // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any
315 // valid content from the previous surface outside of aUpdateRegion, so that
316 // only aUpdateRegion needs to be drawn. If content needs to be copied,
317 // aCopyFn is called to do the copying.
318 // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface,
319 // const gfx::IntRegion& aCopyRegion) -> void
320 template <typename F>
321 void HandlePartialUpdate(const MutexAutoLock& aProofOfLock,
322 const gfx::IntRect& aDisplayRect,
323 const gfx::IntRegion& aUpdateRegion, F&& aCopyFn);
325 struct SurfaceWithInvalidRegion {
326 CFTypeRefPtr<IOSurfaceRef> mSurface;
327 gfx::IntRegion mInvalidRegion;
330 struct SurfaceWithInvalidRegionAndCheckCount {
331 SurfaceWithInvalidRegion mEntry;
332 uint32_t mCheckCount; // The number of calls to IOSurfaceIsInUse
335 Maybe<SurfaceWithInvalidRegion> GetUnusedSurfaceAndCleanUp(
336 const MutexAutoLock& aProofOfLock);
338 bool IsVideo(const MutexAutoLock& aProofOfLock);
339 bool ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock);
340 bool HasExtent() const { return mHasExtent; }
341 void SetHasExtent(bool aHasExtent) { mHasExtent = aHasExtent; }
343 // This function returns a CGRect if a clip should be applied to the layer.
344 // If set, the CGRect has the scaled position of the clip relative to the
345 // surface origin and the scaled size of the clip rect.
346 static Maybe<CGRect> CalculateClipGeometry(
347 const gfx::IntSize& aSize, const gfx::IntPoint& aPosition,
348 const gfx::Matrix4x4& aTransform, const gfx::IntRect& aDisplayRect,
349 const Maybe<gfx::IntRect>& aClipRect, float aBackingScale);
351 // Wraps one CALayer representation of this NativeLayer.
352 struct Representation {
353 Representation();
354 ~Representation();
356 CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
358 bool EnqueueSurface(IOSurfaceRef aSurfaceRef);
360 // Applies buffered changes to the native CALayers. The contract with the
361 // caller is as follows: If any of these values have changed since the last
362 // call to ApplyChanges, mMutated[Field] needs to have been set to true
363 // before the call. If aUpdate is not All, then a partial update will be
364 // applied. In such a case, ApplyChanges may not make any changes that
365 // require a CATransacation, because no transaction will be created. In a
366 // a partial update, the return value will indicate if all the needed
367 // changes were able to be applied under these restrictions. A false return
368 // value indicates an All update is necessary.
369 bool ApplyChanges(
370 UpdateType aUpdate, const gfx::IntSize& aSize, bool aIsOpaque,
371 const gfx::IntPoint& aPosition, const gfx::Matrix4x4& aTransform,
372 const gfx::IntRect& aDisplayRect, const Maybe<gfx::IntRect>& aClipRect,
373 float aBackingScale, bool aSurfaceIsFlipped,
374 gfx::SamplingFilter aSamplingFilter, bool aSpecializeVideo,
375 CFTypeRefPtr<IOSurfaceRef> aFrontSurface,
376 CFTypeRefPtr<CGColorRef> aColor, bool aIsDRM, bool aIsVideo);
378 // Return whether any aspects of this layer representation have been mutated
379 // since the last call to ApplyChanges, i.e. whether ApplyChanges needs to
380 // be called.
381 // This is used to optimize away a CATransaction commit if no layers have
382 // changed.
383 UpdateType HasUpdate(bool aIsVideo);
385 // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the
386 // layer that applies the intersection of mDisplayRect and mClipRect (if
387 // set), and mContentCALayer is the layer that hosts the IOSurface. We do
388 // not share clip layers between consecutive NativeLayerCA objects with the
389 // same clip rect.
390 CALayer* mWrappingCALayer = nullptr; // strong
391 CALayer* mContentCALayer = nullptr; // strong
392 CALayer* mOpaquenessTintLayer = nullptr; // strong
394 #ifdef NIGHTLY_BUILD
395 bool mLogNextVideoSurface = false;
396 #endif
398 bool mMutatedPosition : 1;
399 bool mMutatedTransform : 1;
400 bool mMutatedDisplayRect : 1;
401 bool mMutatedClipRect : 1;
402 bool mMutatedBackingScale : 1;
403 bool mMutatedSize : 1;
404 bool mMutatedSurfaceIsFlipped : 1;
405 bool mMutatedFrontSurface : 1;
406 bool mMutatedSamplingFilter : 1;
407 bool mMutatedSpecializeVideo : 1;
408 bool mMutatedIsDRM : 1;
411 Representation& GetRepresentation(WhichRepresentation aRepresentation);
412 template <typename F>
413 void ForAllRepresentations(F aFn);
415 // Controls access to all fields of this class.
416 Mutex mMutex MOZ_UNANNOTATED;
418 // Each IOSurface is initially created inside NextSurface.
419 // The surface stays alive until the recycling mechanism in NextSurface
420 // determines it is no longer needed (because the swap chain has grown too
421 // long) or until DiscardBackbuffers() is called or the layer is destroyed.
422 // During the surface's lifetime, it will continuously move through the fields
423 // mInProgressSurface, mFrontSurface, and back to front through the mSurfaces
424 // queue:
426 // mSurfaces.front()
427 // ------[NextSurface()]-----> mInProgressSurface
428 // --[NotifySurfaceReady()]--> mFrontSurface
429 // --[NotifySurfaceReady()]--> mSurfaces.back() --> .... -->
430 // mSurfaces.front()
432 // We mark an IOSurface as "in use" as long as it is either in
433 // mInProgressSurface. When it is in mFrontSurface or in the mSurfaces queue,
434 // it is not marked as "in use" by us - but it can be "in use" by the window
435 // server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects
436 // whether the window server is still reading from the surface, and we can use
437 // this indicator to decide when to recycle the surface.
439 // Users of NativeLayerCA normally proceed in this order:
440 // 1. Begin a frame by calling NextSurface to get the surface.
441 // 2. Draw to the surface.
442 // 3. Mark the surface as done by calling NotifySurfaceReady.
443 // 4. Call NativeLayerRoot::CommitToScreen(), which calls ApplyChanges()
444 // during a CATransaction.
446 // The surface we returned from the most recent call to NextSurface, before
447 // the matching call to NotifySurfaceReady.
448 // Will only be Some() between calls to NextSurface and NotifySurfaceReady.
449 Maybe<SurfaceWithInvalidRegion> mInProgressSurface;
450 Maybe<gfx::IntRegion> mInProgressUpdateRegion;
451 Maybe<gfx::IntRect> mInProgressDisplayRect;
453 // The surface that the most recent call to NotifySurfaceReady was for.
454 // Will be Some() after the first call to NotifySurfaceReady, for the rest of
455 // the layer's life time.
456 Maybe<SurfaceWithInvalidRegion> mFrontSurface;
458 // The queue of surfaces which make up the rest of our "swap chain".
459 // mSurfaces.front() is the next surface we'll attempt to use.
460 // mSurfaces.back() is the one that was used most recently.
461 std::vector<SurfaceWithInvalidRegionAndCheckCount> mSurfaces;
463 // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady.
464 RefPtr<MacIOSurface> mInProgressLockedIOSurface;
466 RefPtr<SurfacePoolHandleCA> mSurfacePoolHandle;
467 RefPtr<wr::RenderMacIOSurfaceTextureHost> mTextureHost;
469 Representation mOnscreenRepresentation;
470 Representation mOffscreenRepresentation;
472 gfx::IntPoint mPosition;
473 gfx::Matrix4x4 mTransform;
474 gfx::IntRect mDisplayRect;
475 gfx::IntSize mSize;
476 Maybe<gfx::IntRect> mClipRect;
477 gfx::SamplingFilter mSamplingFilter = gfx::SamplingFilter::POINT;
478 float mBackingScale = 1.0f;
479 bool mSurfaceIsFlipped = false;
480 CFTypeRefPtr<CGColorRef> mColor;
481 const bool mIsOpaque = false;
482 bool mRootWindowIsFullscreen = false;
483 bool mSpecializeVideo = false;
484 bool mHasExtent = false;
485 bool mIsDRM = false;
486 bool mIsTextureHostVideo = false;
488 #ifdef NIGHTLY_BUILD
489 // Track the consistency of our caller's API usage. Layers that are drawn
490 // should only ever be called with NotifySurfaceReady. Layers that are
491 // external should only ever be called with AttachExternalImage.
492 bool mHasEverAttachExternalImage = false;
493 bool mHasEverNotifySurfaceReady = false;
494 #endif
497 } // namespace layers
498 } // namespace mozilla
500 #endif // mozilla_layers_NativeLayerCA_h