From fe2a164d30c4dc13c63f27934877a91803b600d3 Mon Sep 17 00:00:00 2001 From: Dominik Honnef Date: Wed, 3 Jan 2024 14:01:31 +0100 Subject: [PATCH] gpu: rename resourceCache to textureCache and use concrete key The only remaining use of the cache is mapping handles to textures. Using a concrete type for the key avoids the allocation caused by convT. If we need more caches again in the future we can copy the type, or make it generic. Instead of updating the benchmark, we removed it outright. It suffered from several flaws: - The amount of work for each iteration of b.N wasn't constant, because the same cache was reused, growing ever larger in size. - It only tested the cost of insertions. The comment "half are the same and half updated" wasn't true, as calling 'put' with the same key twice would've resulted in a panic. - It didn't simulate any particular workload or cache size, making the benchmark useless for comparing different cache implementations. The cost of insertions isn't particularly interesting. Signed-off-by: Dominik Honnef --- gpu/caches.go | 25 +++++++++++++++---------- gpu/caches_test.go | 24 ------------------------ gpu/gpu.go | 14 +++++--------- 3 files changed, 20 insertions(+), 43 deletions(-) delete mode 100644 gpu/caches_test.go diff --git a/gpu/caches.go b/gpu/caches.go index b44f7ead..0945e7a1 100644 --- a/gpu/caches.go +++ b/gpu/caches.go @@ -8,8 +8,13 @@ import ( "gioui.org/internal/f32" ) -type resourceCache struct { - res map[interface{}]resourceCacheValue +type textureCacheKey struct { + filter byte + handle any +} + +type textureCache struct { + res map[textureCacheKey]resourceCacheValue } type resourceCacheValue struct { @@ -37,13 +42,13 @@ type opCacheValue struct { keep bool } -func newResourceCache() *resourceCache { - return &resourceCache{ - res: make(map[interface{}]resourceCacheValue), +func newTextureCache() *textureCache { + return &textureCache{ + res: make(map[textureCacheKey]resourceCacheValue), } } -func (r *resourceCache) get(key interface{}) (resource, bool) { +func (r *textureCache) get(key textureCacheKey) (resource, bool) { v, exists := r.res[key] if !exists { return nil, false @@ -55,17 +60,17 @@ func (r *resourceCache) get(key interface{}) (resource, bool) { return v.resource, exists } -func (r *resourceCache) put(key interface{}, val resource) { +func (r *textureCache) put(key textureCacheKey, val resource) { v, exists := r.res[key] if exists && v.used { - panic(fmt.Errorf("key exists, %p", key)) + panic(fmt.Errorf("key exists, %v", key)) } v.used = true v.resource = val r.res[key] = v } -func (r *resourceCache) frame() { +func (r *textureCache) frame() { for k, v := range r.res { if v.used { v.used = false @@ -77,7 +82,7 @@ func (r *resourceCache) frame() { } } -func (r *resourceCache) release() { +func (r *textureCache) release() { for _, v := range r.res { v.resource.release() } diff --git a/gpu/caches_test.go b/gpu/caches_test.go deleted file mode 100644 index 411de5af..00000000 --- a/gpu/caches_test.go +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package gpu - -import "testing" - -func BenchmarkResourceCache(b *testing.B) { - offset := 0 - const N = 100 - - cache := newResourceCache() - for i := 0; i < b.N; i++ { - // half are the same and half updated - for k := 0; k < N; k++ { - cache.put(offset+k, nullResource{}) - } - cache.frame() - offset += N / 2 - } -} - -type nullResource struct{} - -func (nullResource) release() {} diff --git a/gpu/gpu.go b/gpu/gpu.go index f616fd29..362eb141 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -51,7 +51,7 @@ type GPU interface { } type gpu struct { - cache *resourceCache + cache *textureCache profile string timers *timers @@ -359,7 +359,7 @@ func NewWithDevice(d driver.Device) (GPU, error) { func newGPU(ctx driver.Device) (*gpu, error) { g := &gpu{ - cache: newResourceCache(), + cache: newTextureCache(), } g.drawOps.pathCache = newOpCache() if err := g.init(ctx); err != nil { @@ -460,12 +460,8 @@ func (g *gpu) Profile() string { return g.profile } -func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture { - type cachekey struct { - filter byte - handle any - } - key := cachekey{ +func (r *renderer) texHandle(cache *textureCache, data imageOpData) driver.Texture { + key := textureCacheKey{ filter: data.filter, handle: data.handle, } @@ -1211,7 +1207,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32 return m } -func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) { +func (r *renderer) uploadImages(cache *textureCache, ops []imageOp) { for i := range ops { img := &ops[i] m := img.material