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
34void
35etna_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
53void
54etna_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
78static void
79compute_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
94static void
95retrieve_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
110static void
111store_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
123bool
124etna_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
159void
160etna_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