github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/caches.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package gpu
     4  
     5  import (
     6  	"fmt"
     7  
     8  	"github.com/cybriq/giocore/f32"
     9  	"github.com/cybriq/giocore/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  }