DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (9136070a0561)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_layers_NativeLayerCA_h
#define mozilla_layers_NativeLayerCA_h

#include <IOSurface/IOSurface.h>

#include <deque>
#include <unordered_map>

#include "mozilla/Mutex.h"

#include "mozilla/gfx/MacIOSurface.h"
#include "mozilla/layers/NativeLayer.h"
#include "CFTypeRefPtr.h"
#include "nsRegion.h"
#include "nsISupportsImpl.h"

#ifdef __OBJC__
@class CALayer;
#else
typedef void CALayer;
#endif

namespace mozilla {

namespace gl {
class GLContextCGL;
class MozFramebuffer;
}  // namespace gl

namespace layers {

class IOSurfaceRegistry {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IOSurfaceRegistry)

  virtual void RegisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;
  virtual void UnregisterSurface(CFTypeRefPtr<IOSurfaceRef> aSurface) = 0;

 protected:
  virtual ~IOSurfaceRegistry() {}
};

// NativeLayerRootCA is the CoreAnimation implementation of the NativeLayerRoot
// interface. A NativeLayerRootCA is created by the widget around an existing
// CALayer with a call to CreateForCALayer.
// All methods can be called from any thread, there is internal locking.
// All effects from mutating methods are buffered locally and don't modify the
// underlying CoreAnimation layers until ApplyChanges() is called. This ensures
// that the modifications can be limited to run within a CoreAnimation
// transaction, and on a thread of the caller's choosing.
class NativeLayerRootCA : public NativeLayerRoot {
 public:
  static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer);

  // Must be called within a current CATransaction on the transaction's thread.
  void ApplyChanges();

  void SetBackingScale(float aBackingScale);

  // Overridden methods
  already_AddRefed<NativeLayer> CreateLayer() override;
  void AppendLayer(NativeLayer* aLayer) override;
  void RemoveLayer(NativeLayer* aLayer) override;

 protected:
  explicit NativeLayerRootCA(CALayer* aLayer);
  ~NativeLayerRootCA() override;

  Mutex mMutex;                                // protects all other fields
  nsTArray<RefPtr<NativeLayerCA>> mSublayers;  // in z-order
  CALayer* mRootCALayer = nullptr;             // strong
  float mBackingScale = 1.0f;
  bool mMutated = false;
};

// NativeLayerCA wraps a CALayer and lets you draw to it. It ensures that only
// fully-drawn frames make their way to the screen, by maintaining a swap chain
// of IOSurfaces.
// All calls to mutating methods are buffered, and don't take effect on the
// underlying CoreAnimation layers until ApplyChanges() is called.
// The two most important methods are NextSurface and NotifySurfaceReady:
// NextSurface takes an available surface from the swap chain or creates a new
// surface if necessary. This surface can then be drawn to. Once drawing is
// finished, NotifySurfaceReady marks the surface as ready. This surface is
// committed to the layer during the next call to ApplyChanges().
// The swap chain keeps track of invalid areas within the surfaces.
//
// Creation and destruction of IOSurface objects is broadcast to an optional
// "surface registry" so that associated objects such as framebuffer objects
// don't need to be recreated on every frame: Instead, the surface registry can
// maintain one object per IOSurface in this layer's swap chain, and those
// objects will be reused in different frames as the layer cycles through the
// surfaces in its swap chain.
class NativeLayerCA : public NativeLayer {
 public:
  virtual NativeLayerCA* AsNativeLayerCA() override { return this; }

