Flutter Engine
The Flutter Engine
DispatchGroup.cpp
Go to the documentation of this file.
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
9
22
23namespace skgpu::graphite {
24
26
28 fPipelines.reserve(fPipelines.size() + fPipelineDescs.size());
29 for (const ComputePipelineDesc& desc : fPipelineDescs) {
30 auto pipeline = resourceProvider->findOrCreateComputePipeline(desc);
31 if (!pipeline) {
32 SKGPU_LOG_W("Failed to create ComputePipeline for dispatch group. Dropping group!");
33 return false;
34 }
35 fPipelines.push_back(std::move(pipeline));
36 }
37
38 for (int i = 0; i < fTextures.size(); ++i) {
39 if (!fTextures[i]->textureInfo().isValid()) {
40 SKGPU_LOG_W("Failed to validate bound texture. Dropping dispatch group!");
41 return false;
42 }
43 if (!TextureProxy::InstantiateIfNotLazy(resourceProvider, fTextures[i].get())) {
44 SKGPU_LOG_W("Failed to instantiate bound texture. Dropping dispatch group!");
45 return false;
46 }
47 }
48
49 for (const SamplerDesc& desc : fSamplerDescs) {
50 sk_sp<Sampler> sampler = resourceProvider->findOrCreateCompatibleSampler(desc);
51 if (!sampler) {
52 SKGPU_LOG_W("Failed to create sampler. Dropping dispatch group!");
53 return false;
54 }
55 fSamplers.push_back(std::move(sampler));
56 }
57
58 // The DispatchGroup may be long lived on a Recording and we no longer need the descriptors
59 // once we've created pipelines.
60 fPipelineDescs.clear();
61 fSamplerDescs.clear();
62
63 return true;
64}
65
67 for (int i = 0; i < fPipelines.size(); ++i) {
68 commandBuffer->trackResource(fPipelines[i]);
69 }
70 for (int i = 0; i < fTextures.size(); ++i) {
71 commandBuffer->trackCommandBufferResource(fTextures[i]->refTexture());
72 }
73}
74
76 if (fClearList.empty()) {
77 return nullptr;
78 }
79 return ClearBuffersTask::Make(std::move(fClearList));
80}
81
82const Texture* DispatchGroup::getTexture(size_t index) const {
83 SkASSERT(index < SkToSizeT(fTextures.size()));
84 SkASSERT(fTextures[index]);
85 SkASSERT(fTextures[index]->texture());
86 return fTextures[index]->texture();
87}
88
89const Sampler* DispatchGroup::getSampler(size_t index) const {
90 SkASSERT(index < SkToSizeT(fSamplers.size()));
91 SkASSERT(fSamplers[index]);
92 return fSamplers[index].get();
93}
94
96
97Builder::Builder(Recorder* recorder) : fObj(new DispatchGroup()), fRecorder(recorder) {
98 SkASSERT(fRecorder);
99}
100
101bool Builder::appendStep(const ComputeStep* step, std::optional<WorkgroupSize> globalSize) {
102 return this->appendStepInternal(step,
103 globalSize ? *globalSize : step->calculateGlobalDispatchSize());
104}
105
107 return this->appendStepInternal(step, indirectBuffer);
108}
109
110bool Builder::appendStepInternal(
111 const ComputeStep* step,
112 const std::variant<WorkgroupSize, BufferView>& globalSizeOrIndirect) {
113 SkASSERT(fObj);
114 SkASSERT(step);
115
116 Dispatch dispatch;
117
118 // Process the step's resources.
119 auto resources = step->resources();
120 dispatch.fBindings.reserve(resources.size());
121
122 // `nextIndex` matches the declaration order of resources as specified by the ComputeStep.
123 int nextIndex = 0;
124
125 // We assign buffer, texture, and sampler indices from separate ranges. This is compatible with
126 // how Graphite assigns indices on Metal, as these map directly to the buffer/texture/sampler
127 // index ranges. On Dawn/Vulkan buffers and textures/samplers are allocated from separate bind
128 // groups/descriptor sets but texture and sampler indices need to not overlap.
129 const auto& bindingReqs = fRecorder->priv().caps()->resourceBindingRequirements();
130 bool distinctRanges = bindingReqs.fDistinctIndexRanges;
131 bool separateSampler = bindingReqs.fSeparateTextureAndSamplerBinding;
132 int bufferOrGlobalIndex = 0;
133 int texIndex = 0;
134 // NOTE: SkSL Metal codegen always assigns the same binding index to a texture and its sampler.
135 // TODO: This could cause sampler indices to not be tightly packed if the sampler2D declaration
136 // comes after 1 or more storage texture declarations (which don't have samplers).
137 for (const ComputeStep::ResourceDesc& r : resources) {
138 SkASSERT(r.fSlot == -1 || (r.fSlot >= 0 && r.fSlot < kMaxComputeDataFlowSlots));
139 const int index = nextIndex++;
140
141 DispatchResourceOptional maybeResource;
142
143 using DataFlow = ComputeStep::DataFlow;
145 switch (r.fFlow) {
146 case DataFlow::kPrivate:
147 // A sampled or fetched-type readonly texture must either get assigned via
148 // `assignSharedTexture()` or internally allocated as a storage texture of a
149 // preceding step. Such a texture always has a data slot.
150 SkASSERT(r.fType != Type::kReadOnlyTexture);
151 SkASSERT(r.fType != Type::kSampledTexture);
152 maybeResource = this->allocateResource(step, r, index);
153 break;
154 case DataFlow::kShared: {
155 SkASSERT(r.fSlot >= 0);
156 // Allocate a new resource only if the shared slot is empty (except for a
157 // SampledTexture which needs its sampler to be allocated internally).
158 DispatchResourceOptional* slot = &fOutputTable.fSharedSlots[r.fSlot];
159 if (std::holds_alternative<std::monostate>(*slot)) {
160 SkASSERT(r.fType != Type::kReadOnlyTexture);
161 SkASSERT(r.fType != Type::kSampledTexture);
162 maybeResource = this->allocateResource(step, r, index);
163 *slot = maybeResource;
164 } else {
165 SkASSERT(((r.fType == Type::kUniformBuffer ||
166 r.fType == Type::kStorageBuffer ||
167 r.fType == Type::kReadOnlyStorageBuffer ||
168 r.fType == Type::kIndirectBuffer) &&
169 std::holds_alternative<BufferView>(*slot)) ||
170 ((r.fType == Type::kReadOnlyTexture ||
171 r.fType == Type::kSampledTexture ||
172 r.fType == Type::kWriteOnlyStorageTexture) &&
173 std::holds_alternative<TextureIndex>(*slot)));
174#ifdef SK_DEBUG
175 // Ensure that the texture has the right format if it was assigned via
176 // `assignSharedTexture()`.
177 const TextureIndex* texIdx = std::get_if<TextureIndex>(slot);
178 if (texIdx && r.fType == Type::kWriteOnlyStorageTexture) {
179 const TextureProxy* t = fObj->fTextures[texIdx->fValue].get();
180 SkASSERT(t);
181 auto [_, colorType] = step->calculateTextureParameters(index, r);
184 }
185#endif // SK_DEBUG
186
187 maybeResource = *slot;
188
189 if (r.fType == Type::kSampledTexture) {
190 // The shared slot holds the texture part of the sampled texture but we
191 // still need to allocate the sampler.
192 SkASSERT(std::holds_alternative<TextureIndex>(*slot));
193 auto samplerResource = this->allocateResource(step, r, index);
194 const SamplerIndex* samplerIdx =
195 std::get_if<SamplerIndex>(&samplerResource);
196 SkASSERT(samplerIdx);
197 int bindingIndex = distinctRanges ? texIndex
198 : separateSampler ? bufferOrGlobalIndex++
199 : bufferOrGlobalIndex;
200 dispatch.fBindings.push_back(
201 {static_cast<BindingIndex>(bindingIndex), *samplerIdx});
202 }
203 }
204 break;
205 }
206 }
207
208 int bindingIndex = 0;
209 DispatchResource dispatchResource;
210 if (const BufferView* buffer = std::get_if<BufferView>(&maybeResource)) {
211 dispatchResource = *buffer;
212 bindingIndex = bufferOrGlobalIndex++;
213 } else if (const TextureIndex* texIdx = std::get_if<TextureIndex>(&maybeResource)) {
214 dispatchResource = *texIdx;
215 bindingIndex = distinctRanges ? texIndex++ : bufferOrGlobalIndex++;
216 } else {
217 SKGPU_LOG_W("Failed to allocate resource for compute dispatch");
218 return false;
219 }
220 dispatch.fBindings.push_back({static_cast<BindingIndex>(bindingIndex), dispatchResource});
221 }
222
223 auto wgBufferDescs = step->workgroupBuffers();
224 if (!wgBufferDescs.empty()) {
225 dispatch.fWorkgroupBuffers.push_back_n(wgBufferDescs.size(), wgBufferDescs.data());
226 }
227
228 // We need to switch pipelines if this step uses a different pipeline from the previous step.
229 if (fObj->fPipelineDescs.empty() ||
230 fObj->fPipelineDescs.back().uniqueID() != step->uniqueID()) {
231 fObj->fPipelineDescs.push_back(ComputePipelineDesc(step));
232 }
233
234 dispatch.fPipelineIndex = fObj->fPipelineDescs.size() - 1;
235 dispatch.fLocalSize = step->localDispatchSize();
236 dispatch.fGlobalSizeOrIndirect = globalSizeOrIndirect;
237
238 fObj->fDispatchList.push_back(std::move(dispatch));
239
240 return true;
241}
242
243void Builder::assignSharedBuffer(BufferView buffer, unsigned int slot, ClearBuffer cleared) {
244 SkASSERT(fObj);
245 SkASSERT(buffer.fInfo);
246 SkASSERT(buffer.fSize);
247
248 fOutputTable.fSharedSlots[slot] = buffer;
249 if (cleared == ClearBuffer::kYes) {
250 fObj->fClearList.push_back({buffer.fInfo.fBuffer, buffer.fInfo.fOffset, buffer.fSize});
251 }
252}
253
255 SkASSERT(fObj);
257
258 fObj->fTextures.push_back(std::move(texture));
259 fOutputTable.fSharedSlots[slot] = TextureIndex{fObj->fTextures.size() - 1u};
260}
261
262std::unique_ptr<DispatchGroup> Builder::finalize() {
263 auto obj = std::move(fObj);
264 fOutputTable.reset();
265 return obj;
266}
267
268#if defined(GRAPHITE_TEST_UTILS)
269void Builder::reset() {
270 fOutputTable.reset();
271 fObj.reset(new DispatchGroup);
272}
273#endif
274
276 SkASSERT(fObj);
277
279 if (const BufferView* slotValue = std::get_if<BufferView>(&fOutputTable.fSharedSlots[slot])) {
280 info = slotValue->fInfo;
281 }
282 return info;
283}
284
286 SkASSERT(fObj);
287
288 const TextureIndex* idx = std::get_if<TextureIndex>(&fOutputTable.fSharedSlots[slot]);
289 if (!idx) {
290 return nullptr;
291 }
292
293 SkASSERT(idx->fValue < SkToSizeT(fObj->fTextures.size()));
294 return fObj->fTextures[idx->fValue];
295}
296
297DispatchResourceOptional Builder::allocateResource(const ComputeStep* step,
299 int resourceIdx) {
300 SkASSERT(step);
301 SkASSERT(fObj);
303 using ResourcePolicy = ComputeStep::ResourcePolicy;
304
305 DrawBufferManager* bufferMgr = fRecorder->priv().drawBufferManager();
307 switch (resource.fType) {
308 case Type::kReadOnlyStorageBuffer:
309 case Type::kStorageBuffer: {
310 size_t bufferSize = step->calculateBufferSize(resourceIdx, resource);
311 SkASSERT(bufferSize);
312 if (resource.fPolicy == ResourcePolicy::kMapped) {
313 auto [ptr, bufInfo] = bufferMgr->getStoragePointer(bufferSize);
314 if (ptr) {
315 step->prepareStorageBuffer(resourceIdx, resource, ptr, bufferSize);
316 result = BufferView{bufInfo, bufferSize};
317 }
318 } else {
319 auto bufInfo = bufferMgr->getStorage(bufferSize,
320 resource.fPolicy == ResourcePolicy::kClear
323 if (bufInfo) {
324 result = BufferView{bufInfo, bufferSize};
325 }
326 }
327 break;
328 }
329 case Type::kIndirectBuffer: {
330 SkASSERT(resource.fPolicy != ResourcePolicy::kMapped);
331
332 size_t bufferSize = step->calculateBufferSize(resourceIdx, resource);
333 SkASSERT(bufferSize);
334 auto bufInfo = bufferMgr->getIndirectStorage(bufferSize,
335 resource.fPolicy == ResourcePolicy::kClear
338 if (bufInfo) {
339 result = BufferView{bufInfo, bufferSize};
340 }
341 break;
342 }
343 case Type::kUniformBuffer: {
344 SkASSERT(resource.fPolicy == ResourcePolicy::kMapped);
345
346 const auto& resourceReqs = fRecorder->priv().caps()->resourceBindingRequirements();
347 UniformManager uboMgr(resourceReqs.fUniformBufferLayout);
348 step->prepareUniformBuffer(resourceIdx, resource, &uboMgr);
349
350 auto dataBlock = uboMgr.finishUniformDataBlock();
351 SkASSERT(dataBlock.size());
352
353 auto [writer, bufInfo] = bufferMgr->getUniformWriter(dataBlock.size());
354 if (bufInfo) {
355 writer.write(dataBlock.data(), dataBlock.size());
356 result = BufferView{bufInfo, dataBlock.size()};
357 }
358 break;
359 }
360 case Type::kWriteOnlyStorageTexture: {
361 auto [size, colorType] = step->calculateTextureParameters(resourceIdx, resource);
362 SkASSERT(!size.isEmpty());
364
365 auto textureInfo = fRecorder->priv().caps()->getDefaultStorageTextureInfo(colorType);
367 fRecorder->priv().caps(), fRecorder->priv().resourceProvider(),
368 size, textureInfo, "DispatchWriteOnlyStorageTexture", skgpu::Budgeted::kYes);
369 if (texture) {
370 fObj->fTextures.push_back(std::move(texture));
371 result = TextureIndex{fObj->fTextures.size() - 1u};
372 }
373 break;
374 }
375 case Type::kReadOnlyTexture:
376 // This resource type is meant to be populated externally (e.g. by an upload or a render
377 // pass) and only read/sampled by a ComputeStep. It's not meaningful to allocate an
378 // internal texture for a DispatchGroup if none of the ComputeSteps will write to it.
379 //
380 // Instead of using internal allocation, this texture must be assigned explicitly to a
381 // slot by calling the Builder::assignSharedTexture() method.
382 //
383 // Note: A ComputeStep is allowed to read/sample from a storage texture that a previous
384 // ComputeStep has written to.
385 SK_ABORT("a readonly texture must be externally assigned to a ComputeStep");
386 break;
387 case Type::kSampledTexture: {
388 fObj->fSamplerDescs.push_back(step->calculateSamplerParameters(resourceIdx, resource));
389 result = SamplerIndex{fObj->fSamplerDescs.size() - 1u};
390 break;
391 }
392 }
393 return result;
394}
395
396} // namespace skgpu::graphite
static int step(int x, SkScalar min, SkScalar max)
Definition: BlurTest.cpp:215
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
m reset()
#define SKGPU_LOG_W(fmt,...)
Definition: Log.h:40
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kUnknown_SkColorType
uninitialized
Definition: SkColorType.h:20
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static SkString resource(SkPDFResourceType type, int index)
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
const ResourceBindingRequirements & resourceBindingRequirements() const
Definition: Caps.h:150
virtual TextureInfo getDefaultStorageTextureInfo(SkColorType) const =0
static sk_sp< ClearBuffersTask > Make(skia_private::TArray< ClearBufferInfo >)
void trackResource(sk_sp< Resource > resource)
void trackCommandBufferResource(sk_sp< Resource > resource)
bool appendStepIndirect(const ComputeStep *, BufferView indirectBuffer)
void assignSharedBuffer(BufferView buffer, unsigned int slot, ClearBuffer cleared=ClearBuffer::kNo)
sk_sp< TextureProxy > getSharedTextureResource(unsigned int slot) const
std::unique_ptr< DispatchGroup > finalize()
bool appendStep(const ComputeStep *, std::optional< WorkgroupSize > globalSize=std::nullopt)
void assignSharedTexture(sk_sp< TextureProxy > texture, unsigned int slot)
BindBufferInfo getSharedBufferResource(unsigned int slot) const
void addResourceRefs(CommandBuffer *) const
const Texture * getTexture(size_t index) const
bool prepareResources(ResourceProvider *)
const Sampler * getSampler(size_t index) const
std::pair< void *, BindBufferInfo > getStoragePointer(size_t requiredBytes)
std::pair< UniformWriter, BindBufferInfo > getUniformWriter(size_t requiredBytes)
BindBufferInfo getStorage(size_t requiredBytes, ClearBuffer cleared=ClearBuffer::kNo)
BindBufferInfo getIndirectStorage(size_t requiredBytes, ClearBuffer cleared=ClearBuffer::kNo)
const Caps * caps() const
Definition: RecorderPriv.h:31
ResourceProvider * resourceProvider()
Definition: RecorderPriv.h:33
DrawBufferManager * drawBufferManager()
Definition: RecorderPriv.h:58
sk_sp< ComputePipeline > findOrCreateComputePipeline(const ComputePipelineDesc &)
sk_sp< Sampler > findOrCreateCompatibleSampler(const SamplerDesc &)
bool isCompatible(const TextureInfo &that) const
Definition: TextureInfo.cpp:98
static bool InstantiateIfNotLazy(ResourceProvider *, TextureProxy *)
static sk_sp< TextureProxy > Make(const Caps *, ResourceProvider *, SkISize dimensions, const TextureInfo &, std::string_view label, skgpu::Budgeted)
const TextureInfo & textureInfo() const
Definition: TextureProxy.h:38
GAsyncResult * result
FlTexture * texture
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
const myers::Point & get(const myers::Segment &)
std::variant< BufferView, TextureIndex, SamplerIndex > DispatchResource
Definition: DispatchGroup.h:38
constexpr int kMaxComputeDataFlowSlots
Definition: ComputeTypes.h:16
DispatchGroup::Builder Builder
uint32_t BindingIndex
Definition: DispatchGroup.h:29
std::variant< std::monostate, BufferView, TextureIndex, SamplerIndex > DispatchResourceOptional
Definition: DispatchGroup.h:40
DispatchResourceOptional fSharedSlots[kMaxComputeDataFlowSlots]
skia_private::TArray< ComputeStep::WorkgroupBufferDesc > fWorkgroupBuffers
Definition: DispatchGroup.h:71
std::variant< WorkgroupSize, BufferView > fGlobalSizeOrIndirect
Definition: DispatchGroup.h:67
skia_private::TArray< ResourceBinding > fBindings
Definition: DispatchGroup.h:70