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 \
#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);
compiler = NULL;
}
+ etna_disk_cache_init(compiler, renderer);
+
return compiler;
}
#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.
struct etna_compiler {
uint32_t shader_count;
struct ra_regs *regs;
+
+ struct disk_cache *disk_cache;
};
/* compiler output per input/output */
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;
};
struct etna_compiler *
-etna_compiler_create(void);
+etna_compiler_create(const char *renderer);
void
etna_compiler_destroy(const struct etna_compiler *compiler);
#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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
{"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
};
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)
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;
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;
#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"
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) {
goto fail;
}
- v->id = ++shader->variant_count;
+ etna_disk_cache_store(shader->compiler, v);
return v;
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
#define H_ETNAVIV_SHADER
#include "pipe/p_state.h"
+#include "util/disk_cache.h"
struct etna_context;
struct etna_shader_variant;
struct etna_compiler *compiler;
struct etna_shader_variant *variants;
+
+ cache_key cache_key; /* shader disk-cache key */
};
bool
'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',