/* -*- 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_NativeLayerMacSurfaceHandler_h #define mozilla_layers_NativeLayerMacSurfaceHandler_h #include "mozilla/Maybe.h" #include "mozilla/gfx/Types.h" #include "GLTypes.h" #include "nsISupportsImpl.h" #include "nsRegion.h" namespace mozilla { namespace gl { class GLContext; } // namespace gl namespace wr { class RenderTextureHost; class RenderMacIOSurfaceTextureHost; } // namespace wr namespace layers { class SurfacePoolHandleCA; struct SurfaceWithInvalidRegion { CFTypeRefPtr mSurface; gfx::IntRegion mInvalidRegion; }; struct SurfaceWithInvalidRegionAndCheckCount { SurfaceWithInvalidRegion mEntry; uint32_t mCheckCount; // The number of calls to IOSurfaceIsInUse }; // A companion to macOS-specific subclasses of NativeLayer, this class // handles the implementation of the surface management calls. The // expectation is that NativeLayerMacIOSurfaceHandler is composed into // these classes, rather than be used as a superclass/. class NativeLayerMacSurfaceHandler { public: NativeLayerMacSurfaceHandler(const gfx::IntSize& aSize, SurfacePoolHandleCA* aSurfacePoolHandle); ~NativeLayerMacSurfaceHandler(); gfx::IntSize Size() { return mSize; } // Returns the "display rect", in content coordinates, of the current front // surface. This rect acts as an extra clip and prevents invalid content from // getting to the screen. The display rect starts out empty before the first // call to NextSurface*. Note the different coordinate space from the regular // clip rect: the clip rect is "outside" the layer position, the display rect // is "inside" the layer position (moves with the layer). gfx::IntRect DisplayRect() { return mDisplayRect; } void SetSurfaceIsFlipped(bool aIsFlipped) { mSurfaceIsFlipped = aIsFlipped; } bool SurfaceIsFlipped() { return mSurfaceIsFlipped; } // 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(); // Invalidates the specified region in all surfaces that are tracked by this // layer. void InvalidateRegionThroughoutSwapchain(const gfx::IntRegion& aRegion); // 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 aValidSourceIOSurface, // const gfx::IntRegion& aCopyRegion) -> void template void HandlePartialUpdate(const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, F&& aCopyFn); Maybe GetUnusedSurfaceAndCleanUp(); // Returns a DrawTarget. The size of the DrawTarget will be the same as the // size of this layer. The caller should draw to that DrawTarget, then drop // its reference to the DrawTarget, and then call NotifySurfaceReady(). It can // limit its drawing to aUpdateRegion (which is in the DrawTarget's device // space). 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. aUpdateRegion and aDisplayRect are in "content // coordinates" and must not extend beyond the layer size. If aDisplayRect // contains parts that were not valid before, then those parts must be updated // (must be part of aUpdateRegion), so that the entirety of aDisplayRect is // valid after the update. The display rect determines the parts of the // surface that will be shown; this allows using surfaces with only // partially-valid content, as long as none of the invalid content is included // in the display rect. RefPtr NextSurfaceAsDrawTarget( const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, gfx::BackendType aBackendType); // Returns a GLuint for a framebuffer that can be used for drawing to the // surface. The size of the framebuffer will be the same as the size of this // layer. If aNeedsDepth is true, the framebuffer is created with a depth // buffer. // The framebuffer's depth buffer (if present) may be shared with other // framebuffers of the same size, even from entirely different NativeLayer // objects. The caller should not assume anything about the depth buffer's // existing contents (i.e. it should clear it at the beginning of the draw). // Callers should draw to one layer at a time, such that there is no // interleaved drawing to different framebuffers that could be tripped up by // the sharing. // The caller should draw to the framebuffer, unbind it, and then call // NotifySurfaceReady(). It can limit its drawing to aUpdateRegion (which is // in the framebuffer's device space, possibly "upside down" if // SurfaceIsFlipped()). // The framebuffer will be created in the GLContext that this layer's // SurfacePoolHandle was created for. // 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. // aUpdateRegion and aDisplayRect are in "content coordinates" and must not // extend beyond the layer size. If aDisplayRect contains parts that were not // valid before, then those parts must be updated (must be part of // aUpdateRegion), so that the entirety of aDisplayRect is valid after the // update. The display rect determines the parts of the surface that will be // shown; this allows using surfaces with only partially-valid content, as // long as none of the invalid content is included in the display rect. Maybe NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, bool aNeedsDepth); // Indicates that the surface which has been returned from the most recent // call to NextSurface* is now finished being drawn to and can be displayed on // the screen. Resets the invalid region on the surface to the empty region. // Returns true if the display rect has changed. bool NotifySurfaceReady(); // If you know that this layer will likely not draw any more frames, then it's // good to call DiscardBackbuffers in order to save memory and allow other // layer's to pick up the released surfaces from the pool. void DiscardBackbuffers(); Maybe FrontSurface() { return mFrontSurface; } Maybe InProgressSurface() { return mInProgressSurface; } std::vector Surfaces() { return mSurfaces; } protected: friend class NativeLayerCA; gfx::IntSize mSize; gfx::IntRect mDisplayRect; bool mSurfaceIsFlipped = false; #ifdef NIGHTLY_BUILD // Track the consistency of our caller's API usage. Layers that are drawn // should only ever be called with NotifySurfaceReady. Layers that are // external should only ever be called with AttachExternalImage. bool mHasEverAttachExternalImage = false; bool mHasEverNotifySurfaceReady = false; #endif // 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 mInProgressSurface; Maybe mInProgressUpdateRegion; Maybe 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 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 mSurfaces; // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady. RefPtr mInProgressLockedIOSurface; RefPtr mSurfacePoolHandle; }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_NativeLayerMacSurfaceHandler_h