etnaviv: add disk cache
authorChristian Gmeiner <christian.gmeiner@gmail.com>
Sat, 8 Aug 2020 15:44:15 +0000 (17:44 +0200)
committerMarge Bot <eric+marge@anholt.net>
Fri, 18 Sep 2020 07:45:11 +0000 (07:45 +0000)
Adds a shader disk-cache for shader variants.  Note that builds with
`-Dshader-cache=false` have no-op stubs with `disk_cache_create()` that
returns NULL.

This shader disk-cache gets used when using NIR only. Helps to save
about 1-2 minutes for a deqp run on gc2000.

Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6669>

src/gallium/drivers/etnaviv/Makefile.sources
src/gallium/drivers/etnaviv/etnaviv_compiler.c
src/gallium/drivers/etnaviv/etnaviv_compiler.h
src/gallium/drivers/etnaviv/etnaviv_debug.h
src/gallium/drivers/etnaviv/etnaviv_disk_cache.c [new file with mode: 0644]
src/gallium/drivers/etnaviv/etnaviv_disk_cache.h [new file with mode: 0644]
src/gallium/drivers/etnaviv/etnaviv_screen.c
src/gallium/drivers/etnaviv/etnaviv_shader.c
src/gallium/drivers/etnaviv/etnaviv_shader.h
src/gallium/drivers/etnaviv/meson.build

index 32fcd79..3fc8607 100644 (file)
@@ -28,6 +28,8 @@ C_SOURCES :=  \
        etnaviv_debug.h \
        etnaviv_disasm.c \
        etnaviv_disasm.h \
+       etnaviv_disk_cache.c \
+       etnaviv_disk_cache.h \
        etnaviv_emit.c \
        etnaviv_emit.h \
        etnaviv_etc2.c \
index 0cb07c0..119a0e2 100644 (file)
 #include "etnaviv_compiler.h"
 #include "etnaviv_compiler_nir.h"
 #include "etnaviv_debug.h"
+#include "etnaviv_disk_cache.h"
 #include "util/ralloc.h"
 
 struct etna_compiler *
-etna_compiler_create(void)
+etna_compiler_create(const char *renderer)
 {
    struct etna_compiler *compiler = rzalloc(NULL, struct etna_compiler);
 
@@ -43,6 +44,8 @@ etna_compiler_create(void)
       compiler = NULL;
    }
 
+   etna_disk_cache_init(compiler, renderer);
+
    return compiler;
 }
 
index 8aaebab..734d5b1 100644 (file)
@@ -33,6 +33,7 @@
 #include "pipe/p_compiler.h"
 #include "pipe/p_shader_tokens.h"
 #include "compiler/shader_enums.h"
+#include "util/disk_cache.h"
 
 /* XXX some of these are pretty arbitrary limits, may be better to switch
  * to dynamic allocation at some point.
@@ -51,6 +52,8 @@
 struct etna_compiler {
    uint32_t shader_count;
    struct ra_regs *regs;
+
+   struct disk_cache *disk_cache;
 };
 
 /* compiler output per input/output */
