1 /*
2 * Copyright © 2020 Google, Inc.
3 * Copyright (c) 2020 Etnaviv Project
4 *
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 * the rights to use, copy, modify, merge, publish, distribute, sub license,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial portions
14 * of the Software.
15 *
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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Christian Gmeiner <christian.gmeiner@gmail.com>
26 */
27
28 #include "etnaviv_debug.h"
29 #include "etnaviv_disk_cache.h"
30 #include "nir_serialize.h"
31
32 #define debug 0
33
34 void
etna_disk_cache_init(struct etna_compiler * compiler,const char * renderer)35 etna_disk_cache_init(struct etna_compiler *compiler, const char *renderer)
36 {
37 if (etna_mesa_debug & ETNA_DBG_NOCACHE)
38 return;
39
40 const struct build_id_note *note =
41 build_id_find_nhdr_for_addr(etna_disk_cache_init);
42 assert(note && build_id_length(note) == 20); /* sha1 */
43
44 const uint8_t *id_sha1 = build_id_data(note);
45 assert(id_sha1);
46
47 char timestamp[41];
48 _mesa_sha1_format(timestamp, id_sha1);
49
50 compiler->disk_cache = disk_cache_create(renderer, timestamp, etna_mesa_debug);
51 }
52
53 void
etna_disk_cache_init_shader_key(struct etna_compiler * compiler,struct etna_shader * shader)54 etna_disk_cache_init_shader_key(struct etna_compiler *compiler, struct etna_shader *shader)
55 {
56 if (!compiler->disk_cache)
57 return;
58
59 struct mesa_sha1 ctx;
60
61 _mesa_sha1_init(&ctx);
62
63 /* Serialize the NIR to a binary blob that we can hash for the disk
64 * cache. Drop unnecessary information (like variable names)
65 * so the serialized NIR is smaller, and also to let us detect more
66 * isomorphic shaders when hashing, increasing cache hits.
67 */
68 struct blob blob;
69
70 blob_init(&blob);
71 nir_serialize(&blob, shader->nir, true);
72 _mesa_sha1_update(&ctx, blob.data, blob.size);
73 blob_finish(&blob);
74
75 _mesa_sha1_final(&ctx, shader->cache_key);
76 }
77
78 static void
compute_variant_key(struct etna_compiler * compiler,struct etna_shader_variant * v,cache_key cache_key)79 compute_variant_key(struct etna_compiler *compiler, struct etna_shader_variant *v,
80 cache_key cache_key)
81 {
82 struct blob blob;
83
84 blob_init(&blob);
85
86 blob_write_bytes(&blob, &v->shader->cache_key, sizeof(v->shader->cache_key));
87 blob_write_bytes(&blob, &v->key, sizeof(v->key));
88
89 disk_cache_compute_key(compiler->disk_cache, blob.data, blob.size, cache_key);
90
91 blob_finish(&blob);
92 }
93
94 static void
retrieve_variant(struct blob_reader * blob,struct etna_shader_variant * v)95 retrieve_variant(struct blob_reader *blob, struct etna_shader_variant *v)
96 {
97 blob_copy_bytes(blob, VARIANT_CACHE_PTR(v), VARIANT_CACHE_SIZE);
98
99 v->code = malloc(4 * v->code_size);
100 blob_copy_bytes(blob, v->code, 4 * v->code_size);
101
102 blob_copy_bytes(blob, &v->uniforms.count, sizeof(v->uniforms.count));
103 v->uniforms.contents = malloc(v->uniforms.count * sizeof(v->uniforms.contents));
104 v->uniforms.data = malloc(v->uniforms.count * sizeof(v->uniforms.data));
105
106 blob_copy_bytes(blob, v->uniforms.contents, v->uniforms.count * sizeof(v->uniforms.contents));
107 blob_copy_bytes(blob, v->uniforms.data, v->uniforms.count * sizeof(v->uniforms.data));
108 }
109
110 static void
store_variant(struct blob * blob,const struct etna_shader_variant * v)111 store_variant(struct blob *blob, const struct etna_shader_variant *v)
112 {
113 const uint32_t imm_count = v->uniforms.count;
114
115 blob_write_bytes(blob, VARIANT_CACHE_PTR(v), VARIANT_CACHE_SIZE);
116 blob_write_bytes(blob, v->code, 4 * v->code_size);
117
118 blob_write_bytes(blob, &v->uniforms.count, sizeof(v->uniforms.count));
119 blob_write_bytes(blob, v->uniforms.contents, imm_count * sizeof(v->uniforms.contents));
120 blob_write_bytes(blob, v->uniforms.data, imm_count * sizeof(v->uniforms.data));
121 }
122
123 bool
etna_disk_cache_retrieve(struct etna_compiler * compiler,struct etna_shader_variant * v)124 etna_disk_cache_retrieve(struct etna_compiler *compiler, struct etna_shader_variant *v)
125 {
126 if (!compiler->disk_cache)
127 return false;
128
129 cache_key cache_key;
130
131 compute_variant_key(compiler, v, cache_key);
132
133 if (debug) {
134 char sha1[41];
135
136 _mesa_sha1_format(sha1, cache_key);
137 fprintf(stderr, "[mesa disk cache] retrieving variant %s: ", sha1);
138 }
139
140 size_t size;
141 void *buffer = disk_cache_get(compiler->disk_cache, cache_key, &size);
142
143 if (debug)
144 fprintf(stderr, "%s\n", buffer ? "found" : "missing");
145
146 if (!buffer)
147 return false;
148
149 struct blob_reader blob;
150 blob_reader_init(&blob, buffer, size);
151
152 retrieve_variant(&blob, v);
153
154 free(buffer);
155
156 return true;
157 }
158
159 void
etna_disk_cache_store(struct etna_compiler * compiler,struct etna_shader_variant * v)160 etna_disk_cache_store(struct etna_compiler *compiler, struct etna_shader_variant *v)
161 {
162 if (!compiler->disk_cache)
163 return;
164
165 cache_key cache_key;
166
167 compute_variant_key(compiler, v, cache_key);
168
169 if (debug) {
170 char sha1[41];
171
172 _mesa_sha1_format(sha1, cache_key);
173 fprintf(stderr, "[mesa disk cache] storing variant %s\n", sha1);
174 }
175
176 struct blob blob;
177 blob_init(&blob);
178
179 store_variant(&blob, v);
180
181 disk_cache_put(compiler->disk_cache, cache_key, blob.data, blob.size, NULL);
182 blob_finish(&blob);
183 }
184