From 4d571e1fe558ea6d6f5a1da9a486554ba249a59a Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 1 Sep 2026 20:45:07 +0200 Subject: [PATCH] feat(vf): add CacheEntry and CacheStats --- core/include/vf/ChunkCache.h | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 core/include/vf/ChunkCache.h diff --git a/core/include/vf/ChunkCache.h b/core/include/vf/ChunkCache.h new file mode 100644 index 0000000..0a68091 --- /dev/null +++ b/core/include/vf/ChunkCache.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "vf/Chunk.h" +#include "vf/Math.h" +#include "vf/Mesh.h" +#include "vf/World.h" + +namespace vf { + +// Lru drops the entry untouched for the longest time. DistanceWeighted mixes +// staleness with the squared chunk distance to the observer, so a chunk far +// behind the player loses to a stale one still under his feet. +enum class CachePolicy : std::uint8_t { + Lru = 0, + DistanceWeighted = 1, +}; + +struct CacheEntry { + Vec3i coord; + Chunk chunk; + ChunkMesh mesh; + std::uint64_t bytes = 0; + std::uint64_t lastUse = 0; + long long distanceSq = 0; + bool hasMesh = false; + bool pinned = false; +}; + +struct CacheStats { + std::uint64_t hits = 0; + std::uint64_t misses = 0; + std::uint64_t evictions = 0; + std::uint64_t invalidations = 0; + std::uint64_t meshInvalidations = 0; + std::uint64_t bytesUsed = 0; + std::size_t entries = 0; + + double hitRate() const { + std::uint64_t lookups = hits + misses; + if (lookups == 0) return 0.0; + return static_cast(hits) / static_cast(lookups); + } +}; + +// Resident chunks and their baked meshes under a hard byte budget. Entries are +// evicted by policy once a store pushes the cache past the budget; pinned +// entries are never candidates, which is how the pipeline keeps everything +// inside the view radius alive. Every method is safe to call concurrently: +// peek reads share the lock, everything that touches recency or the store takes +// it exclusively. +class ChunkCache { +public: + explicit ChunkCache(std::uint64_t byteBudget = 64ull * 1024ull * 1024ull, + CachePolicy policy = CachePolicy::Lru); + + ChunkCache(const ChunkCache&) = delete; + ChunkCache& operator=(const ChunkCache&) = delete; + + void setByteBudget(std::uint64_t bytes); + void setPolicy(CachePolicy policy); + void setDistanceWeight(std::uint64_t weight); + void setObserver(const Vec3i& chunkCoord); + + std::uint64_t byteBudget() const; + CachePolicy policy() const; + Vec3i observer() const; + + void store(const Vec3i& coord, const Chunk& chunk); + void store(const Vec3i& coord, const Chunk& chunk, const ChunkMesh& mesh); + + // Counting lookups: they record a hit or a miss and refresh recency. + bool fetchChunk(const Vec3i& coord, Chunk& out); + bool fetchMesh(const Vec3i& coord, ChunkMesh& out); + + // Silent lookups for the pipeline workers: no recency, no statistics, and + // only a shared lock, so many stages can read at once. + bool peekChunk(const Vec3i& coord, Chunk& out) const; + bool peekMesh(const Vec3i& coord, ChunkMesh& out) const; + bool contains(const Vec3i& coord) const; + bool hasMesh(const Vec3i& coord) const; + bool entryFor(const Vec3i& coord, CacheEntry& out) const; + + void pin(const Vec3i& coord); + void unpin(const Vec3i& coord); + bool isPinned(const Vec3i& coord) const; + + // Drops the owning chunk entirely and the meshes of every neighbour whose + // ambient occlusion or light sampling can read the edited voxel across the + // chunk border. Returns the number of neighbour meshes dropped. + std::size_t invalidateBlock(int gx, int gy, int gz); + void invalidateChunk(const Vec3i& coord); + void invalidateMesh(const Vec3i& coord); + void clear(); + + std::size_t size() const; + std::uint64_t bytesUsed() const; + CacheStats stats() const; + void resetStats(); + + // Victims first. Pinned entries are excluded because they cannot be chosen. + std::vector evictionOrder() const; + std::vector residentCoords() const; + + static std::uint64_t entrySizeBytes(const Chunk& chunk, const ChunkMesh& mesh); + +private: + long long distanceSqTo(const Vec3i& coord) const; + std::uint64_t evictionScoreLocked(const CacheEntry& entry) const; + void enforceBudgetLocked(); + void touchLocked(CacheEntry& entry); + void dropMeshLocked(CacheEntry& entry); + void eraseLocked(const Vec3i& coord); + + mutable std::shared_mutex mutex; + std::unordered_map entries; + CacheStats counters; + Vec3i observerChunk; + std::uint64_t budget; + std::uint64_t used = 0; + std::uint64_t clock = 0; + std::uint64_t distanceWeight = 64; + CachePolicy replacement; +}; + +} // namespace vf