  // Overridden methods
  void SetRect(const gfx::IntRect& aRect) override;
  gfx::IntRect GetRect() override;
  void InvalidateRegionThroughoutSwapchain(
      const gfx::IntRegion& aRegion) override;
  RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
      gfx::BackendType aBackendType) override;
  void SetGLContext(gl::GLContext* aGLContext) override;
  gl::GLContext* GetGLContext() override;
  Maybe<GLuint> NextSurfaceAsFramebuffer(bool aNeedsDepth) override;
  gfx::IntRegion CurrentSurfaceInvalidRegion() override;
  void NotifySurfaceReady() override;
  void SetSurfaceIsFlipped(bool aIsFlipped) override;
  bool SurfaceIsFlipped() override;

  // Returns an IOSurface that can be drawn to. The size of the IOSurface will
  // be the size of the rect that has been passed to SetRect.
  // The returned surface is guaranteed to be not in use by the window server.
  // After a call to NextSurface, NextSurface must not be called again until
  // after NotifySurfaceReady has been called. Can be called on any thread. When
  // used from multiple threads, callers need to make sure that they still only
  // call NextSurface and NotifySurfaceReady alternatingly and not in any other
  // order.
  CFTypeRefPtr<IOSurfaceRef> NextSurface();
  CFTypeRefPtr<IOSurfaceRef> NextSurfaceLocked(const MutexAutoLock&);

  // Consumers may provide an object that implements the IOSurfaceRegistry
  // interface.
  // The registry's methods, Register/UnregisterSurface, will be called
  // synchronously during calls to NextSurface(), SetSurfaceRegistry(), and the
  // NativeLayer destructor, on the thread that those things happen to run on.
  // If this layer already owns surfaces when SetSurfaceRegistry gets called
  // with a non-null surface registry, those surfaces will immediately
  // (synchronously) be registered with that registry. If the current surface
  // registry is unset (via a call to SetSurfaceRegistry with a different value,
  // such as null), and the NativeLayer still owns surfaces, then those surfaces
  // will immediately be unregistered.
  // Since NativeLayer objects are reference counted and can be used from
  // different threads, it is recommended to call SetSurfaceRegistry(nullptr)
  // before destroying the NativeLayer so that the UnregisterSurface calls
  // happen at a deterministic time and on the right thread.
  void SetSurfaceRegistry(RefPtr<IOSurfaceRegistry> aSurfaceRegistry);
  RefPtr<IOSurfaceRegistry> GetSurfaceRegistry();

  // Set an opaque region on the layer. Internally, this causes the creation
  // of opaque and transparent sublayers to cover the regions.
  // The coordinates in aRegion are relative to mPosition.
  void SetOpaqueRegion(const gfx::IntRegion& aRegion) override;
  gfx::IntRegion OpaqueRegion() override;

 protected:
  friend class NativeLayerRootCA;

  NativeLayerCA();
  ~NativeLayerCA() override;

  // To be called by NativeLayerRootCA:
  CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
  void ApplyChanges();
  void SetBackingScale(float aBackingScale);

  GLuint GetOrCreateFramebufferForSurface(const MutexAutoLock&,
                                          CFTypeRefPtr<IOSurfaceRef> aSurface,
                                          bool aNeedsDepth);

  struct SurfaceWithInvalidRegion {
    CFTypeRefPtr<IOSurfaceRef> mSurface;
    gfx::IntRegion mInvalidRegion;
    gfx::IntSize mSize;
  };

  std::vector<SurfaceWithInvalidRegion> RemoveExcessUnusedSurfaces(
      const MutexAutoLock&);
  void PlaceContentLayers(const MutexAutoLock&, const gfx::IntRegion& aRegion,
                          bool aOpaque, std::deque<CALayer*>* aLayersToRecycle);

  // Controls access to all fields of this class.
  Mutex mMutex;

  RefPtr<IOSurfaceRegistry> mSurfaceRegistry;  // can be null

  // Each IOSurface is initially created inside NextSurface.
  // The surface stays alive until the recycling mechanism in NextSurface
  // determines it is no longer needed, for example because the layer size
  // changed or because the swap chain has grown too long, or until the layer
  // is destroyed.
  // During the surface's lifetime, it will continuously move through the fields
  // mInProgressSurface, mReadySurface, and back to front through the
  // mSurfaces queue:
  //
  //  mSurfaces.front()
  //  ------[NextSurface()]-----> mInProgressSurface
  //  --[NotifySurfaceReady()]--> mReadySurface
  //  ----[ApplyChanges()]------> mSurfaces.back()  --> .... -->
  //  mSurfaces.front()
  //
  // We mark an IOSurface as "in use" as long as it is either in
  // mInProgressSurface or in mReadySurface. When it is in the mSurfaces queue,
  // it is not marked as "in use" by us - but it can be "in use" by the window
  // server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects
  // whether the window server is still reading from the surface, and we can use
  // this indicator to decide when to recycle the surface.
  //
  // Users of NativeLayerCA normally proceed in this order:
  //  1. Begin a frame by calling NextSurface to get the surface.
  //  2. Draw to the surface.
  //  3. Mark the surface as done by calling NotifySurfaceReady.
  //  4. Trigger a CoreAnimation transaction, and call ApplyChanges within the
  //  transaction.
  //
  // For two consecutive frames, this results in the following ordering of
  // calls:
  // I. NextSurface, NotifySurfaceReady, ApplyChanges, NextSurface,
  //    NotifySurfaceReady, ApplyChanges
  //
  // In this scenario, either mInProgressSurface or mReadySurface is always
  // Nothing().
  //
  // However, sometimes we see the following ordering instead:
  // II. NextSurface, NotifySurfaceReady, NextSurface, ApplyChanges,
  //     NotifySurfaceReady, ApplyChanges
  //
  // This has the NextSurface and ApplyChanges calls in the middle reversed.
  //
  // In that scenario, both mInProgressSurface and mReadySurface will be Some()
  // between the calls to NextSurface and ApplyChanges in the middle, and the
  // two ApplyChanges invocations will submit two different surfaces. It is
  // important that we don't simply discard the first surface during the second
  // call to NextSurface in this scenario.

  // The surface we returned from the most recent call to NextSurface, before
  // the matching call to NotifySurfaceReady.
  // Will only be Some() between calls to NextSurface and NotifySurfaceReady.
  Maybe<SurfaceWithInvalidRegion> mInProgressSurface;

  // The surface that the most recent call to NotifySurfaceReady was for.
  // Will only be Some() between calls to NotifySurfaceReady and the next call
  // to ApplyChanges.
  // Both mInProgressSurface and mReadySurface can be Some() at the same time.
  Maybe<SurfaceWithInvalidRegion> mReadySurface;

  // The queue of surfaces which make up our "swap chain".
  // mSurfaces.front() is the next surface we'll attempt to use.
  // mSurfaces.back() is the one we submitted most recently.
  std::deque<SurfaceWithInvalidRegion> mSurfaces;

  // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady.
  RefPtr<MacIOSurface> mInProgressLockedIOSurface;

  RefPtr<gl::GLContextCGL> mGLContext;

  std::unordered_map<CFTypeRefPtr<IOSurfaceRef>, UniquePtr<gl::MozFramebuffer>>
      mFramebuffers;

  gfx::IntPoint mPosition;
  gfx::IntSize mSize;
  gfx::IntRegion mOpaqueRegion;  // coordinates relative to mPosition

  // Lazily initialized by first call to ApplyChanges.
  CALayer* mWrappingCALayer = nullptr;    // strong
  std::deque<CALayer*> mContentCALayers;  // strong

  float mBackingScale = 1.0f;
  bool mSurfaceIsFlipped = false;
  bool mMutatedPosition = false;
  bool mMutatedGeometry = false;
};

}  // namespace layers
}  // namespace mozilla

#endif  // mozilla_layers_NativeLayerCA_h