caches.go raw

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