--- /dev/null
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * 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
+ * on 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 AUTHOR(S) AND/OR THEIR SUPPLIERS 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.
+ */
+
+#include "util/u_live_shader_cache.h"
+
+#include "util/u_inlines.h"
+#include "tgsi/tgsi_from_mesa.h"
+#include "tgsi/tgsi_parse.h"
+
+#include "compiler/nir/nir_serialize.h"
+
+#include "util/blob.h"
+#include "util/hash_table.h"
+#include "util/mesa-sha1.h"
+
+static uint32_t key_hash(const void *key)
+{
+ /* Take the first dword of SHA1. */
+ return *(uint32_t*)key;
+}
+
+static bool key_equals(const void *a, const void *b)
+{
+ /* Compare SHA1s. */
+ return memcmp(a, b, 20) == 0;
+}
+
+void
+util_live_shader_cache_init(struct util_live_shader_cache *cache,
+ void *(*create_shader)(struct pipe_context *,
+ const struct pipe_shader_state *state),
+ void (*destroy_shader)(struct pipe_context *, void *))
+{
+ simple_mtx_init(&cache->lock, mtx_plain);
+ cache->hashtable = _mesa_hash_table_create(NULL, key_hash, key_equals);
+ cache->create_shader = create_shader;
+ cache->destroy_shader = destroy_shader;
+}
+
+void
+util_live_shader_cache_deinit(struct util_live_shader_cache *cache)
+{
+ if (cache->hashtable) {
+ /* The hash table should be empty at this point. */
+ _mesa_hash_table_destroy(cache->hashtable, NULL);
+ simple_mtx_destroy(&cache->lock);
+ }
+}
+
+void *
+util_live_shader_cache_get(struct pipe_context *ctx,
+ struct util_live_shader_cache *cache,
+ const struct pipe_shader_state *state)
+{
+ struct blob blob = {0};
+ unsigned ir_size;
+ const void *ir_binary;
+ enum pipe_shader_type stage;
+
+ /* Get the shader binary and shader stage. */
+ if (state->type == PIPE_SHADER_IR_TGSI) {
+ ir_binary = state->tokens;
+ ir_size = tgsi_num_tokens(state->tokens) *
+ sizeof(struct tgsi_token);
+ stage = tgsi_get_processor_type(state->tokens);
+ } else if (state->type == PIPE_SHADER_IR_NIR) {
+ blob_init(&blob);
+ nir_serialize(&blob, state->ir.nir, true);
+ ir_binary = blob.data;
+ ir_size = blob.size;
+ stage = pipe_shader_type_from_mesa(((nir_shader*)state->ir.nir)->info.stage);
+ } else {
+ assert(0);
+ return NULL;
+ }
+
+ /* Compute SHA1 of pipe_shader_state. */
+ struct mesa_sha1 sha1_ctx;
+ unsigned char sha1[20];
+ _mesa_sha1_init(&sha1_ctx);
+ _mesa_sha1_update(&sha1_ctx, ir_binary, ir_size);
+ if ((stage == PIPE_SHADER_VERTEX ||
+ stage == PIPE_SHADER_TESS_EVAL ||
+ stage == PIPE_SHADER_GEOMETRY) &&
+ state->stream_output.num_outputs) {
+ _mesa_sha1_update(&sha1_ctx, &state->stream_output,
+ sizeof(state->stream_output));
+ }
+ _mesa_sha1_final(&sha1_ctx, sha1);
+
+ if (ir_binary == blob.data)
+ blob_finish(&blob);
+
+ /* Find the shader in the live cache. */
+ simple_mtx_lock(&cache->lock);
+ struct hash_entry *entry = _mesa_hash_table_search(cache->hashtable, sha1);
+ struct util_live_shader *shader = entry ? entry->data : NULL;
+
+ /* Increase the refcount. */
+ if (shader)
+ pipe_reference(NULL, &shader->reference);
+ simple_mtx_unlock(&cache->lock);
+
+ /* Return if the shader already exists. */
+ if (shader)
+ return shader;
+
+ /* The cache mutex is unlocked to allow multiple create_shader
+ * invocations to run simultaneously.
+ */
+ shader = (struct util_live_shader*)cache->create_shader(ctx, state);
+ pipe_reference_init(&shader->reference, 1);
+ memcpy(shader->sha1, sha1, sizeof(sha1));
+
+ simple_mtx_lock(&cache->lock);
+ /* The same shader might have been created in parallel. This is rare.
+ * If so, keep the one already in cache.
+ */
+ struct hash_entry *entry2 = _mesa_hash_table_search(cache->hashtable, sha1);
+ struct util_live_shader *shader2 = entry2 ? entry2->data : NULL;
+
+ if (shader2) {
+ cache->destroy_shader(ctx, shader);
+ shader = shader2;
+ /* Increase the refcount. */
+ pipe_reference(NULL, &shader->reference);
+ } else {
+ _mesa_hash_table_insert(cache->hashtable, shader->sha1, shader);
+ }
+ simple_mtx_unlock(&cache->lock);
+
+ return shader;
+}
+
+void
+util_shader_reference(struct pipe_context *ctx,
+ struct util_live_shader_cache *cache,
+ void **dst, void *src)
+{
+ if (*dst == src)
+ return;
+
+ struct util_live_shader *dst_shader = (struct util_live_shader*)*dst;
+ struct util_live_shader *src_shader = (struct util_live_shader*)src;
+
+ simple_mtx_lock(&cache->lock);
+ bool destroy = pipe_reference(&dst_shader->reference, &src_shader->reference);
+ if (destroy) {
+ struct hash_entry *entry = _mesa_hash_table_search(cache->hashtable,
+ dst_shader->sha1);
+ assert(entry);
+ _mesa_hash_table_remove(cache->hashtable, entry);
+ }
+ simple_mtx_unlock(&cache->lock);
+
+ if (destroy)
+ cache->destroy_shader(ctx, dst_shader);
+
+ *dst = src;
+}
--- /dev/null
+/*
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * 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
+ * on 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 AUTHOR(S) AND/OR THEIR SUPPLIERS 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.
+ */
+
+/* This deduplicates live shader CSOs, meaning that creating 2 shaders with
+ * the same IR will return the same CSO.
+ *
+ * How to use this:
+ *
+ * - create_xx_state should only call util_live_shader_cache_get.
+ *
+ * - delete_xx_state should only call util_shader_reference(&shader, NULL).
+ * This will decrease the reference count.
+ *
+ * - Driver shaders must inherit util_live_shader. They don't have to
+ * initialize it.
+ *
+ * - Declare struct util_live_shader_cache in your pipe_screen (no pointer) if
+ * you support shareable shaders. If not, declare it in your pipe_context.
+ *
+ * - Set your create_shader and destroy_shader driver callbacks with
+ * util_live_shader_cache_init. These are your driver versions of
+ * create_xx_state and delete_xx_state. There is no distinction between
+ * vs, tcs, tes, gs, fs. Instead, get the shader type from the IR.
+ *
+ * - Call util_live_shader_cache_deinit when you destroy your screen or context.
+ */
+
+#ifndef U_LIVE_SHADER_CACHE_H
+#define U_LIVE_SHADER_CACHE_H
+
+#include "util/simple_mtx.h"
+#include "pipe/p_state.h"
+
+struct util_live_shader_cache {
+ simple_mtx_t lock;
+ struct hash_table *hashtable;
+
+ void *(*create_shader)(struct pipe_context *,
+ const struct pipe_shader_state *state);
+ void (*destroy_shader)(struct pipe_context *, void *);
+};
+
+struct util_live_shader {
+ struct pipe_reference reference;
+ unsigned char sha1[20];
+};
+
+void
+util_live_shader_cache_init(struct util_live_shader_cache *cache,
+ void *(*create_shader)(struct pipe_context *,
+ const struct pipe_shader_state *state),
+ void (*destroy_shader)(struct pipe_context *, void *));
+
+void
+util_live_shader_cache_deinit(struct util_live_shader_cache *cache);
+
+void *
+util_live_shader_cache_get(struct pipe_context *ctx,
+ struct util_live_shader_cache *cache,
+ const struct pipe_shader_state *state);
+
+void
+util_shader_reference(struct pipe_context *ctx,
+ struct util_live_shader_cache *cache,
+ void **dst, void *src);
+
+#endif