// SPDX-License-Identifier: Unlicense OR MIT package gpu import ( "fmt" "gioui.org/internal/f32" ) type textureCacheKey struct { filter byte handle any } type textureCache struct { res map[textureCacheKey]resourceCacheValue } type resourceCacheValue struct { used bool resource resource } // opCache is like a resourceCache but using concrete types and a // freelist instead of two maps to avoid runtime.mapaccess2 calls // since benchmarking showed them as a bottleneck. type opCache struct { // store the index + 1 in cache this key is stored in index map[opKey]int // list of indexes in cache that are free and can be used freelist []int cache []opCacheValue } type opCacheValue struct { data pathData bounds f32.Rectangle // the fields below are handled by opCache key opKey keep bool } func newTextureCache() *textureCache { return &textureCache{ res: make(map[textureCacheKey]resourceCacheValue), } } func (r *textureCache) get(key textureCacheKey) (resource, bool) { v, exists := r.res[key] if !exists { return nil, false } if !v.used { v.used = true r.res[key] = v } return v.resource, exists } func (r *textureCache) put(key textureCacheKey, val resource) { v, exists := r.res[key] if exists && v.used { panic(fmt.Errorf("key exists, %v", key)) } v.used = true v.resource = val r.res[key] = v } func (r *textureCache) frame() { for k, v := range r.res { if v.used { v.used = false r.res[k] = v } else { delete(r.res, k) v.resource.release() } } } func (r *textureCache) release() { for _, v := range r.res { v.resource.release() } r.res = nil } func newOpCache() *opCache { return &opCache{ index: make(map[opKey]int), freelist: make([]int, 0), cache: make([]opCacheValue, 0), } } func (r *opCache) get(key opKey) (o opCacheValue, exist bool) { v := r.index[key] if v == 0 { return } r.cache[v-1].keep = true return r.cache[v-1], true } func (r *opCache) put(key opKey, val opCacheValue) { v := r.index[key] val.keep = true val.key = key if v == 0 { // not in cache i := len(r.cache) if len(r.freelist) > 0 { i = r.freelist[len(r.freelist)-1] r.freelist = r.freelist[:len(r.freelist)-1] r.cache[i] = val } else { r.cache = append(r.cache, val) } r.index[key] = i + 1 } else { r.cache[v-1] = val } } func (r *opCache) frame() { r.freelist = r.freelist[:0] for i, v := range r.cache { r.cache[i].keep = false if v.keep { continue } if v.data.data != nil { v.data.release() r.cache[i].data.data = nil } delete(r.index, v.key) r.freelist = append(r.freelist, i) } } func (r *opCache) release() { for i := range r.cache { r.cache[i].keep = false } r.frame() r.index = nil r.freelist = nil r.cache = nil }