2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 * \file tnl/t_vb_program.c
28 * \brief Pipeline stage for executing vertex programs.
29 * \author Brian Paul, Keith Whitwell
38 #include "shader/prog_instruction.h"
39 #include "shader/prog_statevars.h"
40 #include "shader/prog_execute.h"
41 #include "swrast/s_context.h"
42 #include "swrast/s_texfilter.h"
45 #include "t_context.h"
46 #include "t_pipeline.h"
51 * Private storage for the vertex program pipeline stage.
53 struct vp_stage_data
{
54 /** The results of running the vertex program go into these arrays. */
55 GLvector4f results
[VERT_RESULT_MAX
];
57 GLvector4f ndcCoords
; /**< normalized device coords */
58 GLubyte
*clipmask
; /**< clip flags */
59 GLubyte ormask
, andmask
; /**< for clipping */
63 #define VP_STAGE_DATA(stage) ((struct vp_stage_data *)(stage->privatePtr))
67 * XXX the texture sampling code in this module is a bit of a hack.
68 * The texture sampling code is in swrast, though it doesn't have any
69 * real dependencies on the rest of swrast. It should probably be
70 * moved into main/ someday.
73 static void userclip( GLcontext
*ctx
,
77 GLubyte
*clipandmask
)
81 for (p
= 0; p
< ctx
->Const
.MaxClipPlanes
; p
++) {
82 if (ctx
->Transform
.ClipPlanesEnabled
& (1 << p
)) {
84 const GLfloat a
= ctx
->Transform
._ClipUserPlane
[p
][0];
85 const GLfloat b
= ctx
->Transform
._ClipUserPlane
[p
][1];
86 const GLfloat c
= ctx
->Transform
._ClipUserPlane
[p
][2];
87 const GLfloat d
= ctx
->Transform
._ClipUserPlane
[p
][3];
88 GLfloat
*coord
= (GLfloat
*)clip
->data
;
89 GLuint stride
= clip
->stride
;
90 GLuint count
= clip
->count
;
92 for (nr
= 0, i
= 0 ; i
< count
; i
++) {
93 GLfloat dp
= (coord
[0] * a
+
100 clipmask
[i
] |= CLIP_USER_BIT
;
103 STRIDE_F(coord
, stride
);
107 *clipormask
|= CLIP_USER_BIT
;
109 *clipandmask
|= CLIP_USER_BIT
;
119 do_ndc_cliptest(GLcontext
*ctx
, struct vp_stage_data
*store
)
121 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
122 struct vertex_buffer
*VB
= &tnl
->vb
;
123 /* Cliptest and perspective divide. Clip functions must clear
127 store
->andmask
= CLIP_FRUSTUM_BITS
;
129 if (tnl
->NeedNdcCoords
) {
131 _mesa_clip_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
139 _mesa_clip_np_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
146 if (store
->andmask
) {
147 /* All vertices are outside the frustum */
151 /* Test userclip planes. This contributes to VB->ClipMask.
153 /** XXX NEW_SLANG _Enabled ??? */
154 if (ctx
->Transform
.ClipPlanesEnabled
&& (!ctx
->VertexProgram
._Enabled
||
155 ctx
->VertexProgram
.Current
->IsPositionInvariant
)) {
162 if (store
->andmask
) {
167 VB
->ClipAndMask
= store
->andmask
;
168 VB
->ClipOrMask
= store
->ormask
;
169 VB
->ClipMask
= store
->clipmask
;
176 vp_fetch_texel(GLcontext
*ctx
, const GLfloat texcoord
[4], GLfloat lambda
,
177 GLuint unit
, GLfloat color
[4])
180 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
182 /* XXX use a float-valued TextureSample routine here!!! */
183 swrast
->TextureSample
[unit
](ctx
, ctx
->Texture
.Unit
[unit
]._Current
,
184 1, (const GLfloat (*)[4]) texcoord
,
186 color
[0] = CHAN_TO_FLOAT(rgba
[0]);
187 color
[1] = CHAN_TO_FLOAT(rgba
[1]);
188 color
[2] = CHAN_TO_FLOAT(rgba
[2]);
189 color
[3] = CHAN_TO_FLOAT(rgba
[3]);
194 * Called via ctx->Driver.ProgramStringNotify() after a new vertex program
195 * string has been parsed.
198 _tnl_program_string(GLcontext
*ctx
, GLenum target
, struct gl_program
*program
)
201 * If we had derived anything from the program that was private to this
202 * stage we'd recompute/validate it here.
208 * Initialize virtual machine state prior to executing vertex program.
211 init_machine(GLcontext
*ctx
, struct gl_program_machine
*machine
)
213 /* Input registers get initialized from the current vertex attribs */
214 MEMCPY(machine
->VertAttribs
, ctx
->Current
.Attrib
,
215 MAX_VERTEX_PROGRAM_ATTRIBS
* 4 * sizeof(GLfloat
));
217 if (ctx
->VertexProgram
._Current
->IsNVProgram
) {
219 /* Output/result regs are initialized to [0,0,0,1] */
220 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_OUTPUTS
; i
++) {
221 ASSIGN_4V(machine
->Outputs
[i
], 0.0F
, 0.0F
, 0.0F
, 1.0F
);
223 /* Temp regs are initialized to [0,0,0,0] */
224 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_TEMPS
; i
++) {
225 ASSIGN_4V(machine
->Temporaries
[i
], 0.0F
, 0.0F
, 0.0F
, 0.0F
);
227 for (i
= 0; i
< MAX_VERTEX_PROGRAM_ADDRESS_REGS
; i
++) {
228 ASSIGN_4V(machine
->AddressReg
[i
], 0, 0, 0, 0);
232 machine
->NumDeriv
= 0;
234 /* init condition codes */
235 machine
->CondCodes
[0] = COND_EQ
;
236 machine
->CondCodes
[1] = COND_EQ
;
237 machine
->CondCodes
[2] = COND_EQ
;
238 machine
->CondCodes
[3] = COND_EQ
;
240 /* init call stack */
241 machine
->StackDepth
= 0;
243 machine
->FetchTexelLod
= vp_fetch_texel
;
244 machine
->FetchTexelDeriv
= NULL
; /* not used by vertex programs */
249 * Copy the 16 elements of a matrix into four consecutive program
250 * registers starting at 'pos'.
253 load_matrix(GLfloat registers
[][4], GLuint pos
, const GLfloat mat
[16])
256 for (i
= 0; i
< 4; i
++) {
257 registers
[pos
+ i
][0] = mat
[0 + i
];
258 registers
[pos
+ i
][1] = mat
[4 + i
];
259 registers
[pos
+ i
][2] = mat
[8 + i
];
260 registers
[pos
+ i
][3] = mat
[12 + i
];
266 * As above, but transpose the matrix.
269 load_transpose_matrix(GLfloat registers
[][4], GLuint pos
,
270 const GLfloat mat
[16])
272 MEMCPY(registers
[pos
], mat
, 16 * sizeof(GLfloat
));
277 * Load current vertex program's parameter registers with tracked
278 * matrices (if NV program). This only needs to be done per
279 * glBegin/glEnd, not per-vertex.
282 _mesa_load_tracked_matrices(GLcontext
*ctx
)
286 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_PARAMS
/ 4; i
++) {
287 /* point 'mat' at source matrix */
289 if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_MODELVIEW
) {
290 mat
= ctx
->ModelviewMatrixStack
.Top
;
292 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_PROJECTION
) {
293 mat
= ctx
->ProjectionMatrixStack
.Top
;
295 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_TEXTURE
) {
296 mat
= ctx
->TextureMatrixStack
[ctx
->Texture
.CurrentUnit
].Top
;
298 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_COLOR
) {
299 mat
= ctx
->ColorMatrixStack
.Top
;
301 else if (ctx
->VertexProgram
.TrackMatrix
[i
]==GL_MODELVIEW_PROJECTION_NV
) {
302 /* XXX verify the combined matrix is up to date */
303 mat
= &ctx
->_ModelProjectMatrix
;
305 else if (ctx
->VertexProgram
.TrackMatrix
[i
] >= GL_MATRIX0_NV
&&
306 ctx
->VertexProgram
.TrackMatrix
[i
] <= GL_MATRIX7_NV
) {
307 GLuint n
= ctx
->VertexProgram
.TrackMatrix
[i
] - GL_MATRIX0_NV
;
308 ASSERT(n
< MAX_PROGRAM_MATRICES
);
309 mat
= ctx
->ProgramMatrixStack
[n
].Top
;
312 /* no matrix is tracked, but we leave the register values as-is */
313 assert(ctx
->VertexProgram
.TrackMatrix
[i
] == GL_NONE
);
317 /* load the matrix values into sequential registers */
318 if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_IDENTITY_NV
) {
319 load_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->m
);
321 else if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_INVERSE_NV
) {
322 _math_matrix_analyse(mat
); /* update the inverse */
323 ASSERT(!_math_matrix_is_dirty(mat
));
324 load_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->inv
);
326 else if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_TRANSPOSE_NV
) {
327 load_transpose_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->m
);
330 assert(ctx
->VertexProgram
.TrackMatrixTransform
[i
]
331 == GL_INVERSE_TRANSPOSE_NV
);
332 _math_matrix_analyse(mat
); /* update the inverse */
333 ASSERT(!_math_matrix_is_dirty(mat
));
334 load_transpose_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->inv
);
341 * This function executes vertex programs
344 run_vp( GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
346 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
347 struct vp_stage_data
*store
= VP_STAGE_DATA(stage
);
348 struct vertex_buffer
*VB
= &tnl
->vb
;
349 struct gl_vertex_program
*program
= ctx
->VertexProgram
._Current
;
350 struct gl_program_machine machine
;
351 GLuint outputs
[VERT_RESULT_MAX
], numOutputs
;
357 if (program
->IsNVProgram
) {
358 _mesa_load_tracked_matrices(ctx
);
361 /* ARB program or vertex shader */
362 _mesa_load_state_parameters(ctx
, program
->Base
.Parameters
);
366 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
367 if (program
->Base
.OutputsWritten
& (1 << i
)) {
368 outputs
[numOutputs
++] = i
;
372 for (i
= 0; i
< VB
->Count
; i
++) {
375 init_machine(ctx
, &machine
);
378 printf("Input %d: %f, %f, %f, %f\n", i
,
379 VB
->AttribPtr
[0]->data
[i
][0],
380 VB
->AttribPtr
[0]->data
[i
][1],
381 VB
->AttribPtr
[0]->data
[i
][2],
382 VB
->AttribPtr
[0]->data
[i
][3]);
383 printf(" color: %f, %f, %f, %f\n",
384 VB
->AttribPtr
[3]->data
[i
][0],
385 VB
->AttribPtr
[3]->data
[i
][1],
386 VB
->AttribPtr
[3]->data
[i
][2],
387 VB
->AttribPtr
[3]->data
[i
][3]);
388 printf(" normal: %f, %f, %f, %f\n",
389 VB
->AttribPtr
[2]->data
[i
][0],
390 VB
->AttribPtr
[2]->data
[i
][1],
391 VB
->AttribPtr
[2]->data
[i
][2],
392 VB
->AttribPtr
[2]->data
[i
][3]);
395 /* the vertex array case */
396 for (attr
= 0; attr
< VERT_ATTRIB_MAX
; attr
++) {
397 if (program
->Base
.InputsRead
& (1 << attr
)) {
398 const GLubyte
*ptr
= (const GLubyte
*) VB
->AttribPtr
[attr
]->data
;
399 const GLuint size
= VB
->AttribPtr
[attr
]->size
;
400 const GLuint stride
= VB
->AttribPtr
[attr
]->stride
;
401 const GLfloat
*data
= (GLfloat
*) (ptr
+ stride
* i
);
402 COPY_CLEAN_4V(machine
.VertAttribs
[attr
], size
, data
);
406 /* execute the program */
407 _mesa_execute_program(ctx
, &program
->Base
, &machine
);
409 /* copy the output registers into the VB->attribs arrays */
410 for (j
= 0; j
< numOutputs
; j
++) {
411 const GLuint attr
= outputs
[j
];
412 COPY_4V(store
->results
[attr
].data
[i
], machine
.Outputs
[attr
]);
415 printf("HPOS: %f %f %f %f\n",
416 machine
.Outputs
[0][0],
417 machine
.Outputs
[0][1],
418 machine
.Outputs
[0][2],
419 machine
.Outputs
[0][3]);
423 /* Fixup fog and point size results if needed */
424 if (program
->IsNVProgram
) {
425 if (ctx
->Fog
.Enabled
&&
426 (program
->Base
.OutputsWritten
& (1 << VERT_RESULT_FOGC
)) == 0) {
427 for (i
= 0; i
< VB
->Count
; i
++) {
428 store
->results
[VERT_RESULT_FOGC
].data
[i
][0] = 1.0;
432 if (ctx
->VertexProgram
.PointSizeEnabled
&&
433 (program
->Base
.OutputsWritten
& (1 << VERT_RESULT_PSIZ
)) == 0) {
434 for (i
= 0; i
< VB
->Count
; i
++) {
435 store
->results
[VERT_RESULT_PSIZ
].data
[i
][0] = ctx
->Point
.Size
;
440 if (program
->IsPositionInvariant
) {
441 /* We need the exact same transform as in the fixed function path here
442 to guarantee invariance, depending on compiler optimization flags results
443 could be different otherwise */
444 VB
->ClipPtr
= TransformRaw( &store
->results
[0],
445 &ctx
->_ModelProjectMatrix
,
448 /* Drivers expect this to be clean to element 4...
450 switch (VB
->ClipPtr
->size
) {
454 _mesa_vector4f_clean_elem( VB
->ClipPtr
, VB
->Count
, 2 );
457 _mesa_vector4f_clean_elem( VB
->ClipPtr
, VB
->Count
, 3 );
465 /* Setup the VB pointers so that the next pipeline stages get
466 * their data from the right place (the program output arrays).
469 VB
->ClipPtr
= &store
->results
[VERT_RESULT_HPOS
];
470 VB
->ClipPtr
->size
= 4;
471 VB
->ClipPtr
->count
= VB
->Count
;
473 VB
->ColorPtr
[0] = &store
->results
[VERT_RESULT_COL0
];
474 VB
->ColorPtr
[1] = &store
->results
[VERT_RESULT_BFC0
];
475 VB
->SecondaryColorPtr
[0] = &store
->results
[VERT_RESULT_COL1
];
476 VB
->SecondaryColorPtr
[1] = &store
->results
[VERT_RESULT_BFC1
];
477 VB
->FogCoordPtr
= &store
->results
[VERT_RESULT_FOGC
];
479 VB
->AttribPtr
[VERT_ATTRIB_COLOR0
] = &store
->results
[VERT_RESULT_COL0
];
480 VB
->AttribPtr
[VERT_ATTRIB_COLOR1
] = &store
->results
[VERT_RESULT_COL1
];
481 VB
->AttribPtr
[VERT_ATTRIB_FOG
] = &store
->results
[VERT_RESULT_FOGC
];
482 VB
->AttribPtr
[_TNL_ATTRIB_POINTSIZE
] = &store
->results
[VERT_RESULT_PSIZ
];
484 for (i
= 0; i
< ctx
->Const
.MaxTextureCoordUnits
; i
++) {
486 VB
->AttribPtr
[_TNL_ATTRIB_TEX0
+ i
]
487 = &store
->results
[VERT_RESULT_TEX0
+ i
];
490 for (i
= 0; i
< ctx
->Const
.MaxVarying
; i
++) {
491 if (program
->Base
.OutputsWritten
& (1 << (VERT_RESULT_VAR0
+ i
))) {
492 /* Note: varying results get put into the generic attributes */
493 VB
->AttribPtr
[VERT_ATTRIB_GENERIC0
+i
]
494 = &store
->results
[VERT_RESULT_VAR0
+ i
];
499 /* Perform NDC and cliptest operations:
501 return do_ndc_cliptest(ctx
, store
);
506 * Called the first time stage->run is called. In effect, don't
507 * allocate data until the first time the stage is run.
510 init_vp(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
512 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
513 struct vertex_buffer
*VB
= &(tnl
->vb
);
514 struct vp_stage_data
*store
;
515 const GLuint size
= VB
->Size
;
518 stage
->privatePtr
= MALLOC(sizeof(*store
));
519 store
= VP_STAGE_DATA(stage
);
523 /* Allocate arrays of vertex output values */
524 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
525 _mesa_vector4f_alloc( &store
->results
[i
], 0, size
, 32 );
526 store
->results
[i
].size
= 4;
529 /* a few other misc allocations */
530 _mesa_vector4f_alloc( &store
->ndcCoords
, 0, size
, 32 );
531 store
->clipmask
= (GLubyte
*) ALIGN_MALLOC(sizeof(GLubyte
)*size
, 32 );
538 * Destructor for this pipeline stage.
541 dtr(struct tnl_pipeline_stage
*stage
)
543 struct vp_stage_data
*store
= VP_STAGE_DATA(stage
);
548 /* free the vertex program result arrays */
549 for (i
= 0; i
< VERT_RESULT_MAX
; i
++)
550 _mesa_vector4f_free( &store
->results
[i
] );
552 /* free misc arrays */
553 _mesa_vector4f_free( &store
->ndcCoords
);
554 ALIGN_FREE( store
->clipmask
);
557 stage
->privatePtr
= NULL
;
563 validate_vp_stage(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
565 if (ctx
->VertexProgram
._Current
) {
566 _swrast_update_texture_samplers(ctx
);
573 * Public description of this pipeline stage.
575 const struct tnl_pipeline_stage _tnl_vertex_program_stage
=
578 NULL
, /* private_data */
579 init_vp
, /* create */
581 validate_vp_stage
, /* validate */
582 run_vp
/* run -- initially set to ctr */