| /* | |
| * Copyright 2021 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef skgpu_graphite_PipelineDataCache_DEFINED | |
| #define skgpu_graphite_PipelineDataCache_DEFINED | |
| #include "src/base/SkArenaAlloc.h" | |
| #include "src/core/SkTHash.h" | |
| #include "src/gpu/graphite/PipelineData.h" | |
| namespace skgpu::graphite { | |
| // Add a block of data to the cache and return a stable pointer to the contents (assuming that a | |
| // resettable gatherer had accumulated the input data pointer). | |
| // | |
| // If an identical block of data is already in the cache, that existing pointer is returned, making | |
| // pointer comparison suitable when comparing data blocks retrieved from the cache. | |
| // | |
| // T must define a hash() function, an operator==, and a static Make(const T&, SkArenaAlloc*) | |
| // factory that's used to copy the data into an arena allocation owned by the PipelineDataCache. | |
| template<typename T> | |
| class PipelineDataCache { | |
| public: | |
| PipelineDataCache() = default; | |
| const T* insert(const T& dataBlock) { | |
| DataRef data{&dataBlock}; // will not be persisted, since pointer isn't from the arena. | |
| const DataRef* existing = fDataPointers.find(data); | |
| if (existing) { | |
| return existing->fPointer; | |
| } else { | |
| // Need to make a copy of dataBlock into the arena | |
| T* copy = T::Make(dataBlock, &fArena); | |
| fDataPointers.add(DataRef{copy}); | |
| return copy; | |
| } | |
| } | |
| // The number of unique T objects in the cache | |
| int count() const { | |
| return fDataPointers.count(); | |
| } | |
| // Call fn on every item in the set. You may not mutate anything. | |
| template <typename Fn> // f(T), f(const T&) | |
| void foreach(Fn&& fn) const { | |
| fDataPointers.foreach([fn](const DataRef& ref){ | |
| fn(ref.fPointer); | |
| }); | |
| } | |
| private: | |
| struct DataRef { | |
| const T* fPointer; | |
| bool operator==(const DataRef& o) const { | |
| if (!fPointer || !o.fPointer) { | |
| return !fPointer && !o.fPointer; | |
| } else { | |
| return *fPointer == *o.fPointer; | |
| } | |
| } | |
| }; | |
| struct Hash { | |
| // This hash operator de-references and hashes the data contents | |
| size_t operator()(const DataRef& dataBlock) const { | |
| return dataBlock.fPointer ? dataBlock.fPointer->hash() : 0; | |
| } | |
| }; | |
| skia_private::THashSet<DataRef, Hash> fDataPointers; | |
| // Holds the data that is pointed to by fDataPointers | |
| SkArenaAlloc fArena{0}; | |
| }; | |
| // A UniformDataCache only lives for a single Recording. It's used to deduplicate uniform data | |
| // blocks uploaded to uniform/storage buffers for a DrawPass pipeline. | |
| using UniformDataCache = PipelineDataCache<UniformDataBlock>; | |
| // A TextureDataCache only lives for a single Recording. When a Recording is snapped it is pulled | |
| // off of the Recorder and goes with the Recording as a record of the required Textures and | |
| // Samplers. | |
| using TextureDataCache = PipelineDataCache<TextureDataBlock>; | |
| } // namespace skgpu::graphite | |
| #endif // skgpu_graphite_PipelineDataCache_DEFINED |