xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/freedreno/ir3/ir3_cache.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2015 Rob Clark <[email protected]>
3  * SPDX-License-Identifier: MIT
4  *
5  * Authors:
6  *    Rob Clark <[email protected]>
7  */
8 
9 #include "util/hash_table.h"
10 #include "util/ralloc.h"
11 #define XXH_INLINE_ALL
12 #include "util/xxhash.h"
13 
14 #include "ir3_cache.h"
15 #include "ir3_gallium.h"
16 
17 static uint32_t
key_hash(const void * _key)18 key_hash(const void *_key)
19 {
20    const struct ir3_cache_key *key = _key;
21    return XXH32(key, sizeof(*key), 0);
22 }
23 
24 static bool
key_equals(const void * _a,const void * _b)25 key_equals(const void *_a, const void *_b)
26 {
27    const struct ir3_cache_key *a = _a;
28    const struct ir3_cache_key *b = _b;
29    // TODO we could optimize the key shader-variant key comparison by not
30    // ignoring has_per_samp.. not really sure if that helps..
31    return memcmp(a, b, sizeof(struct ir3_cache_key)) == 0;
32 }
33 
34 struct ir3_cache {
35    /* cache mapping gallium/etc shader state-objs + shader-key to backend
36     * specific state-object
37     */
38    struct hash_table *ht;
39 
40    const struct ir3_cache_funcs *funcs;
41    void *data;
42 };
43 
44 struct ir3_cache *
ir3_cache_create(const struct ir3_cache_funcs * funcs,void * data)45 ir3_cache_create(const struct ir3_cache_funcs *funcs, void *data)
46 {
47    struct ir3_cache *cache = rzalloc(NULL, struct ir3_cache);
48 
49    cache->ht = _mesa_hash_table_create(cache, key_hash, key_equals);
50    cache->funcs = funcs;
51    cache->data = data;
52 
53    return cache;
54 }
55 
56 void
ir3_cache_destroy(struct ir3_cache * cache)57 ir3_cache_destroy(struct ir3_cache *cache)
58 {
59    if (!cache)
60       return;
61 
62    /* _mesa_hash_table_destroy is so *almost* useful.. */
63    hash_table_foreach (cache->ht, entry) {
64       cache->funcs->destroy_state(cache->data, entry->data);
65    }
66 
67    ralloc_free(cache);
68 }
69 
70 struct ir3_program_state *
ir3_cache_lookup(struct ir3_cache * cache,const struct ir3_cache_key * key,struct util_debug_callback * debug)71 ir3_cache_lookup(struct ir3_cache *cache, const struct ir3_cache_key *key,
72                  struct util_debug_callback *debug)
73 {
74    uint32_t hash = key_hash(key);
75    struct hash_entry *entry =
76       _mesa_hash_table_search_pre_hashed(cache->ht, hash, key);
77 
78    if (entry) {
79       return entry->data;
80    }
81 
82    MESA_TRACE_FUNC();
83 
84    if (key->hs)
85       assert(key->ds);
86 
87    struct ir3_shader *shaders[MESA_SHADER_STAGES] = {
88       [MESA_SHADER_VERTEX] = ir3_get_shader(key->vs),
89       [MESA_SHADER_TESS_CTRL] = ir3_get_shader(key->hs),
90       [MESA_SHADER_TESS_EVAL] = ir3_get_shader(key->ds),
91       [MESA_SHADER_GEOMETRY] = ir3_get_shader(key->gs),
92       [MESA_SHADER_FRAGMENT] = ir3_get_shader(key->fs),
93    };
94 
95    if (shaders[MESA_SHADER_TESS_EVAL] && !shaders[MESA_SHADER_TESS_CTRL]) {
96       struct ir3_shader *vs = shaders[MESA_SHADER_VERTEX];
97       struct ir3_shader *hs =
98             ir3_shader_passthrough_tcs(vs, key->patch_vertices);
99       shaders[MESA_SHADER_TESS_CTRL] = hs;
100    }
101 
102    const struct ir3_shader_variant *variants[MESA_SHADER_STAGES];
103    struct ir3_shader_key shader_key = key->key;
104 
105    for (gl_shader_stage stage = MESA_SHADER_VERTEX; stage < MESA_SHADER_STAGES;
106         stage++) {
107       if (shaders[stage]) {
108          variants[stage] =
109             ir3_shader_variant(shaders[stage], shader_key, false, debug);
110          if (!variants[stage])
111             return NULL;
112       } else {
113          variants[stage] = NULL;
114       }
115    }
116 
117    struct ir3_compiler *compiler = shaders[MESA_SHADER_VERTEX]->compiler;
118    uint32_t safe_constlens = ir3_trim_constlen(variants, compiler);
119    shader_key.safe_constlen = true;
120 
121    for (gl_shader_stage stage = MESA_SHADER_VERTEX; stage < MESA_SHADER_STAGES;
122         stage++) {
123       if (safe_constlens & (1 << stage)) {
124          variants[stage] =
125             ir3_shader_variant(shaders[stage], shader_key, false, debug);
126          if (!variants[stage])
127             return NULL;
128       }
129    }
130 
131    const struct ir3_shader_variant *bs;
132 
133    if (ir3_has_binning_vs(&key->key)) {
134       /* starting with a6xx, the same const state is used for binning and draw
135        * passes, so the binning pass VS variant needs to match the main VS
136        */
137       shader_key.safe_constlen = (compiler->gen >= 6) &&
138             !!(safe_constlens & (1 << MESA_SHADER_VERTEX));
139       bs =
140          ir3_shader_variant(shaders[MESA_SHADER_VERTEX], shader_key, true, debug);
141       if (!bs)
142          return NULL;
143    } else {
144       bs = variants[MESA_SHADER_VERTEX];
145    }
146 
147    struct ir3_program_state *state = cache->funcs->create_state(
148       cache->data, bs, variants[MESA_SHADER_VERTEX],
149       variants[MESA_SHADER_TESS_CTRL], variants[MESA_SHADER_TESS_EVAL],
150       variants[MESA_SHADER_GEOMETRY], variants[MESA_SHADER_FRAGMENT],
151       key);
152    state->key = *key;
153 
154    /* NOTE: uses copy of key in state obj, because pointer passed by caller
155     * is probably on the stack
156     */
157    _mesa_hash_table_insert_pre_hashed(cache->ht, hash, &state->key, state);
158 
159    return state;
160 }
161 
162 /* call when an API level state object is destroyed, to invalidate
163  * cache entries which reference that state object.
164  */
165 void
ir3_cache_invalidate(struct ir3_cache * cache,void * stobj)166 ir3_cache_invalidate(struct ir3_cache *cache, void *stobj)
167 {
168    if (!cache)
169       return;
170 
171    hash_table_foreach (cache->ht, entry) {
172       const struct ir3_cache_key *key = entry->key;
173       if ((key->fs == stobj) || (key->vs == stobj) || (key->ds == stobj) ||
174           (key->hs == stobj) || (key->gs == stobj)) {
175          cache->funcs->destroy_state(cache->data, entry->data);
176          _mesa_hash_table_remove(cache->ht, entry);
177          return;
178       }
179    }
180 }
181