cleanups for t_vb_program.c
[mesa.git] / src / mesa / tnl / t_vb_program.c
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 "tnl.h"
42 #include "t_context.h"
43 #include "t_pipeline.h"
44
45
46
47 /**
48 * Called via ctx->Driver.ProgramStringNotify() after a new vertex program
49 * string has been parsed.
50 */
51 void
52 _tnl_program_string(GLcontext *ctx, GLenum target, struct gl_program *program)
53 {
54 /* No-op.
55 * If we had derived anything from the program that was private to this
56 * stage we'd recompute/validate it here.
57 */
58 }
59
60
61 /*!
62 * Private storage for the vertex program pipeline stage.
63 */
64 struct vp_stage_data {
65 /** The results of running the vertex program go into these arrays. */
66 GLvector4f results[VERT_RESULT_MAX];
67
68 GLvector4f ndcCoords; /**< normalized device coords */
69 GLubyte *clipmask; /**< clip flags */
70 GLubyte ormask, andmask; /**< for clipping */
71 };
72
73
74 #define VP_STAGE_DATA(stage) ((struct vp_stage_data *)(stage->privatePtr))
75
76
77 /**
78 * Initialize virtual machine state prior to executing vertex program.
79 */
80 static void
81 init_machine(GLcontext *ctx, struct gl_program_machine *machine)
82 {
83 /* Input registers get initialized from the current vertex attribs */
84 MEMCPY(machine->VertAttribs, ctx->Current.Attrib,
85 MAX_VERTEX_PROGRAM_ATTRIBS * 4 * sizeof(GLfloat));
86
87 if (ctx->VertexProgram._Current->IsNVProgram) {
88 GLuint i;
89 /* Output/result regs are initialized to [0,0,0,1] */
90 for (i = 0; i < MAX_NV_VERTEX_PROGRAM_OUTPUTS; i++) {
91 ASSIGN_4V(machine->Outputs[i], 0.0F, 0.0F, 0.0F, 1.0F);
92 }
93 /* Temp regs are initialized to [0,0,0,0] */
94 for (i = 0; i < MAX_NV_VERTEX_PROGRAM_TEMPS; i++) {
95 ASSIGN_4V(machine->Temporaries[i], 0.0F, 0.0F, 0.0F, 0.0F);
96 }
97 for (i = 0; i < MAX_VERTEX_PROGRAM_ADDRESS_REGS; i++) {
98 ASSIGN_4V(machine->AddressReg[i], 0, 0, 0, 0);
99 }
100 }
101
102 /* init condition codes */
103 machine->CondCodes[0] = COND_EQ;
104 machine->CondCodes[1] = COND_EQ;
105 machine->CondCodes[2] = COND_EQ;
106 machine->CondCodes[3] = COND_EQ;
107
108 /* init call stack */
109 machine->StackDepth = 0;
110 }
111
112
113 /**
114 * Copy the 16 elements of a matrix into four consecutive program
115 * registers starting at 'pos'.
116 */
117 static void
118 load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16])
119 {
120 GLuint i;
121 for (i = 0; i < 4; i++) {
122 registers[pos + i][0] = mat[0 + i];
123 registers[pos + i][1] = mat[4 + i];
124 registers[pos + i][2] = mat[8 + i];
125 registers[pos + i][3] = mat[12 + i];
126 }
127 }
128
129
130 /**
131 * As above, but transpose the matrix.
132 */
133 static void
134 load_transpose_matrix(GLfloat registers[][4], GLuint pos,
135 const GLfloat mat[16])
136 {
137 MEMCPY(registers[pos], mat, 16 * sizeof(GLfloat));
138 }
139
140
141 /**
142 * Load current vertex program's parameter registers with tracked
143 * matrices (if NV program). This only needs to be done per
144 * glBegin/glEnd, not per-vertex.
145 */
146 void
147 _mesa_load_tracked_matrices(GLcontext *ctx)
148 {
149 GLuint i;
150
151 for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
152 /* point 'mat' at source matrix */
153 GLmatrix *mat;
154 if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) {
155 mat = ctx->ModelviewMatrixStack.Top;
156 }
157 else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) {
158 mat = ctx->ProjectionMatrixStack.Top;
159 }
160 else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) {
161 mat = ctx->TextureMatrixStack[ctx->Texture.CurrentUnit].Top;
162 }
163 else if (ctx->VertexProgram.TrackMatrix[i] == GL_COLOR) {
164 mat = ctx->ColorMatrixStack.Top;
165 }
166 else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) {
167 /* XXX verify the combined matrix is up to date */
168 mat = &ctx->_ModelProjectMatrix;
169 }
170 else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV &&
171 ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) {
172 GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV;
173 ASSERT(n < MAX_PROGRAM_MATRICES);
174 mat = ctx->ProgramMatrixStack[n].Top;
175 }
176 else {
177 /* no matrix is tracked, but we leave the register values as-is */
178 assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE);
179 continue;
180 }
181
182 /* load the matrix values into sequential registers */
183 if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) {
184 load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
185 }
186 else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) {
187 _math_matrix_analyse(mat); /* update the inverse */
188 ASSERT(!_math_matrix_is_dirty(mat));
189 load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
190 }
191 else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) {
192 load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
193 }
194 else {
195 assert(ctx->VertexProgram.TrackMatrixTransform[i]
196 == GL_INVERSE_TRANSPOSE_NV);
197 _math_matrix_analyse(mat); /* update the inverse */
198 ASSERT(!_math_matrix_is_dirty(mat));
199 load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
200 }
201 }
202 }
203
204
205 /**
206 * This function executes vertex programs
207 */
208 static GLboolean
209 run_vp( GLcontext *ctx, struct tnl_pipeline_stage *stage )
210 {
211 TNLcontext *tnl = TNL_CONTEXT(ctx);
212 struct vp_stage_data *store = VP_STAGE_DATA(stage);
213 struct vertex_buffer *VB = &tnl->vb;
214 struct gl_vertex_program *program = ctx->VertexProgram._Current;
215 struct gl_program_machine machine;
216 GLuint outputs[VERT_RESULT_MAX], numOutputs;
217 GLuint i, j;
218
219 #define FORCE_PROG_EXECUTE_C 1
220 #if FORCE_PROG_EXECUTE_C
221 if (!program)
222 return GL_TRUE;
223 #else
224 if (!program || !program->IsNVProgram)
225 return GL_TRUE;
226 #endif
227
228 if (program->IsNVProgram) {
229 _mesa_load_tracked_matrices(ctx);
230 }
231 else {
232 _mesa_load_state_parameters(ctx, program->Base.Parameters);
233 }
234
235 numOutputs = 0;
236 for (i = 0; i < VERT_RESULT_MAX; i++) {
237 if (program->Base.OutputsWritten & (1 << i)) {
238 outputs[numOutputs++] = i;
239 }
240 }
241
242 for (i = 0; i < VB->Count; i++) {
243 GLuint attr;
244
245 init_machine(ctx, &machine);
246
247 #if 0
248 printf("Input %d: %f, %f, %f, %f\n", i,
249 VB->AttribPtr[0]->data[i][0],
250 VB->AttribPtr[0]->data[i][1],
251 VB->AttribPtr[0]->data[i][2],
252 VB->AttribPtr[0]->data[i][3]);
253 printf(" color: %f, %f, %f, %f\n",
254 VB->AttribPtr[3]->data[i][0],
255 VB->AttribPtr[3]->data[i][1],
256 VB->AttribPtr[3]->data[i][2],
257 VB->AttribPtr[3]->data[i][3]);
258 printf(" normal: %f, %f, %f, %f\n",
259 VB->AttribPtr[2]->data[i][0],
260 VB->AttribPtr[2]->data[i][1],
261 VB->AttribPtr[2]->data[i][2],
262 VB->AttribPtr[2]->data[i][3]);
263 #endif
264
265 /* the vertex array case */
266 for (attr = 0; attr < VERT_ATTRIB_MAX; attr++) {
267 if (program->Base.InputsRead & (1 << attr)) {
268 const GLubyte *ptr = (const GLubyte*) VB->AttribPtr[attr]->data;
269 const GLuint size = VB->AttribPtr[attr]->size;
270 const GLuint stride = VB->AttribPtr[attr]->stride;
271 const GLfloat *data = (GLfloat *) (ptr + stride * i);
272 COPY_CLEAN_4V(machine.VertAttribs[attr], size, data);
273 }
274 }
275
276 /* execute the program */
277 _mesa_execute_program(ctx, &program->Base, &machine);
278
279 /* copy the output registers into the VB->attribs arrays */
280 for (j = 0; j < numOutputs; j++) {
281 const GLuint attr = outputs[j];
282 COPY_4V(store->results[attr].data[i], machine.Outputs[attr]);
283 }
284 #if 0
285 printf("HPOS: %f %f %f %f\n",
286 machine.Outputs[0][0],
287 machine.Outputs[0][1],
288 machine.Outputs[0][2],
289 machine.Outputs[0][3]);
290 #endif
291 }
292
293 /* Fixup fog and point size results if needed */
294 if (program->IsNVProgram) {
295 if (ctx->Fog.Enabled &&
296 (program->Base.OutputsWritten & (1 << VERT_RESULT_FOGC)) == 0) {
297 for (i = 0; i < VB->Count; i++) {
298 store->results[VERT_RESULT_FOGC].data[i][0] = 1.0;
299 }
300 }
301
302 if (ctx->VertexProgram.PointSizeEnabled &&
303 (program->Base.OutputsWritten & (1 << VERT_RESULT_PSIZ)) == 0) {
304 for (i = 0; i < VB->Count; i++) {
305 store->results[VERT_RESULT_PSIZ].data[i][0] = ctx->Point.Size;
306 }
307 }
308 }
309
310 /* Setup the VB pointers so that the next pipeline stages get
311 * their data from the right place (the program output arrays).
312 */
313 VB->ClipPtr = &store->results[VERT_RESULT_HPOS];
314 VB->ClipPtr->size = 4;
315 VB->ClipPtr->count = VB->Count;
316 VB->ColorPtr[0] = &store->results[VERT_RESULT_COL0];
317 VB->ColorPtr[1] = &store->results[VERT_RESULT_BFC0];
318 VB->SecondaryColorPtr[0] = &store->results[VERT_RESULT_COL1];
319 VB->SecondaryColorPtr[1] = &store->results[VERT_RESULT_BFC1];
320 VB->FogCoordPtr = &store->results[VERT_RESULT_FOGC];
321
322 VB->AttribPtr[VERT_ATTRIB_COLOR0] = &store->results[VERT_RESULT_COL0];
323 VB->AttribPtr[VERT_ATTRIB_COLOR1] = &store->results[VERT_RESULT_COL1];
324 VB->AttribPtr[VERT_ATTRIB_FOG] = &store->results[VERT_RESULT_FOGC];
325 VB->AttribPtr[_TNL_ATTRIB_POINTSIZE] = &store->results[VERT_RESULT_PSIZ];
326
327 for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) {
328 VB->TexCoordPtr[i] =
329 VB->AttribPtr[_TNL_ATTRIB_TEX0 + i]
330 = &store->results[VERT_RESULT_TEX0 + i];
331 }
332
333 for (i = 0; i < ctx->Const.MaxVarying; i++) {
334 if (program->Base.OutputsWritten & (1 << (VERT_RESULT_VAR0 + i))) {
335 /* Note: varying results get put into the generic attributes */
336 VB->AttribPtr[VERT_ATTRIB_GENERIC0+i]
337 = &store->results[VERT_RESULT_VAR0 + i];
338 }
339 }
340
341 /* Cliptest and perspective divide. Clip functions must clear
342 * the clipmask.
343 */
344 store->ormask = 0;
345 store->andmask = CLIP_FRUSTUM_BITS;
346
347 if (tnl->NeedNdcCoords) {
348 VB->NdcPtr =
349 _mesa_clip_tab[VB->ClipPtr->size]( VB->ClipPtr,
350 &store->ndcCoords,
351 store->clipmask,
352 &store->ormask,
353 &store->andmask );
354 }
355 else {
356 VB->NdcPtr = NULL;
357 _mesa_clip_np_tab[VB->ClipPtr->size]( VB->ClipPtr,
358 NULL,
359 store->clipmask,
360 &store->ormask,
361 &store->andmask );
362 }
363
364 if (store->andmask) /* All vertices are outside the frustum */
365 return GL_FALSE;
366
367
368 /* This is where we'd do clip testing against the user-defined
369 * clipping planes, but they're not supported by vertex programs.
370 */
371
372 VB->ClipOrMask = store->ormask;
373 VB->ClipMask = store->clipmask;
374
375 return GL_TRUE;
376 }
377
378
379 /**
380 * Called the first time stage->run is called. In effect, don't
381 * allocate data until the first time the stage is run.
382 */
383 static GLboolean init_vp( GLcontext *ctx,
384 struct tnl_pipeline_stage *stage )
385 {
386 TNLcontext *tnl = TNL_CONTEXT(ctx);
387 struct vertex_buffer *VB = &(tnl->vb);
388 struct vp_stage_data *store;
389 const GLuint size = VB->Size;
390 GLuint i;
391
392 stage->privatePtr = MALLOC(sizeof(*store));
393 store = VP_STAGE_DATA(stage);
394 if (!store)
395 return GL_FALSE;
396
397 /* Allocate arrays of vertex output values */
398 for (i = 0; i < VERT_RESULT_MAX; i++) {
399 _mesa_vector4f_alloc( &store->results[i], 0, size, 32 );
400 store->results[i].size = 4;
401 }
402
403 /* a few other misc allocations */
404 _mesa_vector4f_alloc( &store->ndcCoords, 0, size, 32 );
405 store->clipmask = (GLubyte *) ALIGN_MALLOC(sizeof(GLubyte)*size, 32 );
406
407 return GL_TRUE;
408 }
409
410
411 /**
412 * Destructor for this pipeline stage.
413 */
414 static void dtr( struct tnl_pipeline_stage *stage )
415 {
416 struct vp_stage_data *store = VP_STAGE_DATA(stage);
417
418 if (store) {
419 GLuint i;
420
421 /* free the vertex program result arrays */
422 for (i = 0; i < VERT_RESULT_MAX; i++)
423 _mesa_vector4f_free( &store->results[i] );
424
425 /* free misc arrays */
426 _mesa_vector4f_free( &store->ndcCoords );
427 ALIGN_FREE( store->clipmask );
428
429 FREE( store );
430 stage->privatePtr = NULL;
431 }
432 }
433
434
435 /**
436 * Public description of this pipeline stage.
437 */
438 const struct tnl_pipeline_stage _tnl_vertex_program_stage =
439 {
440 "vertex-program",
441 NULL, /* private_data */
442 init_vp, /* create */
443 dtr, /* destroy */
444 NULL, /* validate */
445 run_vp /* run -- initially set to ctr */
446 };