xref: /aosp_15_r20/external/skia/src/gpu/graphite/PipelineData.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #ifndef skgpu_graphite_PipelineData_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_graphite_PipelineData_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkColorData.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Caps.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawList.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawTypes.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/TextureProxy.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/UniformManager.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/gradients/SkGradientBaseShader.h"
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker class Uniform;
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker /**
33*c8dee2aaSAndroid Build Coastguard Worker  * Wraps an SkSpan<const char> and provides ==/!= operators and a hash function based on the
34*c8dee2aaSAndroid Build Coastguard Worker  * bit contents of the spans. It is assumed that the bytes are aligned to match some uniform
35*c8dee2aaSAndroid Build Coastguard Worker  * interface declaration that will consume this data once it's copied to the GPU.
36*c8dee2aaSAndroid Build Coastguard Worker  */
37*c8dee2aaSAndroid Build Coastguard Worker class UniformDataBlock {
38*c8dee2aaSAndroid Build Coastguard Worker public:
39*c8dee2aaSAndroid Build Coastguard Worker     constexpr UniformDataBlock(const UniformDataBlock&) = default;
40*c8dee2aaSAndroid Build Coastguard Worker     constexpr UniformDataBlock() = default;
41*c8dee2aaSAndroid Build Coastguard Worker 
Make(UniformDataBlock toClone,SkArenaAlloc * arena)42*c8dee2aaSAndroid Build Coastguard Worker     static UniformDataBlock Make(UniformDataBlock toClone, SkArenaAlloc* arena) {
43*c8dee2aaSAndroid Build Coastguard Worker         const char* copy = arena->makeArrayCopy<char>(toClone.fData);
44*c8dee2aaSAndroid Build Coastguard Worker         return UniformDataBlock(SkSpan(copy, toClone.size()));
45*c8dee2aaSAndroid Build Coastguard Worker     }
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker     // Wraps the finished accumulated uniform data within the manager's underlying storage.
Wrap(UniformManager * uniforms)48*c8dee2aaSAndroid Build Coastguard Worker     static UniformDataBlock Wrap(UniformManager* uniforms) {
49*c8dee2aaSAndroid Build Coastguard Worker         return UniformDataBlock(uniforms->finish());
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     constexpr UniformDataBlock& operator=(const UniformDataBlock&) = default;
53*c8dee2aaSAndroid Build Coastguard Worker 
54*c8dee2aaSAndroid Build Coastguard Worker     explicit operator bool() const { return !this->empty(); }
empty()55*c8dee2aaSAndroid Build Coastguard Worker     bool empty() const { return fData.empty(); }
56*c8dee2aaSAndroid Build Coastguard Worker 
data()57*c8dee2aaSAndroid Build Coastguard Worker     const char* data() const { return fData.data(); }
size()58*c8dee2aaSAndroid Build Coastguard Worker     size_t size() const { return fData.size(); }
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(UniformDataBlock that) const {
61*c8dee2aaSAndroid Build Coastguard Worker         return this->size() == that.size() &&
62*c8dee2aaSAndroid Build Coastguard Worker                (this->data() == that.data() || // Shortcuts the memcmp if the spans are the same
63*c8dee2aaSAndroid Build Coastguard Worker                 memcmp(this->data(), that.data(), this->size()) == 0);
64*c8dee2aaSAndroid Build Coastguard Worker     }
65*c8dee2aaSAndroid Build Coastguard Worker     bool operator!=(UniformDataBlock that) const { return !(*this == that); }
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operatorHash68*c8dee2aaSAndroid Build Coastguard Worker         uint32_t operator()(UniformDataBlock block) const {
69*c8dee2aaSAndroid Build Coastguard Worker             return SkChecksum::Hash32(block.fData.data(), block.fData.size_bytes());
70*c8dee2aaSAndroid Build Coastguard Worker         }
71*c8dee2aaSAndroid Build Coastguard Worker     };
72*c8dee2aaSAndroid Build Coastguard Worker 
73*c8dee2aaSAndroid Build Coastguard Worker private:
74*c8dee2aaSAndroid Build Coastguard Worker     // To ensure that the underlying data is actually aligned properly, UniformDataBlocks can
75*c8dee2aaSAndroid Build Coastguard Worker     // only be created publicly by copying an existing block or wrapping data accumulated by a
76*c8dee2aaSAndroid Build Coastguard Worker     // UniformManager (or transitively a PipelineDataGatherer).
UniformDataBlock(SkSpan<const char> data)77*c8dee2aaSAndroid Build Coastguard Worker     constexpr UniformDataBlock(SkSpan<const char> data) : fData(data) {}
78*c8dee2aaSAndroid Build Coastguard Worker 
79*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const char> fData;
80*c8dee2aaSAndroid Build Coastguard Worker };
81*c8dee2aaSAndroid Build Coastguard Worker 
82*c8dee2aaSAndroid Build Coastguard Worker /**
83*c8dee2aaSAndroid Build Coastguard Worker  * Wraps an SkSpan<const SampledTexture> (list of pairs of TextureProxy and SamplerDesc) and
84*c8dee2aaSAndroid Build Coastguard Worker  * provides ==/!= operators and a hash function based on the proxy addresses and sampler desc
85*c8dee2aaSAndroid Build Coastguard Worker  * bit representation.
86*c8dee2aaSAndroid Build Coastguard Worker  */
87*c8dee2aaSAndroid Build Coastguard Worker class TextureDataBlock {
88*c8dee2aaSAndroid Build Coastguard Worker public:
89*c8dee2aaSAndroid Build Coastguard Worker     using SampledTexture = std::pair<sk_sp<TextureProxy>, SamplerDesc>;
90*c8dee2aaSAndroid Build Coastguard Worker 
91*c8dee2aaSAndroid Build Coastguard Worker     constexpr TextureDataBlock(const TextureDataBlock&) = default;
92*c8dee2aaSAndroid Build Coastguard Worker     constexpr TextureDataBlock() = default;
93*c8dee2aaSAndroid Build Coastguard Worker 
Make(TextureDataBlock toClone,SkArenaAlloc * arena)94*c8dee2aaSAndroid Build Coastguard Worker     static TextureDataBlock Make(TextureDataBlock toClone, SkArenaAlloc* arena) {
95*c8dee2aaSAndroid Build Coastguard Worker         SampledTexture* copy = arena->makeArrayCopy<SampledTexture>(toClone.fTextures);
96*c8dee2aaSAndroid Build Coastguard Worker         return TextureDataBlock(SkSpan(copy, toClone.numTextures()));
97*c8dee2aaSAndroid Build Coastguard Worker     }
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/330864257): Once Device::drawCoverageMask() can keep its texture proxy alive without
100*c8dee2aaSAndroid Build Coastguard Worker     // creating a temporary TextureDataBlock this constructor can go away.
TextureDataBlock(const SampledTexture & texture)101*c8dee2aaSAndroid Build Coastguard Worker     explicit TextureDataBlock(const SampledTexture& texture) : fTextures(&texture, 1) {}
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker     constexpr TextureDataBlock& operator=(const TextureDataBlock&) = default;
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker     explicit operator bool() const { return !this->empty(); }
empty()106*c8dee2aaSAndroid Build Coastguard Worker     bool empty() const { return fTextures.empty(); }
107*c8dee2aaSAndroid Build Coastguard Worker 
numTextures()108*c8dee2aaSAndroid Build Coastguard Worker     int numTextures() const { return SkTo<int>(fTextures.size()); }
texture(int index)109*c8dee2aaSAndroid Build Coastguard Worker     const SampledTexture& texture(int index) const { return fTextures[index]; }
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker     bool operator==(TextureDataBlock other) const {
112*c8dee2aaSAndroid Build Coastguard Worker         if (fTextures.size() != other.fTextures.size()) {
113*c8dee2aaSAndroid Build Coastguard Worker             return false;
114*c8dee2aaSAndroid Build Coastguard Worker         }
115*c8dee2aaSAndroid Build Coastguard Worker         if (fTextures.data() == other.fTextures.data()) {
116*c8dee2aaSAndroid Build Coastguard Worker             return true; // shortcut for the same span
117*c8dee2aaSAndroid Build Coastguard Worker         }
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < fTextures.size(); ++i) {
120*c8dee2aaSAndroid Build Coastguard Worker             if (fTextures[i] != other.fTextures[i]) {
121*c8dee2aaSAndroid Build Coastguard Worker                 return false;
122*c8dee2aaSAndroid Build Coastguard Worker             }
123*c8dee2aaSAndroid Build Coastguard Worker         }
124*c8dee2aaSAndroid Build Coastguard Worker 
125*c8dee2aaSAndroid Build Coastguard Worker         return true;
126*c8dee2aaSAndroid Build Coastguard Worker     }
127*c8dee2aaSAndroid Build Coastguard Worker     bool operator!=(TextureDataBlock other) const { return !(*this == other);  }
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operatorHash130*c8dee2aaSAndroid Build Coastguard Worker         uint32_t operator()(TextureDataBlock block) const {
131*c8dee2aaSAndroid Build Coastguard Worker             uint32_t hash = 0;
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker             for (auto& d : block.fTextures) {
134*c8dee2aaSAndroid Build Coastguard Worker                 SamplerDesc samplerKey = std::get<1>(d);
135*c8dee2aaSAndroid Build Coastguard Worker                 hash = SkChecksum::Hash32(&samplerKey, sizeof(samplerKey), hash);
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker                 // Because the lifetime of the TextureDataCache is for just one Recording and the
138*c8dee2aaSAndroid Build Coastguard Worker                 // TextureDataBlocks hold refs on their proxies, we can just use the proxy's pointer
139*c8dee2aaSAndroid Build Coastguard Worker                 // for the hash here.
140*c8dee2aaSAndroid Build Coastguard Worker                 uintptr_t proxy = reinterpret_cast<uintptr_t>(std::get<0>(d).get());
141*c8dee2aaSAndroid Build Coastguard Worker                 hash = SkChecksum::Hash32(&proxy, sizeof(proxy), hash);
142*c8dee2aaSAndroid Build Coastguard Worker             }
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker             return hash;
145*c8dee2aaSAndroid Build Coastguard Worker         }
146*c8dee2aaSAndroid Build Coastguard Worker     };
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker private:
149*c8dee2aaSAndroid Build Coastguard Worker     friend class PipelineDataGatherer;
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     // Initial TextureDataBlocks must come from a PipelineDataGatherer
TextureDataBlock(SkSpan<const SampledTexture> textures)152*c8dee2aaSAndroid Build Coastguard Worker     constexpr TextureDataBlock(SkSpan<const SampledTexture> textures) : fTextures(textures) {}
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const SampledTexture> fTextures;
155*c8dee2aaSAndroid Build Coastguard Worker };
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker // Add a block of data to the cache and return a stable pointer to the contents (assuming that a
158*c8dee2aaSAndroid Build Coastguard Worker // resettable gatherer had accumulated the input data pointer).
159*c8dee2aaSAndroid Build Coastguard Worker //
160*c8dee2aaSAndroid Build Coastguard Worker // If an identical block of data is already in the cache, that existing pointer is returned, making
161*c8dee2aaSAndroid Build Coastguard Worker // pointer comparison suitable when comparing data blocks retrieved from the cache.
162*c8dee2aaSAndroid Build Coastguard Worker //
163*c8dee2aaSAndroid Build Coastguard Worker // T must define a Hash struct function, an operator==, and a static Make(T, SkArenaAlloc*)
164*c8dee2aaSAndroid Build Coastguard Worker // factory that's used to copy the data into an arena allocation owned by the PipelineDataCache.
165*c8dee2aaSAndroid Build Coastguard Worker template<typename T>
166*c8dee2aaSAndroid Build Coastguard Worker class PipelineDataCache {
167*c8dee2aaSAndroid Build Coastguard Worker public:
168*c8dee2aaSAndroid Build Coastguard Worker     PipelineDataCache() = default;
169*c8dee2aaSAndroid Build Coastguard Worker 
insert(T dataBlock)170*c8dee2aaSAndroid Build Coastguard Worker     T insert(T dataBlock) {
171*c8dee2aaSAndroid Build Coastguard Worker         const T* existing = fData.find(dataBlock);
172*c8dee2aaSAndroid Build Coastguard Worker         if (existing) {
173*c8dee2aaSAndroid Build Coastguard Worker             return *existing;
174*c8dee2aaSAndroid Build Coastguard Worker         } else {
175*c8dee2aaSAndroid Build Coastguard Worker             // Need to make a copy of dataBlock into the arena
176*c8dee2aaSAndroid Build Coastguard Worker             T copy = T::Make(dataBlock, &fArena);
177*c8dee2aaSAndroid Build Coastguard Worker             fData.add(copy);
178*c8dee2aaSAndroid Build Coastguard Worker             return copy;
179*c8dee2aaSAndroid Build Coastguard Worker         }
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     // The number of unique T objects in the cache
count()183*c8dee2aaSAndroid Build Coastguard Worker     int count() const {
184*c8dee2aaSAndroid Build Coastguard Worker         return fData.count();
185*c8dee2aaSAndroid Build Coastguard Worker     }
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker     // Call fn on every item in the set.  You may not mutate anything.
188*c8dee2aaSAndroid Build Coastguard Worker     template <typename Fn>  // f(T), f(const T&)
foreach(Fn && fn)189*c8dee2aaSAndroid Build Coastguard Worker     void foreach(Fn&& fn) const {
190*c8dee2aaSAndroid Build Coastguard Worker         fData.foreach(fn);
191*c8dee2aaSAndroid Build Coastguard Worker     }
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker private:
194*c8dee2aaSAndroid Build Coastguard Worker     skia_private::THashSet<T, typename T::Hash> fData;
195*c8dee2aaSAndroid Build Coastguard Worker     // Holds the data that is pointed to by the span keys in fData
196*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc fArena{0};
197*c8dee2aaSAndroid Build Coastguard Worker };
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker // A TextureDataCache only lives for a single Recording. When a Recording is snapped it is pulled
200*c8dee2aaSAndroid Build Coastguard Worker // off of the Recorder and goes with the Recording as a record of the required Textures and
201*c8dee2aaSAndroid Build Coastguard Worker // Samplers.
202*c8dee2aaSAndroid Build Coastguard Worker using TextureDataCache = PipelineDataCache<TextureDataBlock>;
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker // A UniformDataCache is used to deduplicate uniform data blocks uploaded to uniform / storage
205*c8dee2aaSAndroid Build Coastguard Worker // buffers for a DrawPass pipeline.
206*c8dee2aaSAndroid Build Coastguard Worker // TODO: This is just a combination of PipelineDataCache and DrawPass's DenseBiMap, ideally we can
207*c8dee2aaSAndroid Build Coastguard Worker // merge those two classes rather than defining this new class.
208*c8dee2aaSAndroid Build Coastguard Worker class UniformDataCache {
209*c8dee2aaSAndroid Build Coastguard Worker public:
210*c8dee2aaSAndroid Build Coastguard Worker     using Index = uint32_t;
211*c8dee2aaSAndroid Build Coastguard Worker     static constexpr Index kInvalidIndex{1 << SkNextLog2_portable(DrawList::kMaxRenderSteps)};
212*c8dee2aaSAndroid Build Coastguard Worker 
213*c8dee2aaSAndroid Build Coastguard Worker     // Tracks uniform data on the CPU and then its transition to storage in a GPU buffer (UBO or
214*c8dee2aaSAndroid Build Coastguard Worker     // SSBO).
215*c8dee2aaSAndroid Build Coastguard Worker     struct Entry {
216*c8dee2aaSAndroid Build Coastguard Worker         UniformDataBlock fCpuData;
217*c8dee2aaSAndroid Build Coastguard Worker         BindBufferInfo fBufferBinding;
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker         // Can only be initialized with CPU data.
EntryEntry220*c8dee2aaSAndroid Build Coastguard Worker         Entry(UniformDataBlock cpuData) : fCpuData(cpuData) {}
221*c8dee2aaSAndroid Build Coastguard Worker     };
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker     UniformDataCache() = default;
224*c8dee2aaSAndroid Build Coastguard Worker 
insert(const UniformDataBlock & dataBlock)225*c8dee2aaSAndroid Build Coastguard Worker     Index insert(const UniformDataBlock& dataBlock) {
226*c8dee2aaSAndroid Build Coastguard Worker         Index* index = fDataToIndex.find(dataBlock);
227*c8dee2aaSAndroid Build Coastguard Worker         if (!index) {
228*c8dee2aaSAndroid Build Coastguard Worker             // Need to make a copy of dataBlock into the arena
229*c8dee2aaSAndroid Build Coastguard Worker             UniformDataBlock copy = UniformDataBlock::Make(dataBlock, &fArena);
230*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(SkToU32(fIndexToData.size()) < kInvalidIndex);
231*c8dee2aaSAndroid Build Coastguard Worker             index = fDataToIndex.set(copy, static_cast<Index>(fIndexToData.size()));
232*c8dee2aaSAndroid Build Coastguard Worker             fIndexToData.push_back(Entry{copy});
233*c8dee2aaSAndroid Build Coastguard Worker         }
234*c8dee2aaSAndroid Build Coastguard Worker         return *index;
235*c8dee2aaSAndroid Build Coastguard Worker     }
236*c8dee2aaSAndroid Build Coastguard Worker 
lookup(Index index)237*c8dee2aaSAndroid Build Coastguard Worker     const Entry& lookup(Index index) const {
238*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(index < kInvalidIndex);
239*c8dee2aaSAndroid Build Coastguard Worker         return fIndexToData[index];
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker 
lookup(Index index)242*c8dee2aaSAndroid Build Coastguard Worker     Entry& lookup(Index index) {
243*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(index < kInvalidIndex);
244*c8dee2aaSAndroid Build Coastguard Worker         return fIndexToData[index];
245*c8dee2aaSAndroid Build Coastguard Worker     }
246*c8dee2aaSAndroid Build Coastguard Worker 
247*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
count()248*c8dee2aaSAndroid Build Coastguard Worker     int count() { return fIndexToData.size(); }
249*c8dee2aaSAndroid Build Coastguard Worker #endif
250*c8dee2aaSAndroid Build Coastguard Worker 
251*c8dee2aaSAndroid Build Coastguard Worker private:
252*c8dee2aaSAndroid Build Coastguard Worker     skia_private::THashMap<UniformDataBlock, Index, UniformDataBlock::Hash> fDataToIndex;
253*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<Entry> fIndexToData;
254*c8dee2aaSAndroid Build Coastguard Worker 
255*c8dee2aaSAndroid Build Coastguard Worker     // Holds the de-duplicated data.
256*c8dee2aaSAndroid Build Coastguard Worker     SkArenaAlloc fArena{0};
257*c8dee2aaSAndroid Build Coastguard Worker };
258*c8dee2aaSAndroid Build Coastguard Worker 
259*c8dee2aaSAndroid Build Coastguard Worker // The PipelineDataGatherer is just used to collect information for a given PaintParams object.
260*c8dee2aaSAndroid Build Coastguard Worker //   The UniformData is added to a cache and uniquified. Only that unique ID is passed around.
261*c8dee2aaSAndroid Build Coastguard Worker //   The TextureData is also added to a cache and uniquified. Only that ID is passed around.
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker // TODO: The current plan for fixing uniform padding is for the PipelineDataGatherer to hold a
264*c8dee2aaSAndroid Build Coastguard Worker // persistent uniformManager. A stretch goal for this system would be for this combination
265*c8dee2aaSAndroid Build Coastguard Worker // to accumulate all the uniforms and then rearrange them to minimize padding. This would,
266*c8dee2aaSAndroid Build Coastguard Worker // obviously, vastly complicate uniform accumulation.
267*c8dee2aaSAndroid Build Coastguard Worker class PipelineDataGatherer {
268*c8dee2aaSAndroid Build Coastguard Worker public:
PipelineDataGatherer(Layout layout)269*c8dee2aaSAndroid Build Coastguard Worker     PipelineDataGatherer(Layout layout) : fUniformManager(layout) {}
270*c8dee2aaSAndroid Build Coastguard Worker 
resetWithNewLayout(Layout layout)271*c8dee2aaSAndroid Build Coastguard Worker     void resetWithNewLayout(Layout layout) {
272*c8dee2aaSAndroid Build Coastguard Worker         fUniformManager.resetWithNewLayout(layout);
273*c8dee2aaSAndroid Build Coastguard Worker         fTextures.clear();
274*c8dee2aaSAndroid Build Coastguard Worker     }
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
277*c8dee2aaSAndroid Build Coastguard Worker     // Check that the gatherer has been reset to its initial state prior to collecting new data.
checkReset()278*c8dee2aaSAndroid Build Coastguard Worker     void checkReset() const {
279*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fTextures.empty());
280*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fUniformManager.isReset());
281*c8dee2aaSAndroid Build Coastguard Worker     }
282*c8dee2aaSAndroid Build Coastguard Worker #endif // SK_DEBUG
283*c8dee2aaSAndroid Build Coastguard Worker 
add(sk_sp<TextureProxy> proxy,const SamplerDesc & samplerDesc)284*c8dee2aaSAndroid Build Coastguard Worker     void add(sk_sp<TextureProxy> proxy, const SamplerDesc& samplerDesc) {
285*c8dee2aaSAndroid Build Coastguard Worker         fTextures.push_back({std::move(proxy), samplerDesc});
286*c8dee2aaSAndroid Build Coastguard Worker     }
hasTextures()287*c8dee2aaSAndroid Build Coastguard Worker     bool hasTextures() const { return !fTextures.empty(); }
288*c8dee2aaSAndroid Build Coastguard Worker 
textureDataBlock()289*c8dee2aaSAndroid Build Coastguard Worker     TextureDataBlock textureDataBlock() { return TextureDataBlock(SkSpan(fTextures)); }
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     // Mimic the type-safe API available in UniformManager
write(const T & t)292*c8dee2aaSAndroid Build Coastguard Worker     template <typename T> void write(const T& t) { fUniformManager.write(t); }
writeHalf(const T & t)293*c8dee2aaSAndroid Build Coastguard Worker     template <typename T> void writeHalf(const T& t) { fUniformManager.writeHalf(t); }
writeArray(SkSpan<const T> t)294*c8dee2aaSAndroid Build Coastguard Worker     template <typename T> void writeArray(SkSpan<const T> t) { fUniformManager.writeArray(t); }
writeHalfArray(SkSpan<const T> t)295*c8dee2aaSAndroid Build Coastguard Worker     template <typename T> void writeHalfArray(SkSpan<const T> t) {
296*c8dee2aaSAndroid Build Coastguard Worker         fUniformManager.writeHalfArray(t);
297*c8dee2aaSAndroid Build Coastguard Worker     }
298*c8dee2aaSAndroid Build Coastguard Worker 
write(const Uniform & u,const void * data)299*c8dee2aaSAndroid Build Coastguard Worker     void write(const Uniform& u, const void* data) { fUniformManager.write(u, data); }
300*c8dee2aaSAndroid Build Coastguard Worker 
writePaintColor(const SkPMColor4f & color)301*c8dee2aaSAndroid Build Coastguard Worker     void writePaintColor(const SkPMColor4f& color) { fUniformManager.writePaintColor(color); }
302*c8dee2aaSAndroid Build Coastguard Worker 
beginStruct(int baseAligment)303*c8dee2aaSAndroid Build Coastguard Worker     void beginStruct(int baseAligment) { fUniformManager.beginStruct(baseAligment); }
endStruct()304*c8dee2aaSAndroid Build Coastguard Worker     void endStruct() { fUniformManager.endStruct(); }
305*c8dee2aaSAndroid Build Coastguard Worker 
hasUniforms()306*c8dee2aaSAndroid Build Coastguard Worker     bool hasUniforms() const { return fUniformManager.size(); }
307*c8dee2aaSAndroid Build Coastguard Worker 
hasGradientBufferData()308*c8dee2aaSAndroid Build Coastguard Worker     bool hasGradientBufferData() const { return !fGradientStorage.empty(); }
309*c8dee2aaSAndroid Build Coastguard Worker 
gradientBufferData()310*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const float> gradientBufferData() const { return fGradientStorage; }
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker     // Returns the uniform data written so far. Will automatically pad the end of the data as needed
313*c8dee2aaSAndroid Build Coastguard Worker     // to the overall required alignment, and so should only be called when all writing is done.
finishUniformDataBlock()314*c8dee2aaSAndroid Build Coastguard Worker     UniformDataBlock finishUniformDataBlock() { return UniformDataBlock::Wrap(&fUniformManager); }
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker     // Checks if data already exists for the requested gradient shader, and returns a nullptr
317*c8dee2aaSAndroid Build Coastguard Worker     // and the offset the data begins at. If it doesn't exist, it allocates the data for the
318*c8dee2aaSAndroid Build Coastguard Worker     // required number of stops and caches the start index, returning the data pointer
319*c8dee2aaSAndroid Build Coastguard Worker     // and index offset the data will begin at.
allocateGradientData(int numStops,const SkGradientBaseShader * shader)320*c8dee2aaSAndroid Build Coastguard Worker     std::pair<float*, int> allocateGradientData(int numStops, const SkGradientBaseShader* shader) {
321*c8dee2aaSAndroid Build Coastguard Worker         int* existingOfffset = fGradientOffsetCache.find(shader);
322*c8dee2aaSAndroid Build Coastguard Worker         if (existingOfffset) {
323*c8dee2aaSAndroid Build Coastguard Worker             return std::make_pair(nullptr, *existingOfffset);
324*c8dee2aaSAndroid Build Coastguard Worker         }
325*c8dee2aaSAndroid Build Coastguard Worker 
326*c8dee2aaSAndroid Build Coastguard Worker         auto dataPair = this->allocateFloatData(numStops * 5);
327*c8dee2aaSAndroid Build Coastguard Worker         fGradientOffsetCache.set(shader, dataPair.second);
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker         return dataPair;
330*c8dee2aaSAndroid Build Coastguard Worker     }
331*c8dee2aaSAndroid Build Coastguard Worker 
332*c8dee2aaSAndroid Build Coastguard Worker private:
333*c8dee2aaSAndroid Build Coastguard Worker     // Allocates the data for the requested number of bytes and returns the
334*c8dee2aaSAndroid Build Coastguard Worker     // pointer and buffer index offset the data will begin at.
allocateFloatData(int size)335*c8dee2aaSAndroid Build Coastguard Worker     std::pair<float*, int> allocateFloatData(int size) {
336*c8dee2aaSAndroid Build Coastguard Worker         int lastSize = fGradientStorage.size();
337*c8dee2aaSAndroid Build Coastguard Worker         fGradientStorage.resize(lastSize + size);
338*c8dee2aaSAndroid Build Coastguard Worker         float* startPtr = fGradientStorage.begin() + lastSize;
339*c8dee2aaSAndroid Build Coastguard Worker 
340*c8dee2aaSAndroid Build Coastguard Worker         return std::make_pair(startPtr, lastSize);
341*c8dee2aaSAndroid Build Coastguard Worker     }
342*c8dee2aaSAndroid Build Coastguard Worker 
343*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGCODE(friend class UniformExpectationsValidator;)
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker     UniformManager fUniformManager;
346*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<TextureDataBlock::SampledTexture> fTextures;
347*c8dee2aaSAndroid Build Coastguard Worker 
348*c8dee2aaSAndroid Build Coastguard Worker     SkTDArray<float>  fGradientStorage;
349*c8dee2aaSAndroid Build Coastguard Worker     // Storing the address of the shader as a proxy for comparing
350*c8dee2aaSAndroid Build Coastguard Worker     // the colors and offsets arrays to keep lookup fast.
351*c8dee2aaSAndroid Build Coastguard Worker     skia_private::THashMap<const SkGradientBaseShader*, int> fGradientOffsetCache;
352*c8dee2aaSAndroid Build Coastguard Worker };
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
355*c8dee2aaSAndroid Build Coastguard Worker class UniformExpectationsValidator {
356*c8dee2aaSAndroid Build Coastguard Worker public:
357*c8dee2aaSAndroid Build Coastguard Worker     UniformExpectationsValidator(PipelineDataGatherer* gatherer,
358*c8dee2aaSAndroid Build Coastguard Worker                                  SkSpan<const Uniform> expectedUniforms,
359*c8dee2aaSAndroid Build Coastguard Worker                                  bool isSubstruct=false)
fGatherer(gatherer)360*c8dee2aaSAndroid Build Coastguard Worker             : fGatherer(gatherer) {
361*c8dee2aaSAndroid Build Coastguard Worker         fGatherer->fUniformManager.setExpectedUniforms(expectedUniforms, isSubstruct);
362*c8dee2aaSAndroid Build Coastguard Worker     }
363*c8dee2aaSAndroid Build Coastguard Worker 
~UniformExpectationsValidator()364*c8dee2aaSAndroid Build Coastguard Worker     ~UniformExpectationsValidator() {
365*c8dee2aaSAndroid Build Coastguard Worker         fGatherer->fUniformManager.doneWithExpectedUniforms();
366*c8dee2aaSAndroid Build Coastguard Worker     }
367*c8dee2aaSAndroid Build Coastguard Worker 
368*c8dee2aaSAndroid Build Coastguard Worker private:
369*c8dee2aaSAndroid Build Coastguard Worker     PipelineDataGatherer* fGatherer;
370*c8dee2aaSAndroid Build Coastguard Worker 
371*c8dee2aaSAndroid Build Coastguard Worker     UniformExpectationsValidator(UniformExpectationsValidator &&) = delete;
372*c8dee2aaSAndroid Build Coastguard Worker     UniformExpectationsValidator(const UniformExpectationsValidator &) = delete;
373*c8dee2aaSAndroid Build Coastguard Worker     UniformExpectationsValidator &operator=(UniformExpectationsValidator &&) = delete;
374*c8dee2aaSAndroid Build Coastguard Worker     UniformExpectationsValidator &operator=(const UniformExpectationsValidator &) = delete;
375*c8dee2aaSAndroid Build Coastguard Worker };
376*c8dee2aaSAndroid Build Coastguard Worker #endif // SK_DEBUG
377*c8dee2aaSAndroid Build Coastguard Worker 
378*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker #endif // skgpu_graphite_PipelineData_DEFINED
381