From 9f506d817ee38e421d7ff3f32a28808c685f66da Mon Sep 17 00:00:00 2001 From: Timothy Arceri Date: Wed, 1 Feb 2017 15:52:27 +1100 Subject: [PATCH] st/mesa: implement a tgsi on-disk shader cache MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Implements a tgsi cache for the OpenGL state tracker. V2: add support for compute shaders Reviewed-by: Nicolai Hähnle --- src/mesa/Makefile.sources | 2 + src/mesa/state_tracker/st_glsl_to_tgsi.cpp | 6 + src/mesa/state_tracker/st_program.c | 27 +- src/mesa/state_tracker/st_shader_cache.c | 406 +++++++++++++++++++++ src/mesa/state_tracker/st_shader_cache.h | 46 +++ 5 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 src/mesa/state_tracker/st_shader_cache.c create mode 100644 src/mesa/state_tracker/st_shader_cache.h diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources index ae701138f5d..17f84fd0b61 100644 --- a/src/mesa/Makefile.sources +++ b/src/mesa/Makefile.sources @@ -516,6 +516,8 @@ STATETRACKER_FILES = \ state_tracker/st_sampler_view.h \ state_tracker/st_scissor.c \ state_tracker/st_scissor.h \ + state_tracker/st_shader_cache.c \ + state_tracker/st_shader_cache.h \ state_tracker/st_texture.c \ state_tracker/st_texture.h \ state_tracker/st_tgsi_lower_yuv.c \ diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp index 476d18534d9..d43d821aad0 100644 --- a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp +++ b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp @@ -54,6 +54,7 @@ #include "st_format.h" #include "st_glsl_types.h" #include "st_nir.h" +#include "st_shader_cache.h" #include @@ -6870,6 +6871,11 @@ extern "C" { GLboolean st_link_shader(struct gl_context *ctx, struct gl_shader_program *prog) { + /* Return early if we are loading the shader from on-disk cache */ + if (st_load_tgsi_from_disk_cache(ctx, prog)) { + return GL_TRUE; + } + struct pipe_screen *pscreen = ctx->st->pipe->screen; assert(prog->data->LinkStatus); diff --git a/src/mesa/state_tracker/st_program.c b/src/mesa/state_tracker/st_program.c index 3795f25a74e..4d9250b73cc 100644 --- a/src/mesa/state_tracker/st_program.c +++ b/src/mesa/state_tracker/st_program.c @@ -58,6 +58,7 @@ #include "st_mesa_to_tgsi.h" #include "st_atifs_to_tgsi.h" #include "st_nir.h" +#include "st_shader_cache.h" #include "cso_cache/cso_context.h" @@ -364,7 +365,6 @@ st_release_cp_variants(struct st_context *st, struct st_compute_program *stcp) } } - /** * Translate a vertex program. */ @@ -583,7 +583,6 @@ st_translate_vertex_program(struct st_context *st, &stvp->tgsi.stream_output); free_glsl_to_tgsi_visitor(stvp->glsl_to_tgsi); - stvp->glsl_to_tgsi = NULL; } else error = st_translate_mesa_program(st->ctx, PIPE_SHADER_VERTEX, @@ -608,8 +607,15 @@ st_translate_vertex_program(struct st_context *st, return false; } - stvp->tgsi.tokens = ureg_get_tokens(ureg, NULL); + unsigned num_tokens; + stvp->tgsi.tokens = ureg_get_tokens(ureg, &num_tokens); ureg_destroy(ureg); + + if (stvp->glsl_to_tgsi) { + stvp->glsl_to_tgsi = NULL; + st_store_tgsi_in_disk_cache(st, &stvp->Base, NULL, num_tokens); + } + return stvp->tgsi.tokens != NULL; } @@ -1031,7 +1037,6 @@ st_translate_fragment_program(struct st_context *st, fs_output_semantic_index); free_glsl_to_tgsi_visitor(stfp->glsl_to_tgsi); - stfp->glsl_to_tgsi = NULL; } else if (stfp->ati_fs) st_translate_atifs_program(ureg, stfp->ati_fs, @@ -1064,8 +1069,15 @@ st_translate_fragment_program(struct st_context *st, fs_output_semantic_name, fs_output_semantic_index); - stfp->tgsi.tokens = ureg_get_tokens(ureg, NULL); + unsigned num_tokens; + stfp->tgsi.tokens = ureg_get_tokens(ureg, &num_tokens); ureg_destroy(ureg); + + if (stfp->glsl_to_tgsi) { + stfp->glsl_to_tgsi = NULL; + st_store_tgsi_in_disk_cache(st, &stfp->Base, NULL, num_tokens); + } + return stfp->tgsi.tokens != NULL; } @@ -1600,13 +1612,16 @@ st_translate_program_common(struct st_context *st, output_semantic_name, output_semantic_index); - out_state->tokens = ureg_get_tokens(ureg, NULL); + unsigned num_tokens; + out_state->tokens = ureg_get_tokens(ureg, &num_tokens); ureg_destroy(ureg); st_translate_stream_output_info(glsl_to_tgsi, outputMapping, &out_state->stream_output); + st_store_tgsi_in_disk_cache(st, prog, out_state, num_tokens); + if ((ST_DEBUG & DEBUG_TGSI) && (ST_DEBUG & DEBUG_MESA)) { _mesa_print_program(prog); debug_printf("\n"); diff --git a/src/mesa/state_tracker/st_shader_cache.c b/src/mesa/state_tracker/st_shader_cache.c new file mode 100644 index 00000000000..607e5b10766 --- /dev/null +++ b/src/mesa/state_tracker/st_shader_cache.c @@ -0,0 +1,406 @@ +/* + * Copyright © 2017 Timothy Arceri + * + * 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, sublicense, + * 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 NONINFRINGEMENT. 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. + */ + +#include + +#include "st_program.h" +#include "st_shader_cache.h" +#include "compiler/glsl/program.h" +#include "pipe/p_shader_tokens.h" +#include "program/ir_to_mesa.h" +#include "util/u_memory.h" + +static void +write_stream_out_to_cache(struct blob *blob, + struct pipe_shader_state *tgsi) +{ + blob_write_bytes(blob, &tgsi->stream_output, + sizeof(tgsi->stream_output)); +} + +static void +write_tgsi_to_cache(struct blob *blob, struct pipe_shader_state *tgsi, + struct st_context *st, unsigned char *sha1, + unsigned num_tokens) +{ + blob_write_uint32(blob, num_tokens); + blob_write_bytes(blob, tgsi->tokens, + num_tokens * sizeof(struct tgsi_token)); + + disk_cache_put(st->ctx->Cache, sha1, blob->data, blob->size); +} + +/** + * Store tgsi and any other required state in on-disk shader cache. + */ +void +st_store_tgsi_in_disk_cache(struct st_context *st, struct gl_program *prog, + struct pipe_shader_state *out_state, + unsigned num_tokens) +{ + if (!st->ctx->Cache) + return; + + /* Exit early when we are dealing with a ff shader with no source file to + * generate a source from. + */ + if (*prog->sh.data->sha1 == 0) + return; + + unsigned char *sha1; + struct blob *blob = blob_create(NULL); + + switch (prog->info.stage) { + case MESA_SHADER_VERTEX: { + struct st_vertex_program *stvp = (struct st_vertex_program *) prog; + sha1 = stvp->sha1; + + blob_write_uint32(blob, stvp->num_inputs); + blob_write_bytes(blob, stvp->index_to_input, + sizeof(stvp->index_to_input)); + blob_write_bytes(blob, stvp->result_to_output, + sizeof(stvp->result_to_output)); + + write_stream_out_to_cache(blob, &stvp->tgsi); + write_tgsi_to_cache(blob, &stvp->tgsi, st, sha1, num_tokens); + break; + } + case MESA_SHADER_TESS_CTRL: { + struct st_tessctrl_program *stcp = (struct st_tessctrl_program *) prog; + sha1 = stcp->sha1; + + write_stream_out_to_cache(blob, out_state); + write_tgsi_to_cache(blob, out_state, st, sha1, num_tokens); + break; + } + case MESA_SHADER_TESS_EVAL: { + struct st_tesseval_program *step = (struct st_tesseval_program *) prog; + sha1 = step->sha1; + + write_stream_out_to_cache(blob, out_state); + write_tgsi_to_cache(blob, out_state, st, sha1, num_tokens); + break; + } + case MESA_SHADER_GEOMETRY: { + struct st_geometry_program *stgp = (struct st_geometry_program *) prog; + sha1 = stgp->sha1; + + write_stream_out_to_cache(blob, out_state); + write_tgsi_to_cache(blob, out_state, st, sha1, num_tokens); + break; + } + case MESA_SHADER_FRAGMENT: { + struct st_fragment_program *stfp = (struct st_fragment_program *) prog; + sha1 = stfp->sha1; + + write_tgsi_to_cache(blob, &stfp->tgsi, st, sha1, num_tokens); + break; + } + case MESA_SHADER_COMPUTE: { + struct st_compute_program *stcp = (struct st_compute_program *) prog; + sha1 = stcp->sha1; + + write_tgsi_to_cache(blob, out_state, st, sha1, num_tokens); + break; + } + default: + unreachable("Unsupported stage"); + } + + if (st->ctx->_Shader->Flags & GLSL_CACHE_INFO) { + char sha1_buf[41]; + _mesa_sha1_format(sha1_buf, sha1); + fprintf(stderr, "putting %s tgsi_tokens in cache: %s\n", + _mesa_shader_stage_to_string(prog->info.stage), sha1_buf); + } + + ralloc_free(blob); +} + +static void +read_stream_out_from_cache(struct blob_reader *blob_reader, + struct pipe_shader_state *tgsi) +{ + blob_copy_bytes(blob_reader, (uint8_t *) &tgsi->stream_output, + sizeof(tgsi->stream_output)); +} + +static void +read_tgsi_from_cache(struct blob_reader *blob_reader, + const struct tgsi_token **tokens) +{ + uint32_t num_tokens = blob_read_uint32(blob_reader); + unsigned tokens_size = num_tokens * sizeof(struct tgsi_token); + *tokens = (const struct tgsi_token*) MALLOC(tokens_size); + blob_copy_bytes(blob_reader, (uint8_t *) *tokens, tokens_size); +} + +bool +st_load_tgsi_from_disk_cache(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + if (!ctx->Cache) + return false; + + unsigned char *stage_sha1[MESA_SHADER_STAGES]; + char sha1_buf[41]; + + /* Compute and store sha1 for each stage. These will be reused by the + * cache store pass if we fail to find the cached tgsi. + */ + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + char *buf = ralloc_strdup(NULL, "tgsi_tokens "); + _mesa_sha1_format(sha1_buf, + prog->_LinkedShaders[i]->Program->sh.data->sha1); + ralloc_strcat(&buf, sha1_buf); + + struct gl_program *glprog = prog->_LinkedShaders[i]->Program; + switch (glprog->info.stage) { + case MESA_SHADER_VERTEX: { + struct st_vertex_program *stvp = (struct st_vertex_program *) glprog; + stage_sha1[i] = stvp->sha1; + ralloc_strcat(&buf, " vs"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + case MESA_SHADER_TESS_CTRL: { + struct st_tessctrl_program *stcp = + (struct st_tessctrl_program *) glprog; + stage_sha1[i] = stcp->sha1; + ralloc_strcat(&buf, " tcs"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + case MESA_SHADER_TESS_EVAL: { + struct st_tesseval_program *step = + (struct st_tesseval_program *) glprog; + stage_sha1[i] = step->sha1; + ralloc_strcat(&buf, " tes"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + case MESA_SHADER_GEOMETRY: { + struct st_geometry_program *stgp = + (struct st_geometry_program *) glprog; + stage_sha1[i] = stgp->sha1; + ralloc_strcat(&buf, " gs"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + case MESA_SHADER_FRAGMENT: { + struct st_fragment_program *stfp = + (struct st_fragment_program *) glprog; + stage_sha1[i] = stfp->sha1; + ralloc_strcat(&buf, " fs"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + case MESA_SHADER_COMPUTE: { + struct st_compute_program *stcp = + (struct st_compute_program *) glprog; + stage_sha1[i] = stcp->sha1; + ralloc_strcat(&buf, " cs"); + _mesa_sha1_compute(buf, strlen(buf), stage_sha1[i]); + break; + } + default: + unreachable("Unsupported stage"); + } + + ralloc_free(buf); + } + + struct st_context *st = st_context(ctx); + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + unsigned char *sha1 = stage_sha1[i]; + size_t size; + uint8_t *buffer = (uint8_t *) disk_cache_get(ctx->Cache, sha1, &size); + if (buffer) { + struct blob_reader blob_reader; + blob_reader_init(&blob_reader, buffer, size); + + struct gl_program *glprog = prog->_LinkedShaders[i]->Program; + switch (glprog->info.stage) { + case MESA_SHADER_VERTEX: { + struct st_vertex_program *stvp = + (struct st_vertex_program *) glprog; + + st_release_vp_variants(st, stvp); + + stvp->num_inputs = blob_read_uint32(&blob_reader); + blob_copy_bytes(&blob_reader, (uint8_t *) stvp->index_to_input, + sizeof(stvp->index_to_input)); + blob_copy_bytes(&blob_reader, (uint8_t *) stvp->result_to_output, + sizeof(stvp->result_to_output)); + + read_stream_out_from_cache(&blob_reader, &stvp->tgsi); + read_tgsi_from_cache(&blob_reader, &stvp->tgsi.tokens); + + if (st->vp == stvp) + st->dirty |= ST_NEW_VERTEX_PROGRAM(st, stvp); + + break; + } + case MESA_SHADER_TESS_CTRL: { + struct st_tessctrl_program *sttcp = + (struct st_tessctrl_program *) glprog; + + st_release_basic_variants(st, sttcp->Base.Target, + &sttcp->variants, &sttcp->tgsi); + + read_stream_out_from_cache(&blob_reader, &sttcp->tgsi); + read_tgsi_from_cache(&blob_reader, &sttcp->tgsi.tokens); + + if (st->tcp == sttcp) + st->dirty |= sttcp->affected_states; + + break; + } + case MESA_SHADER_TESS_EVAL: { + struct st_tesseval_program *sttep = + (struct st_tesseval_program *) glprog; + + st_release_basic_variants(st, sttep->Base.Target, + &sttep->variants, &sttep->tgsi); + + read_stream_out_from_cache(&blob_reader, &sttep->tgsi); + read_tgsi_from_cache(&blob_reader, &sttep->tgsi.tokens); + + if (st->tep == sttep) + st->dirty |= sttep->affected_states; + + break; + } + case MESA_SHADER_GEOMETRY: { + struct st_geometry_program *stgp = + (struct st_geometry_program *) glprog; + + st_release_basic_variants(st, stgp->Base.Target, &stgp->variants, + &stgp->tgsi); + + read_stream_out_from_cache(&blob_reader, &stgp->tgsi); + read_tgsi_from_cache(&blob_reader, &stgp->tgsi.tokens); + + if (st->gp == stgp) + st->dirty |= stgp->affected_states; + + break; + } + case MESA_SHADER_FRAGMENT: { + struct st_fragment_program *stfp = + (struct st_fragment_program *) glprog; + + st_release_fp_variants(st, stfp); + + read_tgsi_from_cache(&blob_reader, &stfp->tgsi.tokens); + + if (st->fp == stfp) + st->dirty |= stfp->affected_states; + + break; + } + case MESA_SHADER_COMPUTE: { + struct st_compute_program *stcp = + (struct st_compute_program *) glprog; + + st_release_cp_variants(st, stcp); + + read_tgsi_from_cache(&blob_reader, + (const struct tgsi_token**) &stcp->tgsi.prog); + + stcp->tgsi.req_local_mem = stcp->Base.info.cs.shared_size; + stcp->tgsi.req_private_mem = 0; + stcp->tgsi.req_input_mem = 0; + + if (st->cp == stcp) + st->dirty |= stcp->affected_states; + + break; + } + default: + unreachable("Unsupported stage"); + } + + if (blob_reader.current != blob_reader.end || blob_reader.overrun) { + /* Something very bad has gone wrong discard the item from the + * cache and rebuild/link from source. + */ + assert(!"Invalid TGSI shader disk cache item!"); + + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + fprintf(stderr, "Error reading program from cache (invalid " + "TGSI cache item)\n"); + } + + disk_cache_remove(ctx->Cache, sha1); + + goto fallback_recompile; + } + + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + _mesa_sha1_format(sha1_buf, sha1); + fprintf(stderr, "%s tgsi_tokens retrieved from cache: %s\n", + _mesa_shader_stage_to_string(i), sha1_buf); + } + + st_set_prog_affected_state_flags(glprog); + _mesa_associate_uniform_storage(ctx, prog, glprog->Parameters); + + free(buffer); + } else { + /* Failed to find a matching cached shader so fallback to recompile. + */ + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + fprintf(stderr, "TGSI cache item not found falling back to " + "compile.\n"); + } + + goto fallback_recompile; + } + } + + return true; + +fallback_recompile: + + /* GLSL IR was compiled and linked so just fallback to the regular + * glsl to tgsi path. + */ + if (prog->data->LinkStatus != linking_skipped) + return false; + + for (unsigned i = 0; i < prog->NumShaders; i++) { + _mesa_glsl_compile_shader(ctx, prog->Shaders[i], false, false, true); + } + + prog->data->cache_fallback = true; + _mesa_glsl_link_shader(ctx, prog); + + return true; +} diff --git a/src/mesa/state_tracker/st_shader_cache.h b/src/mesa/state_tracker/st_shader_cache.h new file mode 100644 index 00000000000..f9e46158dc0 --- /dev/null +++ b/src/mesa/state_tracker/st_shader_cache.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2017 Timothy Arceri + * + * 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, sublicense, + * 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 NONINFRINGEMENT. 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. + */ + +#include "st_context.h" +#include "compiler/glsl/blob.h" +#include "main/mtypes.h" +#include "pipe/p_state.h" +#include "util/disk_cache.h" +#include "util/mesa-sha1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +st_load_tgsi_from_disk_cache(struct gl_context *ctx, + struct gl_shader_program *prog); + +void +st_store_tgsi_in_disk_cache(struct st_context *st, struct gl_program *prog, + struct pipe_shader_state *out_state, + unsigned num_tokens); + +#ifdef __cplusplus +} +#endif -- 2.30.2