2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2005 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.
26 * \file t_arb_program.c
27 * Compile vertex programs to an intermediate representation.
28 * Execute vertex programs over a buffer of vertices.
29 * \author Keith Whitwell, Brian Paul
37 #include "arbprogparse.h"
40 #include "math/m_matrix.h"
41 #include "math/m_translate.h"
42 #include "t_context.h"
43 #include "t_pipeline.h"
44 #include "t_vb_arbprogram.h"
50 /*--------------------------------------------------------------------------- */
55 void (*print
)( union instruction
, const struct opcode_info
* );
60 union instruction
*csr
;
64 #define ARB_VP_MACHINE(stage) ((struct arb_vp_machine *)(stage->privatePtr))
66 #define PUFF(x) ((x)[1] = (x)[2] = (x)[3] = (x)[0])
70 /* Lower precision functions for the EXP, LOG and LIT opcodes. The
71 * LOG2() implementation is probably not accurate enough, and the
72 * attempted optimization for Exp2 is definitely not accurate
73 * enough - it discards all of t's fractional bits!
75 static GLfloat
RoughApproxLog2(GLfloat t
)
80 static GLfloat
RoughApproxExp2(GLfloat t
)
85 fi
.i
= (fi
.i
<< 23) + 0x3f800000;
88 return (GLfloat
) _mesa_pow(2.0, t
);
92 static GLfloat
RoughApproxPower(GLfloat x
, GLfloat y
)
94 if (x
== 0.0 && y
== 0.0)
95 return 1.0; /* spec requires this */
97 return RoughApproxExp2(y
* RoughApproxLog2(x
));
101 /* Higher precision functions for the EX2, LG2 and POW opcodes:
103 static GLfloat
ApproxLog2(GLfloat t
)
105 return (GLfloat
) (LOGF(t
) * 1.442695F
);
108 static GLfloat
ApproxExp2(GLfloat t
)
110 return (GLfloat
) _mesa_pow(2.0, t
);
113 static GLfloat
ApproxPower(GLfloat x
, GLfloat y
)
115 return (GLfloat
) _mesa_pow(x
, y
);
118 static GLfloat
rough_approx_log2_0_1(GLfloat x
)
127 * Perform a reduced swizzle:
129 static void do_RSW( struct arb_vp_machine
*m
, union instruction op
)
131 GLfloat
*result
= m
->File
[0][op
.rsw
.dst
];
132 const GLfloat
*arg0
= m
->File
[op
.rsw
.file0
][op
.rsw
.idx0
];
133 GLuint swz
= op
.rsw
.swz
;
134 GLuint neg
= op
.rsw
.neg
;
137 /* Need a temporary to be correct in the case where result == arg0.
141 result
[0] = tmp
[GET_RSW(swz
, 0)];
142 result
[1] = tmp
[GET_RSW(swz
, 1)];
143 result
[2] = tmp
[GET_RSW(swz
, 2)];
144 result
[3] = tmp
[GET_RSW(swz
, 3)];
147 if (neg
& 0x1) result
[0] = -result
[0];
148 if (neg
& 0x2) result
[1] = -result
[1];
149 if (neg
& 0x4) result
[2] = -result
[2];
150 if (neg
& 0x8) result
[3] = -result
[3];
154 /* Used to implement write masking. To make things easier for the sse
155 * generator I've gone back to a 1 argument version of this function
156 * (dst.msk = arg), rather than the semantically cleaner (dst = SEL
159 * That means this is the only instruction which doesn't write a full
160 * 4 dwords out. This would make such a program harder to analyse,
161 * but it looks like analysis is going to take place on a higher level
164 static void do_MSK( struct arb_vp_machine
*m
, union instruction op
)
166 GLfloat
*dst
= m
->File
[0][op
.msk
.dst
];
167 const GLfloat
*arg
= m
->File
[op
.msk
.file
][op
.msk
.idx
];
169 if (op
.msk
.mask
& 0x1) dst
[0] = arg
[0];
170 if (op
.msk
.mask
& 0x2) dst
[1] = arg
[1];
171 if (op
.msk
.mask
& 0x4) dst
[2] = arg
[2];
172 if (op
.msk
.mask
& 0x8) dst
[3] = arg
[3];
176 static void do_PRT( struct arb_vp_machine
*m
, union instruction op
)
178 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
180 _mesa_printf("%d: %f %f %f %f\n", m
->vtx_nr
,
181 arg0
[0], arg0
[1], arg0
[2], arg0
[3]);
186 * The traditional ALU and texturing instructions. All operate on
187 * internal registers and ignore write masks and swizzling issues.
190 static void do_ABS( struct arb_vp_machine
*m
, union instruction op
)
192 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
193 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
195 result
[0] = (arg0
[0] < 0.0) ? -arg0
[0] : arg0
[0];
196 result
[1] = (arg0
[1] < 0.0) ? -arg0
[1] : arg0
[1];
197 result
[2] = (arg0
[2] < 0.0) ? -arg0
[2] : arg0
[2];
198 result
[3] = (arg0
[3] < 0.0) ? -arg0
[3] : arg0
[3];
201 static void do_ADD( struct arb_vp_machine
*m
, union instruction op
)
203 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
204 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
205 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
207 result
[0] = arg0
[0] + arg1
[0];
208 result
[1] = arg0
[1] + arg1
[1];
209 result
[2] = arg0
[2] + arg1
[2];
210 result
[3] = arg0
[3] + arg1
[3];
214 static void do_DP3( struct arb_vp_machine
*m
, union instruction op
)
216 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
217 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
218 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
220 result
[0] = (arg0
[0] * arg1
[0] +
229 static void do_DP4( struct arb_vp_machine
*m
, union instruction op
)
231 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
232 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
233 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
235 result
[0] = (arg0
[0] * arg1
[0] +
243 static void do_DPH( struct arb_vp_machine
*m
, union instruction op
)
245 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
246 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
247 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
249 result
[0] = (arg0
[0] * arg1
[0] +
257 static void do_DST( struct arb_vp_machine
*m
, union instruction op
)
259 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
260 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
261 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
263 /* This should be ok even if result == arg0 or result == arg1.
266 result
[1] = arg0
[1] * arg1
[1];
272 /* Intended to be high precision:
274 static void do_EX2( struct arb_vp_machine
*m
, union instruction op
)
276 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
277 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
279 result
[0] = (GLfloat
)ApproxExp2(arg0
[0]);
284 /* Allowed to be lower precision:
286 static void do_EXP( struct arb_vp_machine
*m
, union instruction op
)
288 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
289 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
290 GLfloat tmp
= arg0
[0];
291 GLfloat flr_tmp
= FLOORF(tmp
);
292 GLfloat frac_tmp
= tmp
- flr_tmp
;
294 result
[0] = LDEXPF(1.0, (int)flr_tmp
);
295 result
[1] = frac_tmp
;
296 result
[2] = LDEXPF(rough_approx_log2_0_1(frac_tmp
), (int)flr_tmp
);
300 static void do_FLR( struct arb_vp_machine
*m
, union instruction op
)
302 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
303 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
305 result
[0] = FLOORF(arg0
[0]);
306 result
[1] = FLOORF(arg0
[1]);
307 result
[2] = FLOORF(arg0
[2]);
308 result
[3] = FLOORF(arg0
[3]);
311 static void do_FRC( struct arb_vp_machine
*m
, union instruction op
)
313 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
314 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
316 result
[0] = arg0
[0] - FLOORF(arg0
[0]);
317 result
[1] = arg0
[1] - FLOORF(arg0
[1]);
318 result
[2] = arg0
[2] - FLOORF(arg0
[2]);
319 result
[3] = arg0
[3] - FLOORF(arg0
[3]);
322 /* High precision log base 2:
324 static void do_LG2( struct arb_vp_machine
*m
, union instruction op
)
326 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
327 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
329 result
[0] = ApproxLog2(arg0
[0]);
335 static void do_LIT( struct arb_vp_machine
*m
, union instruction op
)
337 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
338 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
344 tmp
[2] = RoughApproxPower(arg0
[1], arg0
[3]);
352 COPY_4V(result
, tmp
);
356 /* Intended to allow a lower precision than required for LG2 above.
358 static void do_LOG( struct arb_vp_machine
*m
, union instruction op
)
360 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
361 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
362 GLfloat tmp
= FABSF(arg0
[0]);
364 GLfloat mantissa
= FREXPF(tmp
, &exponent
);
366 result
[0] = (GLfloat
) (exponent
- 1);
367 result
[1] = 2.0 * mantissa
; /* map [.5, 1) -> [1, 2) */
368 result
[2] = exponent
+ LOG2(mantissa
);
372 static void do_MAX( struct arb_vp_machine
*m
, union instruction op
)
374 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
375 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
376 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
378 result
[0] = (arg0
[0] > arg1
[0]) ? arg0
[0] : arg1
[0];
379 result
[1] = (arg0
[1] > arg1
[1]) ? arg0
[1] : arg1
[1];
380 result
[2] = (arg0
[2] > arg1
[2]) ? arg0
[2] : arg1
[2];
381 result
[3] = (arg0
[3] > arg1
[3]) ? arg0
[3] : arg1
[3];
385 static void do_MIN( struct arb_vp_machine
*m
, union instruction op
)
387 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
388 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
389 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
391 result
[0] = (arg0
[0] < arg1
[0]) ? arg0
[0] : arg1
[0];
392 result
[1] = (arg0
[1] < arg1
[1]) ? arg0
[1] : arg1
[1];
393 result
[2] = (arg0
[2] < arg1
[2]) ? arg0
[2] : arg1
[2];
394 result
[3] = (arg0
[3] < arg1
[3]) ? arg0
[3] : arg1
[3];
397 static void do_MOV( struct arb_vp_machine
*m
, union instruction op
)
399 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
400 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
408 static void do_MUL( struct arb_vp_machine
*m
, union instruction op
)
410 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
411 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
412 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
414 result
[0] = arg0
[0] * arg1
[0];
415 result
[1] = arg0
[1] * arg1
[1];
416 result
[2] = arg0
[2] * arg1
[2];
417 result
[3] = arg0
[3] * arg1
[3];
421 /* Intended to be "high" precision
423 static void do_POW( struct arb_vp_machine
*m
, union instruction op
)
425 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
426 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
427 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
429 result
[0] = (GLfloat
)ApproxPower(arg0
[0], arg1
[0]);
433 static void do_REL( struct arb_vp_machine
*m
, union instruction op
)
435 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
436 GLuint idx
= (op
.alu
.idx0
+ (GLint
)m
->File
[0][REG_ADDR
][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS
-1);
437 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][idx
];
445 static void do_RCP( struct arb_vp_machine
*m
, union instruction op
)
447 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
448 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
450 result
[0] = 1.0F
/ arg0
[0];
454 static void do_RSQ( struct arb_vp_machine
*m
, union instruction op
)
456 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
457 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
459 result
[0] = INV_SQRTF(FABSF(arg0
[0]));
464 static void do_SGE( struct arb_vp_machine
*m
, union instruction op
)
466 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
467 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
468 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
470 result
[0] = (arg0
[0] >= arg1
[0]) ? 1.0F
: 0.0F
;
471 result
[1] = (arg0
[1] >= arg1
[1]) ? 1.0F
: 0.0F
;
472 result
[2] = (arg0
[2] >= arg1
[2]) ? 1.0F
: 0.0F
;
473 result
[3] = (arg0
[3] >= arg1
[3]) ? 1.0F
: 0.0F
;
477 static void do_SLT( struct arb_vp_machine
*m
, union instruction op
)
479 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
480 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
481 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
483 result
[0] = (arg0
[0] < arg1
[0]) ? 1.0F
: 0.0F
;
484 result
[1] = (arg0
[1] < arg1
[1]) ? 1.0F
: 0.0F
;
485 result
[2] = (arg0
[2] < arg1
[2]) ? 1.0F
: 0.0F
;
486 result
[3] = (arg0
[3] < arg1
[3]) ? 1.0F
: 0.0F
;
489 static void do_SUB( struct arb_vp_machine
*m
, union instruction op
)
491 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
492 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
493 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
495 result
[0] = arg0
[0] - arg1
[0];
496 result
[1] = arg0
[1] - arg1
[1];
497 result
[2] = arg0
[2] - arg1
[2];
498 result
[3] = arg0
[3] - arg1
[3];
502 static void do_XPD( struct arb_vp_machine
*m
, union instruction op
)
504 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
505 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
506 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
509 tmp
[0] = arg0
[1] * arg1
[2] - arg0
[2] * arg1
[1];
510 tmp
[1] = arg0
[2] * arg1
[0] - arg0
[0] * arg1
[2];
511 tmp
[2] = arg0
[0] * arg1
[1] - arg0
[1] * arg1
[0];
513 /* Need a temporary to be correct in the case where result == arg0
521 static void do_NOP( struct arb_vp_machine
*m
, union instruction op
)
525 /* Some useful debugging functions:
527 static void print_mask( GLuint mask
)
530 if (mask
&0x1) _mesa_printf("x");
531 if (mask
&0x2) _mesa_printf("y");
532 if (mask
&0x4) _mesa_printf("z");
533 if (mask
&0x8) _mesa_printf("w");
536 static void print_reg( GLuint file
, GLuint reg
)
538 static const char *reg_file
[] = {
548 else if (reg
>= REG_ARG0
&& reg
<= REG_ARG1
)
549 _mesa_printf("ARG%d", reg
- REG_ARG0
);
550 else if (reg
>= REG_TMP0
&& reg
<= REG_TMP11
)
551 _mesa_printf("TMP%d", reg
- REG_TMP0
);
552 else if (reg
>= REG_IN0
&& reg
<= REG_IN31
)
553 _mesa_printf("IN%d", reg
- REG_IN0
);
554 else if (reg
>= REG_OUT0
&& reg
<= REG_OUT14
)
555 _mesa_printf("OUT%d", reg
- REG_OUT0
);
556 else if (reg
== REG_ADDR
)
557 _mesa_printf("ADDR");
558 else if (reg
== REG_ID
)
561 _mesa_printf("REG%d", reg
);
564 _mesa_printf("%s:%d", reg_file
[file
], reg
);
568 static void print_RSW( union instruction op
, const struct opcode_info
*info
)
570 GLuint swz
= op
.rsw
.swz
;
571 GLuint neg
= op
.rsw
.neg
;
574 _mesa_printf("%s ", info
->string
);
575 print_reg(0, op
.rsw
.dst
);
577 print_reg(op
.rsw
.file0
, op
.rsw
.idx0
);
579 for (i
= 0; i
< 4; i
++, swz
>>= 2) {
580 const char *cswz
= "xyzw";
583 _mesa_printf("%c", cswz
[swz
&0x3]);
589 static void print_ALU( union instruction op
, const struct opcode_info
*info
)
591 _mesa_printf("%s ", info
->string
);
592 print_reg(0, op
.alu
.dst
);
594 print_reg(op
.alu
.file0
, op
.alu
.idx0
);
595 if (info
->nr_args
> 1) {
597 print_reg(op
.alu
.file1
, op
.alu
.idx1
);
602 static void print_MSK( union instruction op
, const struct opcode_info
*info
)
604 _mesa_printf("%s ", info
->string
);
605 print_reg(0, op
.msk
.dst
);
606 print_mask(op
.msk
.mask
);
608 print_reg(op
.msk
.file
, op
.msk
.idx
);
613 static void print_NOP( union instruction op
, const struct opcode_info
*info
)
621 static const struct opcode_info opcode_info
[] =
623 { 1, "ABS", print_ALU
},
624 { 2, "ADD", print_ALU
},
625 { 1, "ARL", print_NOP
},
626 { 2, "DP3", print_ALU
},
627 { 2, "DP4", print_ALU
},
628 { 2, "DPH", print_ALU
},
629 { 2, "DST", print_ALU
},
630 { 0, "END", print_NOP
},
631 { 1, "EX2", print_ALU
},
632 { 1, "EXP", print_ALU
},
633 { 1, "FLR", print_ALU
},
634 { 1, "FRC", print_ALU
},
635 { 1, "LG2", print_ALU
},
636 { 1, "LIT", print_ALU
},
637 { 1, "LOG", print_ALU
},
638 { 3, "MAD", print_NOP
},
639 { 2, "MAX", print_ALU
},
640 { 2, "MIN", print_ALU
},
641 { 1, "MOV", print_ALU
},
642 { 2, "MUL", print_ALU
},
643 { 2, "POW", print_ALU
},
644 { 1, "PRT", print_ALU
}, /* PRINT */
645 { 1, "RCC", print_NOP
},
646 { 1, "RCP", print_ALU
},
647 { 1, "RSQ", print_ALU
},
648 { 2, "SGE", print_ALU
},
649 { 2, "SLT", print_ALU
},
650 { 2, "SUB", print_ALU
},
651 { 1, "SWZ", print_NOP
},
652 { 2, "XPD", print_ALU
},
653 { 1, "RSW", print_RSW
},
654 { 2, "MSK", print_MSK
},
655 { 1, "REL", print_ALU
},
658 void _tnl_disassem_vba_insn( union instruction op
)
660 const struct opcode_info
*info
= &opcode_info
[op
.alu
.opcode
];
661 info
->print( op
, info
);
665 static void (* const opcode_func
[])(struct arb_vp_machine
*, union instruction
) =
702 static union instruction
*cvp_next_instruction( struct compilation
*cp
)
704 union instruction
*op
= cp
->csr
++;
709 static struct reg
cvp_make_reg( GLuint file
, GLuint idx
)
717 static struct reg
cvp_emit_rel( struct compilation
*cp
,
721 union instruction
*op
= cvp_next_instruction(cp
);
722 op
->alu
.opcode
= REL
;
723 op
->alu
.file0
= reg
.file
;
724 op
->alu
.idx0
= reg
.idx
;
725 op
->alu
.dst
= tmpreg
.idx
;
730 static struct reg
cvp_load_reg( struct compilation
*cp
,
736 struct reg tmpreg
= cvp_make_reg(FILE_REG
, tmpidx
);
740 case PROGRAM_TEMPORARY
:
741 return cvp_make_reg(FILE_REG
, REG_TMP0
+ index
);
744 return cvp_make_reg(FILE_REG
, REG_IN0
+ index
);
747 return cvp_make_reg(FILE_REG
, REG_OUT0
+ index
);
749 /* These two aren't populated by the parser?
751 case PROGRAM_LOCAL_PARAM
:
752 reg
= cvp_make_reg(FILE_LOCAL_PARAM
, index
);
754 return cvp_emit_rel(cp
, reg
, tmpreg
);
758 case PROGRAM_ENV_PARAM
:
759 reg
= cvp_make_reg(FILE_ENV_PARAM
, index
);
761 return cvp_emit_rel(cp
, reg
, tmpreg
);
765 case PROGRAM_STATE_VAR
:
766 reg
= cvp_make_reg(FILE_STATE_PARAM
, index
);
768 return cvp_emit_rel(cp
, reg
, tmpreg
);
774 case PROGRAM_WRITE_ONLY
:
775 case PROGRAM_ADDRESS
:
778 return tmpreg
; /* can't happen */
782 static struct reg
cvp_emit_arg( struct compilation
*cp
,
783 const struct vp_src_register
*src
,
786 struct reg reg
= cvp_load_reg( cp
, src
->File
, src
->Index
, src
->RelAddr
, arg
);
787 union instruction rsw
, noop
;
789 /* Emit any necessary swizzling.
792 rsw
.rsw
.neg
= src
->Negate
? WRITEMASK_XYZW
: 0;
794 /* we're expecting 2-bit swizzles below... */
795 ASSERT(GET_SWZ(src
->Swizzle
, 0) < 4);
796 ASSERT(GET_SWZ(src
->Swizzle
, 1) < 4);
797 ASSERT(GET_SWZ(src
->Swizzle
, 2) < 4);
798 ASSERT(GET_SWZ(src
->Swizzle
, 3) < 4);
800 rsw
.rsw
.swz
= ((GET_SWZ(src
->Swizzle
, 0) << 0) |
801 (GET_SWZ(src
->Swizzle
, 1) << 2) |
802 (GET_SWZ(src
->Swizzle
, 2) << 4) |
803 (GET_SWZ(src
->Swizzle
, 3) << 6));
807 noop
.rsw
.swz
= RSW_NOOP
;
809 if (rsw
.dword
!= noop
.dword
) {
810 union instruction
*op
= cvp_next_instruction(cp
);
811 struct reg rsw_reg
= cvp_make_reg(FILE_REG
, REG_ARG0
+ arg
);
812 op
->dword
= rsw
.dword
;
813 op
->rsw
.opcode
= RSW
;
814 op
->rsw
.file0
= reg
.file
;
815 op
->rsw
.idx0
= reg
.idx
;
816 op
->rsw
.dst
= rsw_reg
.idx
;
823 static GLuint
cvp_choose_result( struct compilation
*cp
,
824 const struct vp_dst_register
*dst
,
825 union instruction
*fixup
)
827 GLuint mask
= dst
->WriteMask
;
831 case PROGRAM_TEMPORARY
:
832 idx
= REG_TMP0
+ dst
->Index
;
835 idx
= REG_OUT0
+ dst
->Index
;
839 return REG_RES
; /* can't happen */
842 /* Optimization: When writing (with a writemask) to an undefined
843 * value for the first time, the writemask may be ignored.
845 if (mask
!= WRITEMASK_XYZW
&& (cp
->reg_active
& (1 << idx
))) {
846 fixup
->msk
.opcode
= MSK
;
847 fixup
->msk
.dst
= idx
;
848 fixup
->msk
.file
= FILE_REG
;
849 fixup
->msk
.idx
= REG_RES
;
850 fixup
->msk
.mask
= mask
;
851 cp
->reg_active
|= 1 << idx
;
856 cp
->reg_active
|= 1 << idx
;
861 static struct reg
cvp_emit_rsw( struct compilation
*cp
,
870 if (swz
!= RSW_NOOP
|| neg
!= 0) {
871 union instruction
*op
= cvp_next_instruction(cp
);
872 op
->rsw
.opcode
= RSW
;
874 op
->rsw
.file0
= src
.file
;
875 op
->rsw
.idx0
= src
.idx
;
879 retval
.file
= FILE_REG
;
884 /* Oops. Degenerate case:
886 union instruction
*op
= cvp_next_instruction(cp
);
887 op
->alu
.opcode
= VP_OPCODE_MOV
;
889 op
->alu
.file0
= src
.file
;
890 op
->alu
.idx0
= src
.idx
;
892 retval
.file
= FILE_REG
;
902 static void cvp_emit_inst( struct compilation
*cp
,
903 const struct vp_instruction
*inst
)
905 const struct opcode_info
*info
= &opcode_info
[inst
->Opcode
];
906 union instruction
*op
;
907 union instruction fixup
;
911 assert(sizeof(*op
) == sizeof(GLuint
));
913 /* Need to handle SWZ, ARL specially.
915 switch (inst
->Opcode
) {
916 /* Split into mul and add:
919 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
920 for (i
= 0; i
< 3; i
++)
921 reg
[i
] = cvp_emit_arg( cp
, &inst
->SrcReg
[i
], REG_ARG0
+i
);
923 op
= cvp_next_instruction(cp
);
924 op
->alu
.opcode
= VP_OPCODE_MUL
;
925 op
->alu
.file0
= reg
[0].file
;
926 op
->alu
.idx0
= reg
[0].idx
;
927 op
->alu
.file1
= reg
[1].file
;
928 op
->alu
.idx1
= reg
[1].idx
;
929 op
->alu
.dst
= REG_ARG0
;
931 op
= cvp_next_instruction(cp
);
932 op
->alu
.opcode
= VP_OPCODE_ADD
;
933 op
->alu
.file0
= FILE_REG
;
934 op
->alu
.idx0
= REG_ARG0
;
935 op
->alu
.file1
= reg
[2].file
;
936 op
->alu
.idx1
= reg
[2].idx
;
937 op
->alu
.dst
= result
;
939 if (result
== REG_RES
) {
940 op
= cvp_next_instruction(cp
);
941 op
->dword
= fixup
.dword
;
946 reg
[0] = cvp_emit_arg( cp
, &inst
->SrcReg
[0], REG_ARG0
);
948 op
= cvp_next_instruction(cp
);
949 op
->alu
.opcode
= VP_OPCODE_FLR
;
950 op
->alu
.dst
= REG_ADDR
;
951 op
->alu
.file0
= reg
[0].file
;
952 op
->alu
.idx0
= reg
[0].idx
;
955 case VP_OPCODE_SWZ
: {
956 GLuint swz0
= 0, swz1
= 0;
957 GLuint neg0
= 0, neg1
= 0;
960 /* Translate 3-bit-per-element swizzle into two 2-bit swizzles,
961 * one from the source register the other from a constant
964 for (i
= 0; i
< 4; i
++) {
965 GLuint swzelt
= GET_SWZ(inst
->SrcReg
[0].Swizzle
, i
);
966 if (swzelt
>= SWIZZLE_ZERO
) {
967 neg0
|= inst
->SrcReg
[0].Negate
& (1<<i
);
968 if (swzelt
== SWIZZLE_ONE
)
969 swz0
|= SWIZZLE_W
<< (i
*2);
970 else if (i
< SWIZZLE_W
)
975 neg1
|= inst
->SrcReg
[0].Negate
& (1<<i
);
976 swz1
|= swzelt
<< (i
*2);
980 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
981 reg
[0].file
= FILE_REG
;
983 reg
[1] = cvp_emit_arg( cp
, &inst
->SrcReg
[0], REG_ARG0
);
985 if (mask
== WRITEMASK_XYZW
) {
986 cvp_emit_rsw(cp
, result
, reg
[0], neg0
, swz0
, GL_TRUE
);
989 else if (mask
== 0) {
990 cvp_emit_rsw(cp
, result
, reg
[1], neg1
, swz1
, GL_TRUE
);
993 cvp_emit_rsw(cp
, result
, reg
[0], neg0
, swz0
, GL_TRUE
);
994 reg
[1] = cvp_emit_rsw(cp
, REG_ARG0
, reg
[1], neg1
, swz1
, GL_FALSE
);
996 op
= cvp_next_instruction(cp
);
997 op
->msk
.opcode
= MSK
;
998 op
->msk
.dst
= result
;
999 op
->msk
.file
= reg
[1].file
;
1000 op
->msk
.idx
= reg
[1].idx
;
1001 op
->msk
.mask
= mask
;
1004 if (result
== REG_RES
) {
1005 op
= cvp_next_instruction(cp
);
1006 op
->dword
= fixup
.dword
;
1015 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
1016 for (i
= 0; i
< info
->nr_args
; i
++)
1017 reg
[i
] = cvp_emit_arg( cp
, &inst
->SrcReg
[i
], REG_ARG0
+ i
);
1019 op
= cvp_next_instruction(cp
);
1020 op
->alu
.opcode
= inst
->Opcode
;
1021 op
->alu
.file0
= reg
[0].file
;
1022 op
->alu
.idx0
= reg
[0].idx
;
1023 op
->alu
.file1
= reg
[1].file
;
1024 op
->alu
.idx1
= reg
[1].idx
;
1025 op
->alu
.dst
= result
;
1027 if (result
== REG_RES
) {
1028 op
= cvp_next_instruction(cp
);
1029 op
->dword
= fixup
.dword
;
1035 static void free_tnl_data( struct vertex_program
*program
)
1037 struct tnl_compiled_program
*p
= program
->TnlData
;
1038 if (p
->compiled_func
)
1039 _mesa_free((void *)p
->compiled_func
);
1041 program
->TnlData
= NULL
;
1044 static void compile_vertex_program( struct vertex_program
*program
,
1045 GLboolean try_codegen
)
1047 struct compilation cp
;
1048 struct tnl_compiled_program
*p
= CALLOC_STRUCT(tnl_compiled_program
);
1051 if (program
->TnlData
)
1052 free_tnl_data( program
);
1054 program
->TnlData
= p
;
1056 /* Initialize cp. Note that ctx and VB aren't used in compilation
1057 * so we don't have to worry about statechanges:
1059 _mesa_memset(&cp
, 0, sizeof(cp
));
1060 cp
.csr
= p
->instructions
;
1062 /* Compile instructions:
1064 for (i
= 0; i
< program
->Base
.NumInstructions
; i
++) {
1065 cvp_emit_inst(&cp
, &program
->Instructions
[i
]);
1070 p
->nr_instructions
= cp
.csr
- p
->instructions
;
1072 /* Print/disassemble:
1075 for (i
= 0; i
< p
->nr_instructions
; i
++) {
1076 _tnl_disassem_vba_insn(p
->instructions
[i
]);
1078 _mesa_printf("\n\n");
1083 _tnl_sse_codegen_vertex_program(p
);
1091 /* ----------------------------------------------------------------------
1094 static void userclip( GLcontext
*ctx
,
1097 GLubyte
*clipormask
,
1098 GLubyte
*clipandmask
)
1102 for (p
= 0; p
< ctx
->Const
.MaxClipPlanes
; p
++) {
1103 if (ctx
->Transform
.ClipPlanesEnabled
& (1 << p
)) {
1105 const GLfloat a
= ctx
->Transform
._ClipUserPlane
[p
][0];
1106 const GLfloat b
= ctx
->Transform
._ClipUserPlane
[p
][1];
1107 const GLfloat c
= ctx
->Transform
._ClipUserPlane
[p
][2];
1108 const GLfloat d
= ctx
->Transform
._ClipUserPlane
[p
][3];
1109 GLfloat
*coord
= (GLfloat
*)clip
->data
;
1110 GLuint stride
= clip
->stride
;
1111 GLuint count
= clip
->count
;
1113 for (nr
= 0, i
= 0 ; i
< count
; i
++) {
1114 GLfloat dp
= (coord
[0] * a
+
1121 clipmask
[i
] |= CLIP_USER_BIT
;
1124 STRIDE_F(coord
, stride
);
1128 *clipormask
|= CLIP_USER_BIT
;
1130 *clipandmask
|= CLIP_USER_BIT
;
1139 static GLboolean
do_ndc_cliptest( struct arb_vp_machine
*m
)
1141 GLcontext
*ctx
= m
->ctx
;
1142 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
1143 struct vertex_buffer
*VB
= m
->VB
;
1145 /* Cliptest and perspective divide. Clip functions must clear
1149 m
->andmask
= CLIP_ALL_BITS
;
1151 if (tnl
->NeedNdcCoords
) {
1153 _mesa_clip_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
1161 _mesa_clip_np_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
1169 /* All vertices are outside the frustum */
1173 /* Test userclip planes. This contributes to VB->ClipMask.
1175 if (ctx
->Transform
.ClipPlanesEnabled
&& !ctx
->VertexProgram
._Enabled
) {
1187 VB
->ClipAndMask
= m
->andmask
;
1188 VB
->ClipOrMask
= m
->ormask
;
1189 VB
->ClipMask
= m
->clipmask
;
1195 static INLINE
void call_func( struct tnl_compiled_program
*p
,
1196 struct arb_vp_machine
*m
)
1198 p
->compiled_func(m
);
1202 * Execute the given vertex program.
1204 * TODO: Integrate the t_vertex.c code here, to build machine vertices
1205 * directly at this point.
1207 * TODO: Eliminate the VB struct entirely and just use
1208 * struct arb_vertex_machine.
1211 run_arb_vertex_program(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
1213 struct vertex_program
*program
= (ctx
->VertexProgram
._Enabled
?
1214 ctx
->VertexProgram
.Current
:
1216 struct vertex_buffer
*VB
= &TNL_CONTEXT(ctx
)->vb
;
1217 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1218 struct tnl_compiled_program
*p
;
1219 GLuint i
, j
, outputs
;
1221 if (!program
|| program
->IsNVProgram
)
1224 if (program
->Parameters
) {
1225 _mesa_load_state_parameters(ctx
, program
->Parameters
);
1228 p
= (struct tnl_compiled_program
*)program
->TnlData
;
1232 m
->nr_inputs
= m
->nr_outputs
= 0;
1234 for (i
= 0; i
< _TNL_ATTRIB_MAX
; i
++) {
1235 if (program
->InputsRead
& (1<<i
)) {
1236 GLuint j
= m
->nr_inputs
++;
1237 m
->input
[j
].idx
= i
;
1238 m
->input
[j
].data
= (GLfloat
*)m
->VB
->AttribPtr
[i
]->data
;
1239 m
->input
[j
].stride
= m
->VB
->AttribPtr
[i
]->stride
;
1240 m
->input
[j
].size
= m
->VB
->AttribPtr
[i
]->size
;
1241 ASSIGN_4V(m
->File
[0][REG_IN0
+ i
], 0, 0, 0, 1);
1245 for (i
= 0; i
< 15; i
++) {
1246 if (program
->OutputsWritten
& (1<<i
)) {
1247 GLuint j
= m
->nr_outputs
++;
1248 m
->output
[j
].idx
= i
;
1249 m
->output
[j
].data
= (GLfloat
*)m
->attribs
[i
].data
;
1254 /* Run the actual program:
1256 for (m
->vtx_nr
= 0; m
->vtx_nr
< VB
->Count
; m
->vtx_nr
++) {
1257 for (j
= 0; j
< m
->nr_inputs
; j
++) {
1258 GLuint idx
= REG_IN0
+ m
->input
[j
].idx
;
1259 switch (m
->input
[j
].size
) {
1260 case 4: m
->File
[0][idx
][3] = m
->input
[j
].data
[3];
1261 case 3: m
->File
[0][idx
][2] = m
->input
[j
].data
[2];
1262 case 2: m
->File
[0][idx
][1] = m
->input
[j
].data
[1];
1263 case 1: m
->File
[0][idx
][0] = m
->input
[j
].data
[0];
1266 STRIDE_F(m
->input
[j
].data
, m
->input
[j
].stride
);
1269 if (p
->compiled_func
) {
1273 for (j
= 0; j
< p
->nr_instructions
; j
++) {
1274 union instruction inst
= p
->instructions
[j
];
1275 opcode_func
[inst
.alu
.opcode
]( m
, inst
);
1279 for (j
= 0; j
< m
->nr_outputs
; j
++) {
1280 GLuint idx
= REG_OUT0
+ m
->output
[j
].idx
;
1281 m
->output
[j
].data
[0] = m
->File
[0][idx
][0];
1282 m
->output
[j
].data
[1] = m
->File
[0][idx
][1];
1283 m
->output
[j
].data
[2] = m
->File
[0][idx
][2];
1284 m
->output
[j
].data
[3] = m
->File
[0][idx
][3];
1285 m
->output
[j
].data
+= 4;
1289 /* Setup the VB pointers so that the next pipeline stages get
1290 * their data from the right place (the program output arrays).
1292 * TODO: 1) Have tnl use these RESULT values for outputs rather
1293 * than trying to shoe-horn inputs and outputs into one set of
1296 * TODO: 2) Integrate t_vertex.c so that we just go straight ahead
1297 * and build machine vertices here.
1299 VB
->ClipPtr
= &m
->attribs
[VERT_RESULT_HPOS
];
1300 VB
->ClipPtr
->count
= VB
->Count
;
1302 outputs
= program
->OutputsWritten
;
1304 if (outputs
& (1<<VERT_RESULT_COL0
)) {
1305 VB
->ColorPtr
[0] = &m
->attribs
[VERT_RESULT_COL0
];
1306 VB
->AttribPtr
[VERT_ATTRIB_COLOR0
] = VB
->ColorPtr
[0];
1309 if (outputs
& (1<<VERT_RESULT_BFC0
)) {
1310 VB
->ColorPtr
[1] = &m
->attribs
[VERT_RESULT_BFC0
];
1313 if (outputs
& (1<<VERT_RESULT_COL1
)) {
1314 VB
->SecondaryColorPtr
[0] = &m
->attribs
[VERT_RESULT_COL1
];
1315 VB
->AttribPtr
[VERT_ATTRIB_COLOR1
] = VB
->SecondaryColorPtr
[0];
1318 if (outputs
& (1<<VERT_RESULT_BFC1
)) {
1319 VB
->SecondaryColorPtr
[1] = &m
->attribs
[VERT_RESULT_BFC1
];
1322 if (outputs
& (1<<VERT_RESULT_FOGC
)) {
1323 VB
->FogCoordPtr
= &m
->attribs
[VERT_RESULT_FOGC
];
1324 VB
->AttribPtr
[VERT_ATTRIB_FOG
] = VB
->FogCoordPtr
;
1327 if (outputs
& (1<<VERT_RESULT_PSIZ
)) {
1328 VB
->PointSizePtr
= &m
->attribs
[VERT_RESULT_PSIZ
];
1329 VB
->AttribPtr
[_TNL_ATTRIB_POINTSIZE
] = &m
->attribs
[VERT_RESULT_PSIZ
];
1332 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++) {
1333 if (outputs
& (1<<(VERT_RESULT_TEX0
+i
))) {
1334 VB
->TexCoordPtr
[i
] = &m
->attribs
[VERT_RESULT_TEX0
+ i
];
1335 VB
->AttribPtr
[VERT_ATTRIB_TEX0
+i
] = VB
->TexCoordPtr
[i
];
1340 for (i
= 0; i
< VB
->Count
; i
++) {
1341 printf("Out %d: %f %f %f %f %f %f %f %f\n", i
,
1342 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[0],
1343 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[1],
1344 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[2],
1345 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[3],
1346 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[0],
1347 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[1],
1348 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[2],
1349 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[3]);
1353 /* Perform NDC and cliptest operations:
1355 return do_ndc_cliptest(m
);
1360 validate_vertex_program( GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
1362 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1363 struct vertex_program
*program
=
1364 (ctx
->VertexProgram
._Enabled
? ctx
->VertexProgram
.Current
: 0);
1366 if (!program
&& ctx
->_MaintainTnlProgram
) {
1367 program
= ctx
->_TnlProgram
;
1371 if (!program
->TnlData
)
1372 compile_vertex_program( program
, m
->try_codegen
);
1374 /* Grab the state GL state and put into registers:
1376 m
->File
[FILE_LOCAL_PARAM
] = program
->Base
.LocalParams
;
1377 m
->File
[FILE_ENV_PARAM
] = ctx
->VertexProgram
.Parameters
;
1378 /* GL_NV_vertex_programs can't reference GL state */
1379 if (program
->Parameters
)
1380 m
->File
[FILE_STATE_PARAM
] = program
->Parameters
->ParameterValues
;
1382 m
->File
[FILE_STATE_PARAM
] = NULL
;
1393 * Called the first time stage->run is called. In effect, don't
1394 * allocate data until the first time the stage is run.
1396 static GLboolean
init_vertex_program( GLcontext
*ctx
,
1397 struct tnl_pipeline_stage
*stage
)
1399 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
1400 struct vertex_buffer
*VB
= &(tnl
->vb
);
1401 struct arb_vp_machine
*m
;
1402 const GLuint size
= VB
->Size
;
1405 stage
->privatePtr
= _mesa_malloc(sizeof(*m
));
1406 m
= ARB_VP_MACHINE(stage
);
1410 /* arb_vertex_machine struct should subsume the VB:
1415 m
->File
[0] = ALIGN_MALLOC(REG_MAX
* sizeof(GLfloat
) * 4, 16);
1417 /* Initialize regs where necessary:
1419 ASSIGN_4V(m
->File
[0][REG_ID
], 0, 0, 0, 1);
1420 ASSIGN_4V(m
->File
[0][REG_ONES
], 1, 1, 1, 1);
1421 ASSIGN_4V(m
->File
[0][REG_SWZ
], -1, 1, 0, 0);
1422 ASSIGN_4V(m
->File
[0][REG_NEG
], -1, -1, -1, -1);
1423 ASSIGN_4V(m
->File
[0][REG_LIT
], 1, 0, 0, 1);
1424 ASSIGN_4V(m
->File
[0][REG_LIT2
], 1, .5, .2, 1); /* debug value */
1426 if (_mesa_getenv("MESA_EXPERIMENTAL"))
1429 /* Allocate arrays of vertex output values */
1430 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
1431 _mesa_vector4f_alloc( &m
->attribs
[i
], 0, size
, 32 );
1432 m
->attribs
[i
].size
= 4;
1435 /* a few other misc allocations */
1436 _mesa_vector4f_alloc( &m
->ndcCoords
, 0, size
, 32 );
1437 m
->clipmask
= (GLubyte
*) ALIGN_MALLOC(sizeof(GLubyte
)*size
, 32 );
1439 if (ctx
->_MaintainTnlProgram
)
1440 _mesa_allow_light_in_model( ctx
, GL_FALSE
);
1442 m
->fpucntl_rnd_neg
= RND_NEG_FPU
; /* const value */
1443 m
->fpucntl_restore
= RESTORE_FPU
; /* const value */
1452 * Destructor for this pipeline stage.
1454 static void dtr( struct tnl_pipeline_stage
*stage
)
1456 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1461 /* free the vertex program result arrays */
1462 for (i
= 0; i
< VERT_RESULT_MAX
; i
++)
1463 _mesa_vector4f_free( &m
->attribs
[i
] );
1465 /* free misc arrays */
1466 _mesa_vector4f_free( &m
->ndcCoords
);
1467 ALIGN_FREE( m
->clipmask
);
1468 ALIGN_FREE( m
->File
[0] );
1471 stage
->privatePtr
= NULL
;
1476 * Public description of this pipeline stage.
1478 const struct tnl_pipeline_stage _tnl_arb_vertex_program_stage
=
1481 NULL
, /* private_data */
1482 init_vertex_program
, /* create */
1484 validate_vertex_program
, /* validate */
1485 run_arb_vertex_program
/* run */
1490 * Called via ctx->Driver.ProgramStringNotify() after a new vertex program
1491 * string has been parsed.
1494 _tnl_program_string(GLcontext
*ctx
, GLenum target
, struct program
*program
)
1496 if (program
->Target
== GL_VERTEX_PROGRAM_ARB
) {
1497 /* free any existing tnl data hanging off the program */
1498 struct vertex_program
*vprog
= (struct vertex_program
*) program
;
1499 if (vprog
->TnlData
) {
1500 free_tnl_data(vprog
);