From: Marek Olšák Date: Sat, 30 Nov 2019 02:01:19 +0000 (-0500) Subject: gallium/util: add a cache of live shaders for shader CSO deduplication X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4bb919b0b8b4ed6f6a7049c3f8d294b74b50e198;p=mesa.git gallium/util: add a cache of live shaders for shader CSO deduplication Reviewed-by: Pierre-Eric Pelloux-Prayer Part-of: --- diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources index d31f71aaf99..2cba524f2bc 100644 --- a/src/gallium/auxiliary/Makefile.sources +++ b/src/gallium/auxiliary/Makefile.sources @@ -262,6 +262,8 @@ C_SOURCES := \ util/u_inlines.h \ util/u_linear.c \ util/u_linear.h \ + util/u_live_shader_cache.c \ + util/u_live_shader_cache.h \ util/u_log.c \ util/u_log.h \ util/u_network.c \ diff --git a/src/gallium/auxiliary/meson.build b/src/gallium/auxiliary/meson.build index d9e91216211..c36f3083739 100644 --- a/src/gallium/auxiliary/meson.build +++ b/src/gallium/auxiliary/meson.build @@ -282,6 +282,8 @@ files_libgallium = files( 'util/u_inlines.h', 'util/u_linear.c', 'util/u_linear.h', + 'util/u_live_shader_cache.c', + 'util/u_live_shader_cache.h', 'util/u_log.c', 'util/u_log.h', 'util/u_network.c', diff --git a/src/gallium/auxiliary/util/u_live_shader_cache.c b/src/gallium/auxiliary/util/u_live_shader_cache.c new file mode 100644 index 00000000000..9299348e0af --- /dev/null +++ b/src/gallium/auxiliary/util/u_live_shader_cache.c @@ -0,0 +1,181 @@ +/* + * 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; +} diff --git a/src/gallium/auxiliary/util/u_live_shader_cache.h b/src/gallium/auxiliary/util/u_live_shader_cache.h new file mode 100644 index 00000000000..9ee0803a1c5 --- /dev/null +++ b/src/gallium/auxiliary/util/u_live_shader_cache.h @@ -0,0 +1,88 @@ +/* + * 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