2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2004 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"
39 #include "math/m_matrix.h"
40 #include "math/m_translate.h"
41 #include "t_context.h"
42 #include "t_pipeline.h"
43 #include "t_vp_build.h"
44 #include "t_vb_arbprogram.h"
48 /*--------------------------------------------------------------------------- */
53 void (*print
)( union instruction
, const struct opcode_info
* );
58 union instruction
*csr
;
59 struct vertex_buffer
*VB
; /* for input sizes! */
63 #define ARB_VP_MACHINE(stage) ((struct arb_vp_machine *)(stage->privatePtr))
68 * Set x to positive or negative infinity.
70 * XXX: FIXME - type punning.
72 #if defined(USE_IEEE) || defined(_WIN32)
73 #define SET_POS_INFINITY(x) ( *((GLuint *) (void *)&x) = 0x7F800000 )
74 #define SET_NEG_INFINITY(x) ( *((GLuint *) (void *)&x) = 0xFF800000 )
76 #define SET_POS_INFINITY(x) x = __MAXFLOAT
77 #define SET_NEG_INFINITY(x) x = -__MAXFLOAT
78 #define IS_INF_OR_NAN(t) ((t) == __MAXFLOAT)
80 #define SET_POS_INFINITY(x) x = (GLfloat) HUGE_VAL
81 #define SET_NEG_INFINITY(x) x = (GLfloat) -HUGE_VAL
84 #define FREXPF(a,b) frexpf(a,b)
86 #define PUFF(x) ((x)[1] = (x)[2] = (x)[3] = (x)[0])
88 /* FIXME: more type punning (despite use of fi_type...)
90 #define SET_FLOAT_BITS(x, bits) ((fi_type *) (void *) &(x))->i = bits
93 static GLfloat
RoughApproxLog2(GLfloat t
)
98 static GLfloat
RoughApproxPow2(GLfloat t
)
102 GLint ii
= (GLint
) t
;
103 ii
= (ii
< 23) + 0x3f800000;
104 SET_FLOAT_BITS(q
, ii
);
105 q
= *((GLfloat
*) (void *)&ii
);
107 q
= (GLfloat
) pow(2.0, floor_t0
);
112 static GLfloat
RoughApproxPower(GLfloat x
, GLfloat y
)
114 return (GLfloat
) _mesa_pow(x
, y
);
122 * Perform a reduced swizzle:
124 static void do_RSW( struct arb_vp_machine
*m
, union instruction op
)
126 GLfloat
*result
= m
->File
[0][op
.rsw
.dst
];
127 const GLfloat
*arg0
= m
->File
[op
.rsw
.file0
][op
.rsw
.idx0
];
128 GLuint swz
= op
.rsw
.swz
;
129 GLuint neg
= op
.rsw
.neg
;
131 result
[0] = arg0
[GET_RSW(swz
, 0)];
132 result
[1] = arg0
[GET_RSW(swz
, 1)];
133 result
[2] = arg0
[GET_RSW(swz
, 2)];
134 result
[3] = arg0
[GET_RSW(swz
, 3)];
137 if (neg
& 0x1) result
[0] = -result
[0];
138 if (neg
& 0x2) result
[1] = -result
[1];
139 if (neg
& 0x4) result
[2] = -result
[2];
140 if (neg
& 0x8) result
[3] = -result
[3];
144 /* Used to implement write masking. To make things easier for the sse
145 * generator I've gone back to a 1 argument version of this function
146 * (dst.msk = arg), rather than the semantically cleaner (dst = SEL
149 * That means this is the only instruction which doesn't write a full
150 * 4 dwords out. This would make such a program harder to analyse,
151 * but it looks like analysis is going to take place on a higher level
154 static void do_MSK( struct arb_vp_machine
*m
, union instruction op
)
156 GLfloat
*dst
= m
->File
[0][op
.msk
.dst
];
157 const GLfloat
*arg
= m
->File
[op
.msk
.file
][op
.msk
.idx
];
159 if (op
.msk
.mask
& 0x1) dst
[0] = arg
[0];
160 if (op
.msk
.mask
& 0x2) dst
[1] = arg
[1];
161 if (op
.msk
.mask
& 0x4) dst
[2] = arg
[2];
162 if (op
.msk
.mask
& 0x8) dst
[3] = arg
[3];
166 static void do_PRT( struct arb_vp_machine
*m
, union instruction op
)
168 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
170 _mesa_printf("%d: %f %f %f %f\n", m
->vtx_nr
,
171 arg0
[0], arg0
[1], arg0
[2], arg0
[3]);
176 * The traditional ALU and texturing instructions. All operate on
177 * internal registers and ignore write masks and swizzling issues.
180 static void do_ABS( struct arb_vp_machine
*m
, union instruction op
)
182 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
183 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
185 result
[0] = (arg0
[0] < 0.0) ? -arg0
[0] : arg0
[0];
186 result
[1] = (arg0
[1] < 0.0) ? -arg0
[1] : arg0
[1];
187 result
[2] = (arg0
[2] < 0.0) ? -arg0
[2] : arg0
[2];
188 result
[3] = (arg0
[3] < 0.0) ? -arg0
[3] : arg0
[3];
191 static void do_ADD( struct arb_vp_machine
*m
, union instruction op
)
193 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
194 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
195 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
197 result
[0] = arg0
[0] + arg1
[0];
198 result
[1] = arg0
[1] + arg1
[1];
199 result
[2] = arg0
[2] + arg1
[2];
200 result
[3] = arg0
[3] + arg1
[3];
204 static void do_DP3( struct arb_vp_machine
*m
, union instruction op
)
206 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
207 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
208 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
210 result
[0] = (arg0
[0] * arg1
[0] +
219 static void do_DP4( struct arb_vp_machine
*m
, union instruction op
)
221 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
222 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
223 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
225 result
[0] = (arg0
[0] * arg1
[0] +
233 static void do_DPH( struct arb_vp_machine
*m
, union instruction op
)
235 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
236 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
237 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
239 result
[0] = (arg0
[0] * arg1
[0] +
247 static void do_DST( struct arb_vp_machine
*m
, union instruction op
)
249 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
250 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
251 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
254 result
[1] = arg0
[1] * arg1
[1];
260 static void do_EX2( struct arb_vp_machine
*m
, union instruction op
)
262 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
263 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
265 result
[0] = (GLfloat
)RoughApproxPow2(arg0
[0]);
269 static void do_EXP( struct arb_vp_machine
*m
, union instruction op
)
271 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
272 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
273 GLfloat tmp
= arg0
[0];
274 GLfloat flr_tmp
= FLOORF(tmp
);
276 /* KW: nvvertexec has an optimized version of this which is pretty
277 * hard to understand/validate, but avoids the RoughApproxPow2.
279 result
[0] = (GLfloat
) (1 << (int)flr_tmp
);
280 result
[1] = tmp
- flr_tmp
;
281 result
[2] = RoughApproxPow2(tmp
);
285 static void do_FLR( struct arb_vp_machine
*m
, union instruction op
)
287 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
288 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
290 result
[0] = FLOORF(arg0
[0]);
291 result
[1] = FLOORF(arg0
[1]);
292 result
[2] = FLOORF(arg0
[2]);
293 result
[3] = FLOORF(arg0
[3]);
296 static void do_FRC( struct arb_vp_machine
*m
, union instruction op
)
298 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
299 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
301 result
[0] = arg0
[0] - FLOORF(arg0
[0]);
302 result
[1] = arg0
[1] - FLOORF(arg0
[1]);
303 result
[2] = arg0
[2] - FLOORF(arg0
[2]);
304 result
[3] = arg0
[3] - FLOORF(arg0
[3]);
307 static void do_LG2( struct arb_vp_machine
*m
, union instruction op
)
309 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
310 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
312 result
[0] = RoughApproxLog2(arg0
[0]);
318 static void do_LIT( struct arb_vp_machine
*m
, union instruction op
)
320 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
321 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
323 const GLfloat epsilon
= 1.0F
/ 256.0F
; /* per NV spec */
326 tmp
[0] = MAX2(arg0
[0], 0.0F
);
327 tmp
[1] = MAX2(arg0
[1], 0.0F
);
328 tmp
[3] = CLAMP(arg0
[3], -(128.0F
- epsilon
), (128.0F
- epsilon
));
332 result
[2] = (tmp
[0] > 0.0) ? RoughApproxPower(tmp
[1], tmp
[3]) : 0.0F
;
337 static void do_LOG( struct arb_vp_machine
*m
, union instruction op
)
339 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
340 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
341 GLfloat tmp
= FABSF(arg0
[0]);
343 GLfloat mantissa
= FREXPF(tmp
, &exponent
);
345 result
[0] = (GLfloat
) (exponent
- 1);
346 result
[1] = 2.0 * mantissa
; /* map [.5, 1) -> [1, 2) */
347 result
[2] = result
[0] + LOG2(result
[1]);
351 static void do_MAX( struct arb_vp_machine
*m
, union instruction op
)
353 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
354 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
355 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
357 result
[0] = (arg0
[0] > arg1
[0]) ? arg0
[0] : arg1
[0];
358 result
[1] = (arg0
[1] > arg1
[1]) ? arg0
[1] : arg1
[1];
359 result
[2] = (arg0
[2] > arg1
[2]) ? arg0
[2] : arg1
[2];
360 result
[3] = (arg0
[3] > arg1
[3]) ? arg0
[3] : arg1
[3];
364 static void do_MIN( struct arb_vp_machine
*m
, union instruction op
)
366 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
367 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
368 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
370 result
[0] = (arg0
[0] < arg1
[0]) ? arg0
[0] : arg1
[0];
371 result
[1] = (arg0
[1] < arg1
[1]) ? arg0
[1] : arg1
[1];
372 result
[2] = (arg0
[2] < arg1
[2]) ? arg0
[2] : arg1
[2];
373 result
[3] = (arg0
[3] < arg1
[3]) ? arg0
[3] : arg1
[3];
376 static void do_MOV( struct arb_vp_machine
*m
, union instruction op
)
378 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
379 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
387 static void do_MUL( struct arb_vp_machine
*m
, union instruction op
)
389 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
390 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
391 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
393 result
[0] = arg0
[0] * arg1
[0];
394 result
[1] = arg0
[1] * arg1
[1];
395 result
[2] = arg0
[2] * arg1
[2];
396 result
[3] = arg0
[3] * arg1
[3];
400 static void do_POW( struct arb_vp_machine
*m
, union instruction op
)
402 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
403 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
404 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
406 result
[0] = (GLfloat
)RoughApproxPower(arg0
[0], arg1
[0]);
410 static void do_REL( struct arb_vp_machine
*m
, union instruction op
)
412 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
413 GLuint idx
= (op
.alu
.idx0
+ (GLint
)m
->File
[0][REG_ADDR
][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS
-1);
414 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][idx
];
422 static void do_RCP( struct arb_vp_machine
*m
, union instruction op
)
424 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
425 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
427 result
[0] = 1.0F
/ arg0
[0];
431 static void do_RSQ( struct arb_vp_machine
*m
, union instruction op
)
433 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
434 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
436 result
[0] = INV_SQRTF(FABSF(arg0
[0]));
441 static void do_SGE( struct arb_vp_machine
*m
, union instruction op
)
443 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
444 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
445 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
447 result
[0] = (arg0
[0] >= arg1
[0]) ? 1.0F
: 0.0F
;
448 result
[1] = (arg0
[1] >= arg1
[1]) ? 1.0F
: 0.0F
;
449 result
[2] = (arg0
[2] >= arg1
[2]) ? 1.0F
: 0.0F
;
450 result
[3] = (arg0
[3] >= arg1
[3]) ? 1.0F
: 0.0F
;
454 static void do_SLT( 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
];
458 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
460 result
[0] = (arg0
[0] < arg1
[0]) ? 1.0F
: 0.0F
;
461 result
[1] = (arg0
[1] < arg1
[1]) ? 1.0F
: 0.0F
;
462 result
[2] = (arg0
[2] < arg1
[2]) ? 1.0F
: 0.0F
;
463 result
[3] = (arg0
[3] < arg1
[3]) ? 1.0F
: 0.0F
;
466 static void do_SUB( struct arb_vp_machine
*m
, union instruction op
)
468 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
469 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
470 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
472 result
[0] = arg0
[0] - arg1
[0];
473 result
[1] = arg0
[1] - arg1
[1];
474 result
[2] = arg0
[2] - arg1
[2];
475 result
[3] = arg0
[3] - arg1
[3];
479 static void do_XPD( struct arb_vp_machine
*m
, union instruction op
)
481 GLfloat
*result
= m
->File
[0][op
.alu
.dst
];
482 const GLfloat
*arg0
= m
->File
[op
.alu
.file0
][op
.alu
.idx0
];
483 const GLfloat
*arg1
= m
->File
[op
.alu
.file1
][op
.alu
.idx1
];
485 result
[0] = arg0
[1] * arg1
[2] - arg0
[2] * arg1
[1];
486 result
[1] = arg0
[2] * arg1
[0] - arg0
[0] * arg1
[2];
487 result
[2] = arg0
[0] * arg1
[1] - arg0
[1] * arg1
[0];
490 static void do_NOP( struct arb_vp_machine
*m
, union instruction op
)
494 /* Some useful debugging functions:
496 static void print_mask( GLuint mask
)
499 if (mask
&0x1) _mesa_printf("x");
500 if (mask
&0x2) _mesa_printf("y");
501 if (mask
&0x4) _mesa_printf("z");
502 if (mask
&0x8) _mesa_printf("w");
505 static void print_reg( GLuint file
, GLuint reg
)
507 static const char *reg_file
[] = {
517 else if (reg
>= REG_ARG0
&& reg
<= REG_ARG1
)
518 _mesa_printf("ARG%d", reg
- REG_ARG0
);
519 else if (reg
>= REG_TMP0
&& reg
<= REG_TMP11
)
520 _mesa_printf("TMP%d", reg
- REG_TMP0
);
521 else if (reg
>= REG_IN0
&& reg
<= REG_IN15
)
522 _mesa_printf("IN%d", reg
- REG_IN0
);
523 else if (reg
>= REG_OUT0
&& reg
<= REG_OUT14
)
524 _mesa_printf("OUT%d", reg
- REG_OUT0
);
525 else if (reg
== REG_ADDR
)
526 _mesa_printf("ADDR");
527 else if (reg
== REG_ID
)
530 _mesa_printf("REG%d", reg
);
533 _mesa_printf("%s:%d", reg_file
[file
], reg
);
537 static void print_RSW( union instruction op
, const struct opcode_info
*info
)
539 GLuint swz
= op
.rsw
.swz
;
540 GLuint neg
= op
.rsw
.neg
;
543 _mesa_printf("%s ", info
->string
);
544 print_reg(0, op
.rsw
.dst
);
546 print_reg(op
.rsw
.file0
, op
.rsw
.idx0
);
548 for (i
= 0; i
< 4; i
++, swz
>>= 2) {
549 const char *cswz
= "xyzw";
552 _mesa_printf("%c", cswz
[swz
&0x3]);
558 static void print_ALU( union instruction op
, const struct opcode_info
*info
)
560 _mesa_printf("%s ", info
->string
);
561 print_reg(0, op
.alu
.dst
);
563 print_reg(op
.alu
.file0
, op
.alu
.idx0
);
564 if (info
->nr_args
> 1) {
566 print_reg(op
.alu
.file1
, op
.alu
.idx1
);
571 static void print_MSK( union instruction op
, const struct opcode_info
*info
)
573 _mesa_printf("%s ", info
->string
);
574 print_reg(0, op
.msk
.dst
);
575 print_mask(op
.msk
.mask
);
577 print_reg(op
.msk
.file
, op
.msk
.idx
);
582 static void print_NOP( union instruction op
, const struct opcode_info
*info
)
590 static const struct opcode_info opcode_info
[] =
592 { 1, "ABS", print_ALU
},
593 { 2, "ADD", print_ALU
},
594 { 1, "ARL", print_NOP
},
595 { 2, "DP3", print_ALU
},
596 { 2, "DP4", print_ALU
},
597 { 2, "DPH", print_ALU
},
598 { 2, "DST", print_ALU
},
599 { 0, "END", print_NOP
},
600 { 1, "EX2", print_ALU
},
601 { 1, "EXP", print_ALU
},
602 { 1, "FLR", print_ALU
},
603 { 1, "FRC", print_ALU
},
604 { 1, "LG2", print_ALU
},
605 { 1, "LIT", print_ALU
},
606 { 1, "LOG", print_ALU
},
607 { 3, "MAD", print_NOP
},
608 { 2, "MAX", print_ALU
},
609 { 2, "MIN", print_ALU
},
610 { 1, "MOV", print_ALU
},
611 { 2, "MUL", print_ALU
},
612 { 2, "POW", print_ALU
},
613 { 1, "PRT", print_ALU
}, /* PRINT */
614 { 1, "RCC", print_NOP
},
615 { 1, "RCP", print_ALU
},
616 { 1, "RSQ", print_ALU
},
617 { 2, "SGE", print_ALU
},
618 { 2, "SLT", print_ALU
},
619 { 2, "SUB", print_ALU
},
620 { 1, "SWZ", print_NOP
},
621 { 2, "XPD", print_ALU
},
622 { 1, "RSW", print_RSW
},
623 { 2, "MSK", print_MSK
},
624 { 1, "REL", print_ALU
},
627 void _tnl_disassem_vba_insn( union instruction op
)
629 const struct opcode_info
*info
= &opcode_info
[op
.alu
.opcode
];
630 info
->print( op
, info
);
634 static void (* const opcode_func
[])(struct arb_vp_machine
*, union instruction
) =
671 static union instruction
*cvp_next_instruction( struct compilation
*cp
)
673 union instruction
*op
= cp
->csr
++;
678 static struct reg
cvp_make_reg( GLuint file
, GLuint idx
)
686 static struct reg
cvp_emit_rel( struct compilation
*cp
,
690 union instruction
*op
= cvp_next_instruction(cp
);
691 op
->alu
.opcode
= REL
;
692 op
->alu
.file0
= reg
.file
;
693 op
->alu
.idx0
= reg
.idx
;
694 op
->alu
.dst
= tmpreg
.idx
;
699 static struct reg
cvp_load_reg( struct compilation
*cp
,
705 struct reg tmpreg
= cvp_make_reg(FILE_REG
, tmpidx
);
709 case PROGRAM_TEMPORARY
:
710 return cvp_make_reg(FILE_REG
, REG_TMP0
+ index
);
713 return cvp_make_reg(FILE_REG
, REG_IN0
+ index
);
716 return cvp_make_reg(FILE_REG
, REG_OUT0
+ index
);
718 /* These two aren't populated by the parser?
720 case PROGRAM_LOCAL_PARAM
:
721 reg
= cvp_make_reg(FILE_LOCAL_PARAM
, index
);
723 return cvp_emit_rel(cp
, reg
, tmpreg
);
727 case PROGRAM_ENV_PARAM
:
728 reg
= cvp_make_reg(FILE_ENV_PARAM
, index
);
730 return cvp_emit_rel(cp
, reg
, tmpreg
);
734 case PROGRAM_STATE_VAR
:
735 reg
= cvp_make_reg(FILE_STATE_PARAM
, index
);
737 return cvp_emit_rel(cp
, reg
, tmpreg
);
743 case PROGRAM_WRITE_ONLY
:
744 case PROGRAM_ADDRESS
:
747 return tmpreg
; /* can't happen */
751 static struct reg
cvp_emit_arg( struct compilation
*cp
,
752 const struct vp_src_register
*src
,
755 struct reg reg
= cvp_load_reg( cp
, src
->File
, src
->Index
, src
->RelAddr
, arg
);
756 union instruction rsw
, noop
;
758 /* Emit any necessary swizzling.
761 rsw
.rsw
.neg
= src
->Negate
? WRITEMASK_XYZW
: 0;
762 rsw
.rsw
.swz
= ((GET_SWZ(src
->Swizzle
, 0) << 0) |
763 (GET_SWZ(src
->Swizzle
, 1) << 2) |
764 (GET_SWZ(src
->Swizzle
, 2) << 4) |
765 (GET_SWZ(src
->Swizzle
, 3) << 6));
769 noop
.rsw
.swz
= RSW_NOOP
;
771 if (rsw
.dword
!= noop
.dword
) {
772 union instruction
*op
= cvp_next_instruction(cp
);
773 struct reg rsw_reg
= cvp_make_reg(FILE_REG
, REG_ARG0
+ arg
);
774 op
->dword
= rsw
.dword
;
775 op
->rsw
.opcode
= RSW
;
776 op
->rsw
.file0
= reg
.file
;
777 op
->rsw
.idx0
= reg
.idx
;
778 op
->rsw
.dst
= rsw_reg
.idx
;
785 static GLuint
cvp_choose_result( struct compilation
*cp
,
786 const struct vp_dst_register
*dst
,
787 union instruction
*fixup
)
789 GLuint mask
= dst
->WriteMask
;
793 case PROGRAM_TEMPORARY
:
794 idx
= REG_TMP0
+ dst
->Index
;
797 idx
= REG_OUT0
+ dst
->Index
;
801 return REG_RES
; /* can't happen */
804 /* Optimization: When writing (with a writemask) to an undefined
805 * value for the first time, the writemask may be ignored.
807 if (mask
!= WRITEMASK_XYZW
&& (cp
->reg_active
& (1 << idx
))) {
808 fixup
->msk
.opcode
= MSK
;
809 fixup
->msk
.dst
= idx
;
810 fixup
->msk
.file
= FILE_REG
;
811 fixup
->msk
.idx
= REG_RES
;
812 fixup
->msk
.mask
= mask
;
813 cp
->reg_active
|= 1 << idx
;
818 cp
->reg_active
|= 1 << idx
;
823 static struct reg
cvp_emit_rsw( struct compilation
*cp
,
832 if (swz
!= RSW_NOOP
|| neg
!= 0) {
833 union instruction
*op
= cvp_next_instruction(cp
);
834 op
->rsw
.opcode
= RSW
;
836 op
->rsw
.file0
= src
.file
;
837 op
->rsw
.idx0
= src
.idx
;
841 retval
.file
= FILE_REG
;
846 /* Oops. Degenerate case:
848 union instruction
*op
= cvp_next_instruction(cp
);
849 op
->alu
.opcode
= VP_OPCODE_MOV
;
851 op
->alu
.file0
= src
.file
;
852 op
->alu
.idx0
= src
.idx
;
854 retval
.file
= FILE_REG
;
864 static void cvp_emit_inst( struct compilation
*cp
,
865 const struct vp_instruction
*inst
)
867 const struct opcode_info
*info
= &opcode_info
[inst
->Opcode
];
868 union instruction
*op
;
869 union instruction fixup
;
873 assert(sizeof(*op
) == sizeof(GLuint
));
875 /* Need to handle SWZ, ARL specially.
877 switch (inst
->Opcode
) {
878 /* Split into mul and add:
881 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
882 for (i
= 0; i
< 3; i
++)
883 reg
[i
] = cvp_emit_arg( cp
, &inst
->SrcReg
[i
], REG_ARG0
+i
);
885 op
= cvp_next_instruction(cp
);
886 op
->alu
.opcode
= VP_OPCODE_MUL
;
887 op
->alu
.file0
= reg
[0].file
;
888 op
->alu
.idx0
= reg
[0].idx
;
889 op
->alu
.file1
= reg
[1].file
;
890 op
->alu
.idx1
= reg
[1].idx
;
891 op
->alu
.dst
= REG_ARG0
;
893 op
= cvp_next_instruction(cp
);
894 op
->alu
.opcode
= VP_OPCODE_ADD
;
895 op
->alu
.file0
= FILE_REG
;
896 op
->alu
.idx0
= REG_ARG0
;
897 op
->alu
.file1
= reg
[2].file
;
898 op
->alu
.idx1
= reg
[2].idx
;
899 op
->alu
.dst
= result
;
903 reg
[0] = cvp_emit_arg( cp
, &inst
->SrcReg
[0], REG_ARG0
);
905 op
= cvp_next_instruction(cp
);
906 op
->alu
.opcode
= VP_OPCODE_FLR
;
907 op
->alu
.dst
= REG_ADDR
;
908 op
->alu
.file0
= reg
[0].file
;
909 op
->alu
.idx0
= reg
[0].idx
;
912 case VP_OPCODE_SWZ
: {
913 GLuint swz0
= 0, swz1
= 0;
914 GLuint neg0
= 0, neg1
= 0;
917 /* Translate 3-bit-per-element swizzle into two 2-bit swizzles,
918 * one from the source register the other from a constant
921 for (i
= 0; i
< 4; i
++) {
922 GLuint swzelt
= GET_SWZ(inst
->SrcReg
[0].Swizzle
, i
);
923 if (swzelt
>= SWIZZLE_ZERO
) {
924 neg0
|= inst
->SrcReg
[0].Negate
& (1<<i
);
925 if (swzelt
== SWIZZLE_ONE
)
926 swz0
|= SWIZZLE_W
<< (i
*2);
927 else if (i
< SWIZZLE_W
)
932 neg1
|= inst
->SrcReg
[0].Negate
& (1<<i
);
933 swz1
|= swzelt
<< (i
*2);
937 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
938 reg
[0].file
= FILE_REG
;
940 reg
[1] = cvp_emit_arg( cp
, &inst
->SrcReg
[0], REG_ARG0
);
942 if (mask
== WRITEMASK_XYZW
) {
943 cvp_emit_rsw(cp
, result
, reg
[0], neg0
, swz0
, GL_TRUE
);
946 else if (mask
== 0) {
947 cvp_emit_rsw(cp
, result
, reg
[1], neg1
, swz1
, GL_TRUE
);
950 cvp_emit_rsw(cp
, result
, reg
[0], neg0
, swz0
, GL_TRUE
);
951 reg
[1] = cvp_emit_rsw(cp
, REG_ARG0
, reg
[1], neg1
, swz1
, GL_FALSE
);
953 op
= cvp_next_instruction(cp
);
954 op
->msk
.opcode
= MSK
;
955 op
->msk
.dst
= result
;
956 op
->msk
.file
= reg
[1].file
;
957 op
->msk
.idx
= reg
[1].idx
;
961 if (result
== REG_RES
) {
962 op
= cvp_next_instruction(cp
);
963 op
->dword
= fixup
.dword
;
967 case VP_OPCODE_PRINT
:
972 result
= cvp_choose_result( cp
, &inst
->DstReg
, &fixup
);
973 for (i
= 0; i
< info
->nr_args
; i
++)
974 reg
[i
] = cvp_emit_arg( cp
, &inst
->SrcReg
[i
], REG_ARG0
+ i
);
976 op
= cvp_next_instruction(cp
);
977 op
->alu
.opcode
= inst
->Opcode
;
978 op
->alu
.file0
= reg
[0].file
;
979 op
->alu
.idx0
= reg
[0].idx
;
980 op
->alu
.file1
= reg
[1].file
;
981 op
->alu
.idx1
= reg
[1].idx
;
982 op
->alu
.dst
= result
;
984 if (result
== REG_RES
) {
985 op
= cvp_next_instruction(cp
);
986 op
->dword
= fixup
.dword
;
993 static void compile_vertex_program( struct arb_vp_machine
*m
,
994 const struct vertex_program
*program
)
996 struct compilation cp
;
1001 memset(&cp
, 0, sizeof(cp
));
1005 /* Compile instructions:
1007 for (i
= 0; i
< program
->Base
.NumInstructions
; i
++) {
1008 cvp_emit_inst(&cp
, &program
->Instructions
[i
]);
1013 m
->instructions
= m
->store
;
1014 m
->nr_instructions
= cp
.csr
- m
->store
;
1017 /* Print/disassemble:
1020 for (i
= 0; i
< m
->nr_instructions
; i
++) {
1021 _tnl_disassem_vba_insn(m
->instructions
[i
]);
1023 _mesa_printf("\n\n");
1027 /* TODO: check if anything changed...
1030 _tnl_sse_codegen_vertex_program(m
);
1038 /* ----------------------------------------------------------------------
1041 static void userclip( GLcontext
*ctx
,
1044 GLubyte
*clipormask
,
1045 GLubyte
*clipandmask
)
1049 for (p
= 0; p
< ctx
->Const
.MaxClipPlanes
; p
++)
1050 if (ctx
->Transform
.ClipPlanesEnabled
& (1 << p
)) {
1052 const GLfloat a
= ctx
->Transform
._ClipUserPlane
[p
][0];
1053 const GLfloat b
= ctx
->Transform
._ClipUserPlane
[p
][1];
1054 const GLfloat c
= ctx
->Transform
._ClipUserPlane
[p
][2];
1055 const GLfloat d
= ctx
->Transform
._ClipUserPlane
[p
][3];
1056 GLfloat
*coord
= (GLfloat
*)clip
->data
;
1057 GLuint stride
= clip
->stride
;
1058 GLuint count
= clip
->count
;
1060 for (nr
= 0, i
= 0 ; i
< count
; i
++) {
1061 GLfloat dp
= (coord
[0] * a
+
1068 clipmask
[i
] |= CLIP_USER_BIT
;
1071 STRIDE_F(coord
, stride
);
1075 *clipormask
|= CLIP_USER_BIT
;
1077 *clipandmask
|= CLIP_USER_BIT
;
1085 static GLboolean
do_ndc_cliptest( struct arb_vp_machine
*m
)
1087 GLcontext
*ctx
= m
->ctx
;
1088 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
1089 struct vertex_buffer
*VB
= m
->VB
;
1091 /* Cliptest and perspective divide. Clip functions must clear
1095 m
->andmask
= CLIP_ALL_BITS
;
1097 if (tnl
->NeedNdcCoords
) {
1099 _mesa_clip_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
1107 _mesa_clip_np_tab
[VB
->ClipPtr
->size
]( VB
->ClipPtr
,
1115 /* All vertices are outside the frustum */
1119 /* Test userclip planes. This contributes to VB->ClipMask.
1121 if (ctx
->Transform
.ClipPlanesEnabled
&& !ctx
->VertexProgram
._Enabled
) {
1133 VB
->ClipAndMask
= m
->andmask
;
1134 VB
->ClipOrMask
= m
->ormask
;
1135 VB
->ClipMask
= m
->clipmask
;
1141 static void call_func( struct arb_vp_machine
*m
)
1147 * Execute the given vertex program.
1149 * TODO: Integrate the t_vertex.c code here, to build machine vertices
1150 * directly at this point.
1152 * TODO: Eliminate the VB struct entirely and just use
1153 * struct arb_vertex_machine.
1156 run_arb_vertex_program(GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
1158 struct vertex_program
*program
= (ctx
->VertexProgram
._Enabled
?
1159 ctx
->VertexProgram
.Current
:
1161 struct vertex_buffer
*VB
= &TNL_CONTEXT(ctx
)->vb
;
1162 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1163 GLuint i
, j
, outputs
= program
->OutputsWritten
;
1165 if (program
->Parameters
) {
1166 _mesa_load_state_parameters(ctx
, program
->Parameters
);
1170 /* Initialize regs where necessary:
1172 ASSIGN_4V(m
->File
[0][REG_ID
], 0, 0, 0, 1);
1174 m
->nr_inputs
= m
->nr_outputs
= 0;
1176 for (i
= 0; i
< 16; i
++) {
1177 if (program
->InputsRead
& (1<<i
)) {
1178 GLuint j
= m
->nr_inputs
++;
1179 m
->input
[j
].idx
= i
;
1180 m
->input
[j
].data
= m
->VB
->AttribPtr
[i
]->data
;
1181 m
->input
[j
].stride
= m
->VB
->AttribPtr
[i
]->stride
;
1182 m
->input
[j
].size
= m
->VB
->AttribPtr
[i
]->size
;
1183 ASSIGN_4V(m
->File
[0][REG_IN0
+ i
], 0, 0, 0, 1);
1187 for (i
= 0; i
< 15; i
++) {
1188 if (program
->OutputsWritten
& (1<<i
)) {
1189 GLuint j
= m
->nr_outputs
++;
1190 m
->output
[j
].idx
= i
;
1191 m
->output
[j
].data
= m
->attribs
[i
].data
;
1196 /* Run the actual program:
1198 for (m
->vtx_nr
= 0; m
->vtx_nr
< VB
->Count
; m
->vtx_nr
++) {
1199 for (j
= 0; j
< m
->nr_inputs
; j
++) {
1200 GLuint idx
= REG_IN0
+ m
->input
[j
].idx
;
1201 switch (m
->input
[j
].size
) {
1202 case 4: m
->File
[0][idx
][3] = m
->input
[j
].data
[3];
1203 case 3: m
->File
[0][idx
][2] = m
->input
[j
].data
[2];
1204 case 2: m
->File
[0][idx
][1] = m
->input
[j
].data
[1];
1205 case 1: m
->File
[0][idx
][0] = m
->input
[j
].data
[0];
1208 STRIDE_F(m
->input
[j
].data
, m
->input
[j
].stride
);
1215 for (j
= 0; j
< m
->nr_instructions
; j
++) {
1216 union instruction inst
= m
->instructions
[j
];
1217 opcode_func
[inst
.alu
.opcode
]( m
, inst
);
1221 for (j
= 0; j
< m
->nr_outputs
; j
++) {
1222 GLuint idx
= REG_OUT0
+ m
->output
[j
].idx
;
1223 m
->output
[j
].data
[0] = m
->File
[0][idx
][0];
1224 m
->output
[j
].data
[1] = m
->File
[0][idx
][1];
1225 m
->output
[j
].data
[2] = m
->File
[0][idx
][2];
1226 m
->output
[j
].data
[3] = m
->File
[0][idx
][3];
1227 m
->output
[j
].data
+= 4;
1231 /* Setup the VB pointers so that the next pipeline stages get
1232 * their data from the right place (the program output arrays).
1234 * TODO: 1) Have tnl use these RESULT values for outputs rather
1235 * than trying to shoe-horn inputs and outputs into one set of
1238 * TODO: 2) Integrate t_vertex.c so that we just go straight ahead
1239 * and build machine vertices here.
1241 VB
->ClipPtr
= &m
->attribs
[VERT_RESULT_HPOS
];
1242 VB
->ClipPtr
->count
= VB
->Count
;
1244 if (outputs
& (1<<VERT_RESULT_COL0
)) {
1245 VB
->ColorPtr
[0] = &m
->attribs
[VERT_RESULT_COL0
];
1246 VB
->AttribPtr
[VERT_ATTRIB_COLOR0
] = VB
->ColorPtr
[0];
1249 if (outputs
& (1<<VERT_RESULT_BFC0
)) {
1250 VB
->ColorPtr
[1] = &m
->attribs
[VERT_RESULT_BFC0
];
1253 if (outputs
& (1<<VERT_RESULT_COL1
)) {
1254 VB
->SecondaryColorPtr
[0] = &m
->attribs
[VERT_RESULT_COL1
];
1255 VB
->AttribPtr
[VERT_ATTRIB_COLOR1
] = VB
->SecondaryColorPtr
[0];
1258 if (outputs
& (1<<VERT_RESULT_BFC1
)) {
1259 VB
->SecondaryColorPtr
[1] = &m
->attribs
[VERT_RESULT_BFC1
];
1262 if (outputs
& (1<<VERT_RESULT_FOGC
)) {
1263 VB
->FogCoordPtr
= &m
->attribs
[VERT_RESULT_FOGC
];
1264 VB
->AttribPtr
[VERT_ATTRIB_FOG
] = VB
->FogCoordPtr
;
1267 if (outputs
& (1<<VERT_RESULT_PSIZ
)) {
1268 VB
->PointSizePtr
= &m
->attribs
[VERT_RESULT_PSIZ
];
1269 VB
->AttribPtr
[_TNL_ATTRIB_POINTSIZE
] = &m
->attribs
[VERT_RESULT_PSIZ
];
1272 for (i
= 0; i
< ctx
->Const
.MaxTextureUnits
; i
++) {
1273 if (outputs
& (1<<(VERT_RESULT_TEX0
+i
))) {
1274 VB
->TexCoordPtr
[i
] = &m
->attribs
[VERT_RESULT_TEX0
+ i
];
1275 VB
->AttribPtr
[VERT_ATTRIB_TEX0
+i
] = VB
->TexCoordPtr
[i
];
1280 for (i
= 0; i
< VB
->Count
; i
++) {
1281 printf("Out %d: %f %f %f %f %f %f %f %f\n", i
,
1282 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[0],
1283 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[1],
1284 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[2],
1285 VEC_ELT(VB
->ClipPtr
, GLfloat
, i
)[3],
1286 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[0],
1287 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[1],
1288 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[2],
1289 VEC_ELT(VB
->TexCoordPtr
[0], GLfloat
, i
)[3]);
1293 /* Perform NDC and cliptest operations:
1295 return do_ndc_cliptest(m
);
1300 validate_vertex_program( GLcontext
*ctx
, struct tnl_pipeline_stage
*stage
)
1302 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1303 struct vertex_program
*program
=
1304 (ctx
->VertexProgram
._Enabled
? ctx
->VertexProgram
.Current
: 0);
1306 #if TNL_FIXED_FUNCTION_PROGRAM
1308 program
= ctx
->_TnlProgram
;
1313 compile_vertex_program( m
, program
);
1315 /* Grab the state GL state and put into registers:
1317 m
->File
[FILE_LOCAL_PARAM
] = program
->Base
.LocalParams
;
1318 m
->File
[FILE_ENV_PARAM
] = ctx
->VertexProgram
.Parameters
;
1319 m
->File
[FILE_STATE_PARAM
] = program
->Parameters
->ParameterValues
;
1330 * Called the first time stage->run is called. In effect, don't
1331 * allocate data until the first time the stage is run.
1333 static GLboolean
init_vertex_program( GLcontext
*ctx
,
1334 struct tnl_pipeline_stage
*stage
)
1336 TNLcontext
*tnl
= TNL_CONTEXT(ctx
);
1337 struct vertex_buffer
*VB
= &(tnl
->vb
);
1338 struct arb_vp_machine
*m
;
1339 const GLuint size
= VB
->Size
;
1342 stage
->privatePtr
= MALLOC(sizeof(*m
));
1343 m
= ARB_VP_MACHINE(stage
);
1347 /* arb_vertex_machine struct should subsume the VB:
1352 m
->File
[0] = ALIGN_MALLOC(REG_MAX
* sizeof(GLfloat
) * 4, 16);
1354 if (_mesa_getenv("MESA_EXPERIMENTAL"))
1357 _mesa_printf("try_codegen %d\n", m
->try_codegen
);
1359 /* Allocate arrays of vertex output values */
1360 for (i
= 0; i
< VERT_RESULT_MAX
; i
++) {
1361 _mesa_vector4f_alloc( &m
->attribs
[i
], 0, size
, 32 );
1362 m
->attribs
[i
].size
= 4;
1365 /* a few other misc allocations */
1366 _mesa_vector4f_alloc( &m
->ndcCoords
, 0, size
, 32 );
1367 m
->clipmask
= (GLubyte
*) ALIGN_MALLOC(sizeof(GLubyte
)*size
, 32 );
1370 #if TNL_FIXED_FUNCTION_PROGRAM
1371 _mesa_allow_light_in_model( ctx
, GL_FALSE
);
1382 * Destructor for this pipeline stage.
1384 static void dtr( struct tnl_pipeline_stage
*stage
)
1386 struct arb_vp_machine
*m
= ARB_VP_MACHINE(stage
);
1391 /* free the vertex program result arrays */
1392 for (i
= 0; i
< VERT_RESULT_MAX
; i
++)
1393 _mesa_vector4f_free( &m
->attribs
[i
] );
1395 /* free misc arrays */
1396 _mesa_vector4f_free( &m
->ndcCoords
);
1397 ALIGN_FREE( m
->clipmask
);
1398 ALIGN_FREE( m
->File
[0] );
1401 stage
->privatePtr
= NULL
;
1406 * Public description of this pipeline stage.
1408 const struct tnl_pipeline_stage _tnl_arb_vertex_program_stage
=
1411 NULL
, /* private_data */
1412 init_vertex_program
, /* create */
1414 validate_vertex_program
, /* validate */
1415 run_arb_vertex_program
/* run */