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