diff options
Diffstat (limited to 'src/mesa/state_tracker/st_atom_shader.c')
-rw-r--r-- | src/mesa/state_tracker/st_atom_shader.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/src/mesa/state_tracker/st_atom_shader.c b/src/mesa/state_tracker/st_atom_shader.c new file mode 100644 index 0000000000..f3bb9effde --- /dev/null +++ b/src/mesa/state_tracker/st_atom_shader.c @@ -0,0 +1,422 @@ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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 TUNGSTEN GRAPHICS 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. + * + **************************************************************************/ + +/** + * State validation for vertex/fragment shaders. + * Note that we have to delay most vertex/fragment shader translation + * until rendering time since the linkage between the vertex outputs and + * fragment inputs can vary depending on the pairing of shaders. + * + * Authors: + * Brian Paul + */ + + + +#include "main/imports.h" +#include "main/mtypes.h" + +#include "pipe/p_context.h" +#include "pipe/tgsi/mesa/mesa_to_tgsi.h" +#include "pipe/tgsi/exec/tgsi_core.h" + +#include "st_context.h" +#include "st_cache.h" +#include "st_atom.h" +#include "st_program.h" +#include "st_atom_shader.h" + + + +/** + * Structure to describe a (vertex program, fragment program) pair + * which is linked together (used together to render something). This + * linkage basically servers the same purpose as the OpenGL Shading + * Language linker, but also applies to ARB programs and Mesa's + * fixed-function-generated programs. + * + * More background: + * + * The translation from Mesa programs to TGSI programs depends on the + * linkage between the vertex program and the fragment program. This is + * because we tightly pack the inputs and outputs of shaders into + * consecutive "slots". + * + * Suppose an app uses one vertex program "VP" (outputting pos, color and tex0) + * and two fragment programs: + * FP1: uses tex0 input only (input slot 0) + * FP2: uses color input only (input slot 0) + * + * When VP is used with FP1 we want VP.output[2] to match FP1.input[0], but + * when VP is used with FP2 we want VP.output[1] to match FP1.input[0]. + * + * We don't want to re-translate the vertex and/or fragment programs + * each time the VP/FP bindings/linkings change. The solution is this + * structure which stores the translated TGSI shaders on a per-linkage + * basis. + * + */ +struct linked_program_pair +{ + struct st_vertex_program *vprog; /**< never changes */ + struct st_fragment_program *fprog; /**< never changes */ + + struct tgsi_token vs_tokens[ST_FP_MAX_TOKENS]; + struct tgsi_token fs_tokens[ST_FP_MAX_TOKENS]; + + const struct cso_vertex_shader *vs; + const struct cso_fragment_shader *fs; + + GLuint vertSerialNo, fragSerialNo; + + /** maps a Mesa VERT_ATTRIB_x to a packed TGSI input index */ + GLuint vp_input_to_index[MAX_VERTEX_PROGRAM_ATTRIBS]; + /** maps a TGSI input index back to a Mesa VERT_ATTRIB_x */ + GLuint vp_index_to_input[MAX_VERTEX_PROGRAM_ATTRIBS]; + + GLuint vp_result_to_slot[VERT_RESULT_MAX]; + + struct linked_program_pair *next; +}; + + +/** XXX temporary - use some kind of hash table instead */ +static struct linked_program_pair *Pairs = NULL; + + +static void +find_and_remove(struct gl_program *prog) +{ + struct linked_program_pair *pair, *prev = NULL, *next; + for (pair = Pairs; pair; pair = next) { + next = pair->next; + if (pair->vprog == (struct st_vertex_program *) prog || + pair->fprog == (struct st_fragment_program *) prog) { + /* unlink */ + if (prev) + prev->next = next; + else + Pairs = next; + /* delete pair->vs */ + /* delete pair->fs */ + free(pair); + } + else { + prev = pair; + } + } +} + + +/** + * Delete any known program pairs that use the given vertex program. + */ +void +st_remove_vertex_program(struct st_context *st, struct st_vertex_program *stvp) +{ + find_and_remove(&stvp->Base.Base); +} + + +/** + * Delete any known program pairs that use the given fragment program. + */ +void +st_remove_fragment_program(struct st_context *st, + struct st_fragment_program *stfp) +{ + find_and_remove(&stfp->Base.Base); +} + + + +/** + * Given a vertex program output attribute, return the corresponding + * fragment program input attribute. + * \return -1 for vertex outputs that have no corresponding fragment input + */ +static GLint +vp_out_to_fp_in(GLuint vertResult) +{ + if (vertResult >= VERT_RESULT_TEX0 && + vertResult < VERT_RESULT_TEX0 + MAX_TEXTURE_COORD_UNITS) + return FRAG_ATTRIB_TEX0 + (vertResult - VERT_RESULT_TEX0); + + if (vertResult >= VERT_RESULT_VAR0 && + vertResult < VERT_RESULT_VAR0 + MAX_VARYING) + return FRAG_ATTRIB_VAR0 + (vertResult - VERT_RESULT_VAR0); + + switch (vertResult) { + case VERT_RESULT_HPOS: + return FRAG_ATTRIB_WPOS; + case VERT_RESULT_COL0: + return FRAG_ATTRIB_COL0; + case VERT_RESULT_COL1: + return FRAG_ATTRIB_COL1; + case VERT_RESULT_FOGC: + return FRAG_ATTRIB_FOGC; + default: + /* Back-face colors, edge flags, etc */ + return -1; + } +} + + +/** + * Examine the outputs written by a vertex program and the inputs read + * by a fragment program to determine which match up and where they + * should be mapped into the generic shader output/input slots. + * \param vert_output_map returns the vertex output register mapping + * \param frag_input_map returns the fragment input register mapping + */ +static GLuint +link_outputs_to_inputs(GLbitfield outputsWritten, + GLbitfield inputsRead, + GLuint vert_output_map[], + GLuint frag_input_map[]) +{ + static const GLuint UNUSED = ~0; + GLint vert_slot_to_attr[50], frag_slot_to_attr[50]; + GLuint outAttr, inAttr; + GLuint numIn = 0, dummySlot; + + for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) { + if (inputsRead & (1 << inAttr)) { + frag_input_map[inAttr] = numIn; + frag_slot_to_attr[numIn] = inAttr; + numIn++; + } + else { + frag_input_map[inAttr] = UNUSED; + } + } + + for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { + if (outputsWritten & (1 << outAttr)) { + /* see if the frag prog wants this vert output */ + GLint fpIn = vp_out_to_fp_in(outAttr); + + if (fpIn >= 0) { + GLuint frag_slot = frag_input_map[fpIn]; + vert_output_map[outAttr] = frag_slot; + vert_slot_to_attr[frag_slot] = outAttr; + } + else { + vert_output_map[outAttr] = UNUSED; + } + } + else { + vert_output_map[outAttr] = UNUSED; + } + } + + /* + * We'll map all unused vertex program outputs to this slot. + * We'll also map all undefined fragment program inputs to this slot. + */ + dummySlot = numIn; + + /* Map vert program outputs that aren't used to the dummy slot */ + for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { + if (outputsWritten & (1 << outAttr)) { + if (vert_output_map[outAttr] == UNUSED) + vert_output_map[outAttr] = dummySlot; + } + } + + /* Map frag program inputs that aren't defined to the dummy slot */ + for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) { + if (inputsRead & (1 << inAttr)) { + if (frag_input_map[inAttr] == UNUSED) + frag_input_map[inAttr] = dummySlot; + } + } + +#if 0 + printf("vOut W slot\n"); + for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) { + printf("%4d %c %4d\n", outAttr, + " *"[(outputsWritten >> outAttr) & 1], + vert_output_map[outAttr]); + } + printf("vIn R slot\n"); + for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) { + printf("%3d %c %4d\n", inAttr, + " *"[(inputsRead >> inAttr) & 1], + frag_input_map[inAttr]); + } +#endif + + return numIn; +} + + +static struct linked_program_pair * +lookup_program_pair(struct st_context *st, + struct st_vertex_program *vprog, + struct st_fragment_program *fprog) +{ + struct linked_program_pair *pair; + + /* search */ + for (pair = Pairs; pair; pair = pair->next) { + if (pair->vprog == vprog && pair->fprog == fprog) { + /* found it */ + break; + } + } + + /* + * Examine the outputs of the vertex shader and the inputs of the + * fragment shader to determine how to match both to a common set + * of slots. + */ + if (!pair) { + pair = CALLOC_STRUCT(linked_program_pair); + if (pair) { + pair->vprog = vprog; + pair->fprog = fprog; + } + } + + return pair; +} + + +static void +link_shaders(struct st_context *st, struct linked_program_pair *pair) +{ + struct st_vertex_program *vprog = pair->vprog; + struct st_fragment_program *fprog = pair->fprog; + + assert(vprog); + assert(fprog); + + if (pair->vertSerialNo != vprog->serialNo || + pair->fragSerialNo != fprog->serialNo) { + /* re-link and re-translate */ + GLuint vert_output_mapping[VERT_RESULT_MAX]; + GLuint frag_input_mapping[FRAG_ATTRIB_MAX]; + + link_outputs_to_inputs(vprog->Base.Base.OutputsWritten, + fprog->Base.Base.InputsRead | FRAG_BIT_WPOS, + vert_output_mapping, + frag_input_mapping); + + /* xlate vp to vs + vs tokens */ + st_translate_vertex_program(st, vprog, + vert_output_mapping, + pair->vs_tokens, ST_FP_MAX_TOKENS); + + pair->vprog = vprog; + /* temp hacks */ + pair->vs = vprog->vs; + vprog->vs = NULL; + + + /* xlate fp to fs + fs tokens */ + st_translate_fragment_program(st, fprog, + frag_input_mapping, + pair->fs_tokens, ST_FP_MAX_TOKENS); + pair->fprog = fprog; + /* temp hacks */ + pair->fs = fprog->fs; + fprog->fs = NULL; + + /* save pair */ + pair->next = Pairs; + Pairs = pair; + + pair->vertSerialNo = vprog->serialNo; + pair->fragSerialNo = fprog->serialNo; + } +} + + +static void +update_linkage( struct st_context *st ) +{ + struct linked_program_pair *pair; + struct st_vertex_program *stvp; + struct st_fragment_program *stfp; + + /* find active shader and params -- Should be covered by + * ST_NEW_VERTEX_PROGRAM + */ + if (st->ctx->Shader.CurrentProgram && + st->ctx->Shader.CurrentProgram->LinkStatus && + st->ctx->Shader.CurrentProgram->VertexProgram) { + struct gl_vertex_program *f + = st->ctx->Shader.CurrentProgram->VertexProgram; + stvp = st_vertex_program(f); + } + else { + assert(st->ctx->VertexProgram._Current); + stvp = st_vertex_program(st->ctx->VertexProgram._Current); + } + + + if (st->ctx->Shader.CurrentProgram && + st->ctx->Shader.CurrentProgram->LinkStatus && + st->ctx->Shader.CurrentProgram->FragmentProgram) { + struct gl_fragment_program *f + = st->ctx->Shader.CurrentProgram->FragmentProgram; + stfp = st_fragment_program(f); + } + else { + assert(st->ctx->FragmentProgram._Current); + stfp = st_fragment_program(st->ctx->FragmentProgram._Current); + } + + + pair = lookup_program_pair(st, stvp, stfp); + assert(pair); + link_shaders(st, pair); + + + /* Bind the vertex program and TGSI shader */ + st->vp = stvp; + st->state.vs = pair->vs; + st->pipe->bind_vs_state(st->pipe, st->state.vs->data); + + /* Bind the fragment program and TGSI shader */ + st->fp = stfp; + st->state.fs = pair->fs; + st->pipe->bind_fs_state(st->pipe, st->state.fs->data); + + st->vertex_result_to_slot = pair->vp_result_to_slot; +} + + +const struct st_tracked_state st_update_shader = { + .name = "st_update_shader", + .dirty = { + .mesa = 0, + .st = ST_NEW_LINKAGE + }, + .update = update_linkage +}; |