Source code

Revision control

Other Tools

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