summaryrefslogtreecommitdiffstats
path: root/gfx/layers/NativeLayerCA.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/NativeLayerCA.h')
-rw-r--r--gfx/layers/NativeLayerCA.h396
1 files changed, 396 insertions, 0 deletions
diff --git a/gfx/layers/NativeLayerCA.h b/gfx/layers/NativeLayerCA.h
new file mode 100644
index 0000000000..bc71f24606
--- /dev/null
+++ b/gfx/layers/NativeLayerCA.h
@@ -0,0 +1,396 @@
+/* -*- 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 wr {
+class RenderMacIOSurfaceTextureHost;
+} // namespace wr
+
+namespace layers {
+
+class NativeLayerRootSnapshotterCA;
+class SurfacePoolHandleCA;
+
+// 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 - this CALayer is the root of the
+// "onscreen" representation of this layer tree.
+// 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 CommitToScreen() is called. This
+// ensures that the modifications happen on the right thread.
+//
+// More specifically: During normal operation, screen updates are driven from a
+// compositing thread. On this thread, the layers are created / destroyed, their
+// contents are painted, and the result is committed to the screen. However,
+// there are some scenarios that need to involve the main thread, most notably
+// window resizing: During a window resize, we still need the drawing part to
+// happen on the compositing thread, but the modifications to the underlying
+// CALayers need to happen on the main thread, once compositing is done.
+//
+// NativeLayerRootCA + NativeLayerCA create and maintain *two* CALayer tree
+// representations: An "onscreen" representation and an "offscreen"
+// representation. These representations are updated via calls to
+// CommitToScreen() and CommitOffscreen(), respectively. The reason for having
+// two representations is the following: Our implementation of the snapshotter
+// API uses CARenderer, which lets us render the composited result of our layer
+// tree into a GPU buffer. But CARenderer requires "ownership" of the rendered
+// CALayers in the sense that it associates the CALayers with a local
+// "CAContext". A CALayer can only be associated with one CAContext at any time.
+// If we wanted te render our *onscreen* CALayers with CARenderer, we would need
+// to remove them from the window, reparent them to the CARenderer, render them,
+// and then put them back into the window. This would lead to a visible flashing
+// effect. To solve this problem, we build two CALayer representations, so that
+// one representation can stay inside the window and the other can stay attached
+// to the CARenderer.
+class NativeLayerRootCA : public NativeLayerRoot {
+ public:
+ static already_AddRefed<NativeLayerRootCA> CreateForCALayer(CALayer* aLayer);
+
+ // Can be called on any thread at any point. Returns whether comitting was
+ // successful. Will return false if called off the main thread while
+ // off-main-thread commits are suspended.
+ bool CommitToScreen() override;
+
+ void CommitOffscreen();
+ void OnNativeLayerRootSnapshotterDestroyed(
+ NativeLayerRootSnapshotterCA* aNativeLayerRootSnapshotter);
+
+ // Enters a mode during which CommitToScreen(), when called on a non-main
+ // thread, will not apply any updates to the CALayer tree.
+ void SuspendOffMainThreadCommits();
+
+ // Exits the mode entered by SuspendOffMainThreadCommits().
+ // Returns true if the last CommitToScreen() was canceled due to suspension,
+ // indicating that another call to CommitToScreen() is needed.
+ bool UnsuspendOffMainThreadCommits();
+
+ bool AreOffMainThreadCommitsSuspended();
+
+ enum class WhichRepresentation : uint8_t { ONSCREEN, OFFSCREEN };
+
+ // Overridden methods
+ already_AddRefed<NativeLayer> CreateLayer(
+ const gfx::IntSize& aSize, bool aIsOpaque,
+ SurfacePoolHandle* aSurfacePoolHandle) override;
+ void AppendLayer(NativeLayer* aLayer) override;
+ void RemoveLayer(NativeLayer* aLayer) override;
+ void SetLayers(const nsTArray<RefPtr<NativeLayer>>& aLayers) override;
+ UniquePtr<NativeLayerRootSnapshotter> CreateSnapshotter() override;
+
+ void SetBackingScale(float aBackingScale);
+ float BackingScale();
+
+ already_AddRefed<NativeLayer> CreateLayerForExternalTexture(
+ bool aIsOpaque) override;
+
+ protected:
+ explicit NativeLayerRootCA(CALayer* aLayer);
+ ~NativeLayerRootCA() override;
+
+ struct Representation {
+ explicit Representation(CALayer* aRootCALayer);
+ ~Representation();
+ void Commit(WhichRepresentation aRepresentation,
+ const nsTArray<RefPtr<NativeLayerCA>>& aSublayers);
+ CALayer* mRootCALayer = nullptr; // strong
+ bool mMutated = false;
+ };
+
+ template <typename F>
+ void ForAllRepresentations(F aFn);
+
+ Mutex mMutex; // protects all other fields
+ Representation mOnscreenRepresentation;
+ Representation mOffscreenRepresentation;
+ NativeLayerRootSnapshotterCA* mWeakSnapshotter = nullptr;
+ nsTArray<RefPtr<NativeLayerCA>> mSublayers; // in z-order
+ float mBackingScale = 1.0f;
+ bool mMutated = false;
+
+ // While mOffMainThreadCommitsSuspended is true, no commits
+ // should happen on a non-main thread, because they might race with
+ // main-thread driven updates such as window shape changes, and cause
+ // glitches.
+ bool mOffMainThreadCommitsSuspended = false;
+
+ // Set to true if CommitToScreen() was aborted because of commit suspension.
+ // Set to false when CommitToScreen() completes successfully. When true,
+ // indicates that CommitToScreen() needs to be called at the next available
+ // opportunity.
+ bool mCommitPending = false;
+};
+
+class RenderSourceNLRS;
+
+class NativeLayerRootSnapshotterCA final : public NativeLayerRootSnapshotter {
+ public:
+ static UniquePtr<NativeLayerRootSnapshotterCA> Create(
+ NativeLayerRootCA* aLayerRoot, CALayer* aRootCALayer);
+ virtual ~NativeLayerRootSnapshotterCA();
+
+ bool ReadbackPixels(const gfx::IntSize& aReadbackSize,
+ gfx::SurfaceFormat aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer) override;
+ already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents(
+ const gfx::IntSize& aWindowSize) override;
+ already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget(
+ const gfx::IntSize& aSize) override;
+ already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
+ CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override;
+
+ protected:
+ NativeLayerRootSnapshotterCA(NativeLayerRootCA* aLayerRoot,
+ RefPtr<gl::GLContext>&& aGL,
+ CALayer* aRootCALayer);
+ void UpdateSnapshot(const gfx::IntSize& aSize);
+
+ RefPtr<NativeLayerRootCA> mLayerRoot;
+ RefPtr<gl::GLContext> mGL;
+
+ // Can be null. Created and updated in UpdateSnapshot.
+ RefPtr<RenderSourceNLRS> mSnapshot;
+ CARenderer* mRenderer = nullptr; // strong
+};
+
+// 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.
+class NativeLayerCA : public NativeLayer {
+ public:
+ virtual NativeLayerCA* AsNativeLayerCA() override { return this; }
+
+ // Overridden methods
+ gfx::IntSize GetSize() override;
+ void SetPosition(const gfx::IntPoint& aPosition) override;
+ gfx::IntPoint GetPosition() override;
+ void SetTransform(const gfx::Matrix4x4& aTransform) override;
+ gfx::Matrix4x4 GetTransform() override;
+ gfx::IntRect GetRect() override;
+ void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) override;
+ RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget(
+ const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion,
+ gfx::BackendType aBackendType) override;
+ Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect,
+ const gfx::IntRegion& aUpdateRegion,
+ bool aNeedsDepth) override;
+ void NotifySurfaceReady() override;
+ void DiscardBackbuffers() override;
+ bool IsOpaque() override;
+ void SetClipRect(const Maybe<gfx::IntRect>& aClipRect) override;
+ Maybe<gfx::IntRect> ClipRect() override;
+ gfx::IntRect CurrentSurfaceDisplayRect() override;
+ void SetSurfaceIsFlipped(bool aIsFlipped) override;
+ bool SurfaceIsFlipped() override;
+
+ void AttachExternalImage(wr::RenderTextureHost* aExternalImage) override;
+
+ protected:
+ friend class NativeLayerRootCA;
+
+ NativeLayerCA(const gfx::IntSize& aSize, bool aIsOpaque,
+ SurfacePoolHandleCA* aSurfacePoolHandle);
+ explicit NativeLayerCA(bool aIsOpaque);
+ ~NativeLayerCA() override;
+
+ // Gets the next surface for drawing from our swap chain and stores it in
+ // mInProgressSurface. Returns whether this was successful.
+ // mInProgressSurface 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.
+ bool NextSurface(const MutexAutoLock&);
+
+ // To be called by NativeLayerRootCA:
+ typedef NativeLayerRootCA::WhichRepresentation WhichRepresentation;
+ CALayer* UnderlyingCALayer(WhichRepresentation aRepresentation);
+ void ApplyChanges(WhichRepresentation aRepresentation);
+ void SetBackingScale(float aBackingScale);
+
+ // Invalidates the specified region in all surfaces that are tracked by this
+ // layer.
+ void InvalidateRegionThroughoutSwapchain(const MutexAutoLock&,
+ const gfx::IntRegion& aRegion);
+
+ GLuint GetOrCreateFramebufferForSurface(const MutexAutoLock&,
+ CFTypeRefPtr<IOSurfaceRef> aSurface,
+ bool aNeedsDepth);
+
+ // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any
+ // valid content from the previous surface outside of aUpdateRegion, so that
+ // only aUpdateRegion needs to be drawn. If content needs to be copied,
+ // aCopyFn is called to do the copying.
+ // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface,
+ // const gfx::IntRegion& aCopyRegion) -> void
+ template <typename F>
+ void HandlePartialUpdate(const MutexAutoLock&,
+ const gfx::IntRect& aDisplayRect,
+ const gfx::IntRegion& aUpdateRegion, F&& aCopyFn);
+
+ struct SurfaceWithInvalidRegion {
+ CFTypeRefPtr<IOSurfaceRef> mSurface;
+ gfx::IntRegion mInvalidRegion;
+ };
+
+ struct SurfaceWithInvalidRegionAndCheckCount {
+ SurfaceWithInvalidRegion mEntry;
+ uint32_t mCheckCount; // The number of calls to IOSurfaceIsInUse
+ };
+
+ Maybe<SurfaceWithInvalidRegion> GetUnusedSurfaceAndCleanUp(
+ const MutexAutoLock&);
+
+ // Wraps one CALayer representation of this NativeLayer.
+ struct Representation {
+ ~Representation();
+
+ CALayer* UnderlyingCALayer() { return mWrappingCALayer; }
+
+ // Applies buffered changes to the native CALayers. The contract with the
+ // caller is as follows: If any of these values have changed since the last
+ // call to ApplyChanges, mMutated[Field] needs to have been set to true
+ // before the call.
+ void ApplyChanges(const gfx::IntSize& aSize, bool aIsOpaque,
+ const gfx::IntPoint& aPosition,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::IntRect& aDisplayRect,
+ const Maybe<gfx::IntRect>& aClipRect, float aBackingScale,
+ bool aSurfaceIsFlipped,
+ gfx::SamplingFilter aSamplingFilter,
+ CFTypeRefPtr<IOSurfaceRef> aFrontSurface);
+
+ // Lazily initialized by first call to ApplyChanges. mWrappingLayer is the
+ // layer that applies the intersection of mDisplayRect and mClipRect (if
+ // set), and mContentCALayer is the layer that hosts the IOSurface. We do
+ // not share clip layers between consecutive NativeLayerCA objects with the
+ // same clip rect.
+ CALayer* mWrappingCALayer = nullptr; // strong
+ CALayer* mContentCALayer = nullptr; // strong
+ CALayer* mOpaquenessTintLayer = nullptr; // strong
+
+ bool mMutatedPosition = true;
+ bool mMutatedTransform = true;
+ bool mMutatedDisplayRect = true;
+ bool mMutatedClipRect = true;
+ bool mMutatedBackingScale = true;
+ bool mMutatedSize = true;
+ bool mMutatedSurfaceIsFlipped = true;
+ bool mMutatedFrontSurface = true;
+ bool mMutatedSamplingFilter = true;
+ };
+
+ Representation& GetRepresentation(WhichRepresentation aRepresentation);
+ template <typename F>
+ void ForAllRepresentations(F aFn);
+
+ // Controls access to all fields of this class.
+ Mutex mMutex;
+
+ // Each IOSurface is initially created inside NextSurface.
+ // The surface stays alive until the recycling mechanism in NextSurface
+ // determines it is no longer needed (because the swap chain has grown too
+ // long) or until DiscardBackbuffers() is called or the layer is destroyed.
+ // During the surface's lifetime, it will continuously move through the fields
+ // mInProgressSurface, mFrontSurface, and back to front through the mSurfaces
+ // queue:
+ //
+ // mSurfaces.front()
+ // ------[NextSurface()]-----> mInProgressSurface
+ // --[NotifySurfaceReady()]--> mFrontSurface
+ // --[NotifySurfaceReady()]--> mSurfaces.back() --> .... -->
+ // mSurfaces.front()
+ //
+ // We mark an IOSurface as "in use" as long as it is either in
+ // mInProgressSurface. When it is in mFrontSurface or 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. Call NativeLayerRoot::CommitToScreen(), which calls ApplyChanges()
+ // during a CATransaction.
+
+ // 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;
+ Maybe<gfx::IntRegion> mInProgressUpdateRegion;
+ Maybe<gfx::IntRect> mInProgressDisplayRect;
+
+ // The surface that the most recent call to NotifySurfaceReady was for.
+ // Will be Some() after the first call to NotifySurfaceReady, for the rest of
+ // the layer's life time.
+ Maybe<SurfaceWithInvalidRegion> mFrontSurface;
+
+ // The queue of surfaces which make up the rest of our "swap chain".
+ // mSurfaces.front() is the next surface we'll attempt to use.
+ // mSurfaces.back() is the one that was used most recently.
+ std::vector<SurfaceWithInvalidRegionAndCheckCount> mSurfaces;
+
+ // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady.
+ RefPtr<MacIOSurface> mInProgressLockedIOSurface;
+
+ RefPtr<SurfacePoolHandleCA> mSurfacePoolHandle;
+ RefPtr<wr::RenderMacIOSurfaceTextureHost> mTextureHost;
+
+ Representation mOnscreenRepresentation;
+ Representation mOffscreenRepresentation;
+
+ gfx::IntPoint mPosition;
+ gfx::Matrix4x4 mTransform;
+ gfx::IntRect mDisplayRect;
+ gfx::IntSize mSize;
+ Maybe<gfx::IntRect> mClipRect;
+ gfx::SamplingFilter mSamplingFilter = gfx::SamplingFilter::POINT;
+ float mBackingScale = 1.0f;
+ bool mSurfaceIsFlipped = false;
+ const bool mIsOpaque = false;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_NativeLayerCA_h