• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrSurfaceProxy_DEFINED
9 #define GrSurfaceProxy_DEFINED
10 
11 #include "include/core/SkRect.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkSize.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTypes.h"
16 #include "include/gpu/GrBackendSurface.h"
17 #include "include/gpu/GrTypes.h"
18 #include "include/private/base/SkDebug.h"
19 #include "include/private/base/SkTo.h"
20 #include "include/private/gpu/ganesh/GrTypesPriv.h"
21 #include "src/gpu/ResourceKey.h"
22 #include "src/gpu/ganesh/GrGpuResource.h"
23 #include "src/gpu/ganesh/GrSurface.h"
24 
25 #include <atomic>
26 #include <cstddef>
27 #include <cstdint>
28 #include <functional>
29 #include <string>
30 #include <string_view>
31 #include <utility>
32 
33 class GrCaps;
34 class GrContext_Base;
35 class GrRecordingContext;
36 class GrRenderTarget;
37 class GrRenderTargetProxy;
38 class GrRenderTask;
39 class GrResourceProvider;
40 class GrSurfaceProxyPriv;
41 class GrTexture;
42 class GrTextureProxy;
43 enum class SkBackingFit;
44 namespace skgpu {
45 enum class Budgeted : bool;
46 }
47 
48 class GrSurfaceProxy : public SkNVRefCnt<GrSurfaceProxy> {
49 public:
50     virtual ~GrSurfaceProxy();
51 
52     /**
53      * Indicates "resolutions" that need to be done on a surface before its pixels can be accessed.
54      * If both types of resolve are requested, the MSAA resolve will happen first.
55      */
56     enum class ResolveFlags {
57         kNone = 0,
58         kMSAA = 1 << 0,  // Blit and resolve an internal MSAA render buffer into the texture.
59         kMipMaps = 1 << 1,  // Regenerate all mipmap levels.
60     };
61 
62     /**
63      * Some lazy proxy callbacks want to set their own (or no key) on the GrSurfaces they return.
64      * Others want the GrSurface's key to be kept in sync with the proxy's key. This enum controls
65      * the key relationship between proxies and their targets.
66      */
67     enum class LazyInstantiationKeyMode {
68         /**
69          * Don't key the GrSurface with the proxy's key. The lazy instantiation callback is free to
70          * return a GrSurface that already has a unique key unrelated to the proxy's key.
71          */
72         kUnsynced,
73         /**
74          * Keep the GrSurface's unique key in sync with the proxy's unique key. The GrSurface
75          * returned from the lazy instantiation callback must not have a unique key or have the same
76          * same unique key as the proxy. If the proxy is later assigned a key it is in turn assigned
77          * to the GrSurface.
78          */
79         kSynced
80     };
81 
82     /**
83      * Specifies the expected properties of the GrSurface returned by a lazy instantiation
84      * callback. The dimensions will be negative in the case of a fully lazy proxy.
85      */
86     struct LazySurfaceDesc {
87         SkISize fDimensions;
88         SkBackingFit fFit;
89         GrRenderable fRenderable;
90         GrMipmapped fMipmapped;
91         int fSampleCnt;
92         const GrBackendFormat& fFormat;
93         GrTextureType fTextureType;
94         GrProtected fProtected;
95         skgpu::Budgeted fBudgeted;
96         std::string_view fLabel;
97     };
98 
99     struct LazyCallbackResult {
100         LazyCallbackResult() = default;
101         LazyCallbackResult(const LazyCallbackResult&) = default;
102         LazyCallbackResult(LazyCallbackResult&& that) = default;
103         LazyCallbackResult(sk_sp<GrSurface> surf,
104                            bool releaseCallback = true,
105                            LazyInstantiationKeyMode mode = LazyInstantiationKeyMode::kSynced);
106         LazyCallbackResult(sk_sp<GrTexture> tex);
107 
108         LazyCallbackResult& operator=(const LazyCallbackResult&) = default;
109         LazyCallbackResult& operator=(LazyCallbackResult&&) = default;
110 
111         sk_sp<GrSurface> fSurface;
112         LazyInstantiationKeyMode fKeyMode = LazyInstantiationKeyMode::kSynced;
113         /**
114          * Should the callback be disposed of after it has returned or preserved until the proxy
115          * is freed. Only honored if fSurface is not-null. If it is null the callback is preserved.
116          */
117         bool fReleaseCallback = true;
118     };
119 
120     using LazyInstantiateCallback =
121             std::function<LazyCallbackResult(GrResourceProvider*, const LazySurfaceDesc&)>;
122 
123     enum class UseAllocator {
124         /**
125          * This proxy will be instantiated outside the allocator (e.g. for proxies that are
126          * instantiated in on-flush callbacks).
127          */
128         kNo = false,
129         /**
130          * GrResourceAllocator should instantiate this proxy.
131          */
132         kYes = true,
133     };
134 
isLazy()135     bool isLazy() const { return !this->isInstantiated() && SkToBool(fLazyInstantiateCallback); }
136 
isFullyLazy()137     bool isFullyLazy() const {
138         bool result = fDimensions.width() < 0;
139         SkASSERT(result == (fDimensions.height() < 0));
140         SkASSERT(!result || this->isLazy());
141         return result;
142     }
143 
dimensions()144     SkISize dimensions() const {
145         SkASSERT(!this->isFullyLazy());
146         return fDimensions;
147     }
width()148     int width() const { return this->dimensions().width(); }
height()149     int height() const { return this->dimensions().height(); }
150 
151     SkISize backingStoreDimensions() const;
152 
153     /**
154      * Helper that gets the width and height of the proxy as a bounding rectangle.
155      */
getBoundsRect()156     SkRect getBoundsRect() const { return SkRect::Make(this->dimensions()); }
157 
158     /* A perhaps faster check for this->dimensions() == this->backingStoreDimensions(). */
159     bool isFunctionallyExact() const;
160 
161     /**
162      * Helper that gets the dimensions the backing GrSurface will have as a bounding rectangle.
163      */
backingStoreBoundsRect()164     SkRect backingStoreBoundsRect() const {
165         return SkRect::Make(this->backingStoreDimensions());
166     }
167 
backingStoreBoundsIRect()168     SkIRect backingStoreBoundsIRect() const {
169         return SkIRect::MakeSize(this->backingStoreDimensions());
170     }
171 
backendFormat()172     const GrBackendFormat& backendFormat() const { return fFormat; }
173 
174     bool isFormatCompressed(const GrCaps*) const;
175 
176     class UniqueID {
177     public:
InvalidID()178         static UniqueID InvalidID() {
179             return UniqueID(uint32_t(SK_InvalidUniqueID));
180         }
181 
182         // wrapped
UniqueID(const GrGpuResource::UniqueID & id)183         explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { }
184         // deferred and lazy-callback
UniqueID()185         UniqueID() : fID(GrGpuResource::CreateUniqueID()) { }
186 
asUInt()187         uint32_t asUInt() const { return fID; }
188 
189         bool operator==(const UniqueID& other) const {
190             return fID == other.fID;
191         }
192         bool operator!=(const UniqueID& other) const {
193             return !(*this == other);
194         }
195 
makeInvalid()196         void makeInvalid() { fID = SK_InvalidUniqueID; }
isInvalid()197         bool isInvalid() const { return SK_InvalidUniqueID == fID; }
198 
199     private:
UniqueID(uint32_t id)200         explicit UniqueID(uint32_t id) : fID(id) {}
201 
202         uint32_t fID;
203     };
204 
205     /*
206      * The contract for the uniqueID is:
207      *   for wrapped resources:
208      *      the uniqueID will match that of the wrapped resource
209      *
210      *   for deferred resources:
211      *      the uniqueID will be different from the real resource, when it is allocated
212      *      the proxy's uniqueID will not change across the instantiate call
213      *
214      *    the uniqueIDs of the proxies and the resources draw from the same pool
215      *
216      * What this boils down to is that the uniqueID of a proxy can be used to consistently
217      * track/identify a proxy but should never be used to distinguish between
218      * resources and proxies - beware!
219      */
uniqueID()220     UniqueID uniqueID() const { return fUniqueID; }
221 
underlyingUniqueID()222     UniqueID underlyingUniqueID() const {
223         if (fTarget) {
224             return UniqueID(fTarget->uniqueID());
225         }
226 
227         return fUniqueID;
228     }
229 
230     virtual bool instantiate(GrResourceProvider*) = 0;
231 
232     void deinstantiate();
233 
234     /**
235      * Proxies that are already instantiated and whose backing surface cannot be recycled to
236      * instantiate other proxies do not need to be considered by GrResourceAllocator.
237      */
238     bool canSkipResourceAllocator() const;
239 
240     /**
241      * @return the texture proxy associated with the surface proxy, may be NULL.
242      */
asTextureProxy()243     virtual GrTextureProxy* asTextureProxy() { return nullptr; }
asTextureProxy()244     virtual const GrTextureProxy* asTextureProxy() const { return nullptr; }
245 
246     /**
247      * @return the render target proxy associated with the surface proxy, may be NULL.
248      */
asRenderTargetProxy()249     virtual GrRenderTargetProxy* asRenderTargetProxy() { return nullptr; }
asRenderTargetProxy()250     virtual const GrRenderTargetProxy* asRenderTargetProxy() const { return nullptr; }
251 
252     /** @return The unique key for this proxy. May be invalid. */
getUniqueKey()253     virtual const skgpu::UniqueKey& getUniqueKey() const {
254         // Base class never has a valid unique key.
255         static const skgpu::UniqueKey kInvalidKey;
256         return kInvalidKey;
257     }
258 
isInstantiated()259     bool isInstantiated() const { return SkToBool(fTarget); }
260 
261     /** Called when this task becomes a target of a GrRenderTask. */
isUsedAsTaskTarget()262     void isUsedAsTaskTarget() { ++fTaskTargetCount; }
263 
264     /** How many render tasks has this proxy been the target of? */
getTaskTargetCount()265     int getTaskTargetCount() const { return fTaskTargetCount; }
266 
267     // If the proxy is already instantiated, return its backing GrTexture; if not, return null.
peekSurface()268     GrSurface* peekSurface() const { return fTarget.get(); }
269 
270     // If this is a texture proxy and the proxy is already instantiated, return its backing
271     // GrTexture; if not, return null.
peekTexture()272     GrTexture* peekTexture() const { return fTarget ? fTarget->asTexture() : nullptr; }
273 
274     // If this is a render target proxy and the proxy is already instantiated, return its backing
275     // GrRenderTarget; if not, return null.
peekRenderTarget()276     GrRenderTarget* peekRenderTarget() const {
277         return fTarget ? fTarget->asRenderTarget() : nullptr;
278     }
279 
280     /**
281      * Does the resource count against the resource budget?
282      */
isBudgeted()283     skgpu::Budgeted isBudgeted() const { return fBudgeted; }
284 
285     /**
286      * The pixel values of this proxy's surface cannot be modified (e.g. doesn't support write
287      * pixels or MIP map level regen). Read-only proxies also bypass interval tracking and
288      * assignment in GrResourceAllocator.
289      */
readOnly()290     bool readOnly() const { return fSurfaceFlags & GrInternalSurfaceFlags::kReadOnly; }
framebufferOnly()291     bool framebufferOnly() const {
292         return fSurfaceFlags & GrInternalSurfaceFlags::kFramebufferOnly;
293     }
294 
295     /**
296      * This means surface is a multisampled render target, and internally holds a non-msaa texture
297      * for resolving into. The render target resolves itself by blitting into this internal texture.
298      * (asTexture() might or might not return the internal texture, but if it does, we always
299      * resolve the render target before accessing this texture's data.)
300      */
requiresManualMSAAResolve()301     bool requiresManualMSAAResolve() const {
302         return fSurfaceFlags & GrInternalSurfaceFlags::kRequiresManualMSAAResolve;
303     }
304 
305     /**
306      * Retrieves the amount of GPU memory that will be or currently is used by this resource
307      * in bytes. It is approximate since we aren't aware of additional padding or copies made
308      * by the driver.
309      *
310      * @return the amount of GPU memory used in bytes
311      */
gpuMemorySize()312     size_t gpuMemorySize() const {
313         SkASSERT(!this->isFullyLazy());
314         if (kInvalidGpuMemorySize == fGpuMemorySize) {
315             fGpuMemorySize = this->onUninstantiatedGpuMemorySize();
316             SkASSERT(kInvalidGpuMemorySize != fGpuMemorySize);
317         }
318         return fGpuMemorySize;
319     }
320 
getLabel()321     std::string_view getLabel() const { return fLabel; }
322 
323     enum class RectsMustMatch : bool {
324         kNo = false,
325         kYes = true
326     };
327 
328     // Helper function that creates a temporary SurfaceContext to perform the copy
329     // The copy is is not a render target and not multisampled.
330     //
331     // The intended use of this copy call is simply to copy exact pixel values from one proxy to a
332     // new one. Thus, there isn't a need for a swizzle when doing the copy. The format of the copy
333     // will be the same as the src. Therefore, the copy can be used in a view with the same swizzle
334     // as the original for use with a given color type.
335     //
336     // Optionally gets the render task that performs the copy. If it is later determined that the
337     // copy is not neccessaru then the task can be marked skippable using GrRenderTask::canSkip() and
338     // the copy will be elided.
339     static sk_sp<GrSurfaceProxy> Copy(GrRecordingContext*,
340                                       sk_sp<GrSurfaceProxy> src,
341                                       GrSurfaceOrigin,
342                                       GrMipmapped,
343                                       SkIRect srcRect,
344                                       SkBackingFit,
345                                       skgpu::Budgeted,
346                                       std::string_view label,
347                                       RectsMustMatch = RectsMustMatch::kNo,
348                                       sk_sp<GrRenderTask>* outTask = nullptr);
349 
350     // Same as above Copy but copies the entire 'src'
351     static sk_sp<GrSurfaceProxy> Copy(GrRecordingContext*,
352                                       sk_sp<GrSurfaceProxy> src,
353                                       GrSurfaceOrigin,
354                                       GrMipmapped,
355                                       SkBackingFit,
356                                       skgpu::Budgeted,
357                                       std::string_view label,
358                                       sk_sp<GrRenderTask>* outTask = nullptr);
359 
360 #if GR_TEST_UTILS
361     int32_t testingOnly_getBackingRefCnt() const;
362     GrInternalSurfaceFlags testingOnly_getFlags() const;
363     SkString dump() const;
364 #endif
365 
366 #ifdef SK_DEBUG
367     void validate(GrContext_Base*) const;
getDebugName()368     SkString getDebugName() {
369         return fDebugName.isEmpty() ? SkStringPrintf("%d", this->uniqueID().asUInt()) : fDebugName;
370     }
setDebugName(SkString name)371     void setDebugName(SkString name) { fDebugName = std::move(name); }
372 #endif
373 
374     // Provides access to functions that aren't part of the public API.
375     inline GrSurfaceProxyPriv priv();
376     inline const GrSurfaceProxyPriv priv() const;  // NOLINT(readability-const-return-type)
377 
isDDLTarget()378     bool isDDLTarget() const { return fIsDDLTarget; }
379 
isProtected()380     GrProtected isProtected() const { return fIsProtected; }
381 
isPromiseProxy()382     bool isPromiseProxy() { return fIsPromiseProxy; }
383 
384 protected:
385     // Deferred version - takes a new UniqueID from the shared resource/proxy pool.
386     GrSurfaceProxy(const GrBackendFormat&,
387                    SkISize,
388                    SkBackingFit,
389                    skgpu::Budgeted,
390                    GrProtected,
391                    GrInternalSurfaceFlags,
392                    UseAllocator,
393                    std::string_view label);
394     // Lazy-callback version - takes a new UniqueID from the shared resource/proxy pool.
395     GrSurfaceProxy(LazyInstantiateCallback&&,
396                    const GrBackendFormat&,
397                    SkISize,
398                    SkBackingFit,
399                    skgpu::Budgeted,
400                    GrProtected,
401                    GrInternalSurfaceFlags,
402                    UseAllocator,
403                    std::string_view label);
404 
405     // Wrapped version - shares the UniqueID of the passed surface.
406     // Takes UseAllocator because even though this is already instantiated it still can participate
407     // in allocation by having its backing resource recycled to other uninstantiated proxies or
408     // not depending on UseAllocator.
409     GrSurfaceProxy(sk_sp<GrSurface>, SkBackingFit, UseAllocator);
410 
411     friend class GrSurfaceProxyPriv;
412 
413     // Methods made available via GrSurfaceProxyPriv
ignoredByResourceAllocator()414     bool ignoredByResourceAllocator() const { return fIgnoredByResourceAllocator; }
setIgnoredByResourceAllocator()415     void setIgnoredByResourceAllocator() { fIgnoredByResourceAllocator = true; }
416 
417     void computeScratchKey(const GrCaps&, skgpu::ScratchKey*) const;
418 
419     virtual sk_sp<GrSurface> createSurface(GrResourceProvider*) const = 0;
420     void assign(sk_sp<GrSurface> surface);
421 
422     sk_sp<GrSurface> createSurfaceImpl(GrResourceProvider*, int sampleCnt, GrRenderable,
423                                        GrMipmapped) const;
424 
425     // Once the dimensions of a fully-lazy proxy are decided, and before it gets instantiated, the
426     // client can use this optional method to specify the proxy's dimensions. (A proxy's dimensions
427     // can be less than the GPU surface that backs it. e.g., SkBackingFit::kApprox.) Otherwise,
428     // the proxy's dimensions will be set to match the underlying GPU surface upon instantiation.
setLazyDimensions(SkISize dimensions)429     void setLazyDimensions(SkISize dimensions) {
430         SkASSERT(this->isFullyLazy());
431         SkASSERT(!dimensions.isEmpty());
432         fDimensions = dimensions;
433     }
434 
435     bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, GrRenderable,
436                          GrMipmapped, const skgpu::UniqueKey*);
437 
438     // For deferred proxies this will be null until the proxy is instantiated.
439     // For wrapped proxies it will point to the wrapped resource.
440     sk_sp<GrSurface>       fTarget;
441 
442     // In many cases these flags aren't actually known until the proxy has been instantiated.
443     // However, Ganesh frequently needs to change its behavior based on these settings. For
444     // internally create proxies we will know these properties ahead of time. For wrapped
445     // proxies we will copy the properties off of the GrSurface. For lazy proxies we force the
446     // call sites to provide the required information ahead of time. At instantiation time
447     // we verify that the assumed properties match the actual properties.
448     GrInternalSurfaceFlags fSurfaceFlags;
449 
450 private:
451     // For wrapped resources, 'fFormat' and 'fDimensions' will always be filled in from the
452     // wrapped resource.
453     const GrBackendFormat  fFormat;
454     SkISize                fDimensions;
455 
456     SkBackingFit           fFit;      // always kApprox for lazy-callback resources
457                                       // always kExact for wrapped resources
458     mutable skgpu::Budgeted fBudgeted;  // always kYes for lazy-callback resources
459                                         // set from the backing resource for wrapped resources
460                                         // mutable bc of SkSurface/SkImage wishy-washiness
461                                         // Only meaningful if fLazyInstantiateCallback is non-null.
462     UseAllocator           fUseAllocator;
463 
464     const UniqueID         fUniqueID; // set from the backing resource for wrapped resources
465 
466     LazyInstantiateCallback fLazyInstantiateCallback;
467 
468     SkDEBUGCODE(void validateSurface(const GrSurface*);)
469     SkDEBUGCODE(virtual void onValidateSurface(const GrSurface*) = 0;)
470 
471     static const size_t kInvalidGpuMemorySize = ~static_cast<size_t>(0);
472     SkDEBUGCODE(size_t getRawGpuMemorySize_debugOnly() const { return fGpuMemorySize; })
473 
474     virtual size_t onUninstantiatedGpuMemorySize() const = 0;
475 
476     virtual LazySurfaceDesc callbackDesc() const = 0;
477 
478     bool                   fIgnoredByResourceAllocator = false;
479     bool                   fIsDDLTarget = false;
480     bool                   fIsPromiseProxy = false;
481     GrProtected            fIsProtected;
482 
483     int                     fTaskTargetCount = 0;
484 
485     const std::string fLabel;
486 
487     // This entry is lazily evaluated so, when the proxy wraps a resource, the resource
488     // will be called but, when the proxy is deferred, it will compute the answer itself.
489     // If the proxy computes its own answer that answer is checked (in debug mode) in
490     // the instantiation method. The image may be shared between threads, hence atomic.
491     mutable std::atomic<size_t>         fGpuMemorySize{kInvalidGpuMemorySize};
492     SkDEBUGCODE(SkString   fDebugName;)
493 };
494 
495 GR_MAKE_BITFIELD_CLASS_OPS(GrSurfaceProxy::ResolveFlags)
496 
497 #endif
498