• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
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 skgpu_graphite_compute_DispatchGroup_DEFINED
9 #define skgpu_graphite_compute_DispatchGroup_DEFINED
10 
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/base/SkTArray.h"
13 #include "src/gpu/graphite/ComputePipelineDesc.h"
14 #include "src/gpu/graphite/ComputeTypes.h"
15 #include "src/gpu/graphite/ResourceTypes.h"
16 #include "src/gpu/graphite/Sampler.h"
17 #include "src/gpu/graphite/TextureProxy.h"
18 #include "src/gpu/graphite/compute/ComputeStep.h"
19 
20 #include <variant>
21 
22 namespace skgpu::graphite {
23 
24 class CommandBuffer;
25 class ComputePipeline;
26 class Recorder;
27 class ResourceProvider;
28 
29 using BindingIndex = uint32_t;
30 struct TextureIndex { uint32_t fValue; };
31 struct SamplerIndex { uint32_t fValue; };
32 
33 struct BufferView {
34     BindBufferInfo fInfo;
35     size_t fSize;
36 };
37 
38 using DispatchResource = std::variant<BufferView, TextureIndex, SamplerIndex>;
39 using DispatchResourceOptional =
40         std::variant<std::monostate, BufferView, TextureIndex, SamplerIndex>;
41 
42 struct ResourceBinding {
43     BindingIndex fIndex;
44     DispatchResource fResource;
45 };
46 
47 /**
48  * DispatchGroup groups a series of compute pipeline dispatches that need to execute sequentially
49  * (i.e. with a barrier). Dispatches are stored in the order that they will be encoded
50  * in the eventual command buffer.
51  *
52  * A DispatchGroup can be constructed from a series of ComputeSteps using a Builder. The Builder
53  * verifies that the data flow specification between successive ComputeSteps are compatible.
54  * The resources required by a ComputeStep (such as Buffers and TextureProxies) are created by
55  * the Builder as they get added.
56  *
57  * Once a DispatchGroup is finalized, it is immutable. It contains the complete ResourceBinding list
58  * for each dispatch. A list of finalized DispatchGroups can be submitted to the command buffer in a
59  * ComputeTask.
60  */
61 class DispatchGroup final {
62 public:
63     class Builder;
64 
65     struct Dispatch {
66         WorkgroupSize fLocalSize;
67         std::variant<WorkgroupSize, BufferView> fGlobalSizeOrIndirect;
68 
69         std::optional<WorkgroupSize> fGlobalDispatchSize;
70         skia_private::TArray<ResourceBinding> fBindings;
71         skia_private::TArray<ComputeStep::WorkgroupBufferDesc> fWorkgroupBuffers;
72         int fPipelineIndex = 0;
73     };
74 
75     ~DispatchGroup();
76 
dispatches()77     const skia_private::TArray<Dispatch>& dispatches() const { return fDispatchList; }
78 
getPipeline(size_t index)79     const ComputePipeline* getPipeline(size_t index) const { return fPipelines[index].get(); }
80     const Texture* getTexture(size_t index) const;
81     const Sampler* getSampler(size_t index) const;
82 
83     bool prepareResources(ResourceProvider*);
84     void addResourceRefs(CommandBuffer*) const;
85 
86     // Returns a single tasks that must execute before this DispatchGroup or nullptr if the group
87     // has no task dependencies.
88     sk_sp<Task> snapChildTask();
89 
90 private:
91     friend class DispatchGroupBuilder;
92 
93     DispatchGroup() = default;
94 
95     // Disallow copy and move.
96     DispatchGroup(const DispatchGroup&) = delete;
97     DispatchGroup(DispatchGroup&&) = delete;
98 
99     skia_private::TArray<Dispatch> fDispatchList;
100 
101     // The list of all buffers that must be cleared before the dispatches.
102     skia_private::TArray<ClearBufferInfo> fClearList;
103 
104     // Pipelines are referenced by index by each Dispatch in `fDispatchList`. They are stored as a
105     // pipeline description until instantiated in `prepareResources()`.
106     skia_private::TArray<ComputePipelineDesc> fPipelineDescs;
107     skia_private::TArray<SamplerDesc> fSamplerDescs;
108 
109     // Resources instantiated by `prepareResources()`
110     skia_private::TArray<sk_sp<ComputePipeline>> fPipelines;
111     skia_private::TArray<sk_sp<TextureProxy>> fTextures;
112     skia_private::TArray<sk_sp<Sampler>> fSamplers;
113 };
114 
115 class DispatchGroup::Builder final {
116 public:
117     // Contains the resource handles assigned to the outputs of the most recently inserted
118     // ComputeStep.
119     struct OutputTable {
120         // Contains the std::monostate variant if the slot is uninitialized
121         DispatchResourceOptional fSharedSlots[kMaxComputeDataFlowSlots];
122 
123         OutputTable() = default;
124 
resetOutputTable125         void reset() { *this = {}; }
126     };
127 
128     explicit Builder(Recorder*);
129 
outputTable()130     const OutputTable& outputTable() const { return fOutputTable; }
131 
132     // Add a new compute step to the dispatch group and initialize its required resources if
133     // necessary.
134     //
135     // If the global dispatch size (i.e. workgroup count) is known ahead of time it can be
136     // optionally provided here while appending a step. If provided, the ComputeStep will not
137     // receive a call to `calculateGlobalDispatchSize`.
138     bool appendStep(const ComputeStep*, std::optional<WorkgroupSize> globalSize = std::nullopt);
139 
140     // Add a new compute step to the dispatch group with an indirectly specified global dispatch
141     // size. Initialize the required resources if necessary.
142     //
143     // The global dispatch size is determined by the GPU by reading the entries in `indirectBuffer`.
144     // The contents of this buffer must conform to the layout of the `IndirectDispatchArgs`
145     // structure declared in ComputeTypes.h.
146     //
147     // The ComputeStep will not receive a call to `calculateGlobalDispatchSize`.
148     bool appendStepIndirect(const ComputeStep*, BufferView indirectBuffer);
149 
150     // Directly assign a buffer range to a shared slot. ComputeSteps that are appended after this
151     // call will use this resouce if they reference the given `slot` index. Builder will not
152     // allocate the resource internally and ComputeSteps will not receive calls to
153     // `calculateBufferSize`.
154     //
155     // If the slot is already assigned a buffer, it will be overwritten. Calling this method does
156     // not have any effect on previously appended ComputeSteps that were already bound that
157     // resource.
158     //
159     // If `cleared` is kYes, the contents of the given view will be cleared to 0 before the current
160     // DispatchGroup gets submitted.
161     void assignSharedBuffer(BufferView buffer,
162                             unsigned int slot,
163                             ClearBuffer cleared = ClearBuffer::kNo);
164 
165     // Directly assign a texture to a shared slot. ComputeSteps that are appended after this call
166     // will use this resource if they reference the given `slot` index. Builder will not allocate
167     // the resource internally and ComputeSteps will not receive calls to
168     // `calculateTextureParameters`.
169     //
170     // If the slot is already assigned a texture, it will be overwritten. Calling this method does
171     // not have any effect on previously appended ComputeSteps that were already bound that
172     // resource.
173     void assignSharedTexture(sk_sp<TextureProxy> texture, unsigned int slot);
174 
175     // Finalize and return the constructed DispatchGroup.
176     //
177     // The Builder can be used to construct a new DispatchGroup by calling "reset()" after this
178     // method returns.
179     std::unique_ptr<DispatchGroup> finalize();
180 
181 #if defined(GRAPHITE_TEST_UTILS)
182     // Clear old state and start a new DispatchGroup.
183     void reset();
184 #endif
185 
186     // Returns the buffer resource assigned to the shared slot with the given index, if any.
187     BindBufferInfo getSharedBufferResource(unsigned int slot) const;
188 
189     // Returns the texture resource assigned to the shared slot with the given index, if any.
190     sk_sp<TextureProxy> getSharedTextureResource(unsigned int slot) const;
191 
192 private:
193     bool appendStepInternal(const ComputeStep*, const std::variant<WorkgroupSize, BufferView>&);
194 
195     // Allocate a resource that can be assigned to the shared or private data flow slots. Returns a
196     // std::monostate if allocation fails.
197     DispatchResourceOptional allocateResource(const ComputeStep* step,
198                                               const ComputeStep::ResourceDesc& resource,
199                                               int resourceIdx);
200 
201     // The object under construction.
202     std::unique_ptr<DispatchGroup> fObj;
203 
204     Recorder* fRecorder;
205     OutputTable fOutputTable;
206 };
207 
208 }  // namespace skgpu::graphite
209 
210 #endif  // skgpu_graphite_compute_DispatchGroup_DEFINED
211