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