1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2018 Intel Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice shall be included
12bf215546Sopenharmony_ci * in all copies or substantial portions of the Software.
13bf215546Sopenharmony_ci *
14bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20bf215546Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
21bf215546Sopenharmony_ci */
22bf215546Sopenharmony_ci
23bf215546Sopenharmony_ci/**
24bf215546Sopenharmony_ci * @file crocus_disk_cache.c
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci * Functions for interacting with the on-disk shader cache.
27bf215546Sopenharmony_ci */
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_ci#include <stdio.h>
30bf215546Sopenharmony_ci#include <stdint.h>
31bf215546Sopenharmony_ci#include <assert.h>
32bf215546Sopenharmony_ci#include <string.h>
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_ci#include "compiler/nir/nir.h"
35bf215546Sopenharmony_ci#include "util/blob.h"
36bf215546Sopenharmony_ci#include "util/build_id.h"
37bf215546Sopenharmony_ci#include "util/disk_cache.h"
38bf215546Sopenharmony_ci#include "util/mesa-sha1.h"
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci#include "crocus_context.h"
41bf215546Sopenharmony_ci
42bf215546Sopenharmony_cistatic bool debug = false;
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ci/**
45bf215546Sopenharmony_ci * Compute a disk cache key for the given uncompiled shader and NOS key.
46bf215546Sopenharmony_ci */
47bf215546Sopenharmony_cistatic void
48bf215546Sopenharmony_cicrocus_disk_cache_compute_key(struct disk_cache *cache,
49bf215546Sopenharmony_ci                              const struct crocus_uncompiled_shader *ish,
50bf215546Sopenharmony_ci                              const void *orig_prog_key,
51bf215546Sopenharmony_ci                              uint32_t prog_key_size,
52bf215546Sopenharmony_ci                              cache_key cache_key)
53bf215546Sopenharmony_ci{
54bf215546Sopenharmony_ci   /* Create a copy of the program key with program_string_id zeroed out.
55bf215546Sopenharmony_ci    * It's essentially random data which we don't want to include in our
56bf215546Sopenharmony_ci    * hashing and comparisons.  We'll set a proper value on a cache hit.
57bf215546Sopenharmony_ci    */
58bf215546Sopenharmony_ci   union brw_any_prog_key prog_key;
59bf215546Sopenharmony_ci   memcpy(&prog_key, orig_prog_key, prog_key_size);
60bf215546Sopenharmony_ci   prog_key.base.program_string_id = 0;
61bf215546Sopenharmony_ci
62bf215546Sopenharmony_ci   uint8_t data[sizeof(prog_key) + sizeof(ish->nir_sha1)];
63bf215546Sopenharmony_ci   uint32_t data_size = prog_key_size + sizeof(ish->nir_sha1);
64bf215546Sopenharmony_ci
65bf215546Sopenharmony_ci   memcpy(data, ish->nir_sha1, sizeof(ish->nir_sha1));
66bf215546Sopenharmony_ci   memcpy(data + sizeof(ish->nir_sha1), &prog_key, prog_key_size);
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci   disk_cache_compute_key(cache, data, data_size, cache_key);
69bf215546Sopenharmony_ci}
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci/**
72bf215546Sopenharmony_ci * Store the given compiled shader in the disk cache.
73bf215546Sopenharmony_ci *
74bf215546Sopenharmony_ci * This should only be called on newly compiled shaders.  No checking is
75bf215546Sopenharmony_ci * done to prevent repeated stores of the same shader.
76bf215546Sopenharmony_ci */
77bf215546Sopenharmony_civoid
78bf215546Sopenharmony_cicrocus_disk_cache_store(struct disk_cache *cache,
79bf215546Sopenharmony_ci                        const struct crocus_uncompiled_shader *ish,
80bf215546Sopenharmony_ci                        const struct crocus_compiled_shader *shader,
81bf215546Sopenharmony_ci                        void *map,
82bf215546Sopenharmony_ci                        const void *prog_key,
83bf215546Sopenharmony_ci                        uint32_t prog_key_size)
84bf215546Sopenharmony_ci{
85bf215546Sopenharmony_ci#ifdef ENABLE_SHADER_CACHE
86bf215546Sopenharmony_ci   if (!cache)
87bf215546Sopenharmony_ci      return;
88bf215546Sopenharmony_ci
89bf215546Sopenharmony_ci   gl_shader_stage stage = ish->nir->info.stage;
90bf215546Sopenharmony_ci   const struct brw_stage_prog_data *prog_data = shader->prog_data;
91bf215546Sopenharmony_ci
92bf215546Sopenharmony_ci   cache_key cache_key;
93bf215546Sopenharmony_ci   crocus_disk_cache_compute_key(cache, ish, prog_key, prog_key_size, cache_key);
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci   if (debug) {
96bf215546Sopenharmony_ci      char sha1[41];
97bf215546Sopenharmony_ci      _mesa_sha1_format(sha1, cache_key);
98bf215546Sopenharmony_ci      fprintf(stderr, "[mesa disk cache] storing %s\n", sha1);
99bf215546Sopenharmony_ci   }
100bf215546Sopenharmony_ci
101bf215546Sopenharmony_ci   struct blob blob;
102bf215546Sopenharmony_ci   blob_init(&blob);
103bf215546Sopenharmony_ci
104bf215546Sopenharmony_ci   /* We write the following data to the cache blob:
105bf215546Sopenharmony_ci    *
106bf215546Sopenharmony_ci    * 1. Prog data (must come first because it has the assembly size)
107bf215546Sopenharmony_ci    * 2. Assembly code
108bf215546Sopenharmony_ci    * 3. Number of entries in the system value array
109bf215546Sopenharmony_ci    * 4. System value array
110bf215546Sopenharmony_ci    * 5. Legacy param array (only used for compute workgroup ID)
111bf215546Sopenharmony_ci    * 6. Binding table
112bf215546Sopenharmony_ci    */
113bf215546Sopenharmony_ci   blob_write_bytes(&blob, shader->prog_data, brw_prog_data_size(stage));
114bf215546Sopenharmony_ci   blob_write_bytes(&blob, map + shader->offset, shader->prog_data->program_size);
115bf215546Sopenharmony_ci   blob_write_bytes(&blob, &shader->num_system_values, sizeof(unsigned));
116bf215546Sopenharmony_ci   blob_write_bytes(&blob, shader->system_values,
117bf215546Sopenharmony_ci                    shader->num_system_values * sizeof(enum brw_param_builtin));
118bf215546Sopenharmony_ci   blob_write_bytes(&blob, prog_data->param,
119bf215546Sopenharmony_ci                    prog_data->nr_params * sizeof(uint32_t));
120bf215546Sopenharmony_ci   blob_write_bytes(&blob, &shader->bt, sizeof(shader->bt));
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_ci   disk_cache_put(cache, cache_key, blob.data, blob.size, NULL);
123bf215546Sopenharmony_ci   blob_finish(&blob);
124bf215546Sopenharmony_ci#endif
125bf215546Sopenharmony_ci}
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_ci/**
128bf215546Sopenharmony_ci * Search for a compiled shader in the disk cache.  If found, upload it
129bf215546Sopenharmony_ci * to the in-memory program cache so we can use it.
130bf215546Sopenharmony_ci */
131bf215546Sopenharmony_cistruct crocus_compiled_shader *
132bf215546Sopenharmony_cicrocus_disk_cache_retrieve(struct crocus_context *ice,
133bf215546Sopenharmony_ci                           const struct crocus_uncompiled_shader *ish,
134bf215546Sopenharmony_ci                           const void *prog_key,
135bf215546Sopenharmony_ci                           uint32_t key_size)
136bf215546Sopenharmony_ci{
137bf215546Sopenharmony_ci#ifdef ENABLE_SHADER_CACHE
138bf215546Sopenharmony_ci   struct crocus_screen *screen = (void *) ice->ctx.screen;
139bf215546Sopenharmony_ci   struct disk_cache *cache = screen->disk_cache;
140bf215546Sopenharmony_ci   gl_shader_stage stage = ish->nir->info.stage;
141bf215546Sopenharmony_ci
142bf215546Sopenharmony_ci   if (!cache)
143bf215546Sopenharmony_ci      return NULL;
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci   cache_key cache_key;
146bf215546Sopenharmony_ci   crocus_disk_cache_compute_key(cache, ish, prog_key, key_size, cache_key);
147bf215546Sopenharmony_ci
148bf215546Sopenharmony_ci   if (debug) {
149bf215546Sopenharmony_ci      char sha1[41];
150bf215546Sopenharmony_ci      _mesa_sha1_format(sha1, cache_key);
151bf215546Sopenharmony_ci      fprintf(stderr, "[mesa disk cache] retrieving %s: ", sha1);
152bf215546Sopenharmony_ci   }
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci   size_t size;
155bf215546Sopenharmony_ci   void *buffer = disk_cache_get(screen->disk_cache, cache_key, &size);
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci   if (debug)
158bf215546Sopenharmony_ci      fprintf(stderr, "%s\n", buffer ? "found" : "missing");
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci   if (!buffer)
161bf215546Sopenharmony_ci      return NULL;
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci   const uint32_t prog_data_size = brw_prog_data_size(stage);
164bf215546Sopenharmony_ci
165bf215546Sopenharmony_ci   struct brw_stage_prog_data *prog_data = ralloc_size(NULL, prog_data_size);
166bf215546Sopenharmony_ci   const void *assembly;
167bf215546Sopenharmony_ci   uint32_t num_system_values;
168bf215546Sopenharmony_ci   uint32_t *system_values = NULL;
169bf215546Sopenharmony_ci   uint32_t *so_decls = NULL;
170bf215546Sopenharmony_ci
171bf215546Sopenharmony_ci   struct blob_reader blob;
172bf215546Sopenharmony_ci   blob_reader_init(&blob, buffer, size);
173bf215546Sopenharmony_ci   blob_copy_bytes(&blob, prog_data, prog_data_size);
174bf215546Sopenharmony_ci   assembly = blob_read_bytes(&blob, prog_data->program_size);
175bf215546Sopenharmony_ci   num_system_values = blob_read_uint32(&blob);
176bf215546Sopenharmony_ci   if (num_system_values) {
177bf215546Sopenharmony_ci      system_values =
178bf215546Sopenharmony_ci         ralloc_array(NULL, enum brw_param_builtin, num_system_values);
179bf215546Sopenharmony_ci      blob_copy_bytes(&blob, system_values,
180bf215546Sopenharmony_ci                      num_system_values * sizeof(enum brw_param_builtin));
181bf215546Sopenharmony_ci   }
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   prog_data->param = NULL;
184bf215546Sopenharmony_ci   if (prog_data->nr_params) {
185bf215546Sopenharmony_ci      prog_data->param = ralloc_array(NULL, uint32_t, prog_data->nr_params);
186bf215546Sopenharmony_ci      blob_copy_bytes(&blob, prog_data->param,
187bf215546Sopenharmony_ci                      prog_data->nr_params * sizeof(uint32_t));
188bf215546Sopenharmony_ci   }
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_ci   struct crocus_binding_table bt;
191bf215546Sopenharmony_ci   blob_copy_bytes(&blob, &bt, sizeof(bt));
192bf215546Sopenharmony_ci
193bf215546Sopenharmony_ci   if ((stage == MESA_SHADER_VERTEX ||
194bf215546Sopenharmony_ci        stage == MESA_SHADER_TESS_EVAL ||
195bf215546Sopenharmony_ci        stage == MESA_SHADER_GEOMETRY) && screen->devinfo.ver > 6) {
196bf215546Sopenharmony_ci      struct brw_vue_prog_data *vue_prog_data = (void *) prog_data;
197bf215546Sopenharmony_ci      so_decls = screen->vtbl.create_so_decl_list(&ish->stream_output,
198bf215546Sopenharmony_ci                                                  &vue_prog_data->vue_map);
199bf215546Sopenharmony_ci   }
200bf215546Sopenharmony_ci
201bf215546Sopenharmony_ci   /* System values and uniforms are stored in constant buffer 0, the
202bf215546Sopenharmony_ci    * user-facing UBOs are indexed by one.  So if any constant buffer is
203bf215546Sopenharmony_ci    * needed, the constant buffer 0 will be needed, so account for it.
204bf215546Sopenharmony_ci    */
205bf215546Sopenharmony_ci   unsigned num_cbufs = ish->nir->info.num_ubos;
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_ci   if (num_cbufs || ish->nir->num_uniforms)
208bf215546Sopenharmony_ci      num_cbufs++;
209bf215546Sopenharmony_ci
210bf215546Sopenharmony_ci   if (num_system_values)
211bf215546Sopenharmony_ci      num_cbufs++;
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci   /* Upload our newly read shader to the in-memory program cache and
214bf215546Sopenharmony_ci    * return it to the caller.
215bf215546Sopenharmony_ci    */
216bf215546Sopenharmony_ci   struct crocus_compiled_shader *shader =
217bf215546Sopenharmony_ci      crocus_upload_shader(ice, stage, key_size, prog_key, assembly,
218bf215546Sopenharmony_ci                           prog_data->program_size,
219bf215546Sopenharmony_ci                           prog_data, prog_data_size, so_decls, system_values,
220bf215546Sopenharmony_ci                           num_system_values, num_cbufs, &bt);
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci   free(buffer);
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_ci   return shader;
225bf215546Sopenharmony_ci#else
226bf215546Sopenharmony_ci   return NULL;
227bf215546Sopenharmony_ci#endif
228bf215546Sopenharmony_ci}
229bf215546Sopenharmony_ci
230bf215546Sopenharmony_ci/**
231bf215546Sopenharmony_ci * Initialize the on-disk shader cache.
232bf215546Sopenharmony_ci */
233bf215546Sopenharmony_civoid
234bf215546Sopenharmony_cicrocus_disk_cache_init(struct crocus_screen *screen)
235bf215546Sopenharmony_ci{
236bf215546Sopenharmony_ci#ifdef ENABLE_SHADER_CACHE
237bf215546Sopenharmony_ci   if (INTEL_DEBUG(DEBUG_DISK_CACHE_DISABLE_MASK))
238bf215546Sopenharmony_ci      return;
239bf215546Sopenharmony_ci
240bf215546Sopenharmony_ci   /* array length = print length + nul char + 1 extra to verify it's unused */
241bf215546Sopenharmony_ci   char renderer[13];
242bf215546Sopenharmony_ci   UNUSED int len =
243bf215546Sopenharmony_ci      snprintf(renderer, sizeof(renderer), "crocus_%04x", screen->pci_id);
244bf215546Sopenharmony_ci   assert(len == sizeof(renderer) - 2);
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci   const struct build_id_note *note =
247bf215546Sopenharmony_ci      build_id_find_nhdr_for_addr(crocus_disk_cache_init);
248bf215546Sopenharmony_ci   assert(note && build_id_length(note) == 20); /* sha1 */
249bf215546Sopenharmony_ci
250bf215546Sopenharmony_ci   const uint8_t *id_sha1 = build_id_data(note);
251bf215546Sopenharmony_ci   assert(id_sha1);
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_ci   char timestamp[41];
254bf215546Sopenharmony_ci   _mesa_sha1_format(timestamp, id_sha1);
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci   const uint64_t driver_flags =
257bf215546Sopenharmony_ci      brw_get_compiler_config_value(screen->compiler);
258bf215546Sopenharmony_ci   screen->disk_cache = disk_cache_create(renderer, timestamp, driver_flags);
259bf215546Sopenharmony_ci#endif
260bf215546Sopenharmony_ci}
261