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