t_vb_program.c revision e382efc85d02ee5a0c582e1a9d9bc35ad262e70b
1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5.3
4 *
5 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26/**
27 * \file tnl/t_vb_program.c
28 * \brief Pipeline stage for executing NVIDIA vertex programs.
29 * \author Brian Paul,  Keith Whitwell
30 */
31
32
33#include "glheader.h"
34#include "context.h"
35#include "macros.h"
36#include "imports.h"
37#include "prog_instruction.h"
38#include "prog_statevars.h"
39#include "prog_execute.h"
40
41#include "t_context.h"
42#include "t_pipeline.h"
43
44
45
46/*!
47 * Private storage for the vertex program pipeline stage.
48 */
49struct vp_stage_data {
50   /** The results of running the vertex program go into these arrays. */
51   GLvector4f attribs[VERT_RESULT_MAX];
52
53   GLvector4f ndcCoords;              /**< normalized device coords */
54   GLubyte *clipmask;                 /**< clip flags */
55   GLubyte ormask, andmask;           /**< for clipping */
56};
57
58
59#define VP_STAGE_DATA(stage) ((struct vp_stage_data *)(stage->privatePtr))
60
61
62/**
63 * Initialize virtual machine state prior to executing vertex program.
64 */
65static void
66init_machine(GLcontext *ctx, struct gl_program_machine *machine)
67{
68   /* Input registers get initialized from the current vertex attribs */
69   MEMCPY(machine->VertAttribs, ctx->Current.Attrib,
70          MAX_VERTEX_PROGRAM_ATTRIBS * 4 * sizeof(GLfloat));
71
72   if (ctx->VertexProgram.Current->IsNVProgram) {
73      GLuint i;
74      /* Output/result regs are initialized to [0,0,0,1] */
75      for (i = 0; i < MAX_NV_VERTEX_PROGRAM_OUTPUTS; i++) {
76         ASSIGN_4V(machine->Outputs[i], 0.0F, 0.0F, 0.0F, 1.0F);
77      }
78      /* Temp regs are initialized to [0,0,0,0] */
79      for (i = 0; i < MAX_NV_VERTEX_PROGRAM_TEMPS; i++) {
80         ASSIGN_4V(machine->Temporaries[i], 0.0F, 0.0F, 0.0F, 0.0F);
81      }
82      for (i = 0; i < MAX_VERTEX_PROGRAM_ADDRESS_REGS; i++) {
83         ASSIGN_4V(machine->AddressReg[i], 0, 0, 0, 0);
84      }
85   }
86
87   /* init condition codes */
88   machine->CondCodes[0] = COND_EQ;
89   machine->CondCodes[1] = COND_EQ;
90   machine->CondCodes[2] = COND_EQ;
91   machine->CondCodes[3] = COND_EQ;
92}
93
94
95/**
96 * Copy the 16 elements of a matrix into four consecutive program
97 * registers starting at 'pos'.
98 */
99static void
100load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16])
101{
102   GLuint i;
103   for (i = 0; i < 4; i++) {
104      registers[pos + i][0] = mat[0 + i];
105      registers[pos + i][1] = mat[4 + i];
106      registers[pos + i][2] = mat[8 + i];
107      registers[pos + i][3] = mat[12 + i];
108   }
109}
110
111
112/**
113 * As above, but transpose the matrix.
114 */
115static void
116load_transpose_matrix(GLfloat registers[][4], GLuint pos,
117                      const GLfloat mat[16])
118{
119   MEMCPY(registers[pos], mat, 16 * sizeof(GLfloat));
120}
121
122
123/**
124 * Load program parameter registers with tracked matrices (if NV program).
125 * This only needs to be done per glBegin/glEnd, not per-vertex.
126 */
127static void
128load_program_parameters(GLcontext *ctx)
129{
130   GLuint i;
131
132   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
133      /* point 'mat' at source matrix */
134      GLmatrix *mat;
135      if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) {
136         mat = ctx->ModelviewMatrixStack.Top;
137      }
138      else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) {
139         mat = ctx->ProjectionMatrixStack.Top;
140      }
141      else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) {
142         mat = ctx->TextureMatrixStack[ctx->Texture.CurrentUnit].Top;
143      }
144      else if (ctx->VertexProgram.TrackMatrix[i] == GL_COLOR) {
145         mat = ctx->ColorMatrixStack.Top;
146      }
147      else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) {
148         /* XXX verify the combined matrix is up to date */
149         mat = &ctx->_ModelProjectMatrix;
150      }
151      else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV &&
152               ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) {
153         GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV;
154         ASSERT(n < MAX_PROGRAM_MATRICES);
155         mat = ctx->ProgramMatrixStack[n].Top;
156      }
157      else {
158         /* no matrix is tracked, but we leave the register values as-is */
159         assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE);
160         continue;
161      }
162
163         /* load the matrix values into sequential registers */
164      if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) {
165         load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
166      }
167      else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) {
168         _math_matrix_analyse(mat); /* update the inverse */
169         ASSERT(!_math_matrix_is_dirty(mat));
170         load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
171      }
172      else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) {
173         load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
174      }
175      else {
176         assert(ctx->VertexProgram.TrackMatrixTransform[i]
177                == GL_INVERSE_TRANSPOSE_NV);
178         _math_matrix_analyse(mat); /* update the inverse */
179         ASSERT(!_math_matrix_is_dirty(mat));
180         load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
181      }
182   }
183}
184
185
186/**
187 * This function executes vertex programs
188 */
189static GLboolean
190run_vp( GLcontext *ctx, struct tnl_pipeline_stage *stage )
191{
192   TNLcontext *tnl = TNL_CONTEXT(ctx);
193   struct vp_stage_data *store = VP_STAGE_DATA(stage);
194   struct vertex_buffer *VB = &tnl->vb;
195   struct gl_vertex_program *program = ctx->VertexProgram._Current;
196   struct gl_program_machine machine;
197   GLuint i;
198
199#define FORCE_PROG_EXECUTE_C 0
200#if FORCE_PROG_EXECUTE_C
201   if (!program)
202      return GL_TRUE;
203#else
204   if (!program || !program->IsNVProgram)
205      return GL_TRUE;
206#endif
207
208   if (ctx->VertexProgram.Current->IsNVProgram) {
209      load_program_parameters(ctx);
210   }
211   else {
212      _mesa_load_state_parameters(ctx, program->Base.Parameters);
213   }
214
215   for (i = 0; i < VB->Count; i++) {
216      GLuint attr;
217
218      init_machine(ctx, &machine);
219
220#if 0
221      printf("Input  %d: %f, %f, %f, %f\n", i,
222             VB->AttribPtr[0]->data[i][0],
223             VB->AttribPtr[0]->data[i][1],
224             VB->AttribPtr[0]->data[i][2],
225             VB->AttribPtr[0]->data[i][3]);
226      printf("   color: %f, %f, %f, %f\n",
227             VB->AttribPtr[3]->data[i][0],
228             VB->AttribPtr[3]->data[i][1],
229             VB->AttribPtr[3]->data[i][2],
230             VB->AttribPtr[3]->data[i][3]);
231      printf("  normal: %f, %f, %f, %f\n",
232             VB->AttribPtr[2]->data[i][0],
233             VB->AttribPtr[2]->data[i][1],
234             VB->AttribPtr[2]->data[i][2],
235             VB->AttribPtr[2]->data[i][3]);
236#endif
237
238      /* the vertex array case */
239      for (attr = 0; attr < VERT_ATTRIB_MAX; attr++) {
240	 if (program->Base.InputsRead & (1 << attr)) {
241	    const GLubyte *ptr = (const GLubyte*) VB->AttribPtr[attr]->data;
242	    const GLuint size = VB->AttribPtr[attr]->size;
243	    const GLuint stride = VB->AttribPtr[attr]->stride;
244	    const GLfloat *data = (GLfloat *) (ptr + stride * i);
245	    COPY_CLEAN_4V(machine.VertAttribs/*Inputs*/[attr], size, data);
246	 }
247      }
248
249      /* execute the program */
250      _mesa_execute_program(ctx, &program->Base, program->Base.NumInstructions,
251                            &machine, 0);
252
253      /* Fixup fog an point size results if needed */
254      if (ctx->Fog.Enabled &&
255          (program->Base.OutputsWritten & (1 << VERT_RESULT_FOGC)) == 0) {
256         machine.Outputs[VERT_RESULT_FOGC][0] = 1.0;
257      }
258
259      if (ctx->VertexProgram.PointSizeEnabled &&
260          (program->Base.OutputsWritten & (1 << VERT_RESULT_PSIZ)) == 0) {
261         machine.Outputs[VERT_RESULT_PSIZ][0] = ctx->Point.Size;
262      }
263
264      /* copy the output registers into the VB->attribs arrays */
265      /* XXX (optimize) could use a conditional and smaller loop limit here */
266      for (attr = 0; attr < VERT_RESULT_MAX; attr++) {
267         COPY_4V(store->attribs[attr].data[i], machine.Outputs[attr]);
268      }
269#if 0
270      printf("HPOS: %f %f %f %f\n",
271             machine.Outputs[0][0],
272             machine.Outputs[0][1],
273             machine.Outputs[0][2],
274             machine.Outputs[0][3]);
275#endif
276   }
277
278   /* Setup the VB pointers so that the next pipeline stages get
279    * their data from the right place (the program output arrays).
280    */
281   VB->ClipPtr = &store->attribs[VERT_RESULT_HPOS];
282   VB->ClipPtr->size = 4;
283   VB->ClipPtr->count = VB->Count;
284   VB->ColorPtr[0] = &store->attribs[VERT_RESULT_COL0];
285   VB->ColorPtr[1] = &store->attribs[VERT_RESULT_BFC0];
286   VB->SecondaryColorPtr[0] = &store->attribs[VERT_RESULT_COL1];
287   VB->SecondaryColorPtr[1] = &store->attribs[VERT_RESULT_BFC1];
288   VB->FogCoordPtr = &store->attribs[VERT_RESULT_FOGC];
289
290   VB->AttribPtr[VERT_ATTRIB_COLOR0] = &store->attribs[VERT_RESULT_COL0];
291   VB->AttribPtr[VERT_ATTRIB_COLOR1] = &store->attribs[VERT_RESULT_COL1];
292   VB->AttribPtr[VERT_ATTRIB_FOG] = &store->attribs[VERT_RESULT_FOGC];
293   VB->AttribPtr[_TNL_ATTRIB_POINTSIZE] = &store->attribs[VERT_RESULT_PSIZ];
294
295   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) {
296      VB->TexCoordPtr[i] =
297      VB->AttribPtr[_TNL_ATTRIB_TEX0 + i]
298         = &store->attribs[VERT_RESULT_TEX0 + i];
299   }
300
301   for (i = 0; i < ctx->Const.MaxVarying; i++) {
302      if (program->Base.OutputsWritten & (1 << (VERT_RESULT_VAR0 + i))) {
303         /* Note: varying results get put into the generic attributes */
304	 VB->AttribPtr[VERT_ATTRIB_GENERIC0+i]
305            = &store->attribs[VERT_RESULT_VAR0 + i];
306      }
307   }
308
309   /* Cliptest and perspective divide.  Clip functions must clear
310    * the clipmask.
311    */
312   store->ormask = 0;
313   store->andmask = CLIP_FRUSTUM_BITS;
314
315   if (tnl->NeedNdcCoords) {
316      VB->NdcPtr =
317         _mesa_clip_tab[VB->ClipPtr->size]( VB->ClipPtr,
318                                            &store->ndcCoords,
319                                            store->clipmask,
320                                            &store->ormask,
321                                            &store->andmask );
322   }
323   else {
324      VB->NdcPtr = NULL;
325      _mesa_clip_np_tab[VB->ClipPtr->size]( VB->ClipPtr,
326                                            NULL,
327                                            store->clipmask,
328                                            &store->ormask,
329                                            &store->andmask );
330   }
331
332   if (store->andmask)  /* All vertices are outside the frustum */
333      return GL_FALSE;
334
335
336   /* This is where we'd do clip testing against the user-defined
337    * clipping planes, but they're not supported by vertex programs.
338    */
339
340   VB->ClipOrMask = store->ormask;
341   VB->ClipMask = store->clipmask;
342
343   return GL_TRUE;
344}
345
346
347/**
348 * Called the first time stage->run is called.  In effect, don't
349 * allocate data until the first time the stage is run.
350 */
351static GLboolean init_vp( GLcontext *ctx,
352			  struct tnl_pipeline_stage *stage )
353{
354   TNLcontext *tnl = TNL_CONTEXT(ctx);
355   struct vertex_buffer *VB = &(tnl->vb);
356   struct vp_stage_data *store;
357   const GLuint size = VB->Size;
358   GLuint i;
359
360   stage->privatePtr = MALLOC(sizeof(*store));
361   store = VP_STAGE_DATA(stage);
362   if (!store)
363      return GL_FALSE;
364
365   /* Allocate arrays of vertex output values */
366   for (i = 0; i < VERT_RESULT_MAX; i++) {
367      _mesa_vector4f_alloc( &store->attribs[i], 0, size, 32 );
368      store->attribs[i].size = 4;
369   }
370
371   /* a few other misc allocations */
372   _mesa_vector4f_alloc( &store->ndcCoords, 0, size, 32 );
373   store->clipmask = (GLubyte *) ALIGN_MALLOC(sizeof(GLubyte)*size, 32 );
374
375   return GL_TRUE;
376}
377
378
379/**
380 * Destructor for this pipeline stage.
381 */
382static void dtr( struct tnl_pipeline_stage *stage )
383{
384   struct vp_stage_data *store = VP_STAGE_DATA(stage);
385
386   if (store) {
387      GLuint i;
388
389      /* free the vertex program result arrays */
390      for (i = 0; i < VERT_RESULT_MAX; i++)
391         _mesa_vector4f_free( &store->attribs[i] );
392
393      /* free misc arrays */
394      _mesa_vector4f_free( &store->ndcCoords );
395      ALIGN_FREE( store->clipmask );
396
397      FREE( store );
398      stage->privatePtr = NULL;
399   }
400}
401
402
403/**
404 * Public description of this pipeline stage.
405 */
406const struct tnl_pipeline_stage _tnl_vertex_program_stage =
407{
408   "vertex-program",
409   NULL,			/* private_data */
410   init_vp,			/* create */
411   dtr,				/* destroy */
412   NULL, 			/* validate */
413   run_vp			/* run -- initially set to ctr */
414};
415