/************************************************************************** * * Copyright 2009 VMware, 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 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 VMWARE AND/OR ITS 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 "shaders_cache.h" #include "vg_context.h" #include "pipe/p_context.h" #include "pipe/p_defines.h" #include "util/u_inlines.h" #include "pipe/p_screen.h" #include "pipe/p_shader_tokens.h" #include "tgsi/tgsi_build.h" #include "tgsi/tgsi_dump.h" #include "tgsi/tgsi_parse.h" #include "tgsi/tgsi_util.h" #include "tgsi/tgsi_text.h" #include "util/u_memory.h" #include "util/u_math.h" #include "util/u_debug.h" #include "cso_cache/cso_hash.h" #include "cso_cache/cso_context.h" #include "VG/openvg.h" #include "asm_fill.h" /* Essentially we construct an ubber-shader based on the state * of the pipeline. The stages are: * 1) Fill (mandatory, solid color/gradient/pattern/image draw) * 2) Image composition (image mode multiply and stencil) * 3) Mask * 4) Extended blend (multiply/screen/darken/lighten) * 5) Premultiply/Unpremultiply * 6) Color transform (to black and white) */ #define SHADER_STAGES 6 struct cached_shader { void *driver_shader; struct pipe_shader_state state; }; struct shaders_cache { struct vg_context *pipe; struct cso_hash *hash; }; static INLINE struct tgsi_token *tokens_from_assembly(const char *txt, int num_tokens) { struct tgsi_token *tokens; tokens = (struct tgsi_token *) MALLOC(num_tokens * sizeof(tokens[0])); tgsi_text_translate(txt, tokens, num_tokens); #if DEBUG_SHADERS tgsi_dump(tokens, 0); #endif return tokens; } #define ALL_FILLS (VEGA_SOLID_FILL_SHADER | \ VEGA_LINEAR_GRADIENT_SHADER | \ VEGA_RADIAL_GRADIENT_SHADER | \ VEGA_PATTERN_SHADER | \ VEGA_IMAGE_NORMAL_SHADER) /* static const char max_shader_preamble[] = "FRAG\n" "DCL IN[0], POSITION, LINEAR\n" "DCL IN[1], GENERIC[0], PERSPECTIVE\n" "DCL OUT[0], COLOR, CONSTANT\n" "DCL CONST[0..9], CONSTANT\n" "DCL TEMP[0..9], CONSTANT\n" "DCL SAMP[0..9], CONSTANT\n"; max_shader_preamble strlen == 175 */ #define MAX_PREAMBLE 175 static INLINE VGint range_min(VGint min, VGint current) { if (min < 0) min = current; else min = MIN2(min, current); return min; } static INLINE VGint range_max(VGint max, VGint current) { return MAX2(max, current); } static void * combine_shaders(const struct shader_asm_info *shaders[SHADER_STAGES], int num_shaders, struct pipe_context *pipe, struct pipe_shader_state *shader) { VGboolean declare_input = VG_FALSE; VGint start_const = -1, end_const = 0; VGint start_temp = -1, end_temp = 0; VGint start_sampler = -1, end_sampler = 0; VGint i, current_shader = 0; VGint num_consts, num_temps, num_samplers; struct ureg_program *ureg; struct ureg_src in[2]; struct ureg_src *sampler = NULL; struct ureg_src *constant = NULL; struct ureg_dst out, *temp = NULL; void *p = NULL; for (i = 0; i < num_shaders; ++i) { if (shaders[i]->num_consts) start_const = range_min(start_const, shaders[i]->start_const); if (shaders[i]->num_temps) start_temp = range_min(start_temp, shaders[i]->start_temp); if (shaders[i]->num_samplers) start_sampler = range_min(start_sampler, shaders[i]->start_sampler); end_const = range_max(end_const, shaders[i]->start_const + shaders[i]->num_consts); end_temp = range_max(end_temp, shaders[i]->start_temp + shaders[i]->num_temps); end_sampler = range_max(end_sampler, shaders[i]->start_sampler + shaders[i]->num_samplers); if (shaders[i]->needs_position) declare_input = VG_TRUE; } /* if they're still unitialized, initialize them */ if (start_const < 0) start_const = 0; if (start_temp < 0) start_temp = 0; if (start_sampler < 0) start_sampler = 0; num_consts = end_const - start_const; num_temps = end_temp - start_temp; num_samplers = end_sampler - start_sampler; ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT); if (!ureg) return NULL; if (declare_input) { in[0] = ureg_DECL_fs_input(ureg, TGSI_SEMANTIC_POSITION, 0, TGSI_INTERPOLATE_LINEAR); in[1] = ureg_DECL_fs_input(ureg, TGSI_SEMANTIC_GENERIC, 0, TGSI_INTERPOLATE_PERSPECTIVE); } /* we always have a color output */ out = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0); if (num_consts >= 1) { constant = (struct ureg_src *) malloc(sizeof(struct ureg_src) * end_const); for (i = start_const; i < end_const; i++) { constant[i] = ureg_DECL_constant(ureg, i); } } if (num_temps >= 1) { temp = (struct ureg_dst *) malloc(sizeof(struct ureg_dst) * end_temp); for (i = start_temp; i < end_temp; i++) { temp[i] = ureg_DECL_temporary(ureg); } } if (num_samplers >= 1) { sampler = (struct ureg_src *) malloc(sizeof(struct ureg_src) * end_sampler); for (i = start_sampler; i < end_sampler; i++) { sampler[i] = ureg_DECL_sampler(ureg, i); } } while (current_shader < num_shaders) { if ((current_shader + 1) == num_shaders) { shaders[current_shader]->func(ureg, &out, in, sampler, temp, constant); } else { shaders[current_shader]->func(ureg, &temp[0], in, sampler, temp, constant); } current_shader++; } ureg_END(ureg); shader->tokens = ureg_finalize(ureg); if(!shader->tokens) return NULL; p = pipe->create_fs_state(pipe, shader); ureg_destroy(ureg); if (num_temps >= 1) { for (i = start_temp; i < end_temp; i++) { ureg_release_temporary(ureg, temp[i]); } } if (temp) free(temp); if (constant) free(constant); if (sampler) free(sampler); return p; } static void * create_shader(struct pipe_context *pipe, int id, struct pipe_shader_state *shader) { int idx = 0; const struct shader_asm_info * shaders[SHADER_STAGES]; /* the shader has to have a fill */ debug_assert(id & ALL_FILLS); /* first stage */ if (id & VEGA_SOLID_FILL_SHADER) { debug_assert(idx == 0); shaders[idx] = &shaders_asm[0]; debug_assert(shaders_asm[0].id == VEGA_SOLID_FILL_SHADER); ++idx; } if ((id & VEGA_LINEAR_GRADIENT_SHADER)) { debug_assert(idx == 0); shaders[idx] = &shaders_asm[1]; debug_assert(shaders_asm[1].id == VEGA_LINEAR_GRADIENT_SHADER); ++idx; } if ((id & VEGA_RADIAL_GRADIENT_SHADER)) { debug_assert(idx == 0); shaders[idx] = &shaders_asm[2]; debug_assert(shaders_asm[2].id == VEGA_RADIAL_GRADIENT_SHADER); ++idx; } if ((id & VEGA_PATTERN_SHADER)) { debug_assert(idx == 0); debug_assert(shaders_asm[3].id == VEGA_PATTERN_SHADER); shaders[idx] = &shaders_asm[3]; ++idx; } if ((id & VEGA_IMAGE_NORMAL_SHADER)) { debug_assert(idx == 0); debug_assert(shaders_asm[4].id == VEGA_IMAGE_NORMAL_SHADER); shaders[idx] = &shaders_asm[4]; ++idx; } /* second stage */ if ((id & VEGA_IMAGE_MULTIPLY_SHADER)) { debug_assert(shaders_asm[5].id == VEGA_IMAGE_MULTIPLY_SHADER); shaders[idx] = &shaders_asm[5]; ++idx; } else if ((id & VEGA_IMAGE_STENCIL_SHADER)) { debug_assert(shaders_asm[6].id == VEGA_IMAGE_STENCIL_SHADER); shaders[idx] = &shaders_asm[6]; ++idx; } /* third stage */ if ((id & VEGA_MASK_SHADER)) { debug_assert(idx == 1); debug_assert(shaders_asm[7].id == VEGA_MASK_SHADER); shaders[idx] = &shaders_asm[7]; ++idx; } /* fourth stage */ if ((id & VEGA_BLEND_MULTIPLY_SHADER)) { debug_assert(shaders_asm[8].id == VEGA_BLEND_MULTIPLY_SHADER); shaders[idx] = &shaders_asm[8]; ++idx; } else if ((id & VEGA_BLEND_SCREEN_SHADER)) { debug_assert(shaders_asm[9].id == VEGA_BLEND_SCREEN_SHADER); shaders[idx] = &shaders_asm[9]; ++idx; } else if ((id & VEGA_BLEND_DARKEN_SHADER)) { debug_assert(shaders_asm[10].id == VEGA_BLEND_DARKEN_SHADER); shaders[idx] = &shaders_asm[10]; ++idx; } else if ((id & VEGA_BLEND_LIGHTEN_SHADER)) { debug_assert(shaders_asm[11].id == VEGA_BLEND_LIGHTEN_SHADER); shaders[idx] = &shaders_asm[11]; ++idx; } /* fifth stage */ if ((id & VEGA_PREMULTIPLY_SHADER)) { debug_assert(shaders_asm[12].id == VEGA_PREMULTIPLY_SHADER); shaders[idx] = &shaders_asm[12]; ++idx; } else if ((id & VEGA_UNPREMULTIPLY_SHADER)) { debug_assert(shaders_asm[13].id == VEGA_UNPREMULTIPLY_SHADER); shaders[idx] = &shaders_asm[13]; ++idx; } /* sixth stage */ if ((id & VEGA_BW_SHADER)) { debug_assert(shaders_asm[14].id == VEGA_BW_SHADER); shaders[idx] = &shaders_asm[14]; ++idx; } return combine_shaders(shaders, idx, pipe, shader); } /*************************************************/ struct shaders_cache * shaders_cache_create(struct vg_context *vg) { struct shaders_cache *sc = CALLOC_STRUCT(shaders_cache); sc->pipe = vg; sc->hash = cso_hash_create(); return sc; } void shaders_cache_destroy(struct shaders_cache *sc) { struct cso_hash_iter iter = cso_hash_first_node(sc->hash); while (!cso_hash_iter_is_null(iter)) { struct cached_shader *cached = (struct cached_shader *)cso_hash_iter_data(iter); cso_delete_fragment_shader(sc->pipe->cso_context, cached->driver_shader); iter = cso_hash_erase(sc->hash, iter); } cso_hash_delete(sc->hash); free(sc); } void * shaders_cache_fill(struct shaders_cache *sc, int shader_key) { VGint key = shader_key; struct cached_shader *cached; struct cso_hash_iter iter = cso_hash_find(sc->hash, key); if (cso_hash_iter_is_null(iter)) { cached = CALLOC_STRUCT(cached_shader); cached->driver_shader = create_shader(sc->pipe->pipe, key, &cached->state); cso_hash_insert(sc->hash, key, cached); return cached->driver_shader; } cached = (struct cached_shader *)cso_hash_iter_data(iter); assert(cached->driver_shader); return cached->driver_shader; } struct vg_shader * shader_create_from_text(struct pipe_context *pipe, const char *txt, int num_tokens, int type) { struct vg_shader *shader = (struct vg_shader *)malloc( sizeof(struct vg_shader)); struct tgsi_token *tokens = tokens_from_assembly(txt, num_tokens); struct pipe_shader_state state; debug_assert(type == PIPE_SHADER_VERTEX || type == PIPE_SHADER_FRAGMENT); state.tokens = tokens; shader->type = type; shader->tokens = tokens; if (type == PIPE_SHADER_FRAGMENT) shader->driver = pipe->create_fs_state(pipe, &state); else shader->driver = pipe->create_vs_state(pipe, &state); return shader; } void vg_shader_destroy(struct vg_context *ctx, struct vg_shader *shader) { if (shader->type == PIPE_SHADER_FRAGMENT) cso_delete_fragment_shader(ctx->cso_context, shader->driver); else cso_delete_vertex_shader(ctx->cso_context, shader->driver); free(shader->tokens); free(shader); }