NativeLayerMacSurfaceHandler.h (10918B)
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_NativeLayerMacSurfaceHandler_h 7 #define mozilla_layers_NativeLayerMacSurfaceHandler_h 8 9 #include "mozilla/Maybe.h" 10 #include "mozilla/gfx/MacIOSurface.h" 11 #include "mozilla/gfx/Types.h" 12 13 #include "CFTypeRefPtr.h" 14 #include "GLTypes.h" 15 #include "nsISupportsImpl.h" 16 #include "nsRegion.h" 17 18 namespace mozilla { 19 20 namespace gl { 21 class GLContext; 22 } // namespace gl 23 24 namespace wr { 25 class RenderTextureHost; 26 class RenderMacIOSurfaceTextureHost; 27 } // namespace wr 28 29 namespace layers { 30 class SurfacePoolHandleCA; 31 32 struct SurfaceWithInvalidRegion { 33 CFTypeRefPtr<IOSurfaceRef> mSurface; 34 gfx::IntRegion mInvalidRegion; 35 }; 36 37 struct SurfaceWithInvalidRegionAndCheckCount { 38 SurfaceWithInvalidRegion mEntry; 39 uint32_t mCheckCount; // The number of calls to IOSurfaceIsInUse 40 }; 41 42 // A companion to macOS-specific subclasses of NativeLayer, this class 43 // handles the implementation of the surface management calls. The 44 // expectation is that NativeLayerMacIOSurfaceHandler is composed into 45 // these classes, rather than be used as a superclass/. 46 class NativeLayerMacSurfaceHandler { 47 public: 48 NativeLayerMacSurfaceHandler(const gfx::IntSize& aSize, 49 SurfacePoolHandleCA* aSurfacePoolHandle); 50 ~NativeLayerMacSurfaceHandler(); 51 52 gfx::IntSize Size() { return mSize; } 53 54 // Returns the "display rect", in content coordinates, of the current front 55 // surface. This rect acts as an extra clip and prevents invalid content from 56 // getting to the screen. The display rect starts out empty before the first 57 // call to NextSurface*. Note the different coordinate space from the regular 58 // clip rect: the clip rect is "outside" the layer position, the display rect 59 // is "inside" the layer position (moves with the layer). 60 gfx::IntRect DisplayRect() { return mDisplayRect; } 61 62 void SetSurfaceIsFlipped(bool aIsFlipped) { mSurfaceIsFlipped = aIsFlipped; } 63 bool SurfaceIsFlipped() { return mSurfaceIsFlipped; } 64 65 // Gets the next surface for drawing from our swap chain and stores it in 66 // mInProgressSurface. Returns whether this was successful. 67 // mInProgressSurface is guaranteed to be not in use by the window server. 68 // After a call to NextSurface, NextSurface must not be called again until 69 // after NotifySurfaceReady has been called. Can be called on any thread. When 70 // used from multiple threads, callers need to make sure that they still only 71 // call NextSurface and NotifySurfaceReady alternatingly and not in any other 72 // order. 73 bool NextSurface(); 74 75 // Invalidates the specified region in all surfaces that are tracked by this 76 // layer. 77 void InvalidateRegionThroughoutSwapchain(const gfx::IntRegion& aRegion); 78 79 // Invalidate aUpdateRegion and make sure that mInProgressSurface retains any 80 // valid content from the previous surface outside of aUpdateRegion, so that 81 // only aUpdateRegion needs to be drawn. If content needs to be copied, 82 // aCopyFn is called to do the copying. 83 // aCopyFn: Fn(CFTypeRefPtr<IOSurfaceRef> aValidSourceIOSurface, 84 // const gfx::IntRegion& aCopyRegion) -> void 85 template <typename F> 86 void HandlePartialUpdate(const gfx::IntRect& aDisplayRect, 87 const gfx::IntRegion& aUpdateRegion, F&& aCopyFn); 88 89 Maybe<SurfaceWithInvalidRegion> GetUnusedSurfaceAndCleanUp(); 90 91 // Returns a DrawTarget. The size of the DrawTarget will be the same as the 92 // size of this layer. The caller should draw to that DrawTarget, then drop 93 // its reference to the DrawTarget, and then call NotifySurfaceReady(). It can 94 // limit its drawing to aUpdateRegion (which is in the DrawTarget's device 95 // space). After a call to NextSurface*, NextSurface* must not be called again 96 // until after NotifySurfaceReady has been called. Can be called on any 97 // thread. When used from multiple threads, callers need to make sure that 98 // they still only call NextSurface* and NotifySurfaceReady alternatingly and 99 // not in any other order. aUpdateRegion and aDisplayRect are in "content 100 // coordinates" and must not extend beyond the layer size. If aDisplayRect 101 // contains parts that were not valid before, then those parts must be updated 102 // (must be part of aUpdateRegion), so that the entirety of aDisplayRect is 103 // valid after the update. The display rect determines the parts of the 104 // surface that will be shown; this allows using surfaces with only 105 // partially-valid content, as long as none of the invalid content is included 106 // in the display rect. 107 RefPtr<gfx::DrawTarget> NextSurfaceAsDrawTarget( 108 const gfx::IntRect& aDisplayRect, const gfx::IntRegion& aUpdateRegion, 109 gfx::BackendType aBackendType); 110 111 // Returns a GLuint for a framebuffer that can be used for drawing to the 112 // surface. The size of the framebuffer will be the same as the size of this 113 // layer. If aNeedsDepth is true, the framebuffer is created with a depth 114 // buffer. 115 // The framebuffer's depth buffer (if present) may be shared with other 116 // framebuffers of the same size, even from entirely different NativeLayer 117 // objects. The caller should not assume anything about the depth buffer's 118 // existing contents (i.e. it should clear it at the beginning of the draw). 119 // Callers should draw to one layer at a time, such that there is no 120 // interleaved drawing to different framebuffers that could be tripped up by 121 // the sharing. 122 // The caller should draw to the framebuffer, unbind it, and then call 123 // NotifySurfaceReady(). It can limit its drawing to aUpdateRegion (which is 124 // in the framebuffer's device space, possibly "upside down" if 125 // SurfaceIsFlipped()). 126 // The framebuffer will be created in the GLContext that this layer's 127 // SurfacePoolHandle was created for. 128 // After a call to NextSurface*, NextSurface* must not be called again until 129 // after NotifySurfaceReady has been called. Can be called on any thread. When 130 // used from multiple threads, callers need to make sure that they still only 131 // call NextSurface and NotifySurfaceReady alternatingly and not in any other 132 // order. 133 // aUpdateRegion and aDisplayRect are in "content coordinates" and must not 134 // extend beyond the layer size. If aDisplayRect contains parts that were not 135 // valid before, then those parts must be updated (must be part of 136 // aUpdateRegion), so that the entirety of aDisplayRect is valid after the 137 // update. The display rect determines the parts of the surface that will be 138 // shown; this allows using surfaces with only partially-valid content, as 139 // long as none of the invalid content is included in the display rect. 140 Maybe<GLuint> NextSurfaceAsFramebuffer(const gfx::IntRect& aDisplayRect, 141 const gfx::IntRegion& aUpdateRegion, 142 bool aNeedsDepth); 143 144 // Indicates that the surface which has been returned from the most recent 145 // call to NextSurface* is now finished being drawn to and can be displayed on 146 // the screen. Resets the invalid region on the surface to the empty region. 147 // Returns true if the display rect has changed. 148 bool NotifySurfaceReady(); 149 150 // If you know that this layer will likely not draw any more frames, then it's 151 // good to call DiscardBackbuffers in order to save memory and allow other 152 // layer's to pick up the released surfaces from the pool. 153 void DiscardBackbuffers(); 154 155 Maybe<SurfaceWithInvalidRegion> FrontSurface() { return mFrontSurface; } 156 std::vector<SurfaceWithInvalidRegionAndCheckCount> Surfaces() { 157 return mSurfaces; 158 } 159 160 protected: 161 friend class NativeLayerCA; 162 163 gfx::IntSize mSize; 164 gfx::IntRect mDisplayRect; 165 bool mSurfaceIsFlipped = false; 166 167 #ifdef NIGHTLY_BUILD 168 // Track the consistency of our caller's API usage. Layers that are drawn 169 // should only ever be called with NotifySurfaceReady. Layers that are 170 // external should only ever be called with AttachExternalImage. 171 bool mHasEverAttachExternalImage = false; 172 bool mHasEverNotifySurfaceReady = false; 173 #endif 174 175 // Each IOSurface is initially created inside NextSurface. 176 // The surface stays alive until the recycling mechanism in NextSurface 177 // determines it is no longer needed (because the swap chain has grown too 178 // long) or until DiscardBackbuffers() is called or the layer is destroyed. 179 // During the surface's lifetime, it will continuously move through the fields 180 // mInProgressSurface, mFrontSurface, and back to front through the mSurfaces 181 // queue: 182 // 183 // mSurfaces.front() 184 // ------[NextSurface()]-----> mInProgressSurface 185 // --[NotifySurfaceReady()]--> mFrontSurface 186 // --[NotifySurfaceReady()]--> mSurfaces.back() --> .... --> 187 // mSurfaces.front() 188 // 189 // We mark an IOSurface as "in use" as long as it is either in 190 // mInProgressSurface. When it is in mFrontSurface or in the mSurfaces queue, 191 // it is not marked as "in use" by us - but it can be "in use" by the window 192 // server. Consequently, IOSurfaceIsInUse on a surface from mSurfaces reflects 193 // whether the window server is still reading from the surface, and we can use 194 // this indicator to decide when to recycle the surface. 195 // 196 // Users of NativeLayerCA normally proceed in this order: 197 // 1. Begin a frame by calling NextSurface to get the surface. 198 // 2. Draw to the surface. 199 // 3. Mark the surface as done by calling NotifySurfaceReady. 200 // 4. Call NativeLayerRoot::CommitToScreen(), which calls ApplyChanges() 201 // during a CATransaction. 202 203 // The surface we returned from the most recent call to NextSurface, before 204 // the matching call to NotifySurfaceReady. 205 // Will only be Some() between calls to NextSurface and NotifySurfaceReady. 206 Maybe<SurfaceWithInvalidRegion> mInProgressSurface; 207 Maybe<gfx::IntRegion> mInProgressUpdateRegion; 208 Maybe<gfx::IntRect> mInProgressDisplayRect; 209 210 // The surface that the most recent call to NotifySurfaceReady was for. 211 // Will be Some() after the first call to NotifySurfaceReady, for the rest of 212 // the layer's life time. 213 Maybe<SurfaceWithInvalidRegion> mFrontSurface; 214 215 // The queue of surfaces which make up the rest of our "swap chain". 216 // mSurfaces.front() is the next surface we'll attempt to use. 217 // mSurfaces.back() is the one that was used most recently. 218 std::vector<SurfaceWithInvalidRegionAndCheckCount> mSurfaces; 219 220 // Non-null between calls to NextSurfaceAsDrawTarget and NotifySurfaceReady. 221 RefPtr<MacIOSurface> mInProgressLockedIOSurface; 222 223 RefPtr<SurfacePoolHandleCA> mSurfacePoolHandle; 224 }; 225 226 } // namespace layers 227 } // namespace mozilla 228 229 #endif // mozilla_layers_NativeLayerMacSurfaceHandler_h