scene/cache.go
Functions
func Contains
func (c *LayerCache) Contains(hash uint64) bool {
c.mu.RLock()
defer c.mu.RUnlock()
_, ok := c.entries[hash]
return ok
}
func DefaultLayerCache
DefaultLayerCache creates a new layer cache with the default 64MB limit.
func DefaultLayerCache() *LayerCache {
return NewLayerCache(DefaultMaxSizeMB)
}
func EntryCount
EntryCount returns the number of entries in the cache.
func (c *LayerCache) EntryCount() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.entries)
}
func Get
Get retrieves a cached pixmap by its hash.
Returns the pixmap and true if found, nil and false otherwise.
On cache hit, the entry is moved to the front of the LRU list.
func (c *LayerCache) Get(hash uint64) (*gg.Pixmap, bool) {
c.mu.RLock()
_, ok := c.entries[hash]
c.mu.RUnlock()
if !ok {
c.misses.Add(1)
return nil, false
}
// Move to front (requires write lock)
c.mu.Lock()
// Re-check after acquiring write lock (entry may have been evicted)
entry, ok := c.entries[hash]
if !ok {
c.mu.Unlock()
c.misses.Add(1)
return nil, false
}
c.lru.MoveToFront(entry.element)
entry.lastUsed = time.Now()
pixmap := entry.pixmap
c.mu.Unlock()
c.hits.Add(1)
return pixmap, true
}
func GetVersion
GetVersion returns the version of a cached entry if it exists.
Returns 0 and false if the entry is not found.
func (c *LayerCache) GetVersion(hash uint64) (uint64, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
if entry, ok := c.entries[hash]; ok {
return entry.version, true
}
return 0, false
}
func Invalidate
Invalidate removes a specific entry from the cache by hash.
func (c *LayerCache) Invalidate(hash uint64) {
c.mu.Lock()
defer c.mu.Unlock()
if entry, ok := c.entries[hash]; ok {
c.lru.Remove(entry.element)
c.size -= entry.size
delete(c.entries, hash)
c.evictions.Add(1)
}
}
func InvalidateAll
InvalidateAll clears the entire cache.
func (c *LayerCache) InvalidateAll() {
c.mu.Lock()
defer c.mu.Unlock()
evicted := uint64(len(c.entries))
c.entries = make(map[uint64]*CacheEntry)
c.lru.Init()
c.size = 0
if evicted > 0 {
c.evictions.Add(evicted)
}
}
func MaxSize
MaxSize returns the memory budget in bytes.
func (c *LayerCache) MaxSize() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.maxSize
}
func NewLayerCache
NewLayerCache creates a new layer cache with the specified maximum size.
The maxSizeMB parameter sets the memory budget in megabytes.
func NewLayerCache(maxSizeMB int) *LayerCache {
if maxSizeMB <= 0 {
maxSizeMB = DefaultMaxSizeMB
}
return &LayerCache{
entries: make(map[uint64]*CacheEntry),
lru: list.New(),
maxSize: int64(maxSizeMB) * bytesPerMB,
}
}
func Put
Put stores a pixmap in the cache with the given hash and version.
If the cache exceeds its memory budget, least recently used entries are evicted.
If an entry with the same hash exists, it is replaced.
func (c *LayerCache) Put(hash uint64, pixmap *gg.Pixmap, version uint64) {
if pixmap == nil {
return
}
entrySize := pixmapSize(pixmap)
if entrySize <= 0 {
return
}
// Don't cache if single entry exceeds budget
if entrySize > c.maxSize {
return
}
c.mu.Lock()
defer c.mu.Unlock()
// Check if entry already exists
if existing, ok := c.entries[hash]; ok {
// Update existing entry
c.size -= existing.size
c.lru.Remove(existing.element)
}
// Evict entries until we have space
c.evictUntilSize(c.maxSize - entrySize)
// Create new entry
entry := &CacheEntry{
hash: hash,
pixmap: pixmap,
size: entrySize,
version: version,
lastUsed: time.Now(),
}
entry.element = c.lru.PushFront(entry)
c.entries[hash] = entry
c.size += entrySize
}
func ResetStats
ResetStats resets the hit, miss, and eviction counters to zero.
func (c *LayerCache) ResetStats() {
c.hits.Store(0)
c.misses.Store(0)
c.evictions.Store(0)
}
func SetMaxSize
SetMaxSize updates the memory budget.
If the new budget is smaller than current usage, entries are evicted.
The mb parameter is the new budget in megabytes.
func (c *LayerCache) SetMaxSize(mb int) {
if mb <= 0 {
mb = DefaultMaxSizeMB
}
newMaxSize := int64(mb) * bytesPerMB
c.mu.Lock()
defer c.mu.Unlock()
c.maxSize = newMaxSize
c.evictUntilSize(newMaxSize)
}
func Size
Size returns the current memory usage in bytes.
func (c *LayerCache) Size() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.size
}
func Stats
Stats returns current cache statistics.
This operation is lock-free for the atomic counters.
func (c *LayerCache) Stats() CacheStats {
c.mu.RLock()
size := c.size
maxSize := c.maxSize
entries := len(c.entries)
c.mu.RUnlock()
hits := c.hits.Load()
misses := c.misses.Load()
evictions := c.evictions.Load()
var hitRate float64
total := hits + misses
if total > 0 {
hitRate = float64(hits) / float64(total)
}
return CacheStats{
Size: size,
MaxSize: maxSize,
Entries: entries,
Hits: hits,
Misses: misses,
HitRate: hitRate,
Evictions: evictions,
}
}
func Trim
Trim evicts entries until the cache size is at or below the target size.
The targetSize parameter is in bytes.
func (c *LayerCache) Trim(targetSize int64) {
if targetSize < 0 {
targetSize = 0
}
c.mu.Lock()
defer c.mu.Unlock()
c.evictUntilSize(targetSize)
}
Structs
type LayerCache struct
LayerCache provides an LRU cache for rendered layer pixmaps.
It is thread-safe and uses atomic counters for statistics.
The cache evicts least recently used entries when the memory limit is exceeded.
Cache entries are keyed by a 64-bit hash computed from the encoding content.
type LayerCache struct {
mu sync.RWMutex
entries map[uint64]*CacheEntry // hash -> entry
lru *list.List // LRU order (front = most recent)
size int64 // Current memory usage in bytes
maxSize int64 // Memory budget in bytes
// Statistics (atomic for zero-allocation reads)
hits atomic.Uint64
misses atomic.Uint64
evictions atomic.Uint64
}
type CacheEntry struct
CacheEntry represents a single cached pixmap with metadata.
type CacheEntry struct {
hash uint64
pixmap *gg.Pixmap
size int64 // Memory size in bytes
element *list.Element
version uint64 // Scene version when cached
lastUsed time.Time // Time of last access
}
type CacheStats struct
CacheStats contains cache statistics for monitoring.
type CacheStats struct {
// Size is the current memory usage in bytes.
Size int64
// MaxSize is the memory budget in bytes.
MaxSize int64
// Entries is the number of cached entries.
Entries int
// Hits is the number of cache hits.
Hits uint64
// Misses is the number of cache misses.
Misses uint64
// HitRate is the cache hit rate (0.0 to 1.0).
HitRate float64
// Evictions is the number of entries evicted.
Evictions uint64
}
Contains checks if an entry with the given hash exists in the cache.
This does not update the LRU order.