This uses the recently-added cache.c to write out a serialization of various state that's required in order to successfully load and use a binary written out by a drivers backend, this state is referred to as "metadata" throughout the implementation.
This patch is based on the initial work done by Carl. --- src/compiler/Makefile.sources | 4 +- src/compiler/glsl/shader_cache.cpp | 464 +++++++++++++++++++++++++++++++++++++ src/compiler/glsl/shader_cache.h | 38 +++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 src/compiler/glsl/shader_cache.cpp create mode 100644 src/compiler/glsl/shader_cache.h diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index 712b33a..46905ef 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -138,7 +138,9 @@ LIBGLSL_FILES = \ LIBGLSL_SHADER_CACHE_FILES = \ glsl/cache.c \ - glsl/cache.h + glsl/cache.h \ + glsl/shader_cache.cpp \ + glsl/shader_cache.h # glsl_compiler diff --git a/src/compiler/glsl/shader_cache.cpp b/src/compiler/glsl/shader_cache.cpp new file mode 100644 index 0000000..d0d0a2e --- /dev/null +++ b/src/compiler/glsl/shader_cache.cpp @@ -0,0 +1,464 @@ +/* + * Copyright © 2014 Intel Corporation + * + * 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. + */ + +/** + * \file shader_cache.c + * + * GLSL shader cache implementation + * + * This uses the generic cache in cache.c to implement a cache of linked + * shader programs. + */ + +#include "main/core.h" +#include "glsl_symbol_table.h" +#include "glsl_parser_extras.h" +#include "ir.h" +#include "program.h" +#include "linker.h" +#include "link_varyings.h" +#include "ir_optimization.h" +#include "ir_rvalue_visitor.h" +#include "ir_uniform.h" +#include "util/mesa-sha1.h" +#include "util/string_to_uint_map.h" +#include "blob.h" +#include "cache.h" + +extern "C" { +#include "main/shaderobj.h" +#include "main/enums.h" +#include "program/program.h" +} + +static void +encode_type_to_blob(struct blob *blob, const glsl_type *type) +{ + uint32_t encoding; + + switch (type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + encoding = (type->base_type << 24) | + (type->vector_elements << 4) | + (type->matrix_columns); + break; + case GLSL_TYPE_SAMPLER: + encoding = (type->base_type) << 24 | + (type->sampler_dimensionality << 4) | + (type->sampler_shadow << 3) | + (type->sampler_array << 2) | + (type->sampled_type); + break; + case GLSL_TYPE_IMAGE: + case GLSL_TYPE_ATOMIC_UINT: + case GLSL_TYPE_STRUCT: + case GLSL_TYPE_INTERFACE: + case GLSL_TYPE_ARRAY: + case GLSL_TYPE_VOID: + case GLSL_TYPE_ERROR: + default: + assert(!"Cannot encode type!"); + encoding = 0; + break; + } + + blob_write_uint32(blob, encoding); +} + +static const glsl_type * +decode_type_from_blob(struct blob_reader *blob) +{ + uint32_t u = blob_read_uint32(blob); + glsl_base_type base_type = (glsl_base_type) (u >> 24); + + switch (base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + return glsl_type::get_instance(base_type, (u >> 4) & 0x0f, u & 0x0f); + case GLSL_TYPE_SAMPLER: + return glsl_type::get_sampler_instance((enum glsl_sampler_dim) ((u >> 4) & 0x07), + (u >> 3) & 0x01, + (u >> 2) & 0x01, + (glsl_base_type) ((u >> 0) & 0x03)); + case GLSL_TYPE_IMAGE: + case GLSL_TYPE_ATOMIC_UINT: + case GLSL_TYPE_STRUCT: + case GLSL_TYPE_INTERFACE: + case GLSL_TYPE_ARRAY: + case GLSL_TYPE_VOID: + case GLSL_TYPE_ERROR: + default: + assert(!"Cannot decode type!"); + return NULL; + } +} + +static void +write_uniforms(struct blob *metadata, struct gl_shader_program *prog) +{ + uint32_t i; + + blob_write_uint32(metadata, prog->SamplersValidated); + blob_write_uint32(metadata, prog->NumUniformStorage); + blob_write_uint32(metadata, prog->NumUniformDataSlots); + + for (i = 0; i < prog->NumUniformStorage; i++) { + encode_type_to_blob(metadata, prog->UniformStorage[i].type); + blob_write_string(metadata, prog->UniformStorage[i].name); + blob_write_uint32(metadata, prog->UniformStorage[i].storage - + prog->UniformDataSlots); + blob_write_uint32(metadata, prog->UniformStorage[i].remap_location); + } +} + +static void +read_uniforms(struct blob_reader *metadata, struct gl_shader_program *prog) +{ + struct gl_uniform_storage *uniforms; + union gl_constant_value *data; + uint32_t i; + + prog->SamplersValidated = blob_read_uint32(metadata); + prog->NumUniformStorage = blob_read_uint32(metadata); + prog->NumUniformDataSlots = blob_read_uint32(metadata); + + uniforms = rzalloc_array(prog, struct gl_uniform_storage, + prog->NumUniformStorage); + prog->UniformStorage = uniforms; + + data = rzalloc_array(uniforms, union gl_constant_value, + prog->NumUniformDataSlots); + prog->UniformDataSlots = data; + + prog->UniformHash = new string_to_uint_map; + + for (i = 0; i < prog->NumUniformStorage; i++) { + uniforms[i].type = decode_type_from_blob(metadata); + uniforms[i].name = ralloc_strdup(prog, blob_read_string (metadata)); + uniforms[i].storage = data + blob_read_uint32(metadata); + uniforms[i].remap_location = blob_read_uint32(metadata); + uniforms[i].block_index = -1; + uniforms[i].atomic_buffer_index = -1; + prog->UniformHash->put(i, uniforms[i].name); + } +} + + +static void +write_uniform_remap_table(struct blob *metadata, + struct gl_shader_program *prog) +{ + blob_write_uint32(metadata, prog->NumUniformRemapTable); + + for (unsigned i = 0; i < prog->NumUniformRemapTable; i++) { + blob_write_uint32(metadata, + prog->UniformRemapTable[i] - prog->UniformStorage); + } +} + +static void +read_uniform_remap_table(struct blob_reader *metadata, + struct gl_shader_program *prog) +{ + unsigned i; + + prog->NumUniformRemapTable = blob_read_uint32(metadata); + + prog->UniformRemapTable =rzalloc_array(prog, struct gl_uniform_storage *, + prog->NumUniformRemapTable); + + for (i = 0; i < prog->NumUniformRemapTable; i++) { + prog->UniformRemapTable[i] = + prog->UniformStorage + blob_read_uint32(metadata); + } +} + +static void +write_shader_parameters(struct blob *metadata, + struct gl_program_parameter_list *params) +{ + unsigned i; + struct gl_program_parameter *param; + + blob_write_uint32(metadata, params->NumParameters); + + for (i = 0; i < params->NumParameters; i++) { + param = ¶ms->Parameters[i]; + + blob_write_uint32(metadata, param->Type); + blob_write_string(metadata, param->Name); + blob_write_uint32(metadata, param->Size); + blob_write_uint32(metadata, param->DataType); + blob_write_bytes(metadata, param->StateIndexes, + sizeof(param->StateIndexes)); + } + + blob_write_uint32(metadata, params->StateFlags); +} + +static void +read_shader_parameters(struct blob_reader *metadata, + struct gl_program_parameter_list *params) +{ + uint32_t i, num_parameters; + const char *name; + gl_register_file type; + GLuint size; + GLenum data_type; + gl_state_index state_indexes[STATE_LENGTH]; + + num_parameters = blob_read_uint32(metadata); + + for (i = 0; i < num_parameters; i++) { + + type = (gl_register_file) blob_read_uint32(metadata); + name = blob_read_string(metadata); + size = blob_read_uint32(metadata); + data_type = blob_read_uint32(metadata); + blob_copy_bytes(metadata, (uint8_t *) state_indexes, + sizeof(state_indexes)); + + _mesa_add_parameter(params, type, name, size, data_type, + NULL, state_indexes); + } + + params->StateFlags = blob_read_uint32(metadata); +} + +static void +write_shader_metadata(struct blob *metadata, gl_linked_shader *shader) +{ + struct gl_program *glprog; + + if (shader->Program) { + glprog = shader->Program; + /* Use the lowest bit to indicate that there is shader_metadata here. */ + blob_write_uint64(metadata, glprog->InputsRead << 1 | 1); + blob_write_uint64(metadata, glprog->OutputsWritten); + blob_write_bytes(metadata, glprog->TexturesUsed, + sizeof(glprog->TexturesUsed)); + blob_write_uint64(metadata, glprog->SamplersUsed); + blob_write_uint64(metadata, shader->num_samplers); + + write_shader_parameters(metadata, glprog->Parameters); + + } else { + /* An initial value of 0 indicates that this shader is not present. */ + blob_write_uint64(metadata, 0); + } +} + +static void +read_shader_metadata(struct blob_reader *metadata, + struct gl_program *glprog, + gl_linked_shader *linked) +{ + uint64_t has_shader; + + has_shader = blob_read_uint64(metadata); + + if (has_shader) { + glprog->InputsRead = has_shader >> 1; + glprog->OutputsWritten = blob_read_uint64(metadata); + blob_copy_bytes(metadata, (uint8_t *) glprog->TexturesUsed, + sizeof(glprog->TexturesUsed)); + glprog->SamplersUsed = blob_read_uint64(metadata); + linked->num_samplers = blob_read_uint64(metadata); + + glprog->Parameters = _mesa_new_parameter_list(); + + read_shader_parameters(metadata, glprog->Parameters); + + linked->Program = glprog; + } else { + linked->Program = NULL; + } +} + +static void +create_binding_str(const char *key, unsigned value, void *closure) +{ + char **bindings_str = (char **) closure; + ralloc_asprintf_append(bindings_str, "%s:%u,", key, value); +} + +static void +create_linked_shader_and_program(struct gl_context *ctx, + gl_shader_stage stage, + struct gl_shader_program *prog, + struct blob_reader *metadata) +{ + struct gl_program *glprog; + struct gl_linked_shader *linked; + + linked = ctx->Driver.NewShader(stage); + glprog = ctx->Driver.NewProgram(ctx, _mesa_shader_stage_to_program(stage), + prog->Name); + + read_shader_metadata(metadata, glprog, linked); + _mesa_reference_program(ctx, &linked->Program, glprog); + prog->_LinkedShaders[stage] = linked; +} + +void +shader_cache_write_program_metadata(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + struct blob *metadata; + char sha1_buf[41]; + struct program_cache *cache; + + cache = ctx->Cache; + if (!cache) + return; + + /* We should be able to serialize any valid combinations of shaders, but + * for now we only support vs and fs. + */ + if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] || + prog->_LinkedShaders[MESA_SHADER_TESS_EVAL] || + prog->_LinkedShaders[MESA_SHADER_TESS_CTRL] || + prog->_LinkedShaders[MESA_SHADER_COMPUTE]) + return; + + metadata = blob_create(NULL); + + write_uniforms(metadata, prog); + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (prog->_LinkedShaders[i]) + write_shader_metadata(metadata, prog->_LinkedShaders[i]); + } + + write_uniform_remap_table(metadata, prog); + + for (unsigned i = 0; i < prog->NumShaders; i++) { + cache_put_key(cache, prog->Shaders[i]->sha1); + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + fprintf(stderr, "marking shader: %s\n", + _mesa_sha1_format(sha1_buf, prog->Shaders[i]->sha1)); + } + } + + cache_put(cache, prog->sha1, metadata->data, metadata->size); + + ralloc_free(metadata); + + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + fprintf(stderr, "putting program metadata in cache: %s\n", + _mesa_sha1_format(sha1_buf, prog->sha1)); + } +} + +bool +shader_cache_read_program_metadata(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + const char *stage_name[] = { "vs", "tcs", "tes", "gs", "fs", "cs" }; + char *buf, sha1buf[41]; + unsigned char sha1[20]; + uint8_t *buffer; + struct program_cache *cache; + size_t size; + struct blob_reader metadata; + + cache = ctx->Cache; + if (!cache) + return false; + + + /* Include bindings when creating sha1. These bindings change the resulting + * binary so they are just as important as the shader source. + */ + char *bindings_str = ralloc_strdup(NULL, "vb: "); + prog->AttributeBindings->iterate(create_binding_str, &bindings_str); + ralloc_strcat(&bindings_str, "fb: "); + prog->FragDataBindings->iterate(create_binding_str, &bindings_str); + ralloc_strcat(&bindings_str, "fbi: "); + prog->FragDataIndexBindings->iterate(create_binding_str, &bindings_str); + _mesa_sha1_compute(bindings_str, strlen(bindings_str), sha1); + ralloc_free(bindings_str); + + buf = ralloc_strdup(NULL, "bindings: "); + ralloc_asprintf_append(&buf, "%s\n", _mesa_sha1_format(sha1buf, sha1)); + + for (unsigned i = 0; i < prog->NumShaders; i++) { + ralloc_asprintf_append(&buf, "%s: %s\n", + stage_name[prog->Shaders[i]->Stage], + _mesa_sha1_format(sha1buf, + prog->Shaders[i]->sha1)); + } + _mesa_sha1_compute(buf, strlen(buf), prog->sha1); + ralloc_free(buf); + + buffer = (uint8_t *) cache_get(cache, prog->sha1, &size); + if (buffer == NULL) { + /* FIXME: Fall back and link shaders here, if necessary, compile any + * shaders we didn't compile earlier. + */ + return false; + } + + if (ctx->_Shader->Flags & GLSL_CACHE_INFO) { + fprintf(stderr, "loading shader program meta data from cache: %s\n", + _mesa_sha1_format(sha1buf, prog->sha1)); + } + + blob_reader_init(&metadata, buffer, size); + + assert(prog->UniformStorage == NULL); + + read_uniforms(&metadata, prog); + + /* Find stages used in the program */ + bool linked_stages[MESA_SHADER_STAGES] = { 0 }; + for (unsigned i = 0; i < prog->NumShaders; i++) { + linked_stages[prog->Shaders[i]->Stage] = true; + } + + for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) { + if (linked_stages[i]) { + create_linked_shader_and_program(ctx, (gl_shader_stage) i, + prog, &metadata); + } + } + + read_uniform_remap_table(&metadata, prog); + + if (metadata.current != metadata.end || metadata.overrun) { + /* FIXME: At this point, we should discard the item from the cache and + * rebuild from source. + */ + } + + prog->LinkStatus = true; + + free (buffer); + + return true; +} diff --git a/src/compiler/glsl/shader_cache.h b/src/compiler/glsl/shader_cache.h new file mode 100644 index 0000000..ffcf4f8 --- /dev/null +++ b/src/compiler/glsl/shader_cache.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2014 Intel Corporation + * + * 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. + */ + +#pragma once +#ifndef SHADER_CACHE +#define SHADER_CACHE + +#include "cache.h" + +void +shader_cache_write_program_metadata(struct gl_context *ctx, + struct gl_shader_program *prog); + +bool +shader_cache_read_program_metadata(struct gl_context *ctx, + struct gl_shader_program *prog); + +#endif /* GLSL_SYMBOL_TABLE */ -- 2.7.4 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev