blob: c6703160063be09c19f421fb2db518247c6a9474 [file] [log] [blame]
/*
* 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