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 <dominik@honnef.co>
This commit is contained in:
Dominik Honnef
2024-01-03 14:01:31 +01:00
committed by Elias Naur
parent 4eca2c7d26
commit fe2a164d30
3 changed files with 20 additions and 43 deletions
+15 -10
View File
@@ -8,8 +8,13 @@ import (
"gioui.org/internal/f32" "gioui.org/internal/f32"
) )
type resourceCache struct { type textureCacheKey struct {
res map[interface{}]resourceCacheValue filter byte
handle any
}
type textureCache struct {
res map[textureCacheKey]resourceCacheValue
} }
type resourceCacheValue struct { type resourceCacheValue struct {
@@ -37,13 +42,13 @@ type opCacheValue struct {
keep bool keep bool
} }
func newResourceCache() *resourceCache { func newTextureCache() *textureCache {
return &resourceCache{ return &textureCache{
res: make(map[interface{}]resourceCacheValue), 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] v, exists := r.res[key]
if !exists { if !exists {
return nil, false return nil, false
@@ -55,17 +60,17 @@ func (r *resourceCache) get(key interface{}) (resource, bool) {
return v.resource, exists 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] v, exists := r.res[key]
if exists && v.used { if exists && v.used {
panic(fmt.Errorf("key exists, %p", key)) panic(fmt.Errorf("key exists, %v", key))
} }
v.used = true v.used = true
v.resource = val v.resource = val
r.res[key] = v r.res[key] = v
} }
func (r *resourceCache) frame() { func (r *textureCache) frame() {
for k, v := range r.res { for k, v := range r.res {
if v.used { if v.used {
v.used = false v.used = false
@@ -77,7 +82,7 @@ func (r *resourceCache) frame() {
} }
} }
func (r *resourceCache) release() { func (r *textureCache) release() {
for _, v := range r.res { for _, v := range r.res {
v.resource.release() v.resource.release()
} }
-24
View File
@@ -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() {}
+5 -9
View File
@@ -51,7 +51,7 @@ type GPU interface {
} }
type gpu struct { type gpu struct {
cache *resourceCache cache *textureCache
profile string profile string
timers *timers timers *timers
@@ -359,7 +359,7 @@ func NewWithDevice(d driver.Device) (GPU, error) {
func newGPU(ctx driver.Device) (*gpu, error) { func newGPU(ctx driver.Device) (*gpu, error) {
g := &gpu{ g := &gpu{
cache: newResourceCache(), cache: newTextureCache(),
} }
g.drawOps.pathCache = newOpCache() g.drawOps.pathCache = newOpCache()
if err := g.init(ctx); err != nil { if err := g.init(ctx); err != nil {
@@ -460,12 +460,8 @@ func (g *gpu) Profile() string {
return g.profile return g.profile
} }
func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture { func (r *renderer) texHandle(cache *textureCache, data imageOpData) driver.Texture {
type cachekey struct { key := textureCacheKey{
filter byte
handle any
}
key := cachekey{
filter: data.filter, filter: data.filter,
handle: data.handle, handle: data.handle,
} }
@@ -1211,7 +1207,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
return m return m
} }
func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) { func (r *renderer) uploadImages(cache *textureCache, ops []imageOp) {
for i := range ops { for i := range ops {
img := &ops[i] img := &ops[i]
m := img.material m := img.material