t_vp_build.c revision f069e5e412eebabe64286d35598173caac5c132e
1/* 2 * Mesa 3-D graphics library 3 * Version: 6.3 4 * 5 * Copyright (C) 2005 Tungsten Graphics All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * TUNGSTEN GRAPHICS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25/** 26 * \file t_vp_build.c 27 * Create a vertex program to execute the current fixed function T&L pipeline. 28 * \author Keith Whitwell 29 */ 30 31 32#include <strings.h> 33 34#include "glheader.h" 35#include "macros.h" 36#include "enums.h" 37#include "t_context.h" 38#include "t_vp_build.h" 39 40#include "shader/program.h" 41#include "shader/nvvertprog.h" 42#include "shader/arbvertparse.h" 43 44 45/* Very useful debugging tool - produces annotated listing of 46 * generated program with line/function references for each 47 * instruction back into this file: 48 */ 49#define DISASSEM 1 50 51/* Use uregs to represent registers internally, translate to Mesa's 52 * expected formats on emit. 53 * 54 * NOTE: These are passed by value extensively in this file rather 55 * than as usual by pointer reference. If this disturbs you, try 56 * remembering they are just 32bits in size. 57 * 58 * GCC is smart enough to deal with these dword-sized structures in 59 * much the same way as if I had defined them as dwords and was using 60 * macros to access and set the fields. This is much nicer and easier 61 * to evolve. 62 */ 63struct ureg { 64 GLuint file:4; 65 GLuint idx:8; 66 GLuint negate:1; 67 GLuint swz:12; 68 GLuint pad:7; 69}; 70 71 72struct tnl_program { 73 GLcontext *ctx; 74 struct vertex_program *program; 75 76 GLuint temp_flag; 77 GLuint temp_reserved; 78 79 struct ureg eye_position; 80 struct ureg eye_position_normalized; 81 struct ureg eye_normal; 82 struct ureg identity; 83 84 GLuint materials; 85 GLuint color_materials; 86}; 87 88 89const static struct ureg undef = { 90 ~0, 91 ~0, 92 0, 93 0, 94 0 95}; 96 97/* Local shorthand: 98 */ 99#define X SWIZZLE_X 100#define Y SWIZZLE_Y 101#define Z SWIZZLE_Z 102#define W SWIZZLE_W 103 104 105/* Construct a ureg: 106 */ 107static struct ureg make_ureg(GLuint file, GLuint idx) 108{ 109 struct ureg reg; 110 reg.file = file; 111 reg.idx = idx; 112 reg.negate = 0; 113 reg.swz = SWIZZLE_NOOP; 114 reg.pad = 0; 115 return reg; 116} 117 118 119 120static struct ureg negate( struct ureg reg ) 121{ 122 reg.negate ^= 1; 123 return reg; 124} 125 126 127static struct ureg swizzle( struct ureg reg, int x, int y, int z, int w ) 128{ 129 reg.swz = MAKE_SWIZZLE4(GET_SWZ(reg.swz, x), 130 GET_SWZ(reg.swz, y), 131 GET_SWZ(reg.swz, z), 132 GET_SWZ(reg.swz, w)); 133 134 return reg; 135} 136 137static struct ureg swizzle1( struct ureg reg, int x ) 138{ 139 return swizzle(reg, x, x, x, x); 140} 141 142static struct ureg get_temp( struct tnl_program *p ) 143{ 144 int bit = ffs( ~p->temp_flag ); 145 if (!bit) { 146 fprintf(stderr, "%s: out of temporaries\n", __FILE__); 147 exit(1); 148 } 149 150 p->temp_flag |= 1<<(bit-1); 151 return make_ureg(PROGRAM_TEMPORARY, bit-1); 152} 153 154static struct ureg reserve_temp( struct tnl_program *p ) 155{ 156 struct ureg temp = get_temp( p ); 157 p->temp_reserved |= 1<<temp.idx; 158 return temp; 159} 160 161static void release_temp( struct tnl_program *p, struct ureg reg ) 162{ 163 if (reg.file == PROGRAM_TEMPORARY) { 164 p->temp_flag &= ~(1<<reg.idx); 165 p->temp_flag |= p->temp_reserved; /* can't release reserved temps */ 166 } 167} 168 169static void release_temps( struct tnl_program *p ) 170{ 171 p->temp_flag = p->temp_reserved; 172} 173 174 175 176static struct ureg register_input( struct tnl_program *p, GLuint input ) 177{ 178 p->program->InputsRead |= (1<<input); 179 return make_ureg(PROGRAM_INPUT, input); 180} 181 182static struct ureg register_output( struct tnl_program *p, GLuint output ) 183{ 184 p->program->OutputsWritten |= (1<<output); 185 return make_ureg(PROGRAM_OUTPUT, output); 186} 187 188static struct ureg register_const4f( struct tnl_program *p, 189 GLfloat s0, 190 GLfloat s1, 191 GLfloat s2, 192 GLfloat s3) 193{ 194 GLfloat values[4]; 195 GLuint idx; 196 values[0] = s0; 197 values[1] = s1; 198 values[2] = s2; 199 values[3] = s3; 200 idx = _mesa_add_unnamed_constant( p->program->Parameters, values ); 201 return make_ureg(PROGRAM_STATE_VAR, idx); 202} 203 204#define register_const1f(p, s0) register_const4f(p, s0, 0, 0, 1) 205#define register_const2f(p, s0, s1) register_const4f(p, s0, s1, 0, 1) 206#define register_const3f(p, s0, s1, s2) register_const4f(p, s0, s1, s2, 1) 207 208static GLboolean is_undef( struct ureg reg ) 209{ 210 return reg.file == 0xf; 211} 212 213static struct ureg get_identity_param( struct tnl_program *p ) 214{ 215 if (is_undef(p->identity)) 216 p->identity = register_const4f(p, 0,0,0,1); 217 218 return p->identity; 219} 220 221static struct ureg register_param6( struct tnl_program *p, 222 GLint s0, 223 GLint s1, 224 GLint s2, 225 GLint s3, 226 GLint s4, 227 GLint s5) 228{ 229 GLint tokens[6]; 230 GLuint idx; 231 tokens[0] = s0; 232 tokens[1] = s1; 233 tokens[2] = s2; 234 tokens[3] = s3; 235 tokens[4] = s4; 236 tokens[5] = s5; 237 idx = _mesa_add_state_reference( p->program->Parameters, tokens ); 238 return make_ureg(PROGRAM_STATE_VAR, idx); 239} 240 241 242#define register_param1(p,s0) register_param6(p,s0,0,0,0,0,0) 243#define register_param2(p,s0,s1) register_param6(p,s0,s1,0,0,0,0) 244#define register_param3(p,s0,s1,s2) register_param6(p,s0,s1,s2,0,0,0) 245#define register_param4(p,s0,s1,s2,s3) register_param6(p,s0,s1,s2,s3,0,0) 246 247 248static void register_matrix_param6( struct tnl_program *p, 249 GLint s0, 250 GLint s1, 251 GLint s2, 252 GLint s3, 253 GLint s4, 254 GLint s5, 255 struct ureg *matrix ) 256{ 257 GLuint i; 258 259 /* This is a bit sad as the support is there to pull the whole 260 * matrix out in one go: 261 */ 262 for (i = 0; i <= s4 - s3; i++) 263 matrix[i] = register_param6( p, s0, s1, s2, i, i, s5 ); 264} 265 266 267static void emit_arg( struct vp_src_register *src, 268 struct ureg reg ) 269{ 270 src->File = reg.file; 271 src->Index = reg.idx; 272 src->Swizzle = reg.swz; 273 src->Negate = reg.negate; 274 src->RelAddr = 0; 275 src->pad = 0; 276} 277 278static void emit_dst( struct vp_dst_register *dst, 279 struct ureg reg, GLuint mask ) 280{ 281 dst->File = reg.file; 282 dst->Index = reg.idx; 283 dst->WriteMask = mask ? mask : WRITEMASK_XYZW; /* allow zero as a shorthand for xyzw */ 284 dst->pad = 0; 285} 286 287static void debug_insn( struct vp_instruction *inst, const char *fn, GLuint line ) 288{ 289#if DISASSEM 290 static const char *last_fn; 291 292 if (fn != last_fn) { 293 last_fn = fn; 294 _mesa_printf("%s:\n", fn); 295 } 296 297 _mesa_printf("%d:\t", line); 298 _mesa_debug_vp_inst(1, inst); 299#endif 300} 301 302 303static void emit_op3fn(struct tnl_program *p, 304 GLuint op, 305 struct ureg dest, 306 GLuint mask, 307 struct ureg src0, 308 struct ureg src1, 309 struct ureg src2, 310 const char *fn, 311 GLuint line) 312{ 313 GLuint nr = p->program->Base.NumInstructions++; 314 struct vp_instruction *inst = &p->program->Instructions[nr]; 315 316 inst->Opcode = op; 317 318 emit_arg( &inst->SrcReg[0], src0 ); 319 emit_arg( &inst->SrcReg[1], src1 ); 320 emit_arg( &inst->SrcReg[2], src2 ); 321 322 emit_dst( &inst->DstReg, dest, mask ); 323 324 debug_insn(inst, fn, line); 325} 326 327 328 329#define emit_op3(p, op, dst, mask, src0, src1, src2) emit_op3fn(p, op, dst, mask, src0, src1, src2, __FUNCTION__, __LINE__) 330#define emit_op2(p, op, dst, mask, src0, src1) emit_op3fn(p, op, dst, mask, src0, src1, undef, __FUNCTION__, __LINE__) 331#define emit_op1(p, op, dst, mask, src0) emit_op3fn(p, op, dst, mask, src0, undef, undef, __FUNCTION__, __LINE__) 332 333 334static struct ureg make_temp( struct tnl_program *p, struct ureg reg ) 335{ 336 if (reg.file == PROGRAM_TEMPORARY && 337 !(p->temp_reserved & (1<<reg.idx))) 338 return reg; 339 else { 340 struct ureg temp = get_temp(p); 341 emit_op1(p, VP_OPCODE_MOV, temp, 0, reg); 342 return temp; 343 } 344} 345 346 347/* Currently no tracking performed of input/output/register size or 348 * active elements. Could be used to reduce these operations, as 349 * could the matrix type. 350 */ 351static void emit_matrix_transform_vec4( struct tnl_program *p, 352 struct ureg dest, 353 const struct ureg *mat, 354 struct ureg src) 355{ 356 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_X, src, mat[0]); 357 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_Y, src, mat[1]); 358 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_Z, src, mat[2]); 359 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_W, src, mat[3]); 360 361} 362 363static void emit_matrix_transform_vec3( struct tnl_program *p, 364 struct ureg dest, 365 const struct ureg *mat, 366 struct ureg src) 367{ 368 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_X, src, mat[0]); 369 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_Y, src, mat[1]); 370 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_Z, src, mat[2]); 371} 372 373 374static void emit_normalize_vec3( struct tnl_program *p, 375 struct ureg dest, 376 struct ureg src ) 377{ 378 struct ureg tmp = get_temp(p); 379 emit_op2(p, VP_OPCODE_DP3, tmp, 0, src, src); 380 emit_op1(p, VP_OPCODE_RSQ, tmp, 0, tmp); 381 emit_op2(p, VP_OPCODE_MUL, dest, 0, src, tmp); 382 release_temp(p, tmp); 383} 384 385static struct ureg get_eye_position( struct tnl_program *p ) 386{ 387 if (is_undef(p->eye_position)) { 388 struct ureg pos = register_input( p, VERT_ATTRIB_POS ); 389 struct ureg modelview[4]; 390 391 register_matrix_param6( p, STATE_MATRIX, STATE_MODELVIEW, 0, 0, 3, 0, modelview ); 392 p->eye_position = reserve_temp(p); 393 394 emit_matrix_transform_vec4( p, p->eye_position, modelview, pos ); 395 } 396 397 return p->eye_position; 398} 399 400 401static struct ureg get_eye_position_normalized( struct tnl_program *p ) 402{ 403 if (is_undef(p->eye_position_normalized)) { 404 struct ureg eye = get_eye_position(p); 405 p->eye_position_normalized = reserve_temp(p); 406 emit_normalize_vec3(p, p->eye_position_normalized, eye); 407 } 408 409 return p->eye_position_normalized; 410} 411 412 413static struct ureg get_eye_normal( struct tnl_program *p ) 414{ 415 if (is_undef(p->eye_normal)) { 416 struct ureg normal = register_input(p, VERT_ATTRIB_NORMAL ); 417 struct ureg mvinv[3]; 418 419 register_matrix_param6( p, STATE_MATRIX, STATE_MODELVIEW, 0, 0, 2, STATE_MATRIX_INVTRANS, mvinv ); 420 421 p->eye_normal = reserve_temp(p); 422 423 /* Transform to eye space: 424 */ 425 emit_matrix_transform_vec3( p, p->eye_normal, mvinv, normal ); 426 427 /* Normalize/Rescale: 428 */ 429 if (p->ctx->Transform.Normalize) { 430 emit_normalize_vec3( p, p->eye_normal, p->eye_normal ); 431 } 432 else if (p->ctx->Transform.RescaleNormals) { 433 struct ureg rescale = register_param2(p, STATE_INTERNAL, STATE_NORMAL_SCALE); 434 emit_op2( p, VP_OPCODE_MUL, p->eye_normal, 0, normal, swizzle1(rescale, X)); 435 } 436 } 437 438 return p->eye_normal; 439} 440 441 442 443static void build_hpos( struct tnl_program *p ) 444{ 445 struct ureg pos = register_input( p, VERT_ATTRIB_POS ); 446 struct ureg hpos = register_output( p, VERT_RESULT_HPOS ); 447 struct ureg mvp[4]; 448 449 register_matrix_param6( p, STATE_MATRIX, STATE_MVP, 0, 0, 3, 0, mvp ); 450 emit_matrix_transform_vec4( p, hpos, mvp, pos ); 451} 452 453 454static GLuint material_attrib( GLuint side, GLuint property ) 455{ 456 return _TNL_ATTRIB_MAT_FRONT_AMBIENT + (property - STATE_AMBIENT) * 2 + side; 457} 458 459static void set_material_flags( struct tnl_program *p ) 460{ 461 GLcontext *ctx = p->ctx; 462 TNLcontext *tnl = TNL_CONTEXT(ctx); 463 GLuint i; 464 465 p->color_materials = 0; 466 p->materials = 0; 467 468 if (ctx->Light.ColorMaterialEnabled) { 469 p->materials = 470 p->color_materials = 471 ctx->Light.ColorMaterialBitmask << _TNL_ATTRIB_MAT_FRONT_AMBIENT; 472 } 473 474 for (i = _TNL_ATTRIB_MAT_FRONT_AMBIENT ; i < _TNL_ATTRIB_INDEX ; i++) 475 if (tnl->vb.AttribPtr[i]->stride) 476 p->materials |= 1<<i; 477} 478 479 480static struct ureg get_material( struct tnl_program *p, GLuint side, GLuint property ) 481{ 482 GLuint attrib = material_attrib(side, property); 483 484 if (p->color_materials & (1<<attrib)) 485 return register_input(p, VERT_ATTRIB_COLOR0); 486 else if (p->materials & (1<<attrib)) 487 return register_input( p, attrib ); 488 else 489 return register_param3( p, STATE_MATERIAL, side, property ); 490} 491 492#define SCENE_COLOR_BITS(side) (( _TNL_BIT_MAT_FRONT_EMISSION | \ 493 _TNL_BIT_MAT_FRONT_AMBIENT | \ 494 _TNL_BIT_MAT_FRONT_DIFFUSE) << (side)) 495 496/* Either return a precalculated constant value or emit code to 497 * calculate these values dynamically in the case where material calls 498 * are present between begin/end pairs. 499 * 500 * Probably want to shift this to the program compilation phase - if 501 * we always emitted the calculation here, a smart compiler could 502 * detect that it was constant (given a certain set of inputs), and 503 * lift it out of the main loop. That way the programs created here 504 * would be independent of the vertex_buffer details. 505 */ 506static struct ureg get_scenecolor( struct tnl_program *p, GLuint side ) 507{ 508 if (p->materials & SCENE_COLOR_BITS(side)) { 509 struct ureg lightmodel_ambient = register_param1(p, STATE_LIGHTMODEL_AMBIENT); 510 struct ureg material_emission = get_material(p, side, STATE_EMISSION); 511 struct ureg material_ambient = get_material(p, side, STATE_AMBIENT); 512 struct ureg material_diffuse = get_material(p, side, STATE_DIFFUSE); 513 struct ureg tmp = make_temp(p, material_diffuse); 514 emit_op3(p, VP_OPCODE_MAD, tmp, WRITEMASK_XYZ, lightmodel_ambient, material_ambient, material_emission); 515 return tmp; 516 } 517 else 518 return register_param2( p, STATE_LIGHTMODEL_SCENECOLOR, side ); 519} 520 521 522static struct ureg get_lightprod( struct tnl_program *p, GLuint light, GLuint side, GLuint property ) 523{ 524 GLuint attrib = material_attrib(side, property); 525 if (p->materials & (1<<attrib)) { 526 struct ureg light_value = register_param3(p, STATE_LIGHT, light, property); 527 struct ureg material_value = get_material(p, side, property); 528 struct ureg tmp = get_temp(p); 529 emit_op2(p, VP_OPCODE_MUL, tmp, 0, light_value, material_value); 530 return tmp; 531 } 532 else 533 return register_param4(p, STATE_LIGHTPROD, light, side, property); 534} 535 536 537 538 539/* Need to add some addtional parameters to allow lighting in object 540 * space - STATE_SPOT_DIRECTION and STATE_HALF implicitly assume eye 541 * space lighting. 542 */ 543static void build_lighting( struct tnl_program *p ) 544{ 545 GLcontext *ctx = p->ctx; 546 const GLboolean twoside = ctx->Light.Model.TwoSide; 547 const GLboolean separate = (ctx->Light.Model.ColorControl == 548 GL_SEPARATE_SPECULAR_COLOR); 549 GLuint nr_lights = 0, count = 0; 550 struct ureg normal = get_eye_normal(p); 551 struct ureg lit = get_temp(p); 552 struct ureg dots = get_temp(p); 553 struct ureg _col0 = undef, _col1 = undef; 554 struct ureg _bfc0 = undef, _bfc1 = undef; 555 GLuint i; 556 557 for (i = 0; i < MAX_LIGHTS; i++) 558 if (ctx->Light.Light[i].Enabled) 559 nr_lights++; 560 561 set_material_flags(p); 562 563 { 564 struct ureg shininess = get_material(p, 0, STATE_SHININESS); 565 emit_op1(p, VP_OPCODE_MOV, dots, WRITEMASK_W, swizzle1(shininess,X)); 566 release_temp(p, shininess); 567 568 _col0 = make_temp(p, get_scenecolor(p, 0)); 569 if (separate) 570 _col1 = make_temp(p, get_identity_param(p)); 571 else 572 _col1 = _col0; 573 574 } 575 576 if (twoside) { 577 struct ureg shininess = get_material(p, 1, STATE_SHININESS); 578 emit_op1(p, VP_OPCODE_MOV, dots, WRITEMASK_Z, negate(swizzle1(shininess,X))); 579 release_temp(p, shininess); 580 581 _bfc0 = make_temp(p, get_scenecolor(p, 1)); 582 if (separate) 583 _bfc1 = make_temp(p, get_identity_param(p)); 584 else 585 _bfc1 = _bfc0; 586 } 587 588 for (i = 0; i < MAX_LIGHTS; i++) { 589 struct gl_light *light = &ctx->Light.Light[i]; 590 591 if (light->Enabled) { 592 struct ureg half = undef; 593 struct ureg att = undef, VPpli = undef; 594 595 count++; 596 597 if (light->EyePosition[3] == 0) { 598 /* Can used precomputed constants in this case: 599 */ 600 VPpli = register_param3(p, STATE_LIGHT, i, STATE_POSITION_NORMALIZED); 601 half = register_param3(p, STATE_LIGHT, i, STATE_HALF); 602 603 /* Spot attenuation maybe applies to this case? Could precompute if so? */ 604 } 605 else { 606 struct ureg Ppli = register_param3(p, STATE_LIGHT, i, STATE_POSITION); 607 struct ureg V = get_eye_position(p); 608 struct ureg dst = get_temp(p); 609 610 VPpli = get_temp(p); 611 half = get_temp(p); 612 613 /* Calulate VPpli vector 614 */ 615 emit_op2(p, VP_OPCODE_SUB, VPpli, 0, Ppli, V); 616 617 /* Normalize VPpli. The dst value also used in 618 * attenuation below. 619 */ 620 emit_op2(p, VP_OPCODE_DP3, dst, 0, VPpli, VPpli); 621 emit_op1(p, VP_OPCODE_RSQ, dst, 0, dst); 622 emit_op2(p, VP_OPCODE_MUL, VPpli, 0, VPpli, dst); 623 624 625 /* Calculate attenuation: 626 */ 627 if (light->SpotCutoff != 180.0 || 628 light->ConstantAttenuation != 1.0 || 629 light->LinearAttenuation != 1.0 || 630 light->QuadraticAttenuation != 1.0) { 631 632 struct ureg attenuation = register_param3(p, STATE_LIGHT, i, STATE_ATTENUATION); 633 att = get_temp(p); 634 635 /* Calculate spot attenuation: 636 */ 637 if (light->SpotCutoff != 180.0F) { 638 struct ureg spot_dir = register_param3(p, STATE_LIGHT, i, STATE_SPOT_DIRECTION); 639 struct ureg spot = get_temp(p); 640 struct ureg slt = get_temp(p); 641 642 emit_normalize_vec3( p, spot, spot_dir ); /* XXX: precompute! */ 643 emit_op2(p, VP_OPCODE_DP3, spot, 0, negate(VPpli), spot_dir); 644 emit_op2(p, VP_OPCODE_SLT, slt, 0, swizzle1(spot_dir,W), spot); 645 emit_op2(p, VP_OPCODE_POW, spot, 0, spot, swizzle1(attenuation, W)); 646 emit_op2(p, VP_OPCODE_MUL, att, 0, slt, spot); 647 648 release_temp(p, spot); 649 release_temp(p, slt); 650 } 651 652 /* Calculate distance attenuation: 653 */ 654 if (light->ConstantAttenuation != 1.0 || 655 light->LinearAttenuation != 1.0 || 656 light->QuadraticAttenuation != 1.0) { 657 658 emit_op1(p, VP_OPCODE_RCP, dst, WRITEMASK_YZ, dst); /* 1/d,d,d,1/d */ 659 emit_op2(p, VP_OPCODE_MUL, dst, WRITEMASK_XZ, dst, swizzle1(dst,Y)); /* 1,d,d*d,1/d */ 660 emit_op2(p, VP_OPCODE_DP3, dst, 0, attenuation, dst); /* 1/dist-atten */ 661 662 if (light->SpotCutoff != 180.0F) { 663 emit_op1(p, VP_OPCODE_RCP, dst, 0, dst); /* dist-atten */ 664 emit_op2(p, VP_OPCODE_MUL, att, 0, dst, att); /* spot-atten * dist-atten */ 665 } else { 666 emit_op1(p, VP_OPCODE_RCP, att, 0, dst); /* dist-atten */ 667 } 668 } 669 } 670 671 672 /* Calculate viewer direction, or use infinite viewer: 673 */ 674 if (ctx->Light.Model.LocalViewer) { 675 struct ureg eye_hat = get_eye_position_normalized(p); 676 emit_op2(p, VP_OPCODE_SUB, half, 0, VPpli, eye_hat); 677 } 678 else { 679 struct ureg z_dir = swizzle(get_identity_param(p),X,Y,W,Z); /* 0,0,1,0 */ 680 emit_op2(p, VP_OPCODE_ADD, half, 0, VPpli, z_dir); 681 } 682 683 emit_normalize_vec3(p, half, half); 684 685 release_temp(p, dst); 686 } 687 688 /* Calculate dot products: 689 */ 690 emit_op2(p, VP_OPCODE_DP3, dots, WRITEMASK_X, normal, VPpli); 691 emit_op2(p, VP_OPCODE_DP3, dots, WRITEMASK_Y, normal, half); 692 693 694 /* Front face lighting: 695 */ 696 { 697 struct ureg ambient = get_lightprod(p, i, 0, STATE_AMBIENT); 698 struct ureg diffuse = get_lightprod(p, i, 0, STATE_DIFFUSE); 699 struct ureg specular = get_lightprod(p, i, 0, STATE_SPECULAR); 700 struct ureg res0, res1; 701 702 emit_op1(p, VP_OPCODE_LIT, lit, 0, dots); 703 704 if (!is_undef(att)) 705 emit_op2(p, VP_OPCODE_MUL, lit, 0, lit, att); 706 707 708 if (count == nr_lights) { 709 if (separate) { 710 res0 = register_output( p, VERT_RESULT_COL0 ); 711 res1 = register_output( p, VERT_RESULT_COL1 ); 712 } 713 else { 714 res0 = _col0; 715 res1 = register_output( p, VERT_RESULT_COL0 ); 716 } 717 } else { 718 res0 = _col0; 719 res1 = _col1; 720 } 721 722 emit_op3(p, VP_OPCODE_MAD, _col0, 0, swizzle1(lit,X), ambient, _col0); 723 emit_op3(p, VP_OPCODE_MAD, res0, 0, swizzle1(lit,Y), diffuse, _col0); 724 emit_op3(p, VP_OPCODE_MAD, res1, 0, swizzle1(lit,Z), specular, _col1); 725 726 release_temp(p, ambient); 727 release_temp(p, diffuse); 728 release_temp(p, specular); 729 } 730 731 /* Back face lighting: 732 */ 733 if (twoside) { 734 struct ureg ambient = get_lightprod(p, i, 1, STATE_AMBIENT); 735 struct ureg diffuse = get_lightprod(p, i, 1, STATE_DIFFUSE); 736 struct ureg specular = get_lightprod(p, i, 1, STATE_SPECULAR); 737 struct ureg res0, res1; 738 739 emit_op1(p, VP_OPCODE_LIT, lit, 0, negate(swizzle(dots,X,Y,W,Z))); 740 741 if (!is_undef(att)) 742 emit_op2(p, VP_OPCODE_MUL, lit, 0, lit, att); 743 744 if (count == nr_lights) { 745 if (separate) { 746 res0 = register_output( p, VERT_RESULT_BFC0 ); 747 res1 = register_output( p, VERT_RESULT_BFC1 ); 748 } 749 else { 750 res0 = _bfc0; 751 res1 = register_output( p, VERT_RESULT_BFC0 ); 752 } 753 } else { 754 res0 = _bfc0; 755 res1 = _bfc1; 756 } 757 758 759 emit_op3(p, VP_OPCODE_MAD, _bfc0, 0, swizzle1(lit,X), ambient, _bfc0); 760 emit_op3(p, VP_OPCODE_MAD, res0, 0, swizzle1(lit,Y), diffuse, _bfc0); 761 emit_op3(p, VP_OPCODE_MAD, res1, 0, swizzle1(lit,Z), specular, _bfc1); 762 763 release_temp(p, ambient); 764 release_temp(p, diffuse); 765 release_temp(p, specular); 766 } 767 768 release_temp(p, half); 769 release_temp(p, VPpli); 770 release_temp(p, att); 771 } 772 } 773 774 release_temps( p ); 775} 776 777 778static void build_fog( struct tnl_program *p ) 779{ 780 GLcontext *ctx = p->ctx; 781 TNLcontext *tnl = TNL_CONTEXT(ctx); 782 struct ureg fog = register_output(p, VERT_RESULT_FOGC); 783 struct ureg input; 784 785 if (ctx->Fog.FogCoordinateSource == GL_FRAGMENT_DEPTH_EXT) { 786 input = swizzle1(get_eye_position(p), Z); 787 } 788 else { 789 input = swizzle1(register_input(p, VERT_ATTRIB_FOG), X); 790 } 791 792 if (tnl->_DoVertexFog) { 793 struct ureg params = register_param1(p, STATE_FOG_PARAMS); 794 struct ureg tmp = get_temp(p); 795 796 switch (ctx->Fog.Mode) { 797 case GL_LINEAR: { 798 struct ureg id = get_identity_param(p); 799 emit_op2(p, VP_OPCODE_SUB, tmp, 0, swizzle1(params,Z), input); 800 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, swizzle1(params,W)); 801 emit_op2(p, VP_OPCODE_MAX, tmp, 0, tmp, swizzle1(id,X)); /* saturate */ 802 emit_op2(p, VP_OPCODE_MIN, fog, WRITEMASK_X, tmp, swizzle1(id,W)); /* saturate */ 803 break; 804 } 805 case GL_EXP: 806 emit_op1(p, VP_OPCODE_ABS, tmp, 0, input); 807 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, swizzle1(params,X)); 808 emit_op2(p, VP_OPCODE_POW, fog, WRITEMASK_X, register_const1f(p, M_E), negate(tmp)); 809 break; 810 case GL_EXP2: 811 emit_op2(p, VP_OPCODE_MUL, tmp, 0, input, swizzle1(params,X)); 812 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, tmp); 813 emit_op2(p, VP_OPCODE_POW, fog, WRITEMASK_X, register_const1f(p, M_E), negate(tmp)); 814 break; 815 } 816 817 release_temp(p, tmp); 818 } 819 else { 820 /* results = incoming fog coords (compute fog per-fragment later) 821 * 822 * KW: Is it really necessary to do anything in this case? 823 */ 824 emit_op1(p, VP_OPCODE_MOV, fog, WRITEMASK_X, input); 825 } 826} 827 828 829 830static void build_texture_transform( struct tnl_program *p ) 831{ 832 GLcontext *ctx = p->ctx; 833 GLuint i, j; 834 835 for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) { 836 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[i]; 837 GLuint texmat_enabled = ctx->Texture._TexMatEnabled & ENABLE_TEXMAT(i); 838 struct ureg out = register_output(p, VERT_RESULT_TEX0 + i); 839 840 if (texUnit->TexGenEnabled || texmat_enabled) { 841 struct ureg out_texgen = undef; 842 843 if (texUnit->TexGenEnabled) { 844 GLuint copy_mask = 0; 845 GLuint sphere_mask = 0; 846 GLuint reflect_mask = 0; 847 GLuint normal_mask = 0; 848 GLuint modes[4]; 849 850 if (texmat_enabled) 851 out_texgen = get_temp(p); 852 else 853 out_texgen = out; 854 855 modes[0] = texUnit->GenModeS; 856 modes[1] = texUnit->GenModeT; 857 modes[2] = texUnit->GenModeR; 858 modes[3] = texUnit->GenModeQ; 859 860 for (j = 0; j < 4; j++) { 861 if (texUnit->TexGenEnabled & (1<<j)) { 862 switch (modes[j]) { 863 case GL_OBJECT_LINEAR: { 864 struct ureg obj = register_input(p, VERT_ATTRIB_POS); 865 struct ureg plane = register_param3(p, STATE_TEXGEN, i, STATE_TEXGEN_OBJECT_S + j); 866 emit_op2(p, VP_OPCODE_DP4, out_texgen, WRITEMASK_X << j, obj, plane ); 867 break; 868 } 869 case GL_EYE_LINEAR: { 870 struct ureg eye = get_eye_position(p); 871 struct ureg plane = register_param3(p, STATE_TEXGEN, i, STATE_TEXGEN_EYE_S + j); 872 emit_op2(p, VP_OPCODE_DP4, out_texgen, WRITEMASK_X << j, eye, plane ); 873 break; 874 } 875 case GL_SPHERE_MAP: 876 sphere_mask |= WRITEMASK_X << j; 877 break; 878 case GL_REFLECTION_MAP_NV: 879 reflect_mask |= WRITEMASK_X << j; 880 break; 881 case GL_NORMAL_MAP_NV: 882 normal_mask |= WRITEMASK_X << j; 883 break; 884 } 885 } 886 else 887 copy_mask |= WRITEMASK_X << j; 888 } 889 890 891 if (sphere_mask) { 892 struct ureg normal = get_eye_normal(p); 893 struct ureg eye_hat = get_eye_position_normalized(p); 894 struct ureg tmp = get_temp(p); 895 struct ureg half = register_const1f(p, .5); 896 struct ureg r = get_temp(p); 897 struct ureg inv_m = get_temp(p); 898 899 emit_op2(p, VP_OPCODE_DP3, tmp, 0, normal, eye_hat); /* n.u */ 900 emit_op2(p, VP_OPCODE_ADD, tmp, 0, tmp, tmp); /* 2n.u */ 901 emit_op3(p, VP_OPCODE_MAD, r, 0, negate(tmp), normal, eye_hat); /* (-2n.u)n + u */ 902 emit_op2(p, VP_OPCODE_ADD, tmp, 0, r, swizzle(get_identity_param(p),X,Y,W,Z)); /* r + 0,0,1 */ 903 emit_op2(p, VP_OPCODE_DP3, tmp, 0, tmp, tmp); /* rx^2 + ry^2 + (rz+1)^2 */ 904 emit_op1(p, VP_OPCODE_RSQ, tmp, 0, tmp); /* 2/m */ 905 emit_op2(p, VP_OPCODE_MUL, inv_m, 0, tmp, swizzle1(half,X)); /* 1/m */ 906 emit_op3(p, VP_OPCODE_MAD, out_texgen, sphere_mask, r, inv_m, swizzle1(half,X)); /* r/m + 1/2 */ 907 908 release_temp(p, tmp); 909 release_temp(p, r); 910 release_temp(p, inv_m); 911 } 912 913 /* Could duplicate the above calculations, but it would be 914 * a fairly odd state for someone to set (both sphere and 915 * reflection active for different texture coordinate 916 * components. Of course - if two texture units enable 917 * reflect and/or sphere, things start to tilt in favour 918 * of seperating this out: 919 */ 920 if (reflect_mask) { 921 struct ureg normal = get_eye_normal(p); 922 struct ureg eye_hat = get_eye_position_normalized(p); 923 struct ureg tmp = get_temp(p); 924 925 emit_op2(p, VP_OPCODE_DP3, tmp, 0, normal, eye_hat); 926 emit_op2(p, VP_OPCODE_ADD, tmp, 0, tmp, tmp); 927 emit_op3(p, VP_OPCODE_MAD, out_texgen, reflect_mask, negate(tmp), normal, eye_hat); 928 } 929 930 if (normal_mask) { 931 struct ureg normal = get_eye_normal(p); 932 emit_op1(p, VP_OPCODE_MOV, out_texgen, normal_mask, normal ); 933 } 934 935 if (copy_mask) { 936 struct ureg in = register_input(p, VERT_ATTRIB_TEX0+i); 937 emit_op1(p, VP_OPCODE_MOV, out_texgen, copy_mask, in ); 938 } 939 } 940 941 if (texmat_enabled) { 942 struct ureg texmat[4]; 943 struct ureg in = !is_undef(out_texgen) ? out_texgen : register_input(p, VERT_ATTRIB_TEX0+i); 944 register_matrix_param6( p, STATE_MATRIX, STATE_TEXTURE, i, 0, 3, 0, texmat ); 945 emit_matrix_transform_vec4( p, out, texmat, in ); 946 } 947 948 release_temps(p); 949 } 950 } 951} 952 953 954/* Seems like it could be tighter: 955 */ 956static void build_pointsize( struct tnl_program *p ) 957{ 958 struct ureg eye = get_eye_position(p); 959 struct ureg state_size = register_param1( p, STATE_POINT_SIZE ); 960 struct ureg state_attenuation = register_param1( p, STATE_POINT_ATTENUATION ); 961 struct ureg out = register_output( p, VERT_RESULT_PSIZ ); 962 struct ureg ut = get_temp(p); 963 964 /* 1, -Z, Z * Z, 1 */ 965 emit_op1(p, VP_OPCODE_MOV, ut, 0, swizzle1(get_identity_param(p), W)); 966 emit_op2(p, VP_OPCODE_MUL, ut, WRITEMASK_YZ, ut, negate(swizzle1(eye, Z))); 967 emit_op2(p, VP_OPCODE_MUL, ut, WRITEMASK_Z, ut, negate(swizzle1(eye, Z))); 968 969 970 /* p1 + p2 * dst + p3 * dst * dst, 0 */ 971 emit_op2(p, VP_OPCODE_DP3, ut, 0, ut, state_attenuation); 972 973 /* 1 / factor */ 974 emit_op1(p, VP_OPCODE_RCP, ut, 0, ut ); 975 976 /* out = pointSize / factor */ 977 emit_op2(p, VP_OPCODE_MUL, out, WRITEMASK_X, ut, state_size); 978 979 release_temp(p, ut); 980} 981 982 983 984void _tnl_UpdateFixedFunctionProgram( GLcontext *ctx ) 985{ 986 struct tnl_program p; 987 988 if (ctx->VertexProgram._Enabled) 989 return; 990 991 memset(&p, 0, sizeof(p)); 992 p.ctx = ctx; 993 p.program = &ctx->_TnlProgram; 994 995 p.eye_position = undef; 996 p.eye_position_normalized = undef; 997 p.eye_normal = undef; 998 p.identity = undef; 999 1000 p.temp_flag = 0; 1001 p.temp_reserved = ~((1<<MAX_NV_VERTEX_PROGRAM_TEMPS)-1); 1002 1003 if (p.program->Instructions == NULL) { 1004 p.program->Instructions = MALLOC(sizeof(struct vp_instruction) * 100); 1005 } 1006 1007 /* Initialize the arb_program struct */ 1008 p.program->Base.String = 0; 1009 p.program->Base.NumInstructions = 1010 p.program->Base.NumTemporaries = 1011 p.program->Base.NumParameters = 1012 p.program->Base.NumAttributes = p.program->Base.NumAddressRegs = 0; 1013 if (p.program->Parameters) 1014 _mesa_free_parameter_list(p.program->Parameters); 1015 p.program->Parameters = _mesa_new_parameter_list (); 1016 p.program->InputsRead = 0; 1017 p.program->OutputsWritten = 0; 1018 1019 /* Emit the program, starting with modelviewproject: 1020 */ 1021 build_hpos(&p); 1022 1023 /* Lighting calculations: 1024 */ 1025 if (ctx->Light.Enabled) 1026 build_lighting(&p); 1027 1028 if (ctx->Fog.Enabled) 1029 build_fog(&p); 1030 1031 if (ctx->Texture._TexGenEnabled || ctx->Texture._TexMatEnabled) 1032 build_texture_transform(&p); 1033 1034 if (ctx->Point._Attenuated) 1035 build_pointsize(&p); 1036 1037 1038 /* Finish up: 1039 */ 1040 emit_op1(&p, VP_OPCODE_END, undef, 0, undef); 1041 1042 /* Disassemble: 1043 */ 1044 if (DISASSEM) { 1045 _mesa_printf ("\n"); 1046 } 1047} 1048