2 * Copyright 2019 Advanced Micro Devices, Inc.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "util/u_live_shader_cache.h"
27 #include "util/u_inlines.h"
28 #include "tgsi/tgsi_from_mesa.h"
29 #include "tgsi/tgsi_parse.h"
31 #include "compiler/nir/nir_serialize.h"
33 #include "util/blob.h"
34 #include "util/hash_table.h"
35 #include "util/mesa-sha1.h"
37 static uint32_t key_hash(const void *key
)
39 /* Take the first dword of SHA1. */
40 return *(uint32_t*)key
;
43 static bool key_equals(const void *a
, const void *b
)
46 return memcmp(a
, b
, 20) == 0;
50 util_live_shader_cache_init(struct util_live_shader_cache
*cache
,
51 void *(*create_shader
)(struct pipe_context
*,
52 const struct pipe_shader_state
*state
),
53 void (*destroy_shader
)(struct pipe_context
*, void *))
55 simple_mtx_init(&cache
->lock
, mtx_plain
);
56 cache
->hashtable
= _mesa_hash_table_create(NULL
, key_hash
, key_equals
);
57 cache
->create_shader
= create_shader
;
58 cache
->destroy_shader
= destroy_shader
;
62 util_live_shader_cache_deinit(struct util_live_shader_cache
*cache
)
64 if (cache
->hashtable
) {
65 /* The hash table should be empty at this point. */
66 _mesa_hash_table_destroy(cache
->hashtable
, NULL
);
67 simple_mtx_destroy(&cache
->lock
);
72 util_live_shader_cache_get(struct pipe_context
*ctx
,
73 struct util_live_shader_cache
*cache
,
74 const struct pipe_shader_state
*state
)
76 struct blob blob
= {0};
78 const void *ir_binary
;
79 enum pipe_shader_type stage
;
81 /* Get the shader binary and shader stage. */
82 if (state
->type
== PIPE_SHADER_IR_TGSI
) {
83 ir_binary
= state
->tokens
;
84 ir_size
= tgsi_num_tokens(state
->tokens
) *
85 sizeof(struct tgsi_token
);
86 stage
= tgsi_get_processor_type(state
->tokens
);
87 } else if (state
->type
== PIPE_SHADER_IR_NIR
) {
89 nir_serialize(&blob
, state
->ir
.nir
, true);
90 ir_binary
= blob
.data
;
92 stage
= pipe_shader_type_from_mesa(((nir_shader
*)state
->ir
.nir
)->info
.stage
);
98 /* Compute SHA1 of pipe_shader_state. */
99 struct mesa_sha1 sha1_ctx
;
100 unsigned char sha1
[20];
101 _mesa_sha1_init(&sha1_ctx
);
102 _mesa_sha1_update(&sha1_ctx
, ir_binary
, ir_size
);
103 if ((stage
== PIPE_SHADER_VERTEX
||
104 stage
== PIPE_SHADER_TESS_EVAL
||
105 stage
== PIPE_SHADER_GEOMETRY
) &&
106 state
->stream_output
.num_outputs
) {
107 _mesa_sha1_update(&sha1_ctx
, &state
->stream_output
,
108 sizeof(state
->stream_output
));
110 _mesa_sha1_final(&sha1_ctx
, sha1
);
112 if (ir_binary
== blob
.data
)
115 /* Find the shader in the live cache. */
116 simple_mtx_lock(&cache
->lock
);
117 struct hash_entry
*entry
= _mesa_hash_table_search(cache
->hashtable
, sha1
);
118 struct util_live_shader
*shader
= entry
? entry
->data
: NULL
;
120 /* Increase the refcount. */
122 pipe_reference(NULL
, &shader
->reference
);
125 simple_mtx_unlock(&cache
->lock
);
127 /* Return if the shader already exists. */
131 /* The cache mutex is unlocked to allow multiple create_shader
132 * invocations to run simultaneously.
134 shader
= (struct util_live_shader
*)cache
->create_shader(ctx
, state
);
135 pipe_reference_init(&shader
->reference
, 1);
136 memcpy(shader
->sha1
, sha1
, sizeof(sha1
));
138 simple_mtx_lock(&cache
->lock
);
139 /* The same shader might have been created in parallel. This is rare.
140 * If so, keep the one already in cache.
142 struct hash_entry
*entry2
= _mesa_hash_table_search(cache
->hashtable
, sha1
);
143 struct util_live_shader
*shader2
= entry2
? entry2
->data
: NULL
;
146 cache
->destroy_shader(ctx
, shader
);
148 /* Increase the refcount. */
149 pipe_reference(NULL
, &shader
->reference
);
151 _mesa_hash_table_insert(cache
->hashtable
, shader
->sha1
, shader
);
154 simple_mtx_unlock(&cache
->lock
);
160 util_shader_reference(struct pipe_context
*ctx
,
161 struct util_live_shader_cache
*cache
,
162 void **dst
, void *src
)
167 struct util_live_shader
*dst_shader
= (struct util_live_shader
*)*dst
;
168 struct util_live_shader
*src_shader
= (struct util_live_shader
*)src
;
170 simple_mtx_lock(&cache
->lock
);
171 bool destroy
= pipe_reference(&dst_shader
->reference
, &src_shader
->reference
);
173 struct hash_entry
*entry
= _mesa_hash_table_search(cache
->hashtable
,
176 _mesa_hash_table_remove(cache
->hashtable
, entry
);
178 simple_mtx_unlock(&cache
->lock
);
181 cache
->destroy_shader(ctx
, dst_shader
);