Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
DrawPass.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 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
37
38#include "src/base/SkMathPriv.h"
40
41#include <algorithm>
42#include <unordered_map>
43
44using namespace skia_private;
45
46namespace skgpu::graphite {
47
48namespace {
49
50// Helper to manage packed fields within a uint64_t
51template <uint64_t Bits, uint64_t Offset>
52struct Bitfield {
53 static constexpr uint64_t kMask = ((uint64_t) 1 << Bits) - 1;
54 static constexpr uint64_t kOffset = Offset;
55 static constexpr uint64_t kBits = Bits;
56
57 static uint32_t get(uint64_t v) { return static_cast<uint32_t>((v >> kOffset) & kMask); }
58 static uint64_t set(uint32_t v) { return (v & kMask) << kOffset; }
59};
60
61// This class maps objects to a dense index which can then be used to look them up later
62template <typename T, typename V = T, typename C = V>
63class DenseBiMap {
64public:
65 using Index = uint32_t;
66
67 // See note below in GeometryUniformField. This value can be round-tripped within the SortKey
68 // packing for all fields but will not be produced when recording actual draw data.
70
71 bool empty() const { return fIndexToData.empty(); }
72 size_t size() const { return fIndexToData.size(); }
73
74 Index insert(const T& data) {
75 Index* index = fDataToIndex.find(data);
76 if (!index) {
77 SkASSERT(SkToU32(fIndexToData.size()) < kInvalidIndex);
78 index = fDataToIndex.set(data, (Index) fIndexToData.size());
79 fIndexToData.push_back(C{data});
80 }
81 return *index;
82 }
83
84 const V& lookup(Index index) {
85 SkASSERT(index < kInvalidIndex);
86 return fIndexToData[index];
87 }
88
89 SkSpan<V> data() { return {fIndexToData.data(), fIndexToData.size()}; }
90
91 TArray<V>&& detach() { return std::move(fIndexToData); }
92
93private:
94 THashMap<T, Index> fDataToIndex;
95 TArray<V> fIndexToData;
96};
97
98// Tracks uniform data on the CPU and then its transition to storage in a GPU buffer (ubo or ssbo).
99struct CpuOrGpuData {
100 union {
101 const UniformDataBlock* fCpuData;
102 BindUniformBufferInfo fGpuData;
103 };
104
105 // Can only start from CPU data
106 CpuOrGpuData(const UniformDataBlock* cpuData) : fCpuData(cpuData) {}
107};
108
109// Tracks the combination of textures from the paint and from the RenderStep to describe the full
110// binding that needs to be in the command list.
111struct TextureBinding {
112 const TextureDataBlock* fPaintTextures;
113 const TextureDataBlock* fStepTextures;
114
115 bool operator==(const TextureBinding& other) const {
116 return fPaintTextures == other.fPaintTextures &&
117 fStepTextures == other.fStepTextures;
118 }
119 bool operator!=(const TextureBinding& other) const { return !(*this == other); }
120
121 int numTextures() const {
122 return (fPaintTextures ? fPaintTextures->numTextures() : 0) +
123 (fStepTextures ? fStepTextures->numTextures() : 0);
124 }
125};
126
127using UniformCache = DenseBiMap<const UniformDataBlock*, CpuOrGpuData>;
128using TextureBindingCache = DenseBiMap<TextureBinding>;
129using GraphicsPipelineCache = DenseBiMap<GraphicsPipelineDesc>;
130
131// Automatically merges and manages texture bindings and uniform bindings sourced from either the
132// paint or the RenderStep. Tracks the bound state based on last-provided unique index to write
133// Bind commands to a CommandList when necessary.
134class TextureBindingTracker {
135public:
136 TextureBindingCache::Index trackTextures(const TextureDataBlock* paintTextures,
137 const TextureDataBlock* stepTextures) {
138 if (!paintTextures && !stepTextures) {
139 return TextureBindingCache::kInvalidIndex;
140 }
141 return fBindingCache.insert({paintTextures, stepTextures});
142 }
143
144 bool setCurrentTextureBindings(TextureBindingCache::Index bindingIndex) {
145 if (bindingIndex < TextureBindingCache::kInvalidIndex && fLastIndex != bindingIndex) {
146 fLastIndex = bindingIndex;
147 return true;
148 }
149 // No binding change
150 return false;
151 }
152
153 void bindTextures(DrawPassCommands::List* commandList) {
154 SkASSERT(fLastIndex < TextureBindingCache::kInvalidIndex);
155 const TextureBinding& binding = fBindingCache.lookup(fLastIndex);
156
157 auto [texIndices, samplerIndices] =
158 commandList->bindDeferredTexturesAndSamplers(binding.numTextures());
159
160 if (binding.fPaintTextures) {
161 for (int i = 0; i < binding.fPaintTextures->numTextures(); ++i) {
162 auto [tex, sampler] = binding.fPaintTextures->texture(i);
163 *texIndices++ = fProxyCache.insert(tex.get());
164 *samplerIndices++ = fSamplerCache.insert(sampler);
165 }
166 }
167 if (binding.fStepTextures) {
168 for (int i = 0; i < binding.fStepTextures->numTextures(); ++i) {
169 auto [tex, sampler] = binding.fStepTextures->texture(i);
170 *texIndices++ = fProxyCache.insert(tex.get());
171 *samplerIndices++ = fSamplerCache.insert(sampler);
172 }
173 }
174 }
175
176 TArray<sk_sp<TextureProxy>>&& detachTextures() { return fProxyCache.detach(); }
177 TArray<SamplerDesc>&& detachSamplers() { return fSamplerCache.detach(); }
178
179private:
180 struct ProxyRef {
181 const TextureProxy* fProxy;
182 operator sk_sp<TextureProxy>() const { return sk_ref_sp(fProxy); }
183 };
184 using TextureProxyCache = DenseBiMap<const TextureProxy*, sk_sp<TextureProxy>, ProxyRef>;
185 using SamplerDescCache = DenseBiMap<SamplerDesc>;
186
187 TextureBindingCache fBindingCache;
188
189 TextureProxyCache fProxyCache;
190 SamplerDescCache fSamplerCache;
191
192 TextureBindingCache::Index fLastIndex = TextureBindingCache::kInvalidIndex;
193};
194
195// Collects and writes uniform data either to uniform buffers or to shared storage buffers, and
196// tracks when bindings need to change between draws.
197class UniformTracker {
198public:
199 UniformTracker(bool useStorageBuffers) : fUseStorageBuffers(useStorageBuffers) {}
200
201 // Maps a given {pipeline index, uniform data cache index} pair to a buffer index within the
202 // pipeline's accumulated array of uniforms.
203 UniformCache::Index trackUniforms(GraphicsPipelineCache::Index pipelineIndex,
204 const UniformDataBlock* cpuData) {
205 if (!cpuData) {
206 return UniformCache::kInvalidIndex;
207 }
208
209 if (pipelineIndex >= SkToU32(fPerPipelineCaches.size())) {
210 fPerPipelineCaches.resize(pipelineIndex + 1);
211 }
212
213 return fPerPipelineCaches[pipelineIndex].insert(cpuData);
214 }
215
216 // Writes all tracked uniform data into buffers, tracking the bindings for the written buffers
217 // by GraphicsPipelineCache::Index and possibly the UniformCache::Index (when not using SSBOs).
218 // When using SSBOs, the buffer is the same for all UniformCache::Indices that share the same
219 // pipeline (and is stored in index 0).
220 bool writeUniforms(DrawBufferManager* bufferMgr) {
221 for (UniformCache& cache : fPerPipelineCaches) {
222 if (cache.empty()) {
223 continue;
224 }
225 // All data blocks for the same pipeline have the same size, so peek the first
226 // to determine the total buffer size
227 size_t udbSize = cache.lookup(0).fCpuData->size();
228 size_t udbDataSize = udbSize;
229 if (!fUseStorageBuffers) {
230 udbSize = bufferMgr->alignUniformBlockSize(udbSize);
231 }
232 auto [writer, bufferInfo] =
233 fUseStorageBuffers ? bufferMgr->getSsboWriter(udbSize * cache.size())
234 : bufferMgr->getUniformWriter(udbSize * cache.size());
235 if (!writer) {
236 return false; // Early out if buffer mapping failed
237 }
238
239 uint32_t bindingSize;
240 if (fUseStorageBuffers) {
241 // For storage buffer we will always bind all the blocks.
242 bindingSize = static_cast<uint32_t>(udbSize * cache.size());
243 }
244 else {
245 // For uniform buffer we will bind one block at a time.
246 bindingSize = static_cast<uint32_t>(udbSize);
247 }
248
249 for (CpuOrGpuData& dataBlock : cache.data()) {
250 SkASSERT(dataBlock.fCpuData->size() == udbDataSize);
251 writer.write(dataBlock.fCpuData->data(), udbDataSize);
252 // Swap from tracking the CPU data to the location of the GPU data
253 dataBlock.fGpuData.fBuffer = bufferInfo.fBuffer;
254 dataBlock.fGpuData.fOffset = bufferInfo.fOffset;
255 dataBlock.fGpuData.fBindingSize = bindingSize;
256
257 if (!fUseStorageBuffers) {
258 bufferInfo.fOffset += bindingSize;
259 writer.skipBytes(bindingSize - udbDataSize);
260 } // else keep bufferInfo pointing to the start of the array
261 }
262 }
263
264 return true;
265 }
266
267 // Updates the current tracked pipeline and uniform index and returns whether or not
268 // bindBuffers() needs to be called, depending on if 'fUseStorageBuffers' is true or not.
269 bool setCurrentUniforms(GraphicsPipelineCache::Index pipelineIndex,
270 UniformCache::Index uniformIndex) {
271 if (uniformIndex >= UniformCache::kInvalidIndex) {
272 return false;
273 }
274 SkASSERT(pipelineIndex < SkToU32(fPerPipelineCaches.size()) &&
275 uniformIndex < fPerPipelineCaches[pipelineIndex].size());
276
277 if (fUseStorageBuffers) {
278 uniformIndex = 0; // The specific index has no effect on binding
279 }
280 if (fLastPipeline != pipelineIndex || fLastIndex != uniformIndex) {
281 fLastPipeline = pipelineIndex;
282 fLastIndex = uniformIndex;
283 return true;
284 } else {
285 return false;
286 }
287 }
288
289 // Binds a new uniform or storage buffer, based on most recently provided batch key and uniform
290 // data cache index.
291 void bindUniforms(UniformSlot slot, DrawPassCommands::List* commandList) {
292 SkASSERT(fLastPipeline < GraphicsPipelineCache::kInvalidIndex &&
293 fLastIndex < UniformCache::kInvalidIndex);
294 SkASSERT(!fUseStorageBuffers || fLastIndex == 0);
295 const BindUniformBufferInfo& binding =
296 fPerPipelineCaches[fLastPipeline].lookup(fLastIndex).fGpuData;
297 commandList->bindUniformBuffer(binding, slot);
298 }
299
300private:
301 // Access first by pipeline index. The final UniformCache::Index is either used to select the
302 // BindBufferInfo for a draw using UBOs, or it's the real index into a packed array of uniforms
303 // in a storage buffer object (whose binding is stored in index 0).
304 TArray<UniformCache> fPerPipelineCaches;
305
306 const bool fUseStorageBuffers;
307
308 GraphicsPipelineCache::Index fLastPipeline = GraphicsPipelineCache::kInvalidIndex;
309 UniformCache::Index fLastIndex = UniformCache::kInvalidIndex;
310};
311
312} // namespace
313
314///////////////////////////////////////////////////////////////////////////////////////////////////
315
316/**
317 * Each Draw in a DrawList might be processed by multiple RenderSteps (determined by the Draw's
318 * Renderer), which can be sorted independently. Each (step, draw) pair produces its own SortKey.
319 *
320 * The goal of sorting draws for the DrawPass is to minimize pipeline transitions and dynamic binds
321 * within a pipeline, while still respecting the overall painter's order. This decreases the number
322 * of low-level draw commands in a command buffer and increases the size of those, allowing the GPU
323 * to operate more efficiently and have fewer bubbles within its own instruction stream.
324 *
325 * The Draw's CompresssedPaintersOrder and DisjointStencilINdex represent the most significant bits
326 * of the key, and are shared by all SortKeys produced by the same draw. Next, the pipeline
327 * description is encoded in two steps:
328 * 1. The index of the RenderStep packed in the high bits to ensure each step for a draw is
329 * ordered correctly.
330 * 2. An index into a cache of pipeline descriptions is used to encode the identity of the
331 * pipeline (SortKeys that differ in the bits from #1 necessarily would have different
332 * descriptions, but then the specific ordering of the RenderSteps isn't enforced).
333 * Last, the SortKey encodes an index into the set of uniform bindings accumulated for a DrawPass.
334 * This allows the SortKey to cluster draw steps that have both a compatible pipeline and do not
335 * require rebinding uniform data or other state (e.g. scissor). Since the uniform data index and
336 * the pipeline description index are packed into indices and not actual pointers, a given SortKey
337 * is only valid for the a specific DrawList->DrawPass conversion.
338 */
340public:
342 int renderStep,
343 GraphicsPipelineCache::Index pipelineIndex,
344 UniformCache::Index geomUniformIndex,
345 UniformCache::Index shadingUniformIndex,
346 TextureBindingCache::Index textureBindingIndex)
347 : fPipelineKey(ColorDepthOrderField::set(draw->fDrawParams.order().paintOrder().bits()) |
348 StencilIndexField::set(draw->fDrawParams.order().stencilIndex().bits()) |
349 RenderStepField::set(static_cast<uint32_t>(renderStep)) |
350 PipelineField::set(pipelineIndex))
351 , fUniformKey(GeometryUniformField::set(geomUniformIndex) |
352 ShadingUniformField::set(shadingUniformIndex) |
353 TextureBindingsField::set(textureBindingIndex))
354 , fDraw(draw) {
355 SkASSERT(pipelineIndex < GraphicsPipelineCache::kInvalidIndex);
356 SkASSERT(renderStep <= draw->fRenderer->numRenderSteps());
357 }
358
359 bool operator<(const SortKey& k) const {
360 return fPipelineKey < k.fPipelineKey ||
361 (fPipelineKey == k.fPipelineKey && fUniformKey < k.fUniformKey);
362 }
363
364 const RenderStep& renderStep() const {
365 return fDraw->fRenderer->step(RenderStepField::get(fPipelineKey));
366 }
367
368 const DrawList::Draw& draw() const { return *fDraw; }
369
370 GraphicsPipelineCache::Index pipelineIndex() const {
371 return PipelineField::get(fPipelineKey);
372 }
373 UniformCache::Index geometryUniformIndex() const {
374 return GeometryUniformField::get(fUniformKey);
375 }
376 UniformCache::Index shadingUniformIndex() const {
377 return ShadingUniformField::get(fUniformKey);
378 }
379 TextureBindingCache::Index textureBindingIndex() const {
380 return TextureBindingsField::get(fUniformKey);
381 }
382
383private:
384 // Fields are ordered from most-significant to least when sorting by 128-bit value.
385 // NOTE: We don't use C++ bit fields because field ordering is implementation defined and we
386 // need to sort consistently.
387 using ColorDepthOrderField = Bitfield<16, 48>; // sizeof(CompressedPaintersOrder)
388 using StencilIndexField = Bitfield<16, 32>; // sizeof(DisjointStencilIndex)
389 using RenderStepField = Bitfield<2, 30>; // bits >= log2(Renderer::kMaxRenderSteps)
390 using PipelineField = Bitfield<30, 0>; // bits >= log2(max total steps in draw list)
391 uint64_t fPipelineKey;
392
393 // The uniform/texture index fields need 1 extra bit to encode "no-data". Values that are
394 // greater than or equal to 2^(bits-1) represent "no-data", while values between
395 // [0, 2^(bits-1)-1] can access data arrays without extra logic.
396 using GeometryUniformField = Bitfield<17, 47>; // bits >= 1+log2(max total steps)
397 using ShadingUniformField = Bitfield<17, 30>; // bits >= 1+log2(max total steps)
398 using TextureBindingsField = Bitfield<30, 0>; // bits >= 1+log2(max total steps)
399 uint64_t fUniformKey;
400
401 // Backpointer to the draw that produced the sort key
402 const DrawList::Draw* fDraw;
403
404 static_assert(ColorDepthOrderField::kBits >= sizeof(CompressedPaintersOrder));
405 static_assert(StencilIndexField::kBits >= sizeof(DisjointStencilIndex));
406 static_assert(RenderStepField::kBits >= SkNextLog2_portable(Renderer::kMaxRenderSteps));
407 static_assert(PipelineField::kBits >= SkNextLog2_portable(DrawList::kMaxRenderSteps));
408 static_assert(GeometryUniformField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
409 static_assert(ShadingUniformField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
410 static_assert(TextureBindingsField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
411};
412
413///////////////////////////////////////////////////////////////////////////////////////////////////
414
417 const SkImageInfo& targetInfo,
418 const SkIPoint& targetOffset) {
419 SkASSERT(recorder->priv().caps()->isTexturable(target->textureInfo()));
420 SkIRect dstSrcRect = SkIRect::MakePtSize(targetOffset, targetInfo.dimensions());
422 recorder->priv().resourceProvider(),
423 targetInfo.dimensions(),
424 target->textureInfo(),
426 if (!copy) {
427 SKGPU_LOG_W("Failed to create destination copy texture for dst read.");
428 return nullptr;
429 }
430
432 std::move(target), dstSrcRect, copy, /*dstPoint=*/{0, 0});
433 if (!copyTask) {
434 SKGPU_LOG_W("Failed to create destination copy task for dst read.");
435 return nullptr;
436 }
437
438 recorder->priv().add(std::move(copyTask));
439 return copy;
440}
441
442DrawPass::DrawPass(sk_sp<TextureProxy> target,
443 std::pair<LoadOp, StoreOp> ops,
444 std::array<float, 4> clearColor)
445 : fTarget(std::move(target))
446 , fBounds(SkIRect::MakeEmpty())
447 , fOps(ops)
448 , fClearColor(clearColor) {}
449
450DrawPass::~DrawPass() = default;
451
452std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
453 std::unique_ptr<DrawList> draws,
455 const SkImageInfo& targetInfo,
456 std::pair<LoadOp, StoreOp> ops,
457 std::array<float, 4> clearColor) {
458 // NOTE: This assert is here to ensure SortKey is as tightly packed as possible. Any change to
459 // its size should be done with care and good reason. The performance of sorting the keys is
460 // heavily tied to the total size.
461 //
462 // At 24 bytes (current), sorting is about 30% slower than if SortKey could be packed into just
463 // 16 bytes. There are several ways this could be done if necessary:
464 // - Restricting the max draw count to 16k (14-bits) and only using a single index to refer to
465 // the uniform data => 8 bytes of key, 8 bytes of pointer.
466 // - Restrict the max draw count to 32k (15-bits), use a single uniform index, and steal the
467 // 4 low bits from the Draw* pointer since it's 16 byte aligned.
468 // - Compact the Draw* to an index into the original collection, although that has extra
469 // indirection and does not work as well with SkTBlockList.
470 // In pseudo tests, manipulating the pointer or having to mask out indices was about 15% slower
471 // than an 8 byte key and unmodified pointer.
472 static_assert(sizeof(DrawPass::SortKey) ==
473 SkAlignTo(16 + sizeof(void*), alignof(DrawPass::SortKey)));
474
475 TRACE_EVENT1("skia.gpu", TRACE_FUNC, "draw count", draws->fDraws.count());
476
477 // The DrawList is converted directly into the DrawPass' data structures, but once the DrawPass
478 // is returned from Make(), it is considered immutable.
479 std::unique_ptr<DrawPass> drawPass(new DrawPass(target, ops, clearColor));
480
481 Rect passBounds = Rect::InfiniteInverted();
482
483 // We don't expect the uniforms from the renderSteps to reappear multiple times across a
484 // recorder's lifetime so we only de-dupe them w/in a given DrawPass.
485 UniformDataCache geometryUniformDataCache;
486 TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
487 DrawBufferManager* bufferMgr = recorder->priv().drawBufferManager();
488 if (bufferMgr->hasMappingFailed()) {
489 SKGPU_LOG_W("Buffer mapping has already failed; dropping draw pass!");
490 return nullptr;
491 }
492
493 GraphicsPipelineCache pipelineCache;
494
495 // Geometry uniforms are currently always UBO-backed.
496 const bool useStorageBuffers = recorder->priv().caps()->storageBufferPreferred();
497 const ResourceBindingRequirements& bindingReqs =
498 recorder->priv().caps()->resourceBindingRequirements();
499 Layout uniformLayout =
500 useStorageBuffers ? bindingReqs.fStorageBufferLayout : bindingReqs.fUniformBufferLayout;
501
502 UniformTracker geometryUniformTracker(useStorageBuffers);
503 UniformTracker shadingUniformTracker(useStorageBuffers);
504 TextureBindingTracker textureBindingTracker;
505
506 ShaderCodeDictionary* dict = recorder->priv().shaderCodeDictionary();
507 PaintParamsKeyBuilder builder(dict);
508
509 // The initial layout we pass here is not important as it will be re-assigned when writing
510 // shading and geometry uniforms below.
511 PipelineDataGatherer gatherer(uniformLayout);
512
513 // Copy of destination, if needed.
515 SkIPoint dstOffset;
516 if (!draws->dstCopyBounds().isEmptyNegativeOrNaN()) {
517 TRACE_EVENT_INSTANT0("skia.gpu", "DrawPass requires dst copy", TRACE_EVENT_SCOPE_THREAD);
518
519 SkIRect dstCopyPixelBounds = draws->dstCopyBounds().makeRoundOut().asSkIRect();
520 dstOffset = dstCopyPixelBounds.topLeft();
522 recorder, target, targetInfo.makeDimensions(dstCopyPixelBounds.size()), dstOffset);
523 if (!dst) {
524 SKGPU_LOG_W("Failed to copy destination for reading. Dropping draw pass!");
525 return nullptr;
526 }
527 }
528
529 std::vector<SortKey> keys;
530 keys.reserve(draws->renderStepCount());
531
532 for (const DrawList::Draw& draw : draws->fDraws.items()) {
533 // If we have two different descriptors, such that the uniforms from the PaintParams can be
534 // bound independently of those used by the rest of the RenderStep, then we can upload now
535 // and remember the location for re-use on any RenderStep that does shading.
536 UniquePaintParamsID shaderID;
537 const UniformDataBlock* shadingUniforms = nullptr;
538 const TextureDataBlock* paintTextures = nullptr;
539 if (draw.fPaintParams.has_value()) {
540 sk_sp<TextureProxy> curDst =
541 draw.fPaintParams->dstReadRequirement() == DstReadRequirement::kTextureCopy
542 ? dst
543 : nullptr;
544 std::tie(shaderID, shadingUniforms, paintTextures) =
545 ExtractPaintData(recorder,
546 &gatherer,
547 &builder,
548 uniformLayout,
549 draw.fDrawParams.transform(),
550 draw.fPaintParams.value(),
551 curDst,
552 dstOffset,
553 targetInfo.colorInfo());
554 } // else depth-only
555
556 for (int stepIndex = 0; stepIndex < draw.fRenderer->numRenderSteps(); ++stepIndex) {
557 const RenderStep* const step = draw.fRenderer->steps()[stepIndex];
558 const bool performsShading = draw.fPaintParams.has_value() && step->performsShading();
559
560 GraphicsPipelineCache::Index pipelineIndex = pipelineCache.insert(
561 {step, performsShading ? shaderID : UniquePaintParamsID::InvalidID()});
562 auto [geometryUniforms, stepTextures] = ExtractRenderStepData(&geometryUniformDataCache,
563 textureDataCache,
564 &gatherer,
565 uniformLayout,
566 step,
567 draw.fDrawParams);
568
569 UniformCache::Index geomUniformIndex = geometryUniformTracker.trackUniforms(
570 pipelineIndex, geometryUniforms);
571 UniformCache::Index shadingUniformIndex = shadingUniformTracker.trackUniforms(
572 pipelineIndex, performsShading ? shadingUniforms : nullptr);
573 TextureBindingCache::Index textureIndex = textureBindingTracker.trackTextures(
574 performsShading ? paintTextures : nullptr, stepTextures);
575
576 keys.push_back({&draw, stepIndex, pipelineIndex,
577 geomUniformIndex, shadingUniformIndex, textureIndex});
578 }
579
580 passBounds.join(draw.fDrawParams.clip().drawBounds());
581 drawPass->fDepthStencilFlags |= draw.fRenderer->depthStencilFlags();
582 drawPass->fRequiresMSAA |= draw.fRenderer->requiresMSAA();
583 }
584
585 if (!geometryUniformTracker.writeUniforms(bufferMgr) ||
586 !shadingUniformTracker.writeUniforms(bufferMgr)) {
587 // The necessary uniform data couldn't be written to the GPU, so the DrawPass is invalid.
588 // Early out now since the next Recording snap will fail.
589 return nullptr;
590 }
591
592 // TODO: Explore sorting algorithms; in all likelihood this will be mostly sorted already, so
593 // algorithms that approach O(n) in that condition may be favorable. Alternatively, could
594 // explore radix sort that is always O(n). Brief testing suggested std::sort was faster than
595 // std::stable_sort and SkTQSort on my [ml]'s Windows desktop. Also worth considering in-place
596 // vs. algorithms that require an extra O(n) storage.
597 // TODO: It's not strictly necessary, but would a stable sort be useful or just end up hiding
598 // bugs in the DrawOrder determination code?
599 std::sort(keys.begin(), keys.end());
600
601 // Used to record vertex/instance data, buffer binds, and draw calls
602 DrawWriter drawWriter(&drawPass->fCommandList, bufferMgr);
603 GraphicsPipelineCache::Index lastPipeline = GraphicsPipelineCache::kInvalidIndex;
604 SkIRect lastScissor = SkIRect::MakeSize(targetInfo.dimensions());
605
606 SkASSERT(drawPass->fTarget->isFullyLazy() ||
607 SkIRect::MakeSize(drawPass->fTarget->dimensions()).contains(lastScissor));
608 drawPass->fCommandList.setScissor(lastScissor);
609
610 for (const SortKey& key : keys) {
611 const DrawList::Draw& draw = key.draw();
612 const RenderStep& renderStep = key.renderStep();
613
614 const bool pipelineChange = key.pipelineIndex() != lastPipeline;
615
616 const bool geomBindingChange = geometryUniformTracker.setCurrentUniforms(
617 key.pipelineIndex(), key.geometryUniformIndex());
618 const bool shadingBindingChange = shadingUniformTracker.setCurrentUniforms(
619 key.pipelineIndex(), key.shadingUniformIndex());
620 const bool textureBindingsChange = textureBindingTracker.setCurrentTextureBindings(
621 key.textureBindingIndex());
622 const SkIRect* newScissor = draw.fDrawParams.clip().scissor() != lastScissor ?
623 &draw.fDrawParams.clip().scissor() : nullptr;
624
625 const bool stateChange = geomBindingChange ||
626 shadingBindingChange ||
627 textureBindingsChange ||
628 SkToBool(newScissor);
629
630 // Update DrawWriter *before* we actually change any state so that accumulated draws from
631 // the previous state use the proper state.
632 if (pipelineChange) {
633 drawWriter.newPipelineState(renderStep.primitiveType(),
634 renderStep.vertexStride(),
635 renderStep.instanceStride());
636 } else if (stateChange) {
637 drawWriter.newDynamicState();
638 }
639
640 // Make state changes before accumulating new draw data
641 if (pipelineChange) {
642 drawPass->fCommandList.bindGraphicsPipeline(key.pipelineIndex());
643 lastPipeline = key.pipelineIndex();
644 }
645 if (stateChange) {
646 if (geomBindingChange) {
647 geometryUniformTracker.bindUniforms(UniformSlot::kRenderStep,
648 &drawPass->fCommandList);
649 }
650 if (shadingBindingChange) {
651 shadingUniformTracker.bindUniforms(UniformSlot::kPaint, &drawPass->fCommandList);
652 }
653 if (textureBindingsChange) {
654 textureBindingTracker.bindTextures(&drawPass->fCommandList);
655 }
656 if (newScissor) {
657 drawPass->fCommandList.setScissor(*newScissor);
658 lastScissor = *newScissor;
659 }
660 }
661
662 UniformCache::Index geometrySsboIndex =
663 (key.geometryUniformIndex() == UniformCache::kInvalidIndex)
664 ? 0
665 : key.geometryUniformIndex();
666 UniformCache::Index shadingSsboIndex =
667 (key.shadingUniformIndex() == UniformCache::kInvalidIndex)
668 ? 0
669 : key.shadingUniformIndex();
670 skvx::ushort2 ssboIndices = {SkToU16(geometrySsboIndex), SkToU16(shadingSsboIndex)};
671 renderStep.writeVertices(&drawWriter, draw.fDrawParams, ssboIndices);
672
673 if (bufferMgr->hasMappingFailed()) {
674 SKGPU_LOG_W("Failed to write necessary vertex/instance data for DrawPass, dropping!");
675 return nullptr;
676 }
677 }
678 // Finish recording draw calls for any collected data at the end of the loop
679 drawWriter.flush();
680
681 drawPass->fBounds = passBounds.roundOut().asSkIRect();
682
683 drawPass->fPipelineDescs = pipelineCache.detach();
684 drawPass->fSamplerDescs = textureBindingTracker.detachSamplers();
685 drawPass->fSampledTextures = textureBindingTracker.detachTextures();
686
687 TRACE_COUNTER1("skia.gpu", "# pipelines", drawPass->fPipelineDescs.size());
688 TRACE_COUNTER1("skia.gpu", "# textures", drawPass->fSampledTextures.size());
689 TRACE_COUNTER1("skia.gpu", "# commands", drawPass->fCommandList.count());
690
691 return drawPass;
692}
693
694bool DrawPass::prepareResources(ResourceProvider* resourceProvider,
695 const RuntimeEffectDictionary* runtimeDict,
696 const RenderPassDesc& renderPassDesc) {
697 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
698
699 fFullPipelines.reserve(fFullPipelines.size() + fPipelineDescs.size());
700 for (const GraphicsPipelineDesc& pipelineDesc : fPipelineDescs) {
701 auto pipeline = resourceProvider->findOrCreateGraphicsPipeline(runtimeDict,
702 pipelineDesc,
703 renderPassDesc);
704 if (!pipeline) {
705 SKGPU_LOG_W("Failed to create GraphicsPipeline for draw in RenderPass. Dropping pass!");
706 return false;
707 }
708 fFullPipelines.push_back(std::move(pipeline));
709 }
710 // The DrawPass may be long lived on a Recording and we no longer need the GraphicPipelineDescs
711 // once we've created pipelines, so we drop the storage for them here.
712 fPipelineDescs.clear();
713
714 for (int i = 0; i < fSampledTextures.size(); ++i) {
715 // TODO: We need to remove this check once we are creating valid SkImages from things like
716 // snapshot, save layers, etc. Right now we only support SkImages directly made for graphite
717 // and all others have a TextureProxy with an invalid TextureInfo.
718 if (!fSampledTextures[i]->textureInfo().isValid()) {
719 SKGPU_LOG_W("Failed to validate sampled texture. Will not create renderpass!");
720 return false;
721 }
722 if (!TextureProxy::InstantiateIfNotLazy(resourceProvider, fSampledTextures[i].get())) {
723 SKGPU_LOG_W("Failed to instantiate sampled texture. Will not create renderpass!");
724 return false;
725 }
726 }
727
728 fSamplers.reserve(fSamplers.size() + fSamplerDescs.size());
729 for (int i = 0; i < fSamplerDescs.size(); ++i) {
730 sk_sp<Sampler> sampler = resourceProvider->findOrCreateCompatibleSampler(fSamplerDescs[i]);
731 if (!sampler) {
732 SKGPU_LOG_W("Failed to create sampler. Will not create renderpass!");
733 return false;
734 }
735 fSamplers.push_back(std::move(sampler));
736 }
737 // The DrawPass may be long lived on a Recording and we no longer need the SamplerDescs
738 // once we've created Samplers, so we drop the storage for them here.
739 fSamplerDescs.clear();
740
741 return true;
742}
743
744void DrawPass::addResourceRefs(CommandBuffer* commandBuffer) const {
745 for (int i = 0; i < fFullPipelines.size(); ++i) {
746 commandBuffer->trackResource(fFullPipelines[i]);
747 }
748 for (int i = 0; i < fSampledTextures.size(); ++i) {
749 commandBuffer->trackCommandBufferResource(fSampledTextures[i]->refTexture());
750 }
751 for (int i = 0; i < fSamplers.size(); ++i) {
752 commandBuffer->trackResource(fSamplers[i]);
753 }
754}
755
756const Texture* DrawPass::getTexture(size_t index) const {
757 SkASSERT(index < SkToSizeT(fSampledTextures.size()));
758 SkASSERT(fSampledTextures[index]);
759 SkASSERT(fSampledTextures[index]->texture());
760 return fSampledTextures[index]->texture();
761}
762const Sampler* DrawPass::getSampler(size_t index) const {
763 SkASSERT(index < SkToSizeT(fSamplers.size()));
764 SkASSERT(fSamplers[index]);
765 return fSamplers[index].get();
766}
767
768} // namespace skgpu::graphite
static int step(int x, SkScalar min, SkScalar max)
Definition BlurTest.cpp:215
const TextureProxy * fProxy
Definition DrawPass.cpp:181
static constexpr uint64_t kBits
Definition DrawPass.cpp:55
const TextureDataBlock * fPaintTextures
Definition DrawPass.cpp:112
const UniformDataBlock * fCpuData
Definition DrawPass.cpp:101
BindUniformBufferInfo fGpuData
Definition DrawPass.cpp:102
static constexpr Index kInvalidIndex
Definition DrawPass.cpp:69
static constexpr uint64_t kOffset
Definition DrawPass.cpp:54
const TextureDataBlock * fStepTextures
Definition DrawPass.cpp:113
#define SKGPU_LOG_W(fmt,...)
Definition Log.h:40
const SkRect fBounds
SkPathOp ops[]
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition SkAlign.h:33
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr int SkNextLog2_portable(uint32_t value)
Definition SkMathPriv.h:243
bool operator!=(const sk_sp< T > &a, const sk_sp< U > &b)
Definition SkRefCnt.h:355
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
constexpr uint16_t SkToU16(S x)
Definition SkTo.h:24
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
constexpr uint32_t SkToU32(S x)
Definition SkTo.h:26
#define TRACE_COUNTER1(category_group, name, value)
#define TRACE_EVENT_SCOPE_THREAD
#define TRACE_FUNC
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
const ResourceBindingRequirements & resourceBindingRequirements() const
Definition Caps.h:143
bool isTexturable(const TextureInfo &) const
Definition Caps.cpp:65
bool storageBufferPreferred() const
Definition Caps.h:232
void trackResource(sk_sp< Resource > resource)
void trackCommandBufferResource(sk_sp< Resource > resource)
static sk_sp< CopyTextureToTextureTask > Make(sk_sp< TextureProxy > srcProxy, SkIRect srcRect, sk_sp< TextureProxy > dstProxy, SkIPoint dstPoint, int dstLevel=0)
Definition CopyTask.cpp:119
static constexpr int kMaxRenderSteps
Definition DrawList.h:68
UniformCache::Index shadingUniformIndex() const
Definition DrawPass.cpp:376
UniformCache::Index geometryUniformIndex() const
Definition DrawPass.cpp:373
SortKey(const DrawList::Draw *draw, int renderStep, GraphicsPipelineCache::Index pipelineIndex, UniformCache::Index geomUniformIndex, UniformCache::Index shadingUniformIndex, TextureBindingCache::Index textureBindingIndex)
Definition DrawPass.cpp:341
const DrawList::Draw & draw() const
Definition DrawPass.cpp:368
const RenderStep & renderStep() const
Definition DrawPass.cpp:364
bool operator<(const SortKey &k) const
Definition DrawPass.cpp:359
TextureBindingCache::Index textureBindingIndex() const
Definition DrawPass.cpp:379
GraphicsPipelineCache::Index pipelineIndex() const
Definition DrawPass.cpp:370
void newPipelineState(PrimitiveType type, size_t vertexStride, size_t instanceStride)
Definition DrawWriter.h:82
const ShaderCodeDictionary * shaderCodeDictionary() const
TextureDataCache * textureDataCache()
const Caps * caps() const
ResourceProvider * resourceProvider()
DrawBufferManager * drawBufferManager()
void add(sk_sp< Task >)
Definition Recorder.cpp:477
AI Rect & roundOut()
Definition Rect.h:144
AI Rect & join(Rect rect)
Definition Rect.h:150
AI SkIRect asSkIRect() const
Definition Rect.h:96
size_t vertexStride() const
Definition Renderer.h:127
PrimitiveType primitiveType() const
Definition Renderer.h:126
virtual void writeVertices(DrawWriter *, const DrawParams &, skvx::ushort2 ssboIndices) const =0
size_t instanceStride() const
Definition Renderer.h:128
bool performsShading() const
Definition Renderer.h:119
static constexpr int kMaxRenderSteps
Definition Renderer.h:227
const RenderStep & step(int i) const
Definition Renderer.h:229
sk_sp< GraphicsPipeline > findOrCreateGraphicsPipeline(const RuntimeEffectDictionary *, const GraphicsPipelineDesc &, const RenderPassDesc &)
sk_sp< Sampler > findOrCreateCompatibleSampler(const SamplerDesc &)
static sk_sp< TextureProxy > Make(const Caps *, ResourceProvider *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted)
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
EMSCRIPTEN_KEEPALIVE void empty()
uint32_t * target
T __attribute__((ext_vector_type(N))) V
FlTexture * texture
Definition copy.py:1
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 Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition switches.h:191
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
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
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 set
Definition switches.h:76
MonotonicValue< DisjointStencilIndexSequence > DisjointStencilIndex
Definition DrawOrder.h:75
std::tuple< UniquePaintParamsID, const UniformDataBlock *, const TextureDataBlock * > ExtractPaintData(Recorder *recorder, PipelineDataGatherer *gatherer, PaintParamsKeyBuilder *builder, const Layout layout, const SkM44 &local2Dev, const PaintParams &p, sk_sp< TextureProxy > dstTexture, SkIPoint dstOffset, const SkColorInfo &targetColorInfo)
sk_sp< TextureProxy > add_copy_target_task(Recorder *recorder, sk_sp< TextureProxy > target, const SkImageInfo &targetInfo, const SkIPoint &targetOffset)
Definition DrawPass.cpp:415
std::tuple< const UniformDataBlock *, const TextureDataBlock * > ExtractRenderStepData(UniformDataCache *uniformDataCache, TextureDataCache *textureDataCache, PipelineDataGatherer *gatherer, const Layout layout, const RenderStep *step, const DrawParams &params)
MonotonicValue< CompressedPaintersOrderSequence > CompressedPaintersOrder
Definition DrawOrder.h:58
Definition ref_ptr.h:256
constexpr SkISize size() const
Definition SkRect.h:172
static constexpr SkIRect MakeSize(const SkISize &size)
Definition SkRect.h:66
constexpr SkIPoint topLeft() const
Definition SkRect.h:151
static constexpr SkIRect MakePtSize(SkIPoint pt, SkISize size)
Definition SkRect.h:78
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
const SkColorInfo & colorInfo() const
SkImageInfo makeDimensions(SkISize newSize) const
SkISize dimensions() const
const Renderer * fRenderer
Definition DrawList.h:95
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT_INSTANT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)