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
|
/* -*- 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/MacIOSurface.h"
#include "mozilla/gfx/Types.h"
#include "CFTypeRefPtr.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<IOSurfaceRef> 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<IOSurfaceRef> aValidSourceIOSurface,
// const gfx::IntRegion& aCopyRegion) -> void
template <typename F>
void HandlePartialUpdate(const gfx::IntRect& aDisplayRect,
const gfx::IntRegion& aUpdateRegion, F&& aCopyFn);
Maybe<SurfaceWithInvalidRegion> 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<gfx::DrawTarget> 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<GLuint> 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<SurfaceWithInvalidRegion> FrontSurface() { return mFrontSurface; }
std::vector<SurfaceWithInvalidRegionAndCheckCount> 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<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;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_NativeLayerMacSurfaceHandler_h
|