st_atom_shader.c revision cedf6892e7cc29aab404b142012eec0b0603a414
1/**************************************************************************
2 *
3 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * State validation for vertex/fragment shaders.
30 * Note that we have to delay most vertex/fragment shader translation
31 * until rendering time since the linkage between the vertex outputs and
32 * fragment inputs can vary depending on the pairing of shaders.
33 *
34 * Authors:
35 *   Brian Paul
36 */
37
38
39
40#include "main/imports.h"
41#include "main/mtypes.h"
42
43#include "pipe/p_context.h"
44#include "pipe/p_shader_tokens.h"
45
46#include "st_context.h"
47#include "st_cache.h"
48#include "st_atom.h"
49#include "st_program.h"
50#include "st_atom_shader.h"
51#include "st_mesa_to_tgsi.h"
52
53
54/**
55 * This represents a vertex program, especially translated to match
56 * the inputs of a particular fragment shader.
57 */
58struct translated_vertex_program
59{
60   struct st_vertex_program *master;
61
62   /** The fragment shader "signature" this vertex shader is meant for: */
63   GLbitfield frag_inputs;
64
65   /** Compared against master vertex program's serialNo: */
66   GLuint serialNo;
67
68   /** Maps VERT_RESULT_x to slot */
69   GLuint output_to_slot[VERT_RESULT_MAX];
70
71   /** The program in TGSI format */
72   struct tgsi_token tokens[ST_MAX_SHADER_TOKENS];
73
74   /** Pointer to the translated, cached vertex shader */
75   const struct cso_vertex_shader *vs;
76
77   struct translated_vertex_program *next;  /**< next in linked list */
78};
79
80
81
82/**
83 * Free data hanging off the st vert prog.
84 */
85void
86st_remove_vertex_program(struct st_context *st, struct st_vertex_program *stvp)
87{
88   /* no-op, for now? */
89}
90
91
92/**
93 * Free data hanging off the st frag prog.
94 */
95void
96st_remove_fragment_program(struct st_context *st,
97                          struct st_fragment_program *stfp)
98{
99   struct translated_vertex_program *xvp, *next;
100
101   for (xvp = stfp->vertex_programs; xvp; xvp = next) {
102      next = xvp->next;
103      /* XXX free xvp->vs */
104      free(xvp);
105   }
106}
107
108
109
110/**
111 * Given a vertex program output attribute, return the corresponding
112 * fragment program input attribute.
113 * \return -1 for vertex outputs that have no corresponding fragment input
114 */
115static GLint
116vp_out_to_fp_in(GLuint vertResult)
117{
118   if (vertResult >= VERT_RESULT_TEX0 &&
119       vertResult < VERT_RESULT_TEX0 + MAX_TEXTURE_COORD_UNITS)
120      return FRAG_ATTRIB_TEX0 + (vertResult - VERT_RESULT_TEX0);
121
122   if (vertResult >= VERT_RESULT_VAR0 &&
123       vertResult < VERT_RESULT_VAR0 + MAX_VARYING)
124      return FRAG_ATTRIB_VAR0 + (vertResult - VERT_RESULT_VAR0);
125
126   switch (vertResult) {
127   case VERT_RESULT_HPOS:
128      return FRAG_ATTRIB_WPOS;
129   case VERT_RESULT_COL0:
130      return FRAG_ATTRIB_COL0;
131   case VERT_RESULT_COL1:
132      return FRAG_ATTRIB_COL1;
133   case VERT_RESULT_FOGC:
134      return FRAG_ATTRIB_FOGC;
135   default:
136      /* Back-face colors, edge flags, etc */
137      return -1;
138   }
139}
140
141
142/**
143 * Find a translated vertex program that corresponds to stvp and
144 * has outputs matched to stfp's inputs.
145 * This performs vertex and fragment translation (to TGSI) when needed.
146 */
147static struct translated_vertex_program *
148find_translated_vp(struct st_context *st,
149                   struct st_vertex_program *stvp,
150                   struct st_fragment_program *stfp)
151{
152   static const GLuint UNUSED = ~0;
153   struct translated_vertex_program *xvp;
154   const GLbitfield fragInputsRead = stfp->Base.Base.InputsRead;
155
156   /*
157    * Translate fragment program if needed.
158    */
159   if (!stfp->fs) {
160      GLuint inAttr, numIn = 0;
161
162      for (inAttr = 0; inAttr < FRAG_ATTRIB_MAX; inAttr++) {
163         if (fragInputsRead & (1 << inAttr)) {
164            stfp->input_to_slot[inAttr] = numIn;
165            numIn++;
166         }
167         else {
168            stfp->input_to_slot[inAttr] = UNUSED;
169         }
170      }
171
172      stfp->num_input_slots = numIn;
173
174      assert(stfp->Base.Base.NumInstructions > 1);
175
176      (void) st_translate_fragment_program(st, stfp,
177                                           stfp->input_to_slot,
178                                           stfp->tokens,
179                                           ST_MAX_SHADER_TOKENS);
180      assert(stfp->fs);
181   }
182
183
184   /* See if we've got a translated vertex program whose outputs match
185    * the fragment program's inputs.
186    * XXX This could be a hash lookup, using InputsRead as the key.
187    */
188   for (xvp = stfp->vertex_programs; xvp; xvp = xvp->next) {
189      if (xvp->master == stvp && xvp->frag_inputs == fragInputsRead) {
190         break;
191      }
192   }
193
194   /* No?  Allocate translated vp object now */
195   if (!xvp) {
196      xvp = CALLOC_STRUCT(translated_vertex_program);
197      xvp->frag_inputs = fragInputsRead;
198      xvp->master = stvp;
199
200      xvp->next = stfp->vertex_programs;
201      stfp->vertex_programs = xvp;
202   }
203
204   /* See if we need to translate vertex program to TGSI form */
205   if (xvp->serialNo != stvp->serialNo) {
206      GLuint outAttr, dummySlot;
207      const GLbitfield outputsWritten = stvp->Base.Base.OutputsWritten;
208      GLuint numVpOuts = 0;
209
210      /* Compute mapping of vertex program outputs to slots, which depends
211       * on the fragment program's input->slot mapping.
212       */
213      for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) {
214         /* set default: */
215         xvp->output_to_slot[outAttr] = UNUSED;
216
217         if (outAttr == VERT_RESULT_HPOS) {
218            /* always put xformed position into slot zero */
219            xvp->output_to_slot[VERT_RESULT_HPOS] = 0;
220            numVpOuts++;
221         }
222         else if (outputsWritten & (1 << outAttr)) {
223            /* see if the frag prog wants this vert output */
224            GLint fpInAttrib = vp_out_to_fp_in(outAttr);
225            if (fpInAttrib >= 0) {
226               GLuint fpInSlot = stfp->input_to_slot[fpInAttrib];
227               GLuint vpOutSlot = stfp->fs->state.input_map[fpInSlot];
228               xvp->output_to_slot[outAttr] = vpOutSlot;
229               numVpOuts++;
230            }
231            else if (outAttr == VERT_RESULT_PSIZ ||
232                     outAttr == VERT_RESULT_BFC0 ||
233                     outAttr == VERT_RESULT_BFC1) {
234               /* backface colors go into last slots */
235               xvp->output_to_slot[outAttr] = numVpOuts++;
236            }
237         }
238         /*
239         printf("output_to_slot[%d] = %d\n", outAttr,
240                xvp->output_to_slot[outAttr]);
241         */
242      }
243
244      /* Unneeded vertex program outputs will go to this slot.
245       * We could use this info to do dead code elimination in the
246       * vertex program.
247       */
248      dummySlot = stfp->num_input_slots;
249
250      /* Map vert program outputs that aren't used to the dummy slot */
251      for (outAttr = 0; outAttr < VERT_RESULT_MAX; outAttr++) {
252         if (outputsWritten & (1 << outAttr)) {
253            if (xvp->output_to_slot[outAttr] == UNUSED)
254               xvp->output_to_slot[outAttr] = dummySlot;
255         }
256      }
257
258      assert(stvp->Base.Base.NumInstructions > 1);
259
260      xvp->vs = st_translate_vertex_program(st, stvp,
261                                            xvp->output_to_slot,
262                                            xvp->tokens,
263                                            ST_MAX_SHADER_TOKENS);
264      assert(xvp->vs);
265      stvp->vs = NULL; /* don't want to use this */
266
267      /* translated VP is up to date now */
268      xvp->serialNo = stvp->serialNo;
269   }
270
271   return xvp;
272}
273
274
275static void
276update_linkage( struct st_context *st )
277{
278   struct st_vertex_program *stvp;
279   struct st_fragment_program *stfp;
280   struct translated_vertex_program *xvp;
281
282   /* find active shader and params -- Should be covered by
283    * ST_NEW_VERTEX_PROGRAM
284    */
285   assert(st->ctx->VertexProgram._Current);
286   stvp = st_vertex_program(st->ctx->VertexProgram._Current);
287
288   assert(st->ctx->FragmentProgram._Current);
289   stfp = st_fragment_program(st->ctx->FragmentProgram._Current);
290
291   xvp = find_translated_vp(st, stvp, stfp);
292
293   st->vp = stvp;
294   st->state.vs = xvp->vs;
295   st->pipe->bind_vs_state(st->pipe, st->state.vs->data);
296
297   st->fp = stfp;
298   st->state.fs = stfp->fs;
299   st->pipe->bind_fs_state(st->pipe, st->state.fs->data);
300
301   st->vertex_result_to_slot = xvp->output_to_slot;
302}
303
304
305const struct st_tracked_state st_update_shader = {
306   .name = "st_update_shader",
307   .dirty = {
308      .mesa  = 0,
309      .st   = ST_NEW_VERTEX_PROGRAM | ST_NEW_FRAGMENT_PROGRAM
310   },
311   .update = update_linkage
312};
313