program.c revision 82635aad4203d44648dd6e345ec2b5e21ff06510
1/* 2 * Mesa 3-D graphics library 3 * Version: 6.5.3 4 * 5 * Copyright (C) 1999-2007 Brian Paul 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 * 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. 23 */ 24 25/** 26 * \file program.c 27 * Vertex and fragment program support functions. 28 * \author Brian Paul 29 */ 30 31 32#include "glheader.h" 33#include "context.h" 34#include "hash.h" 35#include "program.h" 36#include "prog_parameter.h" 37#include "prog_instruction.h" 38 39 40/** 41 * A pointer to this dummy program is put into the hash table when 42 * glGenPrograms is called. 43 */ 44struct gl_program _mesa_DummyProgram; 45 46 47/** 48 * Init context's vertex/fragment program state 49 */ 50void 51_mesa_init_program(GLcontext *ctx) 52{ 53 GLuint i; 54 55 ctx->Program.ErrorPos = -1; 56 ctx->Program.ErrorString = _mesa_strdup(""); 57 58#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program 59 ctx->VertexProgram.Enabled = GL_FALSE; 60 ctx->VertexProgram.PointSizeEnabled = GL_FALSE; 61 ctx->VertexProgram.TwoSideEnabled = GL_FALSE; 62 _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, 63 ctx->Shared->DefaultVertexProgram); 64 assert(ctx->VertexProgram.Current); 65 for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) { 66 ctx->VertexProgram.TrackMatrix[i] = GL_NONE; 67 ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV; 68 } 69#endif 70 71#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program 72 ctx->FragmentProgram.Enabled = GL_FALSE; 73 _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, 74 ctx->Shared->DefaultFragmentProgram); 75 assert(ctx->FragmentProgram.Current); 76#endif 77 78 /* XXX probably move this stuff */ 79#if FEATURE_ATI_fragment_shader 80 ctx->ATIFragmentShader.Enabled = GL_FALSE; 81 ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader; 82 assert(ctx->ATIFragmentShader.Current); 83 ctx->ATIFragmentShader.Current->RefCount++; 84#endif 85} 86 87 88/** 89 * Free a context's vertex/fragment program state 90 */ 91void 92_mesa_free_program_data(GLcontext *ctx) 93{ 94#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program 95 _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, NULL); 96#endif 97#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program 98 _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, NULL); 99#endif 100 /* XXX probably move this stuff */ 101#if FEATURE_ATI_fragment_shader 102 if (ctx->ATIFragmentShader.Current) { 103 ctx->ATIFragmentShader.Current->RefCount--; 104 if (ctx->ATIFragmentShader.Current->RefCount <= 0) { 105 _mesa_free(ctx->ATIFragmentShader.Current); 106 } 107 } 108#endif 109 _mesa_free((void *) ctx->Program.ErrorString); 110} 111 112 113/** 114 * Update the default program objects in the given context to reference those 115 * specified in the shared state and release those referencing the old 116 * shared state. 117 */ 118void 119_mesa_update_default_objects_program(GLcontext *ctx) 120{ 121#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program 122 _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, 123 (struct gl_vertex_program *) 124 ctx->Shared->DefaultVertexProgram); 125 assert(ctx->VertexProgram.Current); 126#endif 127 128#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program 129 _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, 130 (struct gl_fragment_program *) 131 ctx->Shared->DefaultFragmentProgram); 132 assert(ctx->FragmentProgram.Current); 133#endif 134 135 /* XXX probably move this stuff */ 136#if FEATURE_ATI_fragment_shader 137 if (ctx->ATIFragmentShader.Current) { 138 ctx->ATIFragmentShader.Current->RefCount--; 139 if (ctx->ATIFragmentShader.Current->RefCount <= 0) { 140 _mesa_free(ctx->ATIFragmentShader.Current); 141 } 142 } 143 ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader; 144 assert(ctx->ATIFragmentShader.Current); 145 ctx->ATIFragmentShader.Current->RefCount++; 146#endif 147} 148 149 150/** 151 * Set the vertex/fragment program error state (position and error string). 152 * This is generally called from within the parsers. 153 */ 154void 155_mesa_set_program_error(GLcontext *ctx, GLint pos, const char *string) 156{ 157 ctx->Program.ErrorPos = pos; 158 _mesa_free((void *) ctx->Program.ErrorString); 159 if (!string) 160 string = ""; 161 ctx->Program.ErrorString = _mesa_strdup(string); 162} 163 164 165/** 166 * Find the line number and column for 'pos' within 'string'. 167 * Return a copy of the line which contains 'pos'. Free the line with 168 * _mesa_free(). 169 * \param string the program string 170 * \param pos the position within the string 171 * \param line returns the line number corresponding to 'pos'. 172 * \param col returns the column number corresponding to 'pos'. 173 * \return copy of the line containing 'pos'. 174 */ 175const GLubyte * 176_mesa_find_line_column(const GLubyte *string, const GLubyte *pos, 177 GLint *line, GLint *col) 178{ 179 const GLubyte *lineStart = string; 180 const GLubyte *p = string; 181 GLubyte *s; 182 int len; 183 184 *line = 1; 185 186 while (p != pos) { 187 if (*p == (GLubyte) '\n') { 188 (*line)++; 189 lineStart = p + 1; 190 } 191 p++; 192 } 193 194 *col = (pos - lineStart) + 1; 195 196 /* return copy of this line */ 197 while (*p != 0 && *p != '\n') 198 p++; 199 len = p - lineStart; 200 s = (GLubyte *) _mesa_malloc(len + 1); 201 _mesa_memcpy(s, lineStart, len); 202 s[len] = 0; 203 204 return s; 205} 206 207 208/** 209 * Initialize a new vertex/fragment program object. 210 */ 211static struct gl_program * 212_mesa_init_program_struct( GLcontext *ctx, struct gl_program *prog, 213 GLenum target, GLuint id) 214{ 215 (void) ctx; 216 if (prog) { 217 GLuint i; 218 _mesa_bzero(prog, sizeof(*prog)); 219 prog->Id = id; 220 prog->Target = target; 221 prog->Resident = GL_TRUE; 222 prog->RefCount = 1; 223 prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB; 224 225 /* default mapping from samplers to texture units */ 226 for (i = 0; i < MAX_SAMPLERS; i++) 227 prog->SamplerUnits[i] = i; 228 } 229 230 return prog; 231} 232 233 234/** 235 * Initialize a new fragment program object. 236 */ 237struct gl_program * 238_mesa_init_fragment_program( GLcontext *ctx, struct gl_fragment_program *prog, 239 GLenum target, GLuint id) 240{ 241 if (prog) 242 return _mesa_init_program_struct( ctx, &prog->Base, target, id ); 243 else 244 return NULL; 245} 246 247 248/** 249 * Initialize a new vertex program object. 250 */ 251struct gl_program * 252_mesa_init_vertex_program( GLcontext *ctx, struct gl_vertex_program *prog, 253 GLenum target, GLuint id) 254{ 255 if (prog) 256 return _mesa_init_program_struct( ctx, &prog->Base, target, id ); 257 else 258 return NULL; 259} 260 261 262/** 263 * Allocate and initialize a new fragment/vertex program object but 264 * don't put it into the program hash table. Called via 265 * ctx->Driver.NewProgram. May be overridden (ie. replaced) by a 266 * device driver function to implement OO deriviation with additional 267 * types not understood by this function. 268 * 269 * \param ctx context 270 * \param id program id/number 271 * \param target program target/type 272 * \return pointer to new program object 273 */ 274struct gl_program * 275_mesa_new_program(GLcontext *ctx, GLenum target, GLuint id) 276{ 277 struct gl_program *prog; 278 switch (target) { 279 case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */ 280 prog = _mesa_init_vertex_program(ctx, CALLOC_STRUCT(gl_vertex_program), 281 target, id ); 282 break; 283 case GL_FRAGMENT_PROGRAM_NV: 284 case GL_FRAGMENT_PROGRAM_ARB: 285 prog =_mesa_init_fragment_program(ctx, 286 CALLOC_STRUCT(gl_fragment_program), 287 target, id ); 288 break; 289 default: 290 _mesa_problem(ctx, "bad target in _mesa_new_program"); 291 prog = NULL; 292 } 293 return prog; 294} 295 296 297/** 298 * Delete a program and remove it from the hash table, ignoring the 299 * reference count. 300 * Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation) 301 * by a device driver function. 302 */ 303void 304_mesa_delete_program(GLcontext *ctx, struct gl_program *prog) 305{ 306 (void) ctx; 307 ASSERT(prog); 308 ASSERT(prog->RefCount==0); 309 310 if (prog == &_mesa_DummyProgram) 311 return; 312 313 if (prog->String) 314 _mesa_free(prog->String); 315 316 _mesa_free_instructions(prog->Instructions, prog->NumInstructions); 317 318 if (prog->Parameters) { 319 _mesa_free_parameter_list(prog->Parameters); 320 } 321 if (prog->Varying) { 322 _mesa_free_parameter_list(prog->Varying); 323 } 324 if (prog->Attributes) { 325 _mesa_free_parameter_list(prog->Attributes); 326 } 327 328 /* XXX this is a little ugly */ 329 if (prog->Target == GL_VERTEX_PROGRAM_ARB) { 330 struct gl_vertex_program *vprog = (struct gl_vertex_program *) prog; 331 if (vprog->TnlData) 332 _mesa_free(vprog->TnlData); 333 } 334 335 _mesa_free(prog); 336} 337 338 339/** 340 * Return the gl_program object for a given ID. 341 * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of 342 * casts elsewhere. 343 */ 344struct gl_program * 345_mesa_lookup_program(GLcontext *ctx, GLuint id) 346{ 347 if (id) 348 return (struct gl_program *) _mesa_HashLookup(ctx->Shared->Programs, id); 349 else 350 return NULL; 351} 352 353 354/** 355 * Reference counting for vertex/fragment programs 356 */ 357void 358_mesa_reference_program(GLcontext *ctx, 359 struct gl_program **ptr, 360 struct gl_program *prog) 361{ 362 assert(ptr); 363 if (*ptr && prog) { 364 /* sanity check */ 365 ASSERT((*ptr)->Target == prog->Target); 366 } 367 if (*ptr == prog) { 368 return; /* no change */ 369 } 370 if (*ptr) { 371 GLboolean deleteFlag; 372 373 /*_glthread_LOCK_MUTEX((*ptr)->Mutex);*/ 374#if 0 375 printf("Program %p ID=%u Target=%s Refcount-- to %d\n", 376 *ptr, (*ptr)->Id, 377 ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB ? "VP" : "FP"), 378 (*ptr)->RefCount - 1); 379#endif 380 ASSERT((*ptr)->RefCount > 0); 381 (*ptr)->RefCount--; 382 383 deleteFlag = ((*ptr)->RefCount == 0); 384 /*_glthread_UNLOCK_MUTEX((*ptr)->Mutex);*/ 385 386 if (deleteFlag) { 387 ASSERT(ctx); 388 ctx->Driver.DeleteProgram(ctx, *ptr); 389 } 390 391 *ptr = NULL; 392 } 393 394 assert(!*ptr); 395 if (prog) { 396 /*_glthread_LOCK_MUTEX(prog->Mutex);*/ 397 prog->RefCount++; 398#if 0 399 printf("Program %p ID=%u Target=%s Refcount++ to %d\n", 400 prog, prog->Id, 401 (prog->Target == GL_VERTEX_PROGRAM_ARB ? "VP" : "FP"), 402 prog->RefCount); 403#endif 404 /*_glthread_UNLOCK_MUTEX(prog->Mutex);*/ 405 } 406 407 *ptr = prog; 408} 409 410 411/** 412 * Return a copy of a program. 413 * XXX Problem here if the program object is actually OO-derivation 414 * made by a device driver. 415 */ 416struct gl_program * 417_mesa_clone_program(GLcontext *ctx, const struct gl_program *prog) 418{ 419 struct gl_program *clone; 420 421 clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id); 422 if (!clone) 423 return NULL; 424 425 assert(clone->Target == prog->Target); 426 assert(clone->RefCount == 1); 427 428 clone->String = (GLubyte *) _mesa_strdup((char *) prog->String); 429 clone->Format = prog->Format; 430 clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions); 431 if (!clone->Instructions) { 432 _mesa_reference_program(ctx, &clone, NULL); 433 return NULL; 434 } 435 _mesa_copy_instructions(clone->Instructions, prog->Instructions, 436 prog->NumInstructions); 437 clone->InputsRead = prog->InputsRead; 438 clone->OutputsWritten = prog->OutputsWritten; 439 clone->SamplersUsed = prog->SamplersUsed; 440 clone->ShadowSamplers = prog->ShadowSamplers; 441 memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed)); 442 443 if (prog->Parameters) 444 clone->Parameters = _mesa_clone_parameter_list(prog->Parameters); 445 memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); 446 if (prog->Varying) 447 clone->Varying = _mesa_clone_parameter_list(prog->Varying); 448 if (prog->Attributes) 449 clone->Attributes = _mesa_clone_parameter_list(prog->Attributes); 450 memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams)); 451 clone->NumInstructions = prog->NumInstructions; 452 clone->NumTemporaries = prog->NumTemporaries; 453 clone->NumParameters = prog->NumParameters; 454 clone->NumAttributes = prog->NumAttributes; 455 clone->NumAddressRegs = prog->NumAddressRegs; 456 clone->NumNativeInstructions = prog->NumNativeInstructions; 457 clone->NumNativeTemporaries = prog->NumNativeTemporaries; 458 clone->NumNativeParameters = prog->NumNativeParameters; 459 clone->NumNativeAttributes = prog->NumNativeAttributes; 460 clone->NumNativeAddressRegs = prog->NumNativeAddressRegs; 461 clone->NumAluInstructions = prog->NumAluInstructions; 462 clone->NumTexInstructions = prog->NumTexInstructions; 463 clone->NumTexIndirections = prog->NumTexIndirections; 464 clone->NumNativeAluInstructions = prog->NumNativeAluInstructions; 465 clone->NumNativeTexInstructions = prog->NumNativeTexInstructions; 466 clone->NumNativeTexIndirections = prog->NumNativeTexIndirections; 467 468 switch (prog->Target) { 469 case GL_VERTEX_PROGRAM_ARB: 470 { 471 const struct gl_vertex_program *vp 472 = (const struct gl_vertex_program *) prog; 473 struct gl_vertex_program *vpc = (struct gl_vertex_program *) clone; 474 vpc->IsPositionInvariant = vp->IsPositionInvariant; 475 } 476 break; 477 case GL_FRAGMENT_PROGRAM_ARB: 478 { 479 const struct gl_fragment_program *fp 480 = (const struct gl_fragment_program *) prog; 481 struct gl_fragment_program *fpc = (struct gl_fragment_program *) clone; 482 fpc->FogOption = fp->FogOption; 483 fpc->UsesKill = fp->UsesKill; 484 } 485 break; 486 default: 487 _mesa_problem(NULL, "Unexpected target in _mesa_clone_program"); 488 } 489 490 return clone; 491} 492 493 494/** 495 * Insert 'count' NOP instructions at 'start' in the given program. 496 * Adjust branch targets accordingly. 497 */ 498GLboolean 499_mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count) 500{ 501 const GLuint origLen = prog->NumInstructions; 502 const GLuint newLen = origLen + count; 503 struct prog_instruction *newInst; 504 GLuint i; 505 506 /* adjust branches */ 507 for (i = 0; i < prog->NumInstructions; i++) { 508 struct prog_instruction *inst = prog->Instructions + i; 509 if (inst->BranchTarget > 0) { 510 if (inst->BranchTarget >= start) { 511 inst->BranchTarget += count; 512 } 513 } 514 } 515 516 /* Alloc storage for new instructions */ 517 newInst = _mesa_alloc_instructions(newLen); 518 if (!newInst) { 519 return GL_FALSE; 520 } 521 522 /* Copy 'start' instructions into new instruction buffer */ 523 _mesa_copy_instructions(newInst, prog->Instructions, start); 524 525 /* init the new instructions */ 526 _mesa_init_instructions(newInst + start, count); 527 528 /* Copy the remaining/tail instructions to new inst buffer */ 529 _mesa_copy_instructions(newInst + start + count, 530 prog->Instructions + start, 531 origLen - start); 532 533 /* free old instructions */ 534 _mesa_free_instructions(prog->Instructions, origLen); 535 536 /* install new instructions */ 537 prog->Instructions = newInst; 538 prog->NumInstructions = newLen; 539 540 return GL_TRUE; 541} 542 543 544/** 545 * Search instructions for registers that match (oldFile, oldIndex), 546 * replacing them with (newFile, newIndex). 547 */ 548static void 549replace_registers(struct prog_instruction *inst, GLuint numInst, 550 GLuint oldFile, GLuint oldIndex, 551 GLuint newFile, GLuint newIndex) 552{ 553 GLuint i, j; 554 for (i = 0; i < numInst; i++) { 555 /* src regs */ 556 for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) { 557 if (inst[i].SrcReg[j].File == oldFile && 558 inst[i].SrcReg[j].Index == oldIndex) { 559 inst[i].SrcReg[j].File = newFile; 560 inst[i].SrcReg[j].Index = newIndex; 561 } 562 } 563 /* dst reg */ 564 if (inst[i].DstReg.File == oldFile && inst[i].DstReg.Index == oldIndex) { 565 inst[i].DstReg.File = newFile; 566 inst[i].DstReg.Index = newIndex; 567 } 568 } 569} 570 571 572/** 573 * Search instructions for references to program parameters. When found, 574 * increment the parameter index by 'offset'. 575 * Used when combining programs. 576 */ 577static void 578adjust_param_indexes(struct prog_instruction *inst, GLuint numInst, 579 GLuint offset) 580{ 581 GLuint i, j; 582 for (i = 0; i < numInst; i++) { 583 for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) { 584 GLuint f = inst[i].SrcReg[j].File; 585 if (f == PROGRAM_CONSTANT || 586 f == PROGRAM_UNIFORM || 587 f == PROGRAM_STATE_VAR) { 588 inst[i].SrcReg[j].Index += offset; 589 } 590 } 591 } 592} 593 594 595/** 596 * Combine two programs into one. Fix instructions so the outputs of 597 * the first program go to the inputs of the second program. 598 */ 599struct gl_program * 600_mesa_combine_programs(GLcontext *ctx, 601 const struct gl_program *progA, 602 const struct gl_program *progB) 603{ 604 struct prog_instruction *newInst; 605 struct gl_program *newProg; 606 const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */ 607 const GLuint lenB = progB->NumInstructions; 608 const GLuint numParamsA = _mesa_num_parameters(progA->Parameters); 609 const GLuint newLength = lenA + lenB; 610 GLbitfield inputsB; 611 GLuint i; 612 613 ASSERT(progA->Target == progB->Target); 614 615 newInst = _mesa_alloc_instructions(newLength); 616 if (!newInst) 617 return GL_FALSE; 618 619 _mesa_copy_instructions(newInst, progA->Instructions, lenA); 620 _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB); 621 622 /* adjust branch / instruction addresses for B's instructions */ 623 for (i = 0; i < lenB; i++) { 624 newInst[lenA + i].BranchTarget += lenA; 625 } 626 627 newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0); 628 newProg->Instructions = newInst; 629 newProg->NumInstructions = newLength; 630 631 if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) { 632 struct gl_fragment_program *fprogA, *fprogB, *newFprog; 633 fprogA = (struct gl_fragment_program *) progA; 634 fprogB = (struct gl_fragment_program *) progB; 635 newFprog = (struct gl_fragment_program *) newProg; 636 637 newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill; 638 639 /* Connect color outputs of fprogA to color inputs of fprogB, via a 640 * new temporary register. 641 */ 642 if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) && 643 (progB->InputsRead & (1 << FRAG_ATTRIB_COL0))) { 644 GLint tempReg = _mesa_find_free_register(newProg, PROGRAM_TEMPORARY); 645 if (tempReg < 0) { 646 _mesa_problem(ctx, "No free temp regs found in " 647 "_mesa_combine_programs(), using 31"); 648 tempReg = 31; 649 } 650 /* replace writes to result.color[0] with tempReg */ 651 replace_registers(newInst, lenA, 652 PROGRAM_OUTPUT, FRAG_RESULT_COLR, 653 PROGRAM_TEMPORARY, tempReg); 654 /* replace reads from input.color[0] with tempReg */ 655 replace_registers(newInst + lenA, lenB, 656 PROGRAM_INPUT, FRAG_ATTRIB_COL0, 657 PROGRAM_TEMPORARY, tempReg); 658 } 659 660 inputsB = progB->InputsRead; 661 if (progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) { 662 inputsB &= ~(1 << FRAG_ATTRIB_COL0); 663 } 664 newProg->InputsRead = progA->InputsRead | inputsB; 665 newProg->OutputsWritten = progB->OutputsWritten; 666 newProg->SamplersUsed = progA->SamplersUsed | progB->SamplersUsed; 667 } 668 else { 669 /* vertex program */ 670 assert(0); /* XXX todo */ 671 } 672 673 /* 674 * Merge parameters (uniforms, constants, etc) 675 */ 676 newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters, 677 progB->Parameters); 678 679 adjust_param_indexes(newInst + lenA, lenB, numParamsA); 680 681 682 return newProg; 683} 684 685 686 687 688/** 689 * Scan the given program to find a free register of the given type. 690 * \param regFile - PROGRAM_INPUT, PROGRAM_OUTPUT or PROGRAM_TEMPORARY 691 */ 692GLint 693_mesa_find_free_register(const struct gl_program *prog, GLuint regFile) 694{ 695 GLboolean used[MAX_PROGRAM_TEMPS]; 696 GLuint i, k; 697 698 assert(regFile == PROGRAM_INPUT || 699 regFile == PROGRAM_OUTPUT || 700 regFile == PROGRAM_TEMPORARY); 701 702 _mesa_memset(used, 0, sizeof(used)); 703 704 for (i = 0; i < prog->NumInstructions; i++) { 705 const struct prog_instruction *inst = prog->Instructions + i; 706 const GLuint n = _mesa_num_inst_src_regs(inst->Opcode); 707 708 for (k = 0; k < n; k++) { 709 if (inst->SrcReg[k].File == regFile) { 710 used[inst->SrcReg[k].Index] = GL_TRUE; 711 } 712 } 713 } 714 715 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) { 716 if (!used[i]) 717 return i; 718 } 719 720 return -1; 721} 722 723 724 725/** 726 * Mixing ARB and NV vertex/fragment programs can be tricky. 727 * Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV 728 * but, GL_FRAGMENT_PROGRAM_ARB != GL_FRAGMENT_PROGRAM_NV 729 * The two different fragment program targets are supposed to be compatible 730 * to some extent (see GL_ARB_fragment_program spec). 731 * This function does the compatibility check. 732 */ 733static GLboolean 734compatible_program_targets(GLenum t1, GLenum t2) 735{ 736 if (t1 == t2) 737 return GL_TRUE; 738 if (t1 == GL_FRAGMENT_PROGRAM_ARB && t2 == GL_FRAGMENT_PROGRAM_NV) 739 return GL_TRUE; 740 if (t1 == GL_FRAGMENT_PROGRAM_NV && t2 == GL_FRAGMENT_PROGRAM_ARB) 741 return GL_TRUE; 742 return GL_FALSE; 743} 744 745 746 747/**********************************************************************/ 748/* API functions */ 749/**********************************************************************/ 750 751 752/** 753 * Bind a program (make it current) 754 * \note Called from the GL API dispatcher by both glBindProgramNV 755 * and glBindProgramARB. 756 */ 757void GLAPIENTRY 758_mesa_BindProgram(GLenum target, GLuint id) 759{ 760 struct gl_program *curProg, *newProg; 761 GET_CURRENT_CONTEXT(ctx); 762 ASSERT_OUTSIDE_BEGIN_END(ctx); 763 764 FLUSH_VERTICES(ctx, _NEW_PROGRAM); 765 766 /* Error-check target and get curProg */ 767 if ((target == GL_VERTEX_PROGRAM_ARB) && /* == GL_VERTEX_PROGRAM_NV */ 768 (ctx->Extensions.NV_vertex_program || 769 ctx->Extensions.ARB_vertex_program)) { 770 curProg = &ctx->VertexProgram.Current->Base; 771 } 772 else if ((target == GL_FRAGMENT_PROGRAM_NV 773 && ctx->Extensions.NV_fragment_program) || 774 (target == GL_FRAGMENT_PROGRAM_ARB 775 && ctx->Extensions.ARB_fragment_program)) { 776 curProg = &ctx->FragmentProgram.Current->Base; 777 } 778 else { 779 _mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramNV/ARB(target)"); 780 return; 781 } 782 783 /* 784 * Get pointer to new program to bind. 785 * NOTE: binding to a non-existant program is not an error. 786 * That's supposed to be caught in glBegin. 787 */ 788 if (id == 0) { 789 /* Bind a default program */ 790 newProg = NULL; 791 if (target == GL_VERTEX_PROGRAM_ARB) /* == GL_VERTEX_PROGRAM_NV */ 792 newProg = &ctx->Shared->DefaultVertexProgram->Base; 793 else 794 newProg = &ctx->Shared->DefaultFragmentProgram->Base; 795 } 796 else { 797 /* Bind a user program */ 798 newProg = _mesa_lookup_program(ctx, id); 799 if (!newProg || newProg == &_mesa_DummyProgram) { 800 /* allocate a new program now */ 801 newProg = ctx->Driver.NewProgram(ctx, target, id); 802 if (!newProg) { 803 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB"); 804 return; 805 } 806 _mesa_HashInsert(ctx->Shared->Programs, id, newProg); 807 } 808 else if (!compatible_program_targets(newProg->Target, target)) { 809 _mesa_error(ctx, GL_INVALID_OPERATION, 810 "glBindProgramNV/ARB(target mismatch)"); 811 return; 812 } 813 } 814 815 /** All error checking is complete now **/ 816 817 if (curProg->Id == id) { 818 /* binding same program - no change */ 819 return; 820 } 821 822 /* bind newProg */ 823 if (target == GL_VERTEX_PROGRAM_ARB) { /* == GL_VERTEX_PROGRAM_NV */ 824 _mesa_reference_vertprog(ctx, &ctx->VertexProgram.Current, 825 (struct gl_vertex_program *) newProg); 826 } 827 else if (target == GL_FRAGMENT_PROGRAM_NV || 828 target == GL_FRAGMENT_PROGRAM_ARB) { 829 _mesa_reference_fragprog(ctx, &ctx->FragmentProgram.Current, 830 (struct gl_fragment_program *) newProg); 831 } 832 833 /* Never null pointers */ 834 ASSERT(ctx->VertexProgram.Current); 835 ASSERT(ctx->FragmentProgram.Current); 836 837 if (ctx->Driver.BindProgram) 838 ctx->Driver.BindProgram(ctx, target, newProg); 839} 840 841 842/** 843 * Delete a list of programs. 844 * \note Not compiled into display lists. 845 * \note Called by both glDeleteProgramsNV and glDeleteProgramsARB. 846 */ 847void GLAPIENTRY 848_mesa_DeletePrograms(GLsizei n, const GLuint *ids) 849{ 850 GLint i; 851 GET_CURRENT_CONTEXT(ctx); 852 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); 853 854 if (n < 0) { 855 _mesa_error( ctx, GL_INVALID_VALUE, "glDeleteProgramsNV" ); 856 return; 857 } 858 859 for (i = 0; i < n; i++) { 860 if (ids[i] != 0) { 861 struct gl_program *prog = _mesa_lookup_program(ctx, ids[i]); 862 if (prog == &_mesa_DummyProgram) { 863 _mesa_HashRemove(ctx->Shared->Programs, ids[i]); 864 } 865 else if (prog) { 866 /* Unbind program if necessary */ 867 if (prog->Target == GL_VERTEX_PROGRAM_ARB || /* == GL_VERTEX_PROGRAM_NV */ 868 prog->Target == GL_VERTEX_STATE_PROGRAM_NV) { 869 if (ctx->VertexProgram.Current && 870 ctx->VertexProgram.Current->Base.Id == ids[i]) { 871 /* unbind this currently bound program */ 872 _mesa_BindProgram(prog->Target, 0); 873 } 874 } 875 else if (prog->Target == GL_FRAGMENT_PROGRAM_NV || 876 prog->Target == GL_FRAGMENT_PROGRAM_ARB) { 877 if (ctx->FragmentProgram.Current && 878 ctx->FragmentProgram.Current->Base.Id == ids[i]) { 879 /* unbind this currently bound program */ 880 _mesa_BindProgram(prog->Target, 0); 881 } 882 } 883 else { 884 _mesa_problem(ctx, "bad target in glDeleteProgramsNV"); 885 return; 886 } 887 /* The ID is immediately available for re-use now */ 888 _mesa_HashRemove(ctx->Shared->Programs, ids[i]); 889 _mesa_reference_program(ctx, &prog, NULL); 890 } 891 } 892 } 893} 894 895 896/** 897 * Generate a list of new program identifiers. 898 * \note Not compiled into display lists. 899 * \note Called by both glGenProgramsNV and glGenProgramsARB. 900 */ 901void GLAPIENTRY 902_mesa_GenPrograms(GLsizei n, GLuint *ids) 903{ 904 GLuint first; 905 GLuint i; 906 GET_CURRENT_CONTEXT(ctx); 907 ASSERT_OUTSIDE_BEGIN_END(ctx); 908 909 if (n < 0) { 910 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPrograms"); 911 return; 912 } 913 914 if (!ids) 915 return; 916 917 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->Programs, n); 918 919 /* Insert pointer to dummy program as placeholder */ 920 for (i = 0; i < (GLuint) n; i++) { 921 _mesa_HashInsert(ctx->Shared->Programs, first + i, &_mesa_DummyProgram); 922 } 923 924 /* Return the program names */ 925 for (i = 0; i < (GLuint) n; i++) { 926 ids[i] = first + i; 927 } 928} 929