xref: /aosp_15_r20/external/skia/src/gpu/graphite/ScratchResourceManager.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2024 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_ScratchResourceManager_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_ScratchResourceManager_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker class Resource;
21*c8dee2aaSAndroid Build Coastguard Worker class ResourceProvider;
22*c8dee2aaSAndroid Build Coastguard Worker class Texture;
23*c8dee2aaSAndroid Build Coastguard Worker class TextureInfo;
24*c8dee2aaSAndroid Build Coastguard Worker class TextureProxy;
25*c8dee2aaSAndroid Build Coastguard Worker 
26*c8dee2aaSAndroid Build Coastguard Worker // NOTE: This is temporary while atlas management requires flushing an entire Recorder. That
27*c8dee2aaSAndroid Build Coastguard Worker // can break a scratch Device into multiple DrawTasks and the proxy read count needs to count
28*c8dee2aaSAndroid Build Coastguard Worker // all reads regardless of which DrawTask is referenced. Once scratch devices only produce a
29*c8dee2aaSAndroid Build Coastguard Worker // single DrawTask, DrawTask can hold the pending read count directly.
30*c8dee2aaSAndroid Build Coastguard Worker class ProxyReadCountMap {
31*c8dee2aaSAndroid Build Coastguard Worker public:
32*c8dee2aaSAndroid Build Coastguard Worker     ProxyReadCountMap() = default;
33*c8dee2aaSAndroid Build Coastguard Worker 
increment(const TextureProxy * proxy)34*c8dee2aaSAndroid Build Coastguard Worker     void increment(const TextureProxy* proxy) {
35*c8dee2aaSAndroid Build Coastguard Worker         int* count = fCounts.find(proxy);
36*c8dee2aaSAndroid Build Coastguard Worker         if (!count) {
37*c8dee2aaSAndroid Build Coastguard Worker             count = fCounts.set(proxy, 0);
38*c8dee2aaSAndroid Build Coastguard Worker         }
39*c8dee2aaSAndroid Build Coastguard Worker         (*count)++;
40*c8dee2aaSAndroid Build Coastguard Worker     }
41*c8dee2aaSAndroid Build Coastguard Worker 
decrement(const TextureProxy * proxy)42*c8dee2aaSAndroid Build Coastguard Worker     bool decrement(const TextureProxy* proxy) {
43*c8dee2aaSAndroid Build Coastguard Worker         int* count = fCounts.find(proxy);
44*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(count && *count > 0);
45*c8dee2aaSAndroid Build Coastguard Worker         (*count)--;
46*c8dee2aaSAndroid Build Coastguard Worker         return *count == 0;
47*c8dee2aaSAndroid Build Coastguard Worker     }
48*c8dee2aaSAndroid Build Coastguard Worker 
get(const TextureProxy * proxy)49*c8dee2aaSAndroid Build Coastguard Worker     int get(const TextureProxy* proxy) const {
50*c8dee2aaSAndroid Build Coastguard Worker         const int* count = fCounts.find(proxy);
51*c8dee2aaSAndroid Build Coastguard Worker         return count ? *count : 0;
52*c8dee2aaSAndroid Build Coastguard Worker     }
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker private:
55*c8dee2aaSAndroid Build Coastguard Worker     skia_private::THashMap<const TextureProxy*, int> fCounts;
56*c8dee2aaSAndroid Build Coastguard Worker };
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker /**
59*c8dee2aaSAndroid Build Coastguard Worker  * ScratchResourceManager helps coordinate the reuse of resources *within* a Recording that would
60*c8dee2aaSAndroid Build Coastguard Worker  * not otherwise be returned from the ResourceProvider/Cache because the Recorder is holds usage
61*c8dee2aaSAndroid Build Coastguard Worker  * refs on the resources and they are typically not Shareable.
62*c8dee2aaSAndroid Build Coastguard Worker  *
63*c8dee2aaSAndroid Build Coastguard Worker  * A ScratchResourceManager maintains a pool of resources that have been handed out for some use
64*c8dee2aaSAndroid Build Coastguard Worker  * case and then been explicitly returned by the original holder. It is up to the callers to
65*c8dee2aaSAndroid Build Coastguard Worker  * return resources in an optimal manner (for best reuse) and not use them after they've been
66*c8dee2aaSAndroid Build Coastguard Worker  * returned for a later task's use. To help callers manage when they can return resources,
67*c8dee2aaSAndroid Build Coastguard Worker  * the manager maintains a stack that corresponds with the depth-first traversal of the tasks
68*c8dee2aaSAndroid Build Coastguard Worker  * during prepareResources() and provides hooks to register listeners that are invoked when tasks
69*c8dee2aaSAndroid Build Coastguard Worker  * read or sample resources.
70*c8dee2aaSAndroid Build Coastguard Worker  *
71*c8dee2aaSAndroid Build Coastguard Worker  * Once all uninstantiated resources are assigned and prepareResources() succeeds, the
72*c8dee2aaSAndroid Build Coastguard Worker  * ScratchResourceManager can be discarded. The reuse within a Recording's task graph is fixed at
73*c8dee2aaSAndroid Build Coastguard Worker  * that point and remains valid even if the recording is replayed.
74*c8dee2aaSAndroid Build Coastguard Worker  */
75*c8dee2aaSAndroid Build Coastguard Worker class ScratchResourceManager {
76*c8dee2aaSAndroid Build Coastguard Worker public:
77*c8dee2aaSAndroid Build Coastguard Worker     ScratchResourceManager(ResourceProvider* resourceProvider,
78*c8dee2aaSAndroid Build Coastguard Worker                            std::unique_ptr<ProxyReadCountMap>);
79*c8dee2aaSAndroid Build Coastguard Worker     ~ScratchResourceManager();
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker     // Get a scratch texture with the given size and texture info. The returned texture will
82*c8dee2aaSAndroid Build Coastguard Worker     // not be reusable until the caller invokes `returnResource()`. At that point, subsequent
83*c8dee2aaSAndroid Build Coastguard Worker     // compatible calls to getScratchTexture() may return the texture. If there is no compatible
84*c8dee2aaSAndroid Build Coastguard Worker     // available texture to be reused, the ResourceProvider will be used to find or create one.
85*c8dee2aaSAndroid Build Coastguard Worker     //
86*c8dee2aaSAndroid Build Coastguard Worker     // It is the caller's responsibility to determine when it's acceptable to return a resource.
87*c8dee2aaSAndroid Build Coastguard Worker     // That said, it's not mandatory that the scratch resources be returned. In that case, they just
88*c8dee2aaSAndroid Build Coastguard Worker     // stop being available for reuse for later tasks in a Recording.
89*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<Texture> getScratchTexture(SkISize, const TextureInfo&, std::string_view label);
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Eventually update ScratchBuffer and DrawBufferManager to leverage the
92*c8dee2aaSAndroid Build Coastguard Worker     // ScratchResourceManager. There are a few open issues to address first:
93*c8dee2aaSAndroid Build Coastguard Worker     //  - ScratchBuffer uses RAII to return the resource; ScratchResourceManager could adopt this
94*c8dee2aaSAndroid Build Coastguard Worker     //    for buffers but that may only make sense if textures could also operate that way.
95*c8dee2aaSAndroid Build Coastguard Worker     //    Alternatively, ScratchBuffer remains an RAII abstraction on top of ScratchResourceManager.
96*c8dee2aaSAndroid Build Coastguard Worker     //  - ScratchResourceManager is currently only available in snap(), but DrawBufferManager needs
97*c8dee2aaSAndroid Build Coastguard Worker     //    to be available at all times because a DrawPass could be created whenever. b/335644795
98*c8dee2aaSAndroid Build Coastguard Worker     //    considers moving all DrawPass creation into snap() so that would avoid this issue.
99*c8dee2aaSAndroid Build Coastguard Worker     //    Alternatively, ScratchResourceManager could have the same lifetime as the buffer manager.
100*c8dee2aaSAndroid Build Coastguard Worker 
101*c8dee2aaSAndroid Build Coastguard Worker     // Mark the resource as available for reuse. Must have been previously returned by this manager.
102*c8dee2aaSAndroid Build Coastguard Worker     // If the caller does not ensure that all of its uses of the resource are prepared before
103*c8dee2aaSAndroid Build Coastguard Worker     // tasks that are processed after this call, then undefined results can occur.
104*c8dee2aaSAndroid Build Coastguard Worker     void returnTexture(sk_sp<Texture>);
105*c8dee2aaSAndroid Build Coastguard Worker 
106*c8dee2aaSAndroid Build Coastguard Worker     // Graphite accumulates tasks into a graph (implicit dependencies defined by the order they are
107*c8dee2aaSAndroid Build Coastguard Worker     // added to the root task list, or explicitly when appending child tasks). The depth-first
108*c8dee2aaSAndroid Build Coastguard Worker     // traversal of this graph helps impose constraints on the read/write windows of resources. To
109*c8dee2aaSAndroid Build Coastguard Worker     // help Tasks with this tracking, ScratchResourceManager maintains a stack of lists of "pending
110*c8dee2aaSAndroid Build Coastguard Worker     // uses".
111*c8dee2aaSAndroid Build Coastguard Worker     //
112*c8dee2aaSAndroid Build Coastguard Worker     // Each recursion in the depth-first traversal of the task graph pushes the stack. Going up
113*c8dee2aaSAndroid Build Coastguard Worker     // pops the stack. A "pending use" allows a task that modifies a resource to register a
114*c8dee2aaSAndroid Build Coastguard Worker     // listener that is triggered when either its scope is popped off or a consuming task that
115*c8dee2aaSAndroid Build Coastguard Worker     // reads that resource notifies the ScratchResourceManager (e.g. a RenderPassTask or CopyTask
116*c8dee2aaSAndroid Build Coastguard Worker     // that sample a scratch texture). Internally, the listeners can decrement a pending read count
117*c8dee2aaSAndroid Build Coastguard Worker     // or otherwise determine when to call returnResource() without having to be coupled directly to
118*c8dee2aaSAndroid Build Coastguard Worker     // the consuming tasks.
119*c8dee2aaSAndroid Build Coastguard Worker     //
120*c8dee2aaSAndroid Build Coastguard Worker     // When a task calls notifyResourcesConsumed(), all "pending use" listeners in the current
121*c8dee2aaSAndroid Build Coastguard Worker     // scope are invoked and removed from the list. This means that tasks must be externally
122*c8dee2aaSAndroid Build Coastguard Worker     // organized such that only the tasks that prepare the scratch resources for that consuming task
123*c8dee2aaSAndroid Build Coastguard Worker     // are at the same depth. Intermingling writes to multiple scratch textures before they are
124*c8dee2aaSAndroid Build Coastguard Worker     // sampled by separate renderpasses would mean that all the scratch textures could be returned
125*c8dee2aaSAndroid Build Coastguard Worker     // for reuse at the first renderpass. Instead, a TaskList can be used to group the scratch
126*c8dee2aaSAndroid Build Coastguard Worker     // writes with the renderpass that samples it to introduce a scope in the stack. Alternatively,
127*c8dee2aaSAndroid Build Coastguard Worker     // if the caller constructs a single list directly to avoid this issue, the extra stack
128*c8dee2aaSAndroid Build Coastguard Worker     // manipulation can be avoided.
129*c8dee2aaSAndroid Build Coastguard Worker     class PendingUseListener {
130*c8dee2aaSAndroid Build Coastguard Worker     public:
~PendingUseListener()131*c8dee2aaSAndroid Build Coastguard Worker         virtual ~PendingUseListener() {}
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker         virtual void onUseCompleted(ScratchResourceManager*) = 0;
134*c8dee2aaSAndroid Build Coastguard Worker     };
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker     // Push a new scope onto the stack, preventing previously added pending listeners from being
137*c8dee2aaSAndroid Build Coastguard Worker     // invoked when a task consumes resources.
138*c8dee2aaSAndroid Build Coastguard Worker     void pushScope();
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     // Pop the current scope off the stack. This does not invoke any pending listeners that were
141*c8dee2aaSAndroid Build Coastguard Worker     // not consumed by a task within the ending scope. This can happen if an offscreen layer is
142*c8dee2aaSAndroid Build Coastguard Worker     // flushed in a Recording snap() before it's actually been drawn to its target. That final draw
143*c8dee2aaSAndroid Build Coastguard Worker     // can then happen in a subsequent Recording even. By not invoking the pending listener, it will
144*c8dee2aaSAndroid Build Coastguard Worker     // not return the scratch resource, correctly keeping it in use across multiple Recordings.
145*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Eventually, the above scenario should not happen, but that requires atlasing to not
146*c8dee2aaSAndroid Build Coastguard Worker     // force a flush of every Device. Once that is the case, popScope() can ideally assert that
147*c8dee2aaSAndroid Build Coastguard Worker     // there are no more pending listeners to invoke (otherwise it means the tasks were linked
148*c8dee2aaSAndroid Build Coastguard Worker     // incorrectly).
149*c8dee2aaSAndroid Build Coastguard Worker     void popScope();
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     // Invoked by tasks that sample from or read from resources. All pending listeners that were
152*c8dee2aaSAndroid Build Coastguard Worker     // marked in the current scope will be invoked.
153*c8dee2aaSAndroid Build Coastguard Worker     void notifyResourcesConsumed();
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker     // Register a listener that will be invoked on the next call to notifyResourcesConsumed() or
156*c8dee2aaSAndroid Build Coastguard Worker     // popScope() within the current scope. Registering the same listener multiple times will invoke
157*c8dee2aaSAndroid Build Coastguard Worker     // it multiple times.
158*c8dee2aaSAndroid Build Coastguard Worker     //
159*c8dee2aaSAndroid Build Coastguard Worker     // The ScratchResourceManager does not take ownership of these listeners; they are assumed to
160*c8dee2aaSAndroid Build Coastguard Worker     // live for as long as the prepareResources() phase of snapping a Recording.
161*c8dee2aaSAndroid Build Coastguard Worker     void markResourceInUse(PendingUseListener* listener);
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     // Temporary access to the proxy read counts stored in the ScratchResourceManager
pendingReadCount(const TextureProxy * proxy)164*c8dee2aaSAndroid Build Coastguard Worker     int pendingReadCount(const TextureProxy* proxy) const {
165*c8dee2aaSAndroid Build Coastguard Worker         return fProxyReadCounts->get(proxy);
166*c8dee2aaSAndroid Build Coastguard Worker     }
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     // Returns true if the read count reached zero; must only be called if it was > 0 previously.
removePendingRead(const TextureProxy * proxy)169*c8dee2aaSAndroid Build Coastguard Worker     bool removePendingRead(const TextureProxy* proxy) {
170*c8dee2aaSAndroid Build Coastguard Worker         return fProxyReadCounts->decrement(proxy);
171*c8dee2aaSAndroid Build Coastguard Worker     }
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker private:
174*c8dee2aaSAndroid Build Coastguard Worker     struct ScratchTexture {
175*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<Texture> fTexture;
176*c8dee2aaSAndroid Build Coastguard Worker         bool fAvailable;
177*c8dee2aaSAndroid Build Coastguard Worker     };
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker     // If there are no available resources for reuse, new or cached resources will be fetched from
180*c8dee2aaSAndroid Build Coastguard Worker     // this ResourceProvider.
181*c8dee2aaSAndroid Build Coastguard Worker     ResourceProvider* fResourceProvider;
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker     // ScratchResourceManager will maintain separate pools based on the type of Resource since the
184*c8dee2aaSAndroid Build Coastguard Worker     // callers always need a specific sub-Resource and it limits the size of each search pool. It
185*c8dee2aaSAndroid Build Coastguard Worker     // also allows for type-specific search heuristics by when selecting an available resource.
186*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<ScratchTexture> fScratchTextures;
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker     // This single list is organized into a stack of sublists by using null pointers to mark the
189*c8dee2aaSAndroid Build Coastguard Worker     // start of a new scope.
190*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<PendingUseListener*> fListenerStack;
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<ProxyReadCountMap> fProxyReadCounts;
193*c8dee2aaSAndroid Build Coastguard Worker };
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_ResourceReuseManager_DEFINED
198