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