gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/caches.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package gpu
     4  
     5  import (
     6  	"fmt"
     7  
     8  	"gioui.org/internal/f32"
     9  )
    10  
    11  type textureCacheKey struct {
    12  	filter byte
    13  	handle any
    14  }
    15  
    16  type textureCache struct {
    17  	res map[textureCacheKey]resourceCacheValue
    18  }
    19  
    20  type resourceCacheValue struct {
    21  	used     bool
    22  	resource resource
    23  }
    24  
    25  // opCache is like a resourceCache but using concrete types and a
    26  // freelist instead of two maps to avoid runtime.mapaccess2 calls
    27  // since benchmarking showed them as a bottleneck.
    28  type opCache struct {
    29  	// store the index + 1 in cache this key is stored in
    30  	index map[opKey]int
    31  	// list of indexes in cache that are free and can be used
    32  	freelist []int
    33  	cache    []opCacheValue
    34  }
    35  
    36  type opCacheValue struct {
    37  	data pathData
    38  
    39  	bounds f32.Rectangle
    40  	// the fields below are handled by opCache
    41  	key  opKey
    42  	keep bool
    43  }
    44  
    45  func newTextureCache() *textureCache {
    46  	return &textureCache{
    47  		res: make(map[textureCacheKey]resourceCacheValue),
    48  	}
    49  }
    50  
    51  func (r *textureCache) get(key textureCacheKey) (resource, bool) {
    52  	v, exists := r.res[key]
    53  	if !exists {
    54  		return nil, false
    55  	}
    56  	if !v.used {
    57  		v.used = true
    58  		r.res[key] = v
    59  	}
    60  	return v.resource, exists
    61  }
    62  
    63  func (r *textureCache) put(key textureCacheKey, val resource) {
    64  	v, exists := r.res[key]
    65  	if exists && v.used {
    66  		panic(fmt.Errorf("key exists, %v", key))
    67  	}
    68  	v.used = true
    69  	v.resource = val
    70  	r.res[key] = v
    71  }
    72  
    73  func (r *textureCache) frame() {
    74  	for k, v := range r.res {
    75  		if v.used {
    76  			v.used = false
    77  			r.res[k] = v
    78  		} else {
    79  			delete(r.res, k)
    80  			v.resource.release()
    81  		}
    82  	}
    83  }
    84  
    85  func (r *textureCache) release() {
    86  	for _, v := range r.res {
    87  		v.resource.release()
    88  	}
    89  	r.res = nil
    90  }
    91  
    92  func newOpCache() *opCache {
    93  	return &opCache{
    94  		index:    make(map[opKey]int),
    95  		freelist: make([]int, 0),
    96  		cache:    make([]opCacheValue, 0),
    97  	}
    98  }
    99  
   100  func (r *opCache) get(key opKey) (o opCacheValue, exist bool) {
   101  	v := r.index[key]
   102  	if v == 0 {
   103  		return
   104  	}
   105  	r.cache[v-1].keep = true
   106  	return r.cache[v-1], true
   107  }
   108  
   109  func (r *opCache) put(key opKey, val opCacheValue) {
   110  	v := r.index[key]
   111  	val.keep = true
   112  	val.key = key
   113  	if v == 0 {
   114  		// not in cache
   115  		i := len(r.cache)
   116  		if len(r.freelist) > 0 {
   117  			i = r.freelist[len(r.freelist)-1]
   118  			r.freelist = r.freelist[:len(r.freelist)-1]
   119  			r.cache[i] = val
   120  		} else {
   121  			r.cache = append(r.cache, val)
   122  		}
   123  		r.index[key] = i + 1
   124  	} else {
   125  		r.cache[v-1] = val
   126  	}
   127  }
   128  
   129  func (r *opCache) frame() {
   130  	r.freelist = r.freelist[:0]
   131  	for i, v := range r.cache {
   132  		r.cache[i].keep = false
   133  		if v.keep {
   134  			continue
   135  		}
   136  		if v.data.data != nil {
   137  			v.data.release()
   138  			r.cache[i].data.data = nil
   139  		}
   140  		delete(r.index, v.key)
   141  		r.freelist = append(r.freelist, i)
   142  	}
   143  }
   144  
   145  func (r *opCache) release() {
   146  	for i := range r.cache {
   147  		r.cache[i].keep = false
   148  	}
   149  	r.frame()
   150  	r.index = nil
   151  	r.freelist = nil
   152  	r.cache = nil
   153  }