@@ -84,14 +87,27 @@ struct etna_shader_variant {
 
    struct etna_bo *bo; /* cached code memory bo handle (for icache) */
 
+   /*
+    * Below here is serialized when written to disk cache:
+    */
+   uint32_t *code;
+   struct etna_shader_uniform_info uniforms;
+
+   /*
+    * The following macros are used by the shader disk cache save/
+    * restore paths to serialize/deserialize the variant.  Any
+    * pointers that require special handling in store_variant()
+    * and retrieve_variant() should go above here.
+    */
+#define VARIANT_CACHE_START    offsetof(struct etna_shader_variant, stage)
+#define VARIANT_CACHE_PTR(v)   (((char *)v) + VARIANT_CACHE_START)
+#define VARIANT_CACHE_SIZE     (sizeof(struct etna_shader_variant) - VARIANT_CACHE_START)
+
    gl_shader_stage stage;
    uint32_t code_size; /* code size in uint32 words */
-   uint32_t *code;
    unsigned num_loops;
    unsigned num_temps;
 
-   struct etna_shader_uniform_info uniforms;
-
    /* ETNA_DIRTY_* flags that, when set in context dirty, mean that the
     * uniforms have to get (partial) reloaded. */
    uint32_t uniforms_dirty_bits;
@@ -134,7 +150,7 @@ struct etna_shader_link_info {
 };
 
 struct etna_compiler *
-etna_compiler_create(void);
+etna_compiler_create(const char *renderer);
 
 void
 etna_compiler_destroy(const struct etna_compiler *compiler);
index 79e6eba..6070f2c 100644 (file)
@@ -55,6 +55,7 @@
 #define ETNA_DBG_NO_SINGLEBUF    0x1000000 /* disable single buffer feature */
 #define ETNA_DBG_NIR             0x2000000 /* use new NIR compiler */
 #define ETNA_DBG_DEQP            0x4000000 /* Hacks to run dEQP GLES3 tests */
+#define ETNA_DBG_NOCACHE         0x8000000 /* Disable shader cache */
 
 extern int etna_mesa_debug; /* set in etna_screen.c from ETNA_DEBUG */
 
diff --git a/src/gallium/drivers/etnaviv/etnaviv_disk_cache.c b/src/gallium/drivers/etnaviv/etnaviv_disk_cache.c
new file mode 100644 (file)
index 0000000..5b1844d
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2020 Google, Inc.
+ * Copyright (c) 2020 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "etnaviv_debug.h"
+#include "etnaviv_disk_cache.h"
+#include "nir_serialize.h"
+
+#define debug 0
+
+void
+etna_disk_cache_init(struct etna_compiler *compiler, const char *renderer)
+{
+   if (!(etna_mesa_debug & ETNA_DBG_NIR))
+      return;
+
+   if (etna_mesa_debug & ETNA_DBG_NOCACHE)
+      return;
+
+   const struct build_id_note *note =
+         build_id_find_nhdr_for_addr(etna_disk_cache_init);
+   assert(note && build_id_length(note) == 20); /* sha1 */
+
+   const uint8_t *id_sha1 = build_id_data(note);
+   assert(id_sha1);
+
+   char timestamp[41];
+   _mesa_sha1_format(timestamp, id_sha1);
+
+   compiler->disk_cache = disk_cache_create(renderer, timestamp, etna_mesa_debug);
+}
+
+void
+etna_disk_cache_init_shader_key(struct etna_compiler *compiler, struct etna_shader *shader)
+{
+   if (!compiler->disk_cache)
+      return;
+
+   struct mesa_sha1 ctx;
+
+   _mesa_sha1_init(&ctx);
+
+   /* Serialize the NIR to a binary blob that we can hash for the disk
+    * cache.  Drop unnecessary information (like variable names)
+    * so the serialized NIR is smaller, and also to let us detect more
+    * isomorphic shaders when hashing, increasing cache hits.
+    */
+   struct blob blob;
+
+   blob_init(&blob);
+   nir_serialize(&blob, shader->nir, true);
+   _mesa_sha1_update(&ctx, blob.data, blob.size);
+   blob_finish(&blob);
+
+   _mesa_sha1_final(&ctx, shader->cache_key);
+}
+
+static void
+compute_variant_key(struct etna_compiler *compiler, struct etna_shader_variant *v,
+                    cache_key cache_key)
+{
+   struct blob blob;
+
+   blob_init(&blob);
+
+   blob_write_bytes(&blob, &v->shader->cache_key, sizeof(v->shader->cache_key));
+   blob_write_bytes(&blob, &v->key, sizeof(v->key));
+
+   disk_cache_compute_key(compiler->disk_cache, blob.data, blob.size, cache_key);
+
+   blob_finish(&blob);
+}
+
+static void
+retrieve_variant(struct blob_reader *blob, struct etna_shader_variant *v)
+{
+   blob_copy_bytes(blob, VARIANT_CACHE_PTR(v), VARIANT_CACHE_SIZE);
+
+   v->code = malloc(4 * v->code_size);
+   blob_copy_bytes(blob, v->code, 4 * v->code_size);
+
+   blob_copy_bytes(blob, &v->uniforms.imm_count, sizeof(v->uniforms.imm_count));
+   v->uniforms.imm_contents = malloc(v->uniforms.imm_count * sizeof(v->uniforms.imm_contents));
+   v->uniforms.imm_data = malloc(v->uniforms.imm_count * sizeof(v->uniforms.imm_data));
+
+   blob_copy_bytes(blob, v->uniforms.imm_contents, v->uniforms.imm_count * sizeof(v->uniforms.imm_contents));
+   blob_copy_bytes(blob, v->uniforms.imm_data, v->uniforms.imm_count * sizeof(v->uniforms.imm_data));
+}
+
+static void
+store_variant(struct blob *blob, const struct etna_shader_variant *v)
+{
+   const uint32_t imm_count = v->uniforms.imm_count;
+
+   blob_write_bytes(blob, VARIANT_CACHE_PTR(v), VARIANT_CACHE_SIZE);
+   blob_write_bytes(blob, v->code, 4 * v->code_size);
+
+   blob_write_bytes(blob, &v->uniforms.imm_count, sizeof(v->uniforms.imm_count));
+   blob_write_bytes(blob, v->uniforms.imm_contents, imm_count * sizeof(v->uniforms.imm_contents));
+   blob_write_bytes(blob, v->uniforms.imm_data, imm_count * sizeof(v->uniforms.imm_data));
+}
+
+bool
+etna_disk_cache_retrieve(struct etna_compiler *compiler, struct etna_shader_variant *v)
+{
+   if (!compiler->disk_cache)
+      return false;
+
+   cache_key cache_key;
+
+   compute_variant_key(compiler, v, cache_key);
+
+   if (debug) {
+      char sha1[41];
+
+      _mesa_sha1_format(sha1, cache_key);
+      fprintf(stderr, "[mesa disk cache] retrieving variant %s: ", sha1);
+   }
+
+   size_t size;
+   void *buffer = disk_cache_get(compiler->disk_cache, cache_key, &size);
+
+   if (debug)
+      fprintf(stderr, "%s\n", buffer ? "found" : "missing");
+
+   if (!buffer)
+      return false;
+
+   struct blob_reader blob;
+   blob_reader_init(&blob, buffer, size);
+
+   retrieve_variant(&blob, v);
+
+   free(buffer);
+
+   return true;
+}
+
+void
+etna_disk_cache_store(struct etna_compiler *compiler, struct etna_shader_variant *v)
+{
+   if (!compiler->disk_cache)
+      return;
+
+   cache_key cache_key;
+
+   compute_variant_key(compiler, v, cache_key);
+
+   if (debug) {
+      char sha1[41];
+
+      _mesa_sha1_format(sha1, cache_key);
+      fprintf(stderr, "[mesa disk cache] storing variant %s\n", sha1);
+   }
+
+   struct blob blob;
+   blob_init(&blob);
+
+   store_variant(&blob, v);
+
+   disk_cache_put(compiler->disk_cache, cache_key, blob.data, blob.size, NULL);
+   blob_finish(&blob);
+}
diff --git a/src/gallium/drivers/etnaviv/etnaviv_disk_cache.h b/src/gallium/drivers/etnaviv/etnaviv_disk_cache.h
new file mode 100644 (file)
index 0000000..61fa302
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2020 Google, Inc.
+ * Copyright (c) 2020 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef H_ETNAVIV_DISK_CACHE
+#define H_ETNAVIV_DISK_CACHE
+
+#include "etnaviv_compiler.h"
+
+void
+etna_disk_cache_init(struct etna_compiler *compiler, const char *renderer);
+
+void
+etna_disk_cache_init_shader_key(struct etna_compiler *compiler, struct etna_shader *shader);
+
+bool
+etna_disk_cache_retrieve(struct etna_compiler *compiler, struct etna_shader_variant *v);
+
+void
+etna_disk_cache_store(struct etna_compiler *compiler, struct etna_shader_variant *v);
+
+#endif
index 2690898..1637eaa 100644 (file)
@@ -74,6 +74,7 @@ static const struct debug_named_value debug_options[] = {
    {"no_singlebuffer",ETNA_DBG_NO_SINGLEBUF, "Disable single buffer feature"},
    {"nir",            ETNA_DBG_NIR, "use new NIR compiler"},
    {"deqp",           ETNA_DBG_DEQP, "Hacks to run dEQP GLES3 tests"}, /* needs MESA_GLES_VERSION_OVERRIDE=3.0 */
+   {"nocache",        ETNA_DBG_NOCACHE,    "Disable shader cache"},
    DEBUG_NAMED_VALUE_END
 };
 
@@ -883,6 +884,15 @@ etna_get_compiler_options(struct pipe_screen *pscreen,
    return &etna_screen(pscreen)->options;
 }
 
+static struct disk_cache *
+etna_get_disk_shader_cache(struct pipe_screen *pscreen)
+{
+   struct etna_screen *screen = etna_screen(pscreen);
+   struct etna_compiler *compiler = screen->compiler;
+
+   return compiler->disk_cache;
+}
+
 struct pipe_screen *
 etna_screen_create(struct etna_device *dev, struct etna_gpu *gpu,
                    struct renderonly *ro)
@@ -1027,6 +1037,7 @@ etna_screen_create(struct etna_device *dev, struct etna_gpu *gpu,
    pscreen->get_paramf = etna_screen_get_paramf;
    pscreen->get_shader_param = etna_screen_get_shader_param;
    pscreen->get_compiler_options = etna_get_compiler_options;
+   pscreen->get_disk_shader_cache = etna_get_disk_shader_cache;
 
    pscreen->get_name = etna_screen_get_name;
    pscreen->get_vendor = etna_screen_get_vendor;
@@ -1037,7 +1048,7 @@ etna_screen_create(struct etna_device *dev, struct etna_gpu *gpu,
    pscreen->is_format_supported = etna_screen_is_format_supported;
    pscreen->query_dmabuf_modifiers = etna_screen_query_dmabuf_modifiers;
 
-   screen->compiler = etna_compiler_create();
+   screen->compiler = etna_compiler_create(etna_screen_get_name(pscreen));
    if (!screen->compiler)
       goto fail;
 
index b443544..47becee 100644 (file)
@@ -29,6 +29,7 @@
 #include "etnaviv_compiler.h"
 #include "etnaviv_context.h"
 #include "etnaviv_debug.h"
+#include "etnaviv_disk_cache.h"
 #include "etnaviv_screen.h"
 #include "etnaviv_util.h"
 
@@ -353,6 +354,10 @@ create_variant(struct etna_shader *shader, struct etna_shader_key key)
 
    v->shader = shader;
    v->key = key;
+   v->id = ++shader->variant_count;
+
+   if (etna_disk_cache_retrieve(shader->compiler, v))
+      return v;
 
    ret = etna_compile_shader(v);
    if (!ret) {
@@ -360,7 +365,7 @@ create_variant(struct etna_shader *shader, struct etna_shader_key key)
       goto fail;
    }
 
-   v->id = ++shader->variant_count;
+   etna_disk_cache_store(shader->compiler, v);
 
    return v;
 
@@ -412,7 +417,7 @@ etna_create_shader_state(struct pipe_context *pctx,
    else
       shader->tokens = tgsi_dup_tokens(pss->tokens);
 
-
+   etna_disk_cache_init_shader_key(compiler, shader);
 
    if (etna_mesa_debug & ETNA_DBG_SHADERDB) {
       /* if shader-db run, create a standard variant immediately
index c1ea785..ccba5c8 100644 (file)
@@ -28,6 +28,7 @@
 #define H_ETNAVIV_SHADER
 
 #include "pipe/p_state.h"
+#include "util/disk_cache.h"
 
 struct etna_context;
 struct etna_shader_variant;
@@ -69,6 +70,8 @@ struct etna_shader {
    struct etna_compiler *compiler;
 
    struct etna_shader_variant *variants;
+
+   cache_key cache_key;     /* shader disk-cache key */
 };
 
 bool
index 7b7ce26..8805fcc 100644 (file)
@@ -47,6 +47,8 @@ files_etnaviv = files(
   'etnaviv_debug.h',
   'etnaviv_disasm.c',
   'etnaviv_disasm.h',
+  'etnaviv_disk_cache.c',
+  'etnaviv_disk_cache.h',
   'etnaviv_emit.c',
   'etnaviv_emit.h',
   'etnaviv_etc2.c',