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