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"
50 * XXX the texture sampling code in this module is a bit of a hack.
51 * The texture sampling code is in swrast, though it doesn't have any
52 * real dependencies on the rest of swrast. It should probably be
53 * moved into main/ someday.
57 vp_fetch_texel(GLcontext
*ctx
, const GLfloat texcoord
[4], GLfloat lambda
,
58 GLuint unit
, GLfloat color
[4])
61 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
63 /* XXX use a float-valued TextureSample routine here!!! */
64 swrast
->TextureSample
[unit
](ctx
, ctx
->Texture
.Unit
[unit
]._Current
,
65 1, (const GLfloat (*)[4]) texcoord
,
67 color
[0] = CHAN_TO_FLOAT(rgba
[0]);
68 color
[1] = CHAN_TO_FLOAT(rgba
[1]);
69 color
[2] = CHAN_TO_FLOAT(rgba
[2]);
70 color
[3] = CHAN_TO_FLOAT(rgba
[3]);
75 * Called via ctx->Driver.ProgramStringNotify() after a new vertex program
76 * string has been parsed.
79 _tnl_program_string(GLcontext
*ctx
, GLenum target
, struct gl_program
*program
)
82 * If we had derived anything from the program that was private to this
83 * stage we'd recompute/validate it here.
89 * Private storage for the vertex program pipeline stage.
91 struct vp_stage_data
{
92 /** The results of running the vertex program go into these arrays. */
93 GLvector4f results
[VERT_RESULT_MAX
];
95 GLvector4f ndcCoords
; /**< normalized device coords */
96 GLubyte
*clipmask
; /**< clip flags */
97 GLubyte ormask
, andmask
; /**< for clipping */
101 #define VP_STAGE_DATA(stage) ((struct vp_stage_data *)(stage->privatePtr))
105 * Initialize virtual machine state prior to executing vertex program.
108 init_machine(GLcontext
*ctx
, struct gl_program_machine
*machine
)
110 /* Input registers get initialized from the current vertex attribs */
111 MEMCPY(machine
->VertAttribs
, ctx
->Current
.Attrib
,
112 MAX_VERTEX_PROGRAM_ATTRIBS
* 4 * sizeof(GLfloat
));
114 if (ctx
->VertexProgram
._Current
->IsNVProgram
) {
116 /* Output/result regs are initialized to [0,0,0,1] */
117 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_OUTPUTS
; i
++) {
118 ASSIGN_4V(machine
->Outputs
[i
], 0.0F
, 0.0F
, 0.0F
, 1.0F
);
120 /* Temp regs are initialized to [0,0,0,0] */
121 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_TEMPS
; i
++) {
122 ASSIGN_4V(machine
->Temporaries
[i
], 0.0F
, 0.0F
, 0.0F
, 0.0F
);
124 for (i
= 0; i
< MAX_VERTEX_PROGRAM_ADDRESS_REGS
; i
++) {
125 ASSIGN_4V(machine
->AddressReg
[i
], 0, 0, 0, 0);
129 machine
->NumDeriv
= 0;
131 /* init condition codes */
132 machine
->CondCodes
[0] = COND_EQ
;
133 machine
->CondCodes
[1] = COND_EQ
;
134 machine
->CondCodes
[2] = COND_EQ
;
135 machine
->CondCodes
[3] = COND_EQ
;
137 /* init call stack */
138 machine
->StackDepth
= 0;
140 machine
->FetchTexelLod
= vp_fetch_texel
;
141 machine
->FetchTexelDeriv
= NULL
; /* not used by vertex programs */
146 * Copy the 16 elements of a matrix into four consecutive program
147 * registers starting at 'pos'.
150 load_matrix(GLfloat registers
[][4], GLuint pos
, const GLfloat mat
[16])
153 for (i
= 0; i
< 4; i
++) {
154 registers
[pos
+ i
][0] = mat
[0 + i
];
155 registers
[pos
+ i
][1] = mat
[4 + i
];
156 registers
[pos
+ i
][2] = mat
[8 + i
];
157 registers
[pos
+ i
][3] = mat
[12 + i
];
163 * As above, but transpose the matrix.
166 load_transpose_matrix(GLfloat registers
[][4], GLuint pos
,
167 const GLfloat mat
[16])
169 MEMCPY(registers
[pos
], mat
, 16 * sizeof(GLfloat
));
174 * Load current vertex program's parameter registers with tracked
175 * matrices (if NV program). This only needs to be done per
176 * glBegin/glEnd, not per-vertex.
179 _mesa_load_tracked_matrices(GLcontext
*ctx
)
183 for (i
= 0; i
< MAX_NV_VERTEX_PROGRAM_PARAMS
/ 4; i
++) {
184 /* point 'mat' at source matrix */
186 if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_MODELVIEW
) {
187 mat
= ctx
->ModelviewMatrixStack
.Top
;
189 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_PROJECTION
) {
190 mat
= ctx
->ProjectionMatrixStack
.Top
;
192 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_TEXTURE
) {
193 mat
= ctx
->TextureMatrixStack
[ctx
->Texture
.CurrentUnit
].Top
;
195 else if (ctx
->VertexProgram
.TrackMatrix
[i
] == GL_COLOR
) {
196 mat
= ctx
->ColorMatrixStack
.Top
;
198 else if (ctx
->VertexProgram
.TrackMatrix
[i
]==GL_MODELVIEW_PROJECTION_NV
) {
199 /* XXX verify the combined matrix is up to date */
200 mat
= &ctx
->_ModelProjectMatrix
;
202 else if (ctx
->VertexProgram
.TrackMatrix
[i
] >= GL_MATRIX0_NV
&&
203 ctx
->VertexProgram
.TrackMatrix
[i
] <= GL_MATRIX7_NV
) {
204 GLuint n
= ctx
->VertexProgram
.TrackMatrix
[i
] - GL_MATRIX0_NV
;
205 ASSERT(n
< MAX_PROGRAM_MATRICES
);
206 mat
= ctx
->ProgramMatrixStack
[n
].Top
;
209 /* no matrix is tracked, but we leave the register values as-is */
210 assert(ctx
->VertexProgram
.TrackMatrix
[i
] == GL_NONE
);
214 /* load the matrix values into sequential registers */
215 if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_IDENTITY_NV
) {
216 load_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->m
);
218 else if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_INVERSE_NV
) {
219 _math_matrix_analyse(mat
); /* update the inverse */
220 ASSERT(!_math_matrix_is_dirty(mat
));
221 load_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->inv
);
223 else if (ctx
->VertexProgram
.TrackMatrixTransform
[i
] == GL_TRANSPOSE_NV
) {
224 load_transpose_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->m
);
227 assert(ctx
->VertexProgram
.TrackMatrixTransform
[i
]
228 == GL_INVERSE_TRANSPOSE_NV
);
229 _math_matrix_analyse(mat
); /* update the inverse */
230 ASSERT(!_math_matrix_is_dirty(mat
));
231 load_transpose_matrix(ctx
->VertexProgram
.Parameters
, i
*4, mat
->inv
);
238 * This function executes vertex programs
241 run_vp( GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
243 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
244 struct vp_stage_data
*store
= VP_STAGE_DATA(stage
);
245 struct vertex_buffer
*VB
= &tnl
->vb
;
246 struct gl_vertex_program
*program
= ctx
->VertexProgram
._Current
;
247 struct gl_program_machine machine
;
248 GLuint outputs
[VERT_RESULT_MAX
], numOutputs
;
254 if (program
->IsNVProgram
) {
255 _mesa_load_tracked_matrices(ctx
);
258 /* ARB program or vertex shader */
259 _mesa_load_state_parameters(ctx
, program
->Base
.Parameters
);
263 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
264 if (program
->Base
.OutputsWritten
& (1 << i
)) {
265 outputs
[numOutputs
++] = i
;
269 for (i
= 0; i
< VB
->Count
; i
++) {
272 init_machine(ctx
, &machine
);
275 printf("Input %d: %f, %f, %f, %f\n", i
,
276 VB
->AttribPtr
[0]->data
[i
][0],
277 VB
->AttribPtr
[0]->data
[i
][1],
278 VB
->AttribPtr
[0]->data
[i
][2],
279 VB
->AttribPtr
[0]->data
[i
][3]);
280 printf(" color: %f, %f, %f, %f\n",
281 VB
->AttribPtr
[3]->data
[i
][0],
282 VB
->AttribPtr
[3]->data
[i
][1],
283 VB
->AttribPtr
[3]->data
[i
][2],
284 VB
->AttribPtr
[3]->data
[i
][3]);
285 printf(" normal: %f, %f, %f, %f\n",
286 VB
->AttribPtr
[2]->data
[i
][0],
287 VB
->AttribPtr
[2]->data
[i
][1],
288 VB
->AttribPtr
[2]->data
[i
][2],
289 VB
->AttribPtr
[2]->data
[i
][3]);
292 /* the vertex array case */
293 for (attr
= 0; attr
< VERT_ATTRIB_MAX
; attr
++) {
294 if (program
->Base
.InputsRead
& (1 << attr
)) {
295 const GLubyte
*ptr
= (const GLubyte
*) VB
->AttribPtr
[attr
]->data
;
296 const GLuint size
= VB
->AttribPtr
[attr
]->size
;
297 const GLuint stride
= VB
->AttribPtr
[attr
]->stride
;
298 const GLfloat
*data
= (GLfloat
*) (ptr
+ stride
* i
);
299 COPY_CLEAN_4V(machine
.VertAttribs
[attr
], size
, data
);
303 /* execute the program */
304 _mesa_execute_program(ctx
, &program
->Base
, &machine
);
306 /* copy the output registers into the VB->attribs arrays */
307 for (j
= 0; j
< numOutputs
; j
++) {
308 const GLuint attr
= outputs
[j
];
309 COPY_4V(store
->results
[attr
].data
[i
], machine
.Outputs
[attr
]);
312 printf("HPOS: %f %f %f %f\n",
313 machine
.Outputs
[0][0],
314 machine
.Outputs
[0][1],
315 machine
.Outputs
[0][2],
316 machine
.Outputs
[0][3]);
320 /* Fixup fog and point size results if needed */
321 if (program
->IsNVProgram
) {
322 if (ctx
->Fog
.Enabled
&&
323 (program
->Base
.OutputsWritten
& (1 << VERT_RESULT_FOGC
)) == 0) {
324 for (i
= 0; i
< VB
->Count
; i
++) {
325 store
->results
[VERT_RESULT_FOGC
].data
[i
][0] = 1.0;
329 if (ctx
->VertexProgram
.PointSizeEnabled
&&
330 (program
->Base
.OutputsWritten
& (1 << VERT_RESULT_PSIZ
)) == 0) {
331 for (i
= 0; i
< VB
->Count
; i
++) {
332 store
->results
[VERT_RESULT_PSIZ
].data
[i
][0] = ctx
->Point
.Size
;
337 /* Setup the VB pointers so that the next pipeline stages get
338 * their data from the right place (the program output arrays).
340 VB
->ClipPtr
= &store
->results
[VERT_RESULT_HPOS
];
341 VB
->ClipPtr
->size
= 4;
342 VB
->ClipPtr
->count
= VB
->Count
;
343 VB
->ColorPtr
[0] = &store
->results
[VERT_RESULT_COL0
];
344 VB
->ColorPtr
[1] = &store
->results
[VERT_RESULT_BFC0
];
345 VB
->SecondaryColorPtr
[0] = &store
->results
[VERT_RESULT_COL1
];
346 VB
->SecondaryColorPtr
[1] = &store
->results
[VERT_RESULT_BFC1
];
347 VB
->FogCoordPtr
= &store
->results
[VERT_RESULT_FOGC
];
349 VB
->AttribPtr
[VERT_ATTRIB_COLOR0
] = &store
->results
[VERT_RESULT_COL0
];
350 VB
->AttribPtr
[VERT_ATTRIB_COLOR1
] = &store
->results
[VERT_RESULT_COL1
];
351 VB
->AttribPtr
[VERT_ATTRIB_FOG
] = &store
->results
[VERT_RESULT_FOGC
];
352 VB
->AttribPtr
[_TNL_ATTRIB_POINTSIZE
] = &store
->results
[VERT_RESULT_PSIZ
];
354 for (i
= 0; i
< ctx
->Const
.MaxTextureCoordUnits
; i
++) {
356 VB
->AttribPtr
[_TNL_ATTRIB_TEX0
+ i
]
357 = &store
->results
[VERT_RESULT_TEX0
+ i
];
360 for (i
= 0; i
< ctx
->Const
.MaxVarying
; i
++) {
361 if (program
->Base
.OutputsWritten
& (1 << (VERT_RESULT_VAR0
+ i
))) {
362 /* Note: varying results get put into the generic attributes */
363 VB
->AttribPtr
[VERT_ATTRIB_GENERIC0
+i
]
364 = &store
->results
[VERT_RESULT_VAR0
+ i
];
368 /* Cliptest and perspective divide. Clip functions must clear
372 store
->andmask
= CLIP_FRUSTUM_BITS
;
374 if (tnl
->NeedNdcCoords
) {
376 _mesa_clip_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
384 _mesa_clip_np_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
391 if (store
->andmask
) /* All vertices are outside the frustum */
395 /* This is where we'd do clip testing against the user-defined
396 * clipping planes, but they're not supported by vertex programs.
399 VB
->ClipOrMask
= store
->ormask
;
400 VB
->ClipMask
= store
->clipmask
;
407 * Called the first time stage->run is called. In effect, don't
408 * allocate data until the first time the stage is run.
411 init_vp(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
413 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
414 struct vertex_buffer
*VB
= &(tnl
->vb
);
415 struct vp_stage_data
*store
;
416 const GLuint size
= VB
->Size
;
419 stage
->privatePtr
= MALLOC(sizeof(*store
));
420 store
= VP_STAGE_DATA(stage
);
424 /* Allocate arrays of vertex output values */
425 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
426 _mesa_vector4f_alloc( &store
->results
[i
], 0, size
, 32 );
427 store
->results
[i
].size
= 4;
430 /* a few other misc allocations */
431 _mesa_vector4f_alloc( &store
->ndcCoords
, 0, size
, 32 );
432 store
->clipmask
= (GLubyte
*) ALIGN_MALLOC(sizeof(GLubyte
)*size
, 32 );
439 * Destructor for this pipeline stage.
442 dtr(struct tnl_pipeline_stage
*stage
)
444 struct vp_stage_data
*store
= VP_STAGE_DATA(stage
);
449 /* free the vertex program result arrays */
450 for (i
= 0; i
< VERT_RESULT_MAX
; i
++)
451 _mesa_vector4f_free( &store
->results
[i
] );
453 /* free misc arrays */
454 _mesa_vector4f_free( &store
->ndcCoords
);
455 ALIGN_FREE( store
->clipmask
);
458 stage
->privatePtr
= NULL
;
464 validate_vp_stage(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
466 if (ctx
->VertexProgram
._Current
) {
467 _swrast_update_texture_samplers(ctx
);
474 * Public description of this pipeline stage.
476 const struct tnl_pipeline_stage _tnl_vertex_program_stage
=
479 NULL
, /* private_data */
480 init_vp
, /* create */
482 validate_vp_stage
, /* validate */
483 run_vp
/* run -- initially set to ctr */