Coverage Report

Created: 2024-05-20 07:14

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