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 | |
135 | bool isLazy() const { return !this->isInstantiated() && SkToBool(x: fLazyInstantiateCallback); } |
136 | |
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 | |
144 | SkISize dimensions() const { |
145 | SkASSERT(!this->isFullyLazy()); |
146 | return fDimensions; |
147 | } |
148 | int width() const { return this->dimensions().width(); } |
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 | */ |
156 | SkRect getBoundsRect() const { return SkRect::Make(size: 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 | */ |
164 | SkRect backingStoreBoundsRect() const { |
165 | return SkRect::Make(size: this->backingStoreDimensions()); |
166 | } |
167 | |
168 | SkIRect backingStoreBoundsIRect() const { |
169 | return SkIRect::MakeSize(size: this->backingStoreDimensions()); |
170 | } |
171 | |
172 | const GrBackendFormat& backendFormat() const { return fFormat; } |
173 | |
174 | bool isFormatCompressed(const GrCaps*) const; |
175 | |
176 | class UniqueID { |
177 | public: |
178 | static UniqueID InvalidID() { |
179 | return UniqueID(uint32_t(SK_InvalidUniqueID)); |
180 | } |
181 | |
182 | // wrapped |
183 | explicit UniqueID(const GrGpuResource::UniqueID& id) : fID(id.asUInt()) { } |
184 | // deferred and lazy-callback |
185 | UniqueID() : fID(GrGpuResource::CreateUniqueID()) { } |
186 | |
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 | |
196 | void makeInvalid() { fID = SK_InvalidUniqueID; } |
197 | bool isInvalid() const { return SK_InvalidUniqueID == fID; } |
198 | |
199 | private: |
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 | */ |
220 | UniqueID uniqueID() const { return fUniqueID; } |
221 | |
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 | */ |
243 | virtual GrTextureProxy* asTextureProxy() { return nullptr; } |
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 | */ |
249 | virtual GrRenderTargetProxy* asRenderTargetProxy() { return nullptr; } |
250 | virtual const GrRenderTargetProxy* asRenderTargetProxy() const { return nullptr; } |
251 | |
252 | /** @return The unique key for this proxy. May be invalid. */ |
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 | |
259 | bool isInstantiated() const { return SkToBool(x: fTarget); } |
260 | |
261 | /** Called when this task becomes a target of a GrRenderTask. */ |
262 | void isUsedAsTaskTarget() { ++fTaskTargetCount; } |
263 | |
264 | /** How many render tasks has this proxy been the target of? */ |
265 | int getTaskTargetCount() const { return fTaskTargetCount; } |
266 | |
267 | // If the proxy is already instantiated, return its backing GrTexture; if not, return null. |
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. |
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. |
276 | GrRenderTarget* peekRenderTarget() const { |
277 | return fTarget ? fTarget->asRenderTarget() : nullptr; |
278 | } |
279 | |
280 | /** |
281 | * Does the resource count against the resource budget? |
282 | */ |
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 | */ |
290 | bool readOnly() const { return fSurfaceFlags & GrInternalSurfaceFlags::kReadOnly; } |
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 | */ |
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 | */ |
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 | |
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; |
368 | SkString getDebugName() { |
369 | return fDebugName.isEmpty() ? SkStringPrintf(format: "%d", this->uniqueID().asUInt()) : fDebugName; |
370 | } |
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 | |
378 | bool isDDLTarget() const { return fIsDDLTarget; } |
379 | |
380 | GrProtected isProtected() const { return fIsProtected; } |
381 | |
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 |
414 | bool ignoredByResourceAllocator() const { return fIgnoredByResourceAllocator; } |
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. |
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 |
Definitions
- GrSurfaceProxy
- ResolveFlags
- LazyInstantiationKeyMode
- LazySurfaceDesc
- LazyCallbackResult
- LazyCallbackResult
- LazyCallbackResult
- LazyCallbackResult
- operator=
- operator=
- UseAllocator
- isLazy
- isFullyLazy
- dimensions
- width
- height
- getBoundsRect
- backingStoreBoundsRect
- backingStoreBoundsIRect
- backendFormat
- UniqueID
- InvalidID
- UniqueID
- UniqueID
- asUInt
- operator==
- operator!=
- makeInvalid
- isInvalid
- UniqueID
- uniqueID
- underlyingUniqueID
- asTextureProxy
- asTextureProxy
- asRenderTargetProxy
- asRenderTargetProxy
- getUniqueKey
- isInstantiated
- isUsedAsTaskTarget
- getTaskTargetCount
- peekSurface
- peekTexture
- peekRenderTarget
- isBudgeted
- readOnly
- framebufferOnly
- requiresManualMSAAResolve
- gpuMemorySize
- getLabel
- RectsMustMatch
- getDebugName
- setDebugName
- isDDLTarget
- isProtected
- isPromiseProxy
- ignoredByResourceAllocator
- setIgnoredByResourceAllocator
- setLazyDimensions
Learn more about Flutter for embedded and desktop on industrialflutter.com