xref: /aosp_15_r20/external/skia/src/gpu/ganesh/ops/OpsTask.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2019 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 #ifndef OpsTask_DEFINED
8 #define OpsTask_DEFINED
9 
10 #include "include/core/SkRect.h"
11 #include "include/core/SkRefCnt.h"
12 #include "include/core/SkSpan.h"
13 #include "include/core/SkTypes.h"
14 #include "include/private/base/SkDebug.h"
15 #include "include/private/base/SkTArray.h"
16 #include "include/private/base/SkTo.h"
17 #include "include/private/base/SkTypeTraits.h"
18 #include "include/private/gpu/ganesh/GrTypesPriv.h"
19 #include "src/gpu/Swizzle.h"
20 #include "src/gpu/ganesh/GrDstProxyView.h"
21 #include "src/gpu/ganesh/GrProcessorSet.h"
22 #include "src/gpu/ganesh/GrRenderTask.h"
23 #include "src/gpu/ganesh/GrSurfaceProxy.h"
24 #include "src/gpu/ganesh/GrXferProcessor.h"
25 #include "src/gpu/ganesh/ops/GrOp.h"
26 
27 #include <array>
28 #include <cstdint>
29 
30 class GrAppliedClip;
31 class GrArenas;
32 class GrAuditTrail;
33 class GrCaps;
34 class GrDrawingManager;
35 class GrOpFlushState;
36 class GrRecordingContext;
37 class GrResourceAllocator;
38 class GrSurfaceProxyView;
39 class GrTextureResolveManager;
40 class OpsTaskTestingAccess;
41 class SkArenaAlloc;
42 class SkString;
43 enum GrSurfaceOrigin : int;
44 
45 namespace skgpu::ganesh {
46 
47 class SurfaceDrawContext;
48 
49 class OpsTask : public GrRenderTask {
50 public:
51     // Manage the arenas life time by maintaining are reference to it.
52     OpsTask(GrDrawingManager*, GrSurfaceProxyView, GrAuditTrail*, sk_sp<GrArenas>);
53     ~OpsTask() override;
54 
asOpsTask()55     OpsTask* asOpsTask() override { return this; }
56 
isEmpty()57     bool isEmpty() const { return fOpChains.empty(); }
usesMSAASurface()58     bool usesMSAASurface() const { return fUsesMSAASurface; }
renderPassXferBarriers()59     GrXferBarrierFlags renderPassXferBarriers() const { return fRenderPassXferBarriers; }
60 
61     /**
62      * Empties the draw buffer of any queued up draws.
63      */
64     void endFlush(GrDrawingManager*) override;
65 
66     void onPrePrepare(GrRecordingContext*) override;
67     /**
68      * Together these two functions flush all queued up draws to GrCommandBuffer. The return value
69      * of onExecute() indicates whether any commands were actually issued to the GPU.
70      */
71     void onPrepare(GrOpFlushState* flushState) override;
72     bool onExecute(GrOpFlushState* flushState) override;
73 
addSampledTexture(GrSurfaceProxy * proxy)74     void addSampledTexture(GrSurfaceProxy* proxy) {
75         // This function takes a GrSurfaceProxy because all subsequent uses of the proxy do not
76         // require the specifics of GrTextureProxy, so this avoids a number of unnecessary virtual
77         // asTextureProxy() calls. However, sampling the proxy implicitly requires that the proxy
78         // be a texture. Eventually, when proxies are a unified type with flags, this can just
79         // assert that capability.
80         SkASSERT(proxy->asTextureProxy());
81         fSampledProxies.push_back(proxy);
82     }
83 
84     void addOp(GrDrawingManager*, GrOp::Owner, GrTextureResolveManager, const GrCaps&);
85 
86     void addDrawOp(GrDrawingManager*, GrOp::Owner, bool usesMSAA, const GrProcessorSet::Analysis&,
87                    GrAppliedClip&&, const GrDstProxyView&, GrTextureResolveManager, const GrCaps&);
88 
89     void discard();
90 
91     enum class CanDiscardPreviousOps : bool {
92         kYes = true,
93         kNo = false
94     };
95 
96     // Perform book-keeping for a fullscreen clear, regardless of how the clear is implemented later
97     // (i.e. setColorLoadOp(), adding a ClearOp, or adding a FillRectOp that covers the device).
98     // Returns true if the clear can be converted into a load op (barring device caps).
99     bool resetForFullscreenClear(CanDiscardPreviousOps);
100 
101     // Must only be called if native color buffer clearing is enabled.
102     void setColorLoadOp(GrLoadOp op, std::array<float, 4> color = {0, 0, 0, 0});
103 
104     // Returns whether the given opsTask can be appended at the end of this one.
105     bool canMerge(const OpsTask*) const;
106 
107     // Merge as many opsTasks as possible from the head of 'tasks'. They should all be
108     // renderPass compatible. Return the number of tasks merged into 'this'.
109     int mergeFrom(SkSpan<const sk_sp<GrRenderTask>> tasks);
110 
111 #ifdef SK_DEBUG
numClips()112     int numClips() const override { return fNumClips; }
113     void visitProxies_debugOnly(const GrVisitProxyFunc&) const override;
114 #endif
115 
116 #if defined(GPU_TEST_UTILS)
117     void dump(const SkString& label,
118               SkString indent,
119               bool printDependencies,
120               bool close) const override;
name()121     const char* name() const final { return "Ops"; }
numOpChains()122     int numOpChains() const { return fOpChains.size(); }
getChain(int index)123     const GrOp* getChain(int index) const { return fOpChains[index].head(); }
124 #endif
125 
126 protected:
127     enum class StencilContent {
128         kDontCare,
129         kUserBitsCleared,  // User bits: cleared
130                            // Clip bit: don't care (Ganesh always pre-clears the clip bit.)
131         kPreserved
132     };
133 
134     // Lets the caller specify what the content of the stencil buffer should be at the beginning
135     // of the render pass.
136     //
137     // When requesting kClear: Tilers will load the stencil buffer with a "clear" op; non-tilers
138     // will clear the stencil on first load, and then preserve it on subsequent loads. (Preserving
139     // works because SurfaceDrawContexts are required to leave the user bits in a cleared state
140     // once finished.)
141     //
142     // NOTE: initialContent must not be kClear if caps.performStencilClearsAsDraws() is true.
setInitialStencilContent(StencilContent initialContent)143     void setInitialStencilContent(StencilContent initialContent) {
144         fInitialStencilContent = initialContent;
145     }
146 
147     void recordOp(GrOp::Owner, bool usesMSAA, GrProcessorSet::Analysis, GrAppliedClip*,
148                   const GrDstProxyView*, const GrCaps&);
149 
150     ExpectedOutcome onMakeClosed(GrRecordingContext*, SkIRect* targetUpdateBounds) override;
151 
152 private:
isColorNoOp()153     bool isColorNoOp() const {
154         // TODO: GrLoadOp::kDiscard (i.e., storing a discard) should also be grounds for skipping
155         // execution. We currently don't because of Vulkan. See http://skbug.com/9373.
156         return fOpChains.empty() && GrLoadOp::kLoad == fColorLoadOp;
157     }
158 
159     void deleteOps();
160 
161     // If a surfaceDrawContext splits its opsTask, it uses this method to guarantee stencil values
162     // get preserved across its split tasks.
setMustPreserveStencil()163     void setMustPreserveStencil() { fMustPreserveStencil = true; }
164 
165     // Prevents this opsTask from merging backward. This is used by DMSAA when a non-multisampled
166     // opsTask cannot be promoted to MSAA, or when we split a multisampled opsTask in order to
167     // resolve its texture.
setCannotMergeBackward()168     void setCannotMergeBackward() { fCannotMergeBackward = true; }
169 
170     class OpChain {
171     public:
172         OpChain(GrOp::Owner, GrProcessorSet::Analysis, GrAppliedClip*, const GrDstProxyView*);
~OpChain()173         ~OpChain() {
174             // The ops are stored in a GrMemoryPool and must be explicitly deleted via the pool.
175             SkASSERT(fList.empty());
176         }
177 
178         OpChain(const OpChain&) = delete;
179         OpChain& operator=(const OpChain&) = delete;
180         OpChain(OpChain&&) = default;
181         OpChain& operator=(OpChain&&) = default;
182 
183         void visitProxies(const GrVisitProxyFunc&) const;
184 
head()185         GrOp* head() const { return fList.head(); }
186 
appliedClip()187         GrAppliedClip* appliedClip() const { return fAppliedClip; }
dstProxyView()188         const GrDstProxyView& dstProxyView() const { return fDstProxyView; }
bounds()189         const SkRect& bounds() const { return fBounds; }
190 
191         // Deletes all the ops in the chain.
192         void deleteOps();
193 
194         // Attempts to move the ops from the passed chain to this chain at the head. Also attempts
195         // to merge ops between the chains. Upon success the passed chain is empty.
196         // Fails when the chains aren't of the same op type, have different clips or dst proxies.
197         bool prependChain(OpChain*, const GrCaps&, SkArenaAlloc* opsTaskArena, GrAuditTrail*);
198 
199         // Attempts to add 'op' to this chain either by merging or adding to the tail. Returns
200         // 'op' to the caller upon failure, otherwise null. Fails when the op and chain aren't of
201         // the same op type, have different clips or dst proxies.
202         GrOp::Owner appendOp(GrOp::Owner op, GrProcessorSet::Analysis, const GrDstProxyView*,
203                              const GrAppliedClip*, const GrCaps&, SkArenaAlloc* opsTaskArena,
204                              GrAuditTrail*);
205 
shouldExecute()206         bool shouldExecute() const {
207             return SkToBool(this->head());
208         }
209 
210         using sk_is_trivially_relocatable = std::true_type;
211 
212     private:
213         class List {
214         public:
215             List() = default;
216             List(GrOp::Owner);
217             List(List&&);
218             List& operator=(List&& that);
219 
empty()220             bool empty() const { return !SkToBool(fHead); }
head()221             GrOp* head() const { return fHead.get(); }
tail()222             GrOp* tail() const { return fTail; }
223 
224             GrOp::Owner popHead();
225             GrOp::Owner removeOp(GrOp* op);
226             void pushHead(GrOp::Owner op);
227             void pushTail(GrOp::Owner);
228 
229             void validate() const;
230 
231             using sk_is_trivially_relocatable = std::true_type;
232 
233         private:
234             GrOp::Owner fHead{nullptr};
235             GrOp* fTail{nullptr};
236 
237             static_assert(::sk_is_trivially_relocatable<decltype(fHead)>::value);
238             static_assert(::sk_is_trivially_relocatable<decltype(fTail)>::value);
239         };
240 
241         void validate() const;
242 
243         bool tryConcat(List*, GrProcessorSet::Analysis, const GrDstProxyView&, const GrAppliedClip*,
244                        const SkRect& bounds, const GrCaps&, SkArenaAlloc* opsTaskArena,
245                        GrAuditTrail*);
246         static List DoConcat(List, List, const GrCaps&, SkArenaAlloc* opsTaskArena, GrAuditTrail*);
247 
248         List fList;
249         GrProcessorSet::Analysis fProcessorAnalysis;
250         GrDstProxyView fDstProxyView;
251         GrAppliedClip* fAppliedClip;
252         SkRect fBounds;
253 
254         static_assert(::sk_is_trivially_relocatable<decltype(fProcessorAnalysis)>::value);
255         static_assert(::sk_is_trivially_relocatable<decltype(fDstProxyView)>::value);
256         static_assert(::sk_is_trivially_relocatable<decltype(fAppliedClip)>::value);
257         static_assert(::sk_is_trivially_relocatable<decltype(fBounds)>::value);
258     };
259 
260     void onMakeSkippable() override;
261 
262     bool onIsUsed(GrSurfaceProxy*) const override;
263 
264     void gatherProxyIntervals(GrResourceAllocator*) const override;
265 
266     void forwardCombine(const GrCaps&);
267 
268     // Remove all ops, proxies, etc. Used in the merging algorithm when tasks can be skipped.
269     void reset();
270 
271     friend class ::OpsTaskTestingAccess;
272 
273     // The SDC and OpsTask have to work together to handle buffer clears. In most cases, buffer
274     // clearing can be done natively, in which case the op list's load ops are sufficient. In other
275     // cases, draw ops must be used, which makes the SDC the best place for those decisions. This,
276     // however, requires that the SDC be able to coordinate with the op list to achieve similar ends
277     friend class skgpu::ganesh::SurfaceDrawContext;
278 
279     GrAuditTrail* fAuditTrail;
280 
281     bool fUsesMSAASurface;
282     skgpu::Swizzle fTargetSwizzle;
283     GrSurfaceOrigin fTargetOrigin;
284 
285     GrLoadOp fColorLoadOp = GrLoadOp::kLoad;
286     std::array<float, 4> fLoadClearColor = {0, 0, 0, 0};
287     StencilContent fInitialStencilContent = StencilContent::kDontCare;
288     bool fMustPreserveStencil = false;
289     bool fCannotMergeBackward = false;
290 
291     uint32_t fLastClipStackGenID = SK_InvalidUniqueID;
292     SkIRect fLastDevClipBounds;
293     int fLastClipNumAnalyticElements;
294 
295     GrXferBarrierFlags fRenderPassXferBarriers = GrXferBarrierFlags::kNone;
296 
297     // For ops/opsTask we have mean: 5 stdDev: 28
298     skia_private::STArray<25, OpChain> fOpChains;
299 
300     sk_sp<GrArenas> fArenas;
301     SkDEBUGCODE(int fNumClips;)
302 
303     // TODO: We could look into this being a set if we find we're adding a lot of duplicates that is
304     // causing slow downs.
305     skia_private::TArray<GrSurfaceProxy*, true> fSampledProxies;
306 
307     SkRect fTotalBounds = SkRect::MakeEmpty();
308     SkIRect fClippedContentBounds = SkIRect::MakeEmpty();
309 };
310 
311 }  // namespace skgpu::ganesh
312 
313 #endif // OpsTask_DEFINED
314