t_vp_build.c revision 26b1c97a9dfba05334ecbe7ac312c7ee5b3a554e
1/* 2 * Mesa 3-D graphics library 3 * Version: 6.3.1 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, 21 * WHETHER IN 22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 26/** 27 * \file t_vp_build.c 28 * Create a vertex program to execute the current fixed function T&L pipeline. 29 * \author Keith Whitwell 30 */ 31 32 33#include "glheader.h" 34#include "macros.h" 35#include "enums.h" 36#include "t_context.h" 37#include "t_vp_build.h" 38 39#include "shader/program.h" 40#include "shader/nvvertprog.h" 41#include "shader/arbvertparse.h" 42 43struct state_key { 44 unsigned light_global_enabled:1; 45 unsigned light_local_viewer:1; 46 unsigned light_twoside:1; 47 unsigned light_color_material:1; 48 unsigned light_color_material_mask:12; 49 unsigned light_material_mask:12; 50 51 unsigned normalize:1; 52 unsigned rescale_normals:1; 53 unsigned fog_source_is_depth:1; 54 unsigned tnl_do_vertex_fog:1; 55 unsigned separate_specular:1; 56 unsigned fog_mode:2; 57 unsigned point_attenuated:1; 58 unsigned texture_enabled_global:1; 59 unsigned fragprog_inputs_read:12; 60 61 struct { 62 unsigned light_enabled:1; 63 unsigned light_eyepos3_is_zero:1; 64 unsigned light_spotcutoff_is_180:1; 65 unsigned light_attenuated:1; 66 unsigned texunit_really_enabled:1; 67 unsigned texmat_enabled:1; 68 unsigned texgen_enabled:4; 69 unsigned texgen_mode0:4; 70 unsigned texgen_mode1:4; 71 unsigned texgen_mode2:4; 72 unsigned texgen_mode3:4; 73 } unit[8]; 74}; 75 76 77 78#define FOG_NONE 0 79#define FOG_LINEAR 1 80#define FOG_EXP 2 81#define FOG_EXP2 3 82 83static GLuint translate_fog_mode( GLenum mode ) 84{ 85 switch (mode) { 86 case GL_LINEAR: return FOG_LINEAR; 87 case GL_EXP: return FOG_EXP; 88 case GL_EXP2: return FOG_EXP2; 89 default: return FOG_NONE; 90 } 91} 92 93#define TXG_NONE 0 94#define TXG_OBJ_LINEAR 1 95#define TXG_EYE_LINEAR 2 96#define TXG_SPHERE_MAP 3 97#define TXG_REFLECTION_MAP 4 98#define TXG_NORMAL_MAP 5 99 100static GLuint translate_texgen( GLboolean enabled, GLenum mode ) 101{ 102 if (!enabled) 103 return TXG_NONE; 104 105 switch (mode) { 106 case GL_OBJECT_LINEAR: return TXG_OBJ_LINEAR; 107 case GL_EYE_LINEAR: return TXG_EYE_LINEAR; 108 case GL_SPHERE_MAP: return TXG_SPHERE_MAP; 109 case GL_REFLECTION_MAP_NV: return TXG_REFLECTION_MAP; 110 case GL_NORMAL_MAP_NV: return TXG_NORMAL_MAP; 111 default: return TXG_NONE; 112 } 113} 114 115static struct state_key *make_state_key( GLcontext *ctx ) 116{ 117 TNLcontext *tnl = TNL_CONTEXT(ctx); 118 struct vertex_buffer *VB = &tnl->vb; 119 struct fragment_program *fp = ctx->FragmentProgram._Current; 120 struct state_key *key = CALLOC_STRUCT(state_key); 121 GLuint i; 122 123 /* This now relies on texenvprogram.c being active: 124 */ 125 assert(fp); 126 127 key->fragprog_inputs_read = fp->InputsRead; 128 129 key->separate_specular = (ctx->Light.Model.ColorControl == 130 GL_SEPARATE_SPECULAR_COLOR); 131 132 if (ctx->Light.Enabled) { 133 key->light_global_enabled = 1; 134 135 if (ctx->Light.Model.LocalViewer) 136 key->light_local_viewer = 1; 137 138 if (ctx->Light.Model.TwoSide) 139 key->light_twoside = 1; 140 141 if (ctx->Light.ColorMaterialEnabled) { 142 key->light_color_material = 1; 143 key->light_color_material_mask = ctx->Light.ColorMaterialBitmask; 144 } 145 146 for (i = _TNL_ATTRIB_MAT_FRONT_AMBIENT ; i < _TNL_ATTRIB_INDEX ; i++) 147 if (VB->AttribPtr[i]->stride) 148 key->light_material_mask |= 1<<(i-_TNL_ATTRIB_MAT_FRONT_AMBIENT); 149 150 for (i = 0; i < MAX_LIGHTS; i++) { 151 struct gl_light *light = &ctx->Light.Light[i]; 152 153 if (light->Enabled) { 154 key->unit[i].light_enabled = 1; 155 156 if (light->EyePosition[3] == 0.0) 157 key->unit[i].light_eyepos3_is_zero = 1; 158 159 if (light->SpotCutoff == 180.0) 160 key->unit[i].light_spotcutoff_is_180 = 1; 161 162 if (light->ConstantAttenuation != 1.0 || 163 light->LinearAttenuation != 0.0 || 164 light->QuadraticAttenuation != 0.0) 165 key->unit[i].light_attenuated = 1; 166 } 167 } 168 } 169 170 if (ctx->Transform.Normalize) 171 key->normalize = 1; 172 173 if (ctx->Transform.RescaleNormals) 174 key->rescale_normals = 1; 175 176 key->fog_mode = translate_fog_mode(fp->FogOption); 177 178 if (ctx->Fog.FogCoordinateSource == GL_FRAGMENT_DEPTH_EXT) 179 key->fog_source_is_depth = 1; 180 181 if (tnl->_DoVertexFog) 182 key->tnl_do_vertex_fog = 1; 183 184 if (ctx->Point._Attenuated) 185 key->point_attenuated = 1; 186 187 if (ctx->Texture._TexGenEnabled || 188 ctx->Texture._TexMatEnabled || 189 ctx->Texture._EnabledUnits) 190 key->texture_enabled_global = 1; 191 192 for (i = 0; i < MAX_TEXTURE_UNITS; i++) { 193 struct gl_texture_unit *texUnit = &ctx->Texture.Unit[i]; 194 195 if (texUnit->_ReallyEnabled) 196 key->unit[i].texunit_really_enabled = 1; 197 198 if (ctx->Texture._TexMatEnabled & ENABLE_TEXMAT(i)) 199 key->unit[i].texmat_enabled = 1; 200 201 if (texUnit->TexGenEnabled) { 202 key->unit[i].texgen_enabled = 1; 203 204 key->unit[i].texgen_mode0 = 205 translate_texgen( texUnit->TexGenEnabled & (1<<0), 206 texUnit->GenModeS ); 207 key->unit[i].texgen_mode1 = 208 translate_texgen( texUnit->TexGenEnabled & (1<<1), 209 texUnit->GenModeT ); 210 key->unit[i].texgen_mode2 = 211 translate_texgen( texUnit->TexGenEnabled & (1<<2), 212 texUnit->GenModeR ); 213 key->unit[i].texgen_mode3 = 214 translate_texgen( texUnit->TexGenEnabled & (1<<3), 215 texUnit->GenModeQ ); 216 } 217 } 218 219 return key; 220} 221 222 223 224/* Very useful debugging tool - produces annotated listing of 225 * generated program with line/function references for each 226 * instruction back into this file: 227 */ 228#define DISASSEM (MESA_VERBOSE&VERBOSE_DISASSEM) 229 230/* Should be tunable by the driver - do we want to do matrix 231 * multiplications with DP4's or with MUL/MAD's? SSE works better 232 * with the latter, drivers may differ. 233 */ 234#define PREFER_DP4 0 235 236#define MAX_INSN 256 237 238/* Use uregs to represent registers internally, translate to Mesa's 239 * expected formats on emit. 240 * 241 * NOTE: These are passed by value extensively in this file rather 242 * than as usual by pointer reference. If this disturbs you, try 243 * remembering they are just 32bits in size. 244 * 245 * GCC is smart enough to deal with these dword-sized structures in 246 * much the same way as if I had defined them as dwords and was using 247 * macros to access and set the fields. This is much nicer and easier 248 * to evolve. 249 */ 250struct ureg { 251 GLuint file:4; 252 GLint idx:8; /* relative addressing may be negative */ 253 GLuint negate:1; 254 GLuint swz:12; 255 GLuint pad:7; 256}; 257 258 259struct tnl_program { 260 const struct state_key *state; 261 struct vertex_program *program; 262 263 GLuint temp_in_use; 264 GLuint temp_reserved; 265 266 struct ureg eye_position; 267 struct ureg eye_position_normalized; 268 struct ureg eye_normal; 269 struct ureg identity; 270 271 GLuint materials; 272 GLuint color_materials; 273}; 274 275 276const static struct ureg undef = { 277 ~0, 278 ~0, 279 0, 280 0, 281 0 282}; 283 284/* Local shorthand: 285 */ 286#define X SWIZZLE_X 287#define Y SWIZZLE_Y 288#define Z SWIZZLE_Z 289#define W SWIZZLE_W 290 291 292/* Construct a ureg: 293 */ 294static struct ureg make_ureg(GLuint file, GLint idx) 295{ 296 struct ureg reg; 297 reg.file = file; 298 reg.idx = idx; 299 reg.negate = 0; 300 reg.swz = SWIZZLE_NOOP; 301 reg.pad = 0; 302 return reg; 303} 304 305 306 307static struct ureg negate( struct ureg reg ) 308{ 309 reg.negate ^= 1; 310 return reg; 311} 312 313 314static struct ureg swizzle( struct ureg reg, int x, int y, int z, int w ) 315{ 316 reg.swz = MAKE_SWIZZLE4(GET_SWZ(reg.swz, x), 317 GET_SWZ(reg.swz, y), 318 GET_SWZ(reg.swz, z), 319 GET_SWZ(reg.swz, w)); 320 321 return reg; 322} 323 324static struct ureg swizzle1( struct ureg reg, int x ) 325{ 326 return swizzle(reg, x, x, x, x); 327} 328 329static struct ureg get_temp( struct tnl_program *p ) 330{ 331 int bit = _mesa_ffs( ~p->temp_in_use ); 332 if (!bit) { 333 _mesa_problem(NULL, "%s: out of temporaries\n", __FILE__); 334 _mesa_exit(1); 335 } 336 337 if (bit > p->program->Base.NumTemporaries) 338 p->program->Base.NumTemporaries = bit; 339 340 p->temp_in_use |= 1<<(bit-1); 341 return make_ureg(PROGRAM_TEMPORARY, bit-1); 342} 343 344static struct ureg reserve_temp( struct tnl_program *p ) 345{ 346 struct ureg temp = get_temp( p ); 347 p->temp_reserved |= 1<<temp.idx; 348 return temp; 349} 350 351static void release_temp( struct tnl_program *p, struct ureg reg ) 352{ 353 if (reg.file == PROGRAM_TEMPORARY) { 354 p->temp_in_use &= ~(1<<reg.idx); 355 p->temp_in_use |= p->temp_reserved; /* can't release reserved temps */ 356 } 357} 358 359static void release_temps( struct tnl_program *p ) 360{ 361 p->temp_in_use = p->temp_reserved; 362} 363 364 365 366static struct ureg register_input( struct tnl_program *p, GLuint input ) 367{ 368 p->program->InputsRead |= (1<<input); 369 return make_ureg(PROGRAM_INPUT, input); 370} 371 372static struct ureg register_output( struct tnl_program *p, GLuint output ) 373{ 374 p->program->OutputsWritten |= (1<<output); 375 return make_ureg(PROGRAM_OUTPUT, output); 376} 377 378static struct ureg register_const4f( struct tnl_program *p, 379 GLfloat s0, 380 GLfloat s1, 381 GLfloat s2, 382 GLfloat s3) 383{ 384 GLfloat values[4]; 385 GLint idx; 386 values[0] = s0; 387 values[1] = s1; 388 values[2] = s2; 389 values[3] = s3; 390 idx = _mesa_add_unnamed_constant( p->program->Parameters, values ); 391 return make_ureg(PROGRAM_STATE_VAR, idx); 392} 393 394#define register_const1f(p, s0) register_const4f(p, s0, 0, 0, 1) 395#define register_scalar_const(p, s0) register_const4f(p, s0, s0, s0, s0) 396#define register_const2f(p, s0, s1) register_const4f(p, s0, s1, 0, 1) 397#define register_const3f(p, s0, s1, s2) register_const4f(p, s0, s1, s2, 1) 398 399static GLboolean is_undef( struct ureg reg ) 400{ 401 return reg.file == 0xf; 402} 403 404static struct ureg get_identity_param( struct tnl_program *p ) 405{ 406 if (is_undef(p->identity)) 407 p->identity = register_const4f(p, 0,0,0,1); 408 409 return p->identity; 410} 411 412static struct ureg register_param6( struct tnl_program *p, 413 GLint s0, 414 GLint s1, 415 GLint s2, 416 GLint s3, 417 GLint s4, 418 GLint s5) 419{ 420 GLint tokens[6]; 421 GLint idx; 422 tokens[0] = s0; 423 tokens[1] = s1; 424 tokens[2] = s2; 425 tokens[3] = s3; 426 tokens[4] = s4; 427 tokens[5] = s5; 428 idx = _mesa_add_state_reference( p->program->Parameters, tokens ); 429 return make_ureg(PROGRAM_STATE_VAR, idx); 430} 431 432 433#define register_param1(p,s0) register_param6(p,s0,0,0,0,0,0) 434#define register_param2(p,s0,s1) register_param6(p,s0,s1,0,0,0,0) 435#define register_param3(p,s0,s1,s2) register_param6(p,s0,s1,s2,0,0,0) 436#define register_param4(p,s0,s1,s2,s3) register_param6(p,s0,s1,s2,s3,0,0) 437 438 439static void register_matrix_param6( struct tnl_program *p, 440 GLint s0, 441 GLint s1, 442 GLint s2, 443 GLint s3, 444 GLint s4, 445 GLint s5, 446 struct ureg *matrix ) 447{ 448 GLuint i; 449 450 /* This is a bit sad as the support is there to pull the whole 451 * matrix out in one go: 452 */ 453 for (i = 0; i <= s4 - s3; i++) 454 matrix[i] = register_param6( p, s0, s1, s2, i, i, s5 ); 455} 456 457 458static void emit_arg( struct vp_src_register *src, 459 struct ureg reg ) 460{ 461 src->File = reg.file; 462 src->Index = reg.idx; 463 src->Swizzle = reg.swz; 464 src->Negate = reg.negate; 465 src->RelAddr = 0; 466 src->pad = 0; 467} 468 469static void emit_dst( struct vp_dst_register *dst, 470 struct ureg reg, GLuint mask ) 471{ 472 dst->File = reg.file; 473 dst->Index = reg.idx; 474 /* allow zero as a shorthand for xyzw */ 475 dst->WriteMask = mask ? mask : WRITEMASK_XYZW; 476 dst->pad = 0; 477} 478 479static void debug_insn( struct vp_instruction *inst, const char *fn, 480 GLuint line ) 481{ 482 if (DISASSEM) { 483 static const char *last_fn; 484 485 if (fn != last_fn) { 486 last_fn = fn; 487 _mesa_printf("%s:\n", fn); 488 } 489 490 _mesa_printf("%d:\t", line); 491 _mesa_debug_vp_inst(1, inst); 492 } 493} 494 495 496static void emit_op3fn(struct tnl_program *p, 497 GLuint op, 498 struct ureg dest, 499 GLuint mask, 500 struct ureg src0, 501 struct ureg src1, 502 struct ureg src2, 503 const char *fn, 504 GLuint line) 505{ 506 GLuint nr = p->program->Base.NumInstructions++; 507 struct vp_instruction *inst = &p->program->Instructions[nr]; 508 509 if (p->program->Base.NumInstructions > MAX_INSN) { 510 _mesa_problem(0, "Out of instructions in emit_op3fn\n"); 511 return; 512 } 513 514 inst->Opcode = op; 515 inst->StringPos = 0; 516 inst->Data = 0; 517 518 emit_arg( &inst->SrcReg[0], src0 ); 519 emit_arg( &inst->SrcReg[1], src1 ); 520 emit_arg( &inst->SrcReg[2], src2 ); 521 522 emit_dst( &inst->DstReg, dest, mask ); 523 524 debug_insn(inst, fn, line); 525} 526 527 528#define emit_op3(p, op, dst, mask, src0, src1, src2) \ 529 emit_op3fn(p, op, dst, mask, src0, src1, src2, __FUNCTION__, __LINE__) 530 531#define emit_op2(p, op, dst, mask, src0, src1) \ 532 emit_op3fn(p, op, dst, mask, src0, src1, undef, __FUNCTION__, __LINE__) 533 534#define emit_op1(p, op, dst, mask, src0) \ 535 emit_op3fn(p, op, dst, mask, src0, undef, undef, __FUNCTION__, __LINE__) 536 537 538static struct ureg make_temp( struct tnl_program *p, struct ureg reg ) 539{ 540 if (reg.file == PROGRAM_TEMPORARY && 541 !(p->temp_reserved & (1<<reg.idx))) 542 return reg; 543 else { 544 struct ureg temp = get_temp(p); 545 emit_op1(p, VP_OPCODE_MOV, temp, 0, reg); 546 return temp; 547 } 548} 549 550 551/* Currently no tracking performed of input/output/register size or 552 * active elements. Could be used to reduce these operations, as 553 * could the matrix type. 554 */ 555static void emit_matrix_transform_vec4( struct tnl_program *p, 556 struct ureg dest, 557 const struct ureg *mat, 558 struct ureg src) 559{ 560 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_X, src, mat[0]); 561 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_Y, src, mat[1]); 562 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_Z, src, mat[2]); 563 emit_op2(p, VP_OPCODE_DP4, dest, WRITEMASK_W, src, mat[3]); 564} 565 566/* This version is much easier to implement if writemasks are not 567 * supported natively on the target or (like SSE), the target doesn't 568 * have a clean/obvious dotproduct implementation. 569 */ 570static void emit_transpose_matrix_transform_vec4( struct tnl_program *p, 571 struct ureg dest, 572 const struct ureg *mat, 573 struct ureg src) 574{ 575 struct ureg tmp; 576 577 if (dest.file != PROGRAM_TEMPORARY) 578 tmp = get_temp(p); 579 else 580 tmp = dest; 581 582 emit_op2(p, VP_OPCODE_MUL, tmp, 0, swizzle1(src,X), mat[0]); 583 emit_op3(p, VP_OPCODE_MAD, tmp, 0, swizzle1(src,Y), mat[1], tmp); 584 emit_op3(p, VP_OPCODE_MAD, tmp, 0, swizzle1(src,Z), mat[2], tmp); 585 emit_op3(p, VP_OPCODE_MAD, dest, 0, swizzle1(src,W), mat[3], tmp); 586 587 if (dest.file != PROGRAM_TEMPORARY) 588 release_temp(p, tmp); 589} 590 591static void emit_matrix_transform_vec3( struct tnl_program *p, 592 struct ureg dest, 593 const struct ureg *mat, 594 struct ureg src) 595{ 596 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_X, src, mat[0]); 597 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_Y, src, mat[1]); 598 emit_op2(p, VP_OPCODE_DP3, dest, WRITEMASK_Z, src, mat[2]); 599} 600 601 602static void emit_normalize_vec3( struct tnl_program *p, 603 struct ureg dest, 604 struct ureg src ) 605{ 606 struct ureg tmp = get_temp(p); 607 emit_op2(p, VP_OPCODE_DP3, tmp, 0, src, src); 608 emit_op1(p, VP_OPCODE_RSQ, tmp, 0, tmp); 609 emit_op2(p, VP_OPCODE_MUL, dest, 0, src, tmp); 610 release_temp(p, tmp); 611} 612 613static void emit_passthrough( struct tnl_program *p, 614 GLuint input, 615 GLuint output ) 616{ 617 struct ureg out = register_output(p, output); 618 emit_op1(p, VP_OPCODE_MOV, out, 0, register_input(p, input)); 619} 620 621static struct ureg get_eye_position( struct tnl_program *p ) 622{ 623 if (is_undef(p->eye_position)) { 624 struct ureg pos = register_input( p, VERT_ATTRIB_POS ); 625 struct ureg modelview[4]; 626 627 p->eye_position = reserve_temp(p); 628 629 if (PREFER_DP4) { 630 register_matrix_param6( p, STATE_MATRIX, STATE_MODELVIEW, 0, 0, 3, 631 STATE_MATRIX, modelview ); 632 633 emit_matrix_transform_vec4(p, p->eye_position, modelview, pos); 634 } 635 else { 636 register_matrix_param6( p, STATE_MATRIX, STATE_MODELVIEW, 0, 0, 3, 637 STATE_MATRIX_TRANSPOSE, modelview ); 638 639 emit_transpose_matrix_transform_vec4(p, p->eye_position, modelview, pos); 640 } 641 } 642 643 return p->eye_position; 644} 645 646 647static struct ureg get_eye_position_normalized( struct tnl_program *p ) 648{ 649 if (is_undef(p->eye_position_normalized)) { 650 struct ureg eye = get_eye_position(p); 651 p->eye_position_normalized = reserve_temp(p); 652 emit_normalize_vec3(p, p->eye_position_normalized, eye); 653 } 654 655 return p->eye_position_normalized; 656} 657 658 659static struct ureg get_eye_normal( struct tnl_program *p ) 660{ 661 if (is_undef(p->eye_normal)) { 662 struct ureg normal = register_input(p, VERT_ATTRIB_NORMAL ); 663 struct ureg mvinv[3]; 664 665 register_matrix_param6( p, STATE_MATRIX, STATE_MODELVIEW, 0, 0, 2, 666 STATE_MATRIX_INVTRANS, mvinv ); 667 668 p->eye_normal = reserve_temp(p); 669 670 /* Transform to eye space: 671 */ 672 emit_matrix_transform_vec3( p, p->eye_normal, mvinv, normal ); 673 674 /* Normalize/Rescale: 675 */ 676 if (p->state->normalize) { 677 emit_normalize_vec3( p, p->eye_normal, p->eye_normal ); 678 } 679 else if (p->state->rescale_normals) { 680 struct ureg rescale = register_param2(p, STATE_INTERNAL, 681 STATE_NORMAL_SCALE); 682 683 emit_op2( p, VP_OPCODE_MUL, p->eye_normal, 0, normal, 684 swizzle1(rescale, X)); 685 } 686 } 687 688 return p->eye_normal; 689} 690 691 692 693static void build_hpos( struct tnl_program *p ) 694{ 695 struct ureg pos = register_input( p, VERT_ATTRIB_POS ); 696 struct ureg hpos = register_output( p, VERT_RESULT_HPOS ); 697 struct ureg mvp[4]; 698 699 if (PREFER_DP4) { 700 register_matrix_param6( p, STATE_MATRIX, STATE_MVP, 0, 0, 3, 701 STATE_MATRIX, mvp ); 702 emit_matrix_transform_vec4( p, hpos, mvp, pos ); 703 } 704 else { 705 register_matrix_param6( p, STATE_MATRIX, STATE_MVP, 0, 0, 3, 706 STATE_MATRIX_TRANSPOSE, mvp ); 707 emit_transpose_matrix_transform_vec4( p, hpos, mvp, pos ); 708 } 709} 710 711 712static GLuint material_attrib( GLuint side, GLuint property ) 713{ 714 return ((property - STATE_AMBIENT) * 2 + 715 side); 716} 717 718/* Get a bitmask of which material values vary on a per-vertex basis. 719 */ 720static void set_material_flags( struct tnl_program *p ) 721{ 722 p->color_materials = 0; 723 p->materials = 0; 724 725 if (p->state->light_color_material) { 726 p->materials = 727 p->color_materials = p->state->light_color_material_mask; 728 } 729 730 p->materials |= p->state->light_material_mask; 731} 732 733 734static struct ureg get_material( struct tnl_program *p, GLuint side, 735 GLuint property ) 736{ 737 GLuint attrib = material_attrib(side, property); 738 739 if (p->color_materials & (1<<attrib)) 740 return register_input(p, VERT_ATTRIB_COLOR0); 741 else if (p->materials & (1<<attrib)) 742 return register_input( p, attrib + _TNL_ATTRIB_MAT_FRONT_AMBIENT ); 743 else 744 return register_param3( p, STATE_MATERIAL, side, property ); 745} 746 747#define SCENE_COLOR_BITS(side) (( MAT_BIT_FRONT_EMISSION | \ 748 MAT_BIT_FRONT_AMBIENT | \ 749 MAT_BIT_FRONT_DIFFUSE) << (side)) 750 751/* Either return a precalculated constant value or emit code to 752 * calculate these values dynamically in the case where material calls 753 * are present between begin/end pairs. 754 * 755 * Probably want to shift this to the program compilation phase - if 756 * we always emitted the calculation here, a smart compiler could 757 * detect that it was constant (given a certain set of inputs), and 758 * lift it out of the main loop. That way the programs created here 759 * would be independent of the vertex_buffer details. 760 */ 761static struct ureg get_scenecolor( struct tnl_program *p, GLuint side ) 762{ 763 if (p->materials & SCENE_COLOR_BITS(side)) { 764 struct ureg lm_ambient = register_param1(p, STATE_LIGHTMODEL_AMBIENT); 765 struct ureg material_emission = get_material(p, side, STATE_EMISSION); 766 struct ureg material_ambient = get_material(p, side, STATE_AMBIENT); 767 struct ureg material_diffuse = get_material(p, side, STATE_DIFFUSE); 768 struct ureg tmp = make_temp(p, material_diffuse); 769 emit_op3(p, VP_OPCODE_MAD, tmp, WRITEMASK_XYZ, lm_ambient, 770 material_ambient, material_emission); 771 return tmp; 772 } 773 else 774 return register_param2( p, STATE_LIGHTMODEL_SCENECOLOR, side ); 775} 776 777 778static struct ureg get_lightprod( struct tnl_program *p, GLuint light, 779 GLuint side, GLuint property ) 780{ 781 GLuint attrib = material_attrib(side, property); 782 if (p->materials & (1<<attrib)) { 783 struct ureg light_value = 784 register_param3(p, STATE_LIGHT, light, property); 785 struct ureg material_value = get_material(p, side, property); 786 struct ureg tmp = get_temp(p); 787 emit_op2(p, VP_OPCODE_MUL, tmp, 0, light_value, material_value); 788 return tmp; 789 } 790 else 791 return register_param4(p, STATE_LIGHTPROD, light, side, property); 792} 793 794static struct ureg calculate_light_attenuation( struct tnl_program *p, 795 GLuint i, 796 struct ureg VPpli, 797 struct ureg dist ) 798{ 799 struct ureg attenuation = register_param3(p, STATE_LIGHT, i, 800 STATE_ATTENUATION); 801 struct ureg att = get_temp(p); 802 803 /* Calculate spot attenuation: 804 */ 805 if (!p->state->unit[i].light_spotcutoff_is_180) { 806 struct ureg spot_dir = register_param3(p, STATE_LIGHT, i, 807 STATE_SPOT_DIRECTION); 808 struct ureg spot = get_temp(p); 809 struct ureg slt = get_temp(p); 810 811 emit_normalize_vec3( p, spot, spot_dir ); /* XXX: precompute! */ 812 emit_op2(p, VP_OPCODE_DP3, spot, 0, negate(VPpli), spot); 813 emit_op2(p, VP_OPCODE_SLT, slt, 0, swizzle1(spot_dir,W), spot); 814 emit_op2(p, VP_OPCODE_POW, spot, 0, spot, swizzle1(attenuation, W)); 815 emit_op2(p, VP_OPCODE_MUL, att, 0, slt, spot); 816 817 release_temp(p, spot); 818 release_temp(p, slt); 819 } 820 821 /* Calculate distance attenuation: 822 */ 823 if (p->state->unit[i].light_attenuated) { 824 825 /* 1/d,d,d,1/d */ 826 emit_op1(p, VP_OPCODE_RCP, dist, WRITEMASK_YZ, dist); 827 /* 1,d,d*d,1/d */ 828 emit_op2(p, VP_OPCODE_MUL, dist, WRITEMASK_XZ, dist, swizzle1(dist,Y)); 829 /* 1/dist-atten */ 830 emit_op2(p, VP_OPCODE_DP3, dist, 0, attenuation, dist); 831 832 if (!p->state->unit[i].light_spotcutoff_is_180) { 833 /* dist-atten */ 834 emit_op1(p, VP_OPCODE_RCP, dist, 0, dist); 835 /* spot-atten * dist-atten */ 836 emit_op2(p, VP_OPCODE_MUL, att, 0, dist, att); 837 } else { 838 /* dist-atten */ 839 emit_op1(p, VP_OPCODE_RCP, att, 0, dist); 840 } 841 } 842 843 return att; 844} 845 846 847 848 849 850/* Need to add some addtional parameters to allow lighting in object 851 * space - STATE_SPOT_DIRECTION and STATE_HALF implicitly assume eye 852 * space lighting. 853 */ 854static void build_lighting( struct tnl_program *p ) 855{ 856 const GLboolean twoside = p->state->light_twoside; 857 const GLboolean separate = p->state->separate_specular; 858 GLuint nr_lights = 0, count = 0; 859 struct ureg normal = get_eye_normal(p); 860 struct ureg lit = get_temp(p); 861 struct ureg dots = get_temp(p); 862 struct ureg _col0 = undef, _col1 = undef; 863 struct ureg _bfc0 = undef, _bfc1 = undef; 864 GLuint i; 865 866 for (i = 0; i < MAX_LIGHTS; i++) 867 if (p->state->unit[i].light_enabled) 868 nr_lights++; 869 870 set_material_flags(p); 871 872 { 873 struct ureg shininess = get_material(p, 0, STATE_SHININESS); 874 emit_op1(p, VP_OPCODE_MOV, dots, WRITEMASK_W, swizzle1(shininess,X)); 875 release_temp(p, shininess); 876 877 _col0 = make_temp(p, get_scenecolor(p, 0)); 878 if (separate) 879 _col1 = make_temp(p, get_identity_param(p)); 880 else 881 _col1 = _col0; 882 883 } 884 885 if (twoside) { 886 struct ureg shininess = get_material(p, 1, STATE_SHININESS); 887 emit_op1(p, VP_OPCODE_MOV, dots, WRITEMASK_Z, 888 negate(swizzle1(shininess,X))); 889 release_temp(p, shininess); 890 891 _bfc0 = make_temp(p, get_scenecolor(p, 1)); 892 if (separate) 893 _bfc1 = make_temp(p, get_identity_param(p)); 894 else 895 _bfc1 = _bfc0; 896 } 897 898 899 /* If no lights, still need to emit the scenecolor. 900 */ 901 { 902 struct ureg res0 = register_output( p, VERT_RESULT_COL0 ); 903 emit_op1(p, VP_OPCODE_MOV, res0, 0, _col0); 904 } 905 906 if (separate) { 907 struct ureg res1 = register_output( p, VERT_RESULT_COL1 ); 908 emit_op1(p, VP_OPCODE_MOV, res1, 0, _col1); 909 } 910 911 if (twoside) { 912 struct ureg res0 = register_output( p, VERT_RESULT_BFC0 ); 913 emit_op1(p, VP_OPCODE_MOV, res0, 0, _bfc0); 914 } 915 916 if (twoside && separate) { 917 struct ureg res1 = register_output( p, VERT_RESULT_BFC1 ); 918 emit_op1(p, VP_OPCODE_MOV, res1, 0, _bfc1); 919 } 920 921 if (nr_lights == 0) { 922 release_temps(p); 923 return; 924 } 925 926 927 for (i = 0; i < MAX_LIGHTS; i++) { 928 if (p->state->unit[i].light_enabled) { 929 struct ureg half = undef; 930 struct ureg att = undef, VPpli = undef; 931 932 count++; 933 934 if (p->state->unit[i].light_eyepos3_is_zero) { 935 /* Can used precomputed constants in this case. 936 * Attenuation never applies to infinite lights. 937 */ 938 VPpli = register_param3(p, STATE_LIGHT, i, 939 STATE_POSITION_NORMALIZED); 940 half = register_param3(p, STATE_LIGHT, i, STATE_HALF); 941 } 942 else { 943 struct ureg Ppli = register_param3(p, STATE_LIGHT, i, 944 STATE_POSITION); 945 struct ureg V = get_eye_position(p); 946 struct ureg dist = get_temp(p); 947 948 VPpli = get_temp(p); 949 half = get_temp(p); 950 951 /* Calulate VPpli vector 952 */ 953 emit_op2(p, VP_OPCODE_SUB, VPpli, 0, Ppli, V); 954 955 /* Normalize VPpli. The dist value also used in 956 * attenuation below. 957 */ 958 emit_op2(p, VP_OPCODE_DP3, dist, 0, VPpli, VPpli); 959 emit_op1(p, VP_OPCODE_RSQ, dist, 0, dist); 960 emit_op2(p, VP_OPCODE_MUL, VPpli, 0, VPpli, dist); 961 962 963 /* Calculate attenuation: 964 */ 965 if (!p->state->unit[i].light_spotcutoff_is_180 || 966 p->state->unit[i].light_attenuated) { 967 att = calculate_light_attenuation(p, i, VPpli, dist); 968 } 969 970 971 /* Calculate viewer direction, or use infinite viewer: 972 */ 973 if (p->state->light_local_viewer) { 974 struct ureg eye_hat = get_eye_position_normalized(p); 975 emit_op2(p, VP_OPCODE_SUB, half, 0, VPpli, eye_hat); 976 } 977 else { 978 struct ureg z_dir = swizzle(get_identity_param(p),X,Y,W,Z); 979 emit_op2(p, VP_OPCODE_ADD, half, 0, VPpli, z_dir); 980 } 981 982 emit_normalize_vec3(p, half, half); 983 984 release_temp(p, dist); 985 } 986 987 /* Calculate dot products: 988 */ 989 emit_op2(p, VP_OPCODE_DP3, dots, WRITEMASK_X, normal, VPpli); 990 emit_op2(p, VP_OPCODE_DP3, dots, WRITEMASK_Y, normal, half); 991 992 993 /* Front face lighting: 994 */ 995 { 996 struct ureg ambient = get_lightprod(p, i, 0, STATE_AMBIENT); 997 struct ureg diffuse = get_lightprod(p, i, 0, STATE_DIFFUSE); 998 struct ureg specular = get_lightprod(p, i, 0, STATE_SPECULAR); 999 struct ureg res0, res1; 1000 GLuint mask0, mask1; 1001 1002 emit_op1(p, VP_OPCODE_LIT, lit, 0, dots); 1003 1004 if (!is_undef(att)) 1005 emit_op2(p, VP_OPCODE_MUL, lit, 0, lit, att); 1006 1007 1008 if (count == nr_lights) { 1009 if (separate) { 1010 mask0 = WRITEMASK_XYZ; 1011 mask1 = WRITEMASK_XYZ; 1012 res0 = register_output( p, VERT_RESULT_COL0 ); 1013 res1 = register_output( p, VERT_RESULT_COL1 ); 1014 } 1015 else { 1016 mask0 = 0; 1017 mask1 = WRITEMASK_XYZ; 1018 res0 = _col0; 1019 res1 = register_output( p, VERT_RESULT_COL0 ); 1020 } 1021 } else { 1022 mask0 = 0; 1023 mask1 = 0; 1024 res0 = _col0; 1025 res1 = _col1; 1026 } 1027 1028 emit_op3(p, VP_OPCODE_MAD, _col0, 0, swizzle1(lit,X), ambient, _col0); 1029 emit_op3(p, VP_OPCODE_MAD, res0, mask0, swizzle1(lit,Y), diffuse, _col0); 1030 emit_op3(p, VP_OPCODE_MAD, res1, mask1, swizzle1(lit,Z), specular, _col1); 1031 1032 release_temp(p, ambient); 1033 release_temp(p, diffuse); 1034 release_temp(p, specular); 1035 } 1036 1037 /* Back face lighting: 1038 */ 1039 if (twoside) { 1040 struct ureg ambient = get_lightprod(p, i, 1, STATE_AMBIENT); 1041 struct ureg diffuse = get_lightprod(p, i, 1, STATE_DIFFUSE); 1042 struct ureg specular = get_lightprod(p, i, 1, STATE_SPECULAR); 1043 struct ureg res0, res1; 1044 GLuint mask0, mask1; 1045 1046 emit_op1(p, VP_OPCODE_LIT, lit, 0, negate(swizzle(dots,X,Y,W,Z))); 1047 1048 if (!is_undef(att)) 1049 emit_op2(p, VP_OPCODE_MUL, lit, 0, lit, att); 1050 1051 if (count == nr_lights) { 1052 if (separate) { 1053 mask0 = WRITEMASK_XYZ; 1054 mask1 = WRITEMASK_XYZ; 1055 res0 = register_output( p, VERT_RESULT_BFC0 ); 1056 res1 = register_output( p, VERT_RESULT_BFC1 ); 1057 } 1058 else { 1059 mask0 = 0; 1060 mask1 = WRITEMASK_XYZ; 1061 res0 = _bfc0; 1062 res1 = register_output( p, VERT_RESULT_BFC0 ); 1063 } 1064 } else { 1065 res0 = _bfc0; 1066 res1 = _bfc1; 1067 mask0 = 0; 1068 mask1 = 0; 1069 } 1070 1071 emit_op3(p, VP_OPCODE_MAD, _bfc0, 0, swizzle1(lit,X), ambient, _bfc0); 1072 emit_op3(p, VP_OPCODE_MAD, res0, mask0, swizzle1(lit,Y), diffuse, _bfc0); 1073 emit_op3(p, VP_OPCODE_MAD, res1, mask1, swizzle1(lit,Z), specular, _bfc1); 1074 1075 release_temp(p, ambient); 1076 release_temp(p, diffuse); 1077 release_temp(p, specular); 1078 } 1079 1080 release_temp(p, half); 1081 release_temp(p, VPpli); 1082 release_temp(p, att); 1083 } 1084 } 1085 1086 release_temps( p ); 1087} 1088 1089 1090static void build_fog( struct tnl_program *p ) 1091{ 1092 struct ureg fog = register_output(p, VERT_RESULT_FOGC); 1093 struct ureg input; 1094 1095 if (p->state->fog_source_is_depth) { 1096 input = swizzle1(get_eye_position(p), Z); 1097 } 1098 else { 1099 input = swizzle1(register_input(p, VERT_ATTRIB_FOG), X); 1100 } 1101 1102 if (p->state->tnl_do_vertex_fog) { 1103 struct ureg params = register_param1(p, STATE_FOG_PARAMS); 1104 struct ureg tmp = get_temp(p); 1105 1106 switch (p->state->fog_mode) { 1107 case FOG_LINEAR: { 1108 struct ureg id = get_identity_param(p); 1109 emit_op2(p, VP_OPCODE_SUB, tmp, 0, swizzle1(params,Z), input); 1110 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, swizzle1(params,W)); 1111 emit_op2(p, VP_OPCODE_MAX, tmp, 0, tmp, swizzle1(id,X)); /* saturate */ 1112 emit_op2(p, VP_OPCODE_MIN, fog, WRITEMASK_X, tmp, swizzle1(id,W)); 1113 break; 1114 } 1115 case FOG_EXP: 1116 emit_op1(p, VP_OPCODE_ABS, tmp, 0, input); 1117 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, swizzle1(params,X)); 1118 emit_op2(p, VP_OPCODE_POW, fog, WRITEMASK_X, 1119 register_const1f(p, M_E), negate(tmp)); 1120 break; 1121 case FOG_EXP2: 1122 emit_op2(p, VP_OPCODE_MUL, tmp, 0, input, swizzle1(params,X)); 1123 emit_op2(p, VP_OPCODE_MUL, tmp, 0, tmp, tmp); 1124 emit_op2(p, VP_OPCODE_POW, fog, WRITEMASK_X, 1125 register_const1f(p, M_E), negate(tmp)); 1126 break; 1127 } 1128 1129 release_temp(p, tmp); 1130 } 1131 else { 1132 /* results = incoming fog coords (compute fog per-fragment later) 1133 * 1134 * KW: Is it really necessary to do anything in this case? 1135 */ 1136 emit_op1(p, VP_OPCODE_MOV, fog, WRITEMASK_X, input); 1137 } 1138} 1139 1140static void build_reflect_texgen( struct tnl_program *p, 1141 struct ureg dest, 1142 GLuint writemask ) 1143{ 1144 struct ureg normal = get_eye_normal(p); 1145 struct ureg eye_hat = get_eye_position_normalized(p); 1146 struct ureg tmp = get_temp(p); 1147 1148 /* n.u */ 1149 emit_op2(p, VP_OPCODE_DP3, tmp, 0, normal, eye_hat); 1150 /* 2n.u */ 1151 emit_op2(p, VP_OPCODE_ADD, tmp, 0, tmp, tmp); 1152 /* (-2n.u)n + u */ 1153 emit_op3(p, VP_OPCODE_MAD, dest, writemask, negate(tmp), normal, eye_hat); 1154} 1155 1156static void build_sphere_texgen( struct tnl_program *p, 1157 struct ureg dest, 1158 GLuint writemask ) 1159{ 1160 struct ureg normal = get_eye_normal(p); 1161 struct ureg eye_hat = get_eye_position_normalized(p); 1162 struct ureg tmp = get_temp(p); 1163 struct ureg half = register_scalar_const(p, .5); 1164 struct ureg r = get_temp(p); 1165 struct ureg inv_m = get_temp(p); 1166 struct ureg id = get_identity_param(p); 1167 1168 /* Could share the above calculations, but it would be 1169 * a fairly odd state for someone to set (both sphere and 1170 * reflection active for different texture coordinate 1171 * components. Of course - if two texture units enable 1172 * reflect and/or sphere, things start to tilt in favour 1173 * of seperating this out: 1174 */ 1175 1176 /* n.u */ 1177 emit_op2(p, VP_OPCODE_DP3, tmp, 0, normal, eye_hat); 1178 /* 2n.u */ 1179 emit_op2(p, VP_OPCODE_ADD, tmp, 0, tmp, tmp); 1180 /* (-2n.u)n + u */ 1181 emit_op3(p, VP_OPCODE_MAD, r, 0, negate(tmp), normal, eye_hat); 1182 /* r + 0,0,1 */ 1183 emit_op2(p, VP_OPCODE_ADD, tmp, 0, r, swizzle(id,X,Y,W,Z)); 1184 /* rx^2 + ry^2 + (rz+1)^2 */ 1185 emit_op2(p, VP_OPCODE_DP3, tmp, 0, tmp, tmp); 1186 /* 2/m */ 1187 emit_op1(p, VP_OPCODE_RSQ, tmp, 0, tmp); 1188 /* 1/m */ 1189 emit_op2(p, VP_OPCODE_MUL, inv_m, 0, tmp, half); 1190 /* r/m + 1/2 */ 1191 emit_op3(p, VP_OPCODE_MAD, dest, writemask, r, inv_m, half); 1192 1193 release_temp(p, tmp); 1194 release_temp(p, r); 1195 release_temp(p, inv_m); 1196} 1197 1198 1199static void build_texture_transform( struct tnl_program *p ) 1200{ 1201 GLuint i, j; 1202 1203 for (i = 0; i < MAX_TEXTURE_UNITS; i++) { 1204 1205 if (!(p->state->fragprog_inputs_read & (FRAG_BIT_TEX0<<i))) 1206 continue; 1207 1208 if (p->state->unit[i].texgen_enabled || 1209 p->state->unit[i].texmat_enabled) { 1210 1211 GLuint texmat_enabled = p->state->unit[i].texmat_enabled; 1212 struct ureg out = register_output(p, VERT_RESULT_TEX0 + i); 1213 struct ureg out_texgen = undef; 1214 1215 if (p->state->unit[i].texgen_enabled) { 1216 GLuint copy_mask = 0; 1217 GLuint sphere_mask = 0; 1218 GLuint reflect_mask = 0; 1219 GLuint normal_mask = 0; 1220 GLuint modes[4]; 1221 1222 if (texmat_enabled) 1223 out_texgen = get_temp(p); 1224 else 1225 out_texgen = out; 1226 1227 modes[0] = p->state->unit[i].texgen_mode0; 1228 modes[1] = p->state->unit[i].texgen_mode1; 1229 modes[2] = p->state->unit[i].texgen_mode2; 1230 modes[3] = p->state->unit[i].texgen_mode3; 1231 1232 for (j = 0; j < 4; j++) { 1233 switch (modes[j]) { 1234 case TXG_OBJ_LINEAR: { 1235 struct ureg obj = register_input(p, VERT_ATTRIB_POS); 1236 struct ureg plane = 1237 register_param3(p, STATE_TEXGEN, i, 1238 STATE_TEXGEN_OBJECT_S + j); 1239 1240 emit_op2(p, VP_OPCODE_DP4, out_texgen, WRITEMASK_X << j, 1241 obj, plane ); 1242 break; 1243 } 1244 case TXG_EYE_LINEAR: { 1245 struct ureg eye = get_eye_position(p); 1246 struct ureg plane = 1247 register_param3(p, STATE_TEXGEN, i, 1248 STATE_TEXGEN_EYE_S + j); 1249 1250 emit_op2(p, VP_OPCODE_DP4, out_texgen, WRITEMASK_X << j, 1251 eye, plane ); 1252 break; 1253 } 1254 case TXG_SPHERE_MAP: 1255 sphere_mask |= WRITEMASK_X << j; 1256 break; 1257 case TXG_REFLECTION_MAP: 1258 reflect_mask |= WRITEMASK_X << j; 1259 break; 1260 case TXG_NORMAL_MAP: 1261 normal_mask |= WRITEMASK_X << j; 1262 break; 1263 case TXG_NONE: 1264 copy_mask |= WRITEMASK_X << j; 1265 } 1266 1267 } 1268 1269 1270 if (sphere_mask) { 1271 build_sphere_texgen(p, out_texgen, sphere_mask); 1272 } 1273 1274 if (reflect_mask) { 1275 build_reflect_texgen(p, out_texgen, reflect_mask); 1276 } 1277 1278 if (normal_mask) { 1279 struct ureg normal = get_eye_normal(p); 1280 emit_op1(p, VP_OPCODE_MOV, out_texgen, normal_mask, normal ); 1281 } 1282 1283 if (copy_mask) { 1284 struct ureg in = register_input(p, VERT_ATTRIB_TEX0+i); 1285 emit_op1(p, VP_OPCODE_MOV, out_texgen, copy_mask, in ); 1286 } 1287 } 1288 1289 if (texmat_enabled) { 1290 struct ureg texmat[4]; 1291 struct ureg in = (!is_undef(out_texgen) ? 1292 out_texgen : 1293 register_input(p, VERT_ATTRIB_TEX0+i)); 1294 if (PREFER_DP4) { 1295 register_matrix_param6( p, STATE_MATRIX, STATE_TEXTURE, i, 1296 0, 3, STATE_MATRIX, texmat ); 1297 emit_matrix_transform_vec4( p, out, texmat, in ); 1298 } 1299 else { 1300 register_matrix_param6( p, STATE_MATRIX, STATE_TEXTURE, i, 1301 0, 3, STATE_MATRIX_TRANSPOSE, texmat ); 1302 emit_transpose_matrix_transform_vec4( p, out, texmat, in ); 1303 } 1304 } 1305 1306 release_temps(p); 1307 } 1308 else { 1309 emit_passthrough(p, VERT_ATTRIB_TEX0+i, VERT_RESULT_TEX0+i); 1310 } 1311 } 1312} 1313 1314 1315/* Seems like it could be tighter: 1316 */ 1317static void build_pointsize( struct tnl_program *p ) 1318{ 1319 struct ureg eye = get_eye_position(p); 1320 struct ureg state_size = register_param1(p, STATE_POINT_SIZE); 1321 struct ureg state_attenuation = register_param1(p, STATE_POINT_ATTENUATION); 1322 struct ureg out = register_output(p, VERT_RESULT_PSIZ); 1323 struct ureg ut = get_temp(p); 1324 1325 /* 1, -Z, Z * Z, 1 */ 1326 emit_op1(p, VP_OPCODE_MOV, ut, 0, swizzle1(get_identity_param(p), W)); 1327 emit_op2(p, VP_OPCODE_MUL, ut, WRITEMASK_YZ, ut, negate(swizzle1(eye, Z))); 1328 emit_op2(p, VP_OPCODE_MUL, ut, WRITEMASK_Z, ut, negate(swizzle1(eye, Z))); 1329 1330 1331 /* p1 + p2 * dist + p3 * dist * dist, 0 */ 1332 emit_op2(p, VP_OPCODE_DP3, ut, 0, ut, state_attenuation); 1333 1334 /* 1 / factor */ 1335 emit_op1(p, VP_OPCODE_RCP, ut, 0, ut ); 1336 1337 /* out = pointSize / factor */ 1338 emit_op2(p, VP_OPCODE_MUL, out, WRITEMASK_X, ut, state_size); 1339 1340 release_temp(p, ut); 1341} 1342 1343static void build_tnl_program( struct tnl_program *p ) 1344{ /* Emit the program, starting with modelviewproject: 1345 */ 1346 build_hpos(p); 1347 1348 /* Lighting calculations: 1349 */ 1350 if (p->state->fragprog_inputs_read & (FRAG_BIT_COL0|FRAG_BIT_COL1)) { 1351 if (p->state->light_global_enabled) 1352 build_lighting(p); 1353 else { 1354 if (p->state->fragprog_inputs_read & FRAG_BIT_COL0) 1355 emit_passthrough(p, VERT_ATTRIB_COLOR0, VERT_RESULT_COL0); 1356 1357 if (p->state->fragprog_inputs_read & FRAG_BIT_COL1) 1358 emit_passthrough(p, VERT_ATTRIB_COLOR0, VERT_RESULT_COL1); 1359 } 1360 } 1361 1362 if ((p->state->fragprog_inputs_read & FRAG_BIT_FOGC) || 1363 p->state->fog_mode != FOG_NONE) 1364 build_fog(p); 1365 1366 if (p->state->fragprog_inputs_read & FRAG_BITS_TEX_ANY) 1367 build_texture_transform(p); 1368 1369 if (p->state->point_attenuated) 1370 build_pointsize(p); 1371 1372 /* Finish up: 1373 */ 1374 emit_op1(p, VP_OPCODE_END, undef, 0, undef); 1375 1376 /* Disassemble: 1377 */ 1378 if (DISASSEM) { 1379 _mesa_printf ("\n"); 1380 } 1381} 1382 1383 1384static void 1385create_new_program( const struct state_key *key, 1386 struct vertex_program *program, 1387 GLuint max_temps) 1388{ 1389 struct tnl_program p; 1390 1391 _mesa_memset(&p, 0, sizeof(p)); 1392 p.state = key; 1393 p.program = program; 1394 p.eye_position = undef; 1395 p.eye_position_normalized = undef; 1396 p.eye_normal = undef; 1397 p.identity = undef; 1398 p.temp_in_use = 0; 1399 1400 if (max_temps >= sizeof(int) * 8) 1401 p.temp_reserved = 0; 1402 else 1403 p.temp_reserved = ~((1<<max_temps)-1); 1404 1405 p.program->Instructions = MALLOC(sizeof(struct vp_instruction) * MAX_INSN); 1406 p.program->Base.String = 0; 1407 p.program->Base.NumInstructions = 1408 p.program->Base.NumTemporaries = 1409 p.program->Base.NumParameters = 1410 p.program->Base.NumAttributes = p.program->Base.NumAddressRegs = 0; 1411 p.program->Parameters = _mesa_new_parameter_list(); 1412 p.program->InputsRead = 0; 1413 p.program->OutputsWritten = 0; 1414 1415 build_tnl_program( &p ); 1416} 1417 1418static void *search_cache( struct tnl_cache *cache, 1419 GLuint hash, 1420 const void *key, 1421 GLuint keysize) 1422{ 1423 struct tnl_cache_item *c; 1424 1425 for (c = cache->items[hash % cache->size]; c; c = c->next) { 1426 if (c->hash == hash && _mesa_memcmp(c->key, key, keysize) == 0) 1427 return c->data; 1428 } 1429 1430 return NULL; 1431} 1432 1433static void rehash( struct tnl_cache *cache ) 1434{ 1435 struct tnl_cache_item **items; 1436 struct tnl_cache_item *c, *next; 1437 GLuint size, i; 1438 1439 size = cache->size * 3; 1440 items = MALLOC(size * sizeof(*items)); 1441 _mesa_memset(items, 0, size * sizeof(*items)); 1442 1443 for (i = 0; i < cache->size; i++) 1444 for (c = cache->items[i]; c; c = next) { 1445 next = c->next; 1446 c->next = items[c->hash % size]; 1447 items[c->hash % size] = c; 1448 } 1449 1450 FREE(cache->items); 1451 cache->items = items; 1452 cache->size = size; 1453} 1454 1455static void cache_item( struct tnl_cache *cache, 1456 GLuint hash, 1457 void *key, 1458 void *data ) 1459{ 1460 struct tnl_cache_item *c = MALLOC(sizeof(*c)); 1461 c->hash = hash; 1462 c->key = key; 1463 c->data = data; 1464 1465 if (++cache->n_items > cache->size * 1.5) 1466 rehash(cache); 1467 1468 c->next = cache->items[hash % cache->size]; 1469 cache->items[hash % cache->size] = c; 1470} 1471 1472static GLuint hash_key( struct state_key *key ) 1473{ 1474 GLuint *ikey = (GLuint *)key; 1475 GLuint hash = 0, i; 1476 1477 /* I'm sure this can be improved on, but speed is important: 1478 */ 1479 for (i = 0; i < sizeof(*key)/sizeof(GLuint); i++) 1480 hash ^= ikey[i]; 1481 1482 return hash; 1483} 1484 1485void _tnl_UpdateFixedFunctionProgram( GLcontext *ctx ) 1486{ 1487 TNLcontext *tnl = TNL_CONTEXT(ctx); 1488 struct state_key *key; 1489 GLuint hash; 1490 struct vertex_program *prev = ctx->VertexProgram._Current; 1491 1492 if (ctx->VertexProgram._Enabled == GL_FALSE) { 1493 /* Grab all the relevent state and put it in a single structure: 1494 */ 1495 key = make_state_key(ctx); 1496 hash = hash_key(key); 1497 1498 /* Look for an already-prepared program for this state: 1499 */ 1500 ctx->_TnlProgram = (struct vertex_program *) 1501 search_cache( tnl->vp_cache, hash, key, sizeof(*key) ); 1502 1503 /* OK, we'll have to build a new one: 1504 */ 1505 if (!ctx->_TnlProgram) { 1506 if (0) 1507 _mesa_printf("Build new TNL program\n"); 1508 1509 ctx->_TnlProgram = (struct vertex_program *) 1510 ctx->Driver.NewProgram(ctx, GL_VERTEX_PROGRAM_ARB, 0); 1511 1512 create_new_program( key, ctx->_TnlProgram, 1513 ctx->Const.VertexProgram.MaxTemps ); 1514 1515 1516 cache_item(tnl->vp_cache, hash, key, ctx->_TnlProgram ); 1517 } 1518 else { 1519 FREE(key); 1520 if (0) 1521 _mesa_printf("Found existing TNL program for key %x\n", hash); 1522 } 1523 ctx->VertexProgram._Current = ctx->_TnlProgram; 1524 } 1525 else { 1526 ctx->VertexProgram._Current = ctx->VertexProgram.Current; 1527 } 1528 1529 /* Tell the driver about the change. Could define a new target for 1530 * this? 1531 */ 1532 if (ctx->VertexProgram._Current != prev) 1533 ctx->Driver.BindProgram(ctx, GL_VERTEX_PROGRAM_ARB, (struct program *) 1534 ctx->VertexProgram._Current); 1535} 1536 1537 1538void _tnl_ProgramCacheDestroy( GLcontext *ctx ) 1539{ 1540 TNLcontext *tnl = TNL_CONTEXT(ctx); 1541 struct tnl_cache_item *c, *next; 1542 GLuint i; 1543 1544 for (i = 0; i < tnl->vp_cache->size; i++) 1545 for (c = tnl->vp_cache->items[i]; c; c = next) { 1546 next = c->next; 1547 FREE(c->key); 1548 FREE(c->data); 1549 FREE(c); 1550 } 1551 1552 FREE(tnl->vp_cache->items); 1553 FREE(tnl->vp_cache); 1554} 1555