/** * Combine two programs into one. Fix instructions so the outputs of * the first program go to the inputs of the second program. */ struct gl_program * _mesa_combine_programs(GLcontext *ctx, const struct gl_program *progA, const struct gl_program *progB) { struct prog_instruction *newInst; struct gl_program *newProg; const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */ const GLuint lenB = progB->NumInstructions; const GLuint numParamsA = _mesa_num_parameters(progA->Parameters); const GLuint newLength = lenA + lenB; GLbitfield inputsB; GLuint i; ASSERT(progA->Target == progB->Target); newInst = _mesa_alloc_instructions(newLength); if (!newInst) return GL_FALSE; _mesa_copy_instructions(newInst, progA->Instructions, lenA); _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB); /* adjust branch / instruction addresses for B's instructions */ for (i = 0; i < lenB; i++) { newInst[lenA + i].BranchTarget += lenA; } newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0); newProg->Instructions = newInst; newProg->NumInstructions = newLength; if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) { struct gl_fragment_program *fprogA, *fprogB, *newFprog; fprogA = (struct gl_fragment_program *) progA; fprogB = (struct gl_fragment_program *) progB; newFprog = (struct gl_fragment_program *) newProg; newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill; /* Connect color outputs of fprogA to color inputs of fprogB, via a * new temporary register. */ if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) && (progB->InputsRead & (1 << FRAG_ATTRIB_COL0))) { GLint tempReg = _mesa_find_free_register(newProg, PROGRAM_TEMPORARY); if (tempReg < 0) { _mesa_problem(ctx, "No free temp regs found in " "_mesa_combine_programs(), using 31"); tempReg = 31; } /* replace writes to result.color[0] with tempReg */ replace_registers(newInst, lenA, PROGRAM_OUTPUT, FRAG_RESULT_COLR, PROGRAM_TEMPORARY, tempReg); /* replace reads from input.color[0] with tempReg */ replace_registers(newInst + lenA, lenB, PROGRAM_INPUT, FRAG_ATTRIB_COL0, PROGRAM_TEMPORARY, tempReg); } inputsB = progB->InputsRead; if (progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) { inputsB &= ~(1 << FRAG_ATTRIB_COL0); } newProg->InputsRead = progA->InputsRead | inputsB; newProg->OutputsWritten = progB->OutputsWritten; newProg->SamplersUsed = progA->SamplersUsed | progB->SamplersUsed; } else { /* vertex program */ assert(0); /* XXX todo */ } /* * Merge parameters (uniforms, constants, etc) */ newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters, progB->Parameters); adjust_param_indexes(newInst + lenA, lenB, numParamsA); return newProg; }
/** * Combine two programs into one. Fix instructions so the outputs of * the first program go to the inputs of the second program. */ struct gl_program * _mesa_combine_programs(struct gl_context *ctx, const struct gl_program *progA, const struct gl_program *progB) { struct prog_instruction *newInst; struct gl_program *newProg; const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */ const GLuint lenB = progB->NumInstructions; const GLuint numParamsA = _mesa_num_parameters(progA->Parameters); const GLuint newLength = lenA + lenB; GLboolean usedTemps[MAX_PROGRAM_TEMPS]; GLuint firstTemp = 0; GLbitfield inputsB; GLuint i; ASSERT(progA->Target == progB->Target); newInst = _mesa_alloc_instructions(newLength); if (!newInst) return GL_FALSE; _mesa_copy_instructions(newInst, progA->Instructions, lenA); _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB); /* adjust branch / instruction addresses for B's instructions */ for (i = 0; i < lenB; i++) { newInst[lenA + i].BranchTarget += lenA; } newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0); newProg->Instructions = newInst; newProg->NumInstructions = newLength; /* find used temp regs (we may need new temps below) */ _mesa_find_used_registers(newProg, PROGRAM_TEMPORARY, usedTemps, MAX_PROGRAM_TEMPS); if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) { struct gl_fragment_program *fprogA, *fprogB, *newFprog; GLbitfield progB_inputsRead = progB->InputsRead; GLint progB_colorFile, progB_colorIndex; fprogA = (struct gl_fragment_program *) progA; fprogB = (struct gl_fragment_program *) progB; newFprog = (struct gl_fragment_program *) newProg; newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill; /* We'll do a search and replace for instances * of progB_colorFile/progB_colorIndex below... */ progB_colorFile = PROGRAM_INPUT; progB_colorIndex = FRAG_ATTRIB_COL0; /* * The fragment program may get color from a state var rather than * a fragment input (vertex output) if it's constant. * See the texenvprogram.c code. * So, search the program's parameter list now to see if the program * gets color from a state var instead of a conventional fragment * input register. */ for (i = 0; i < progB->Parameters->NumParameters; i++) { struct gl_program_parameter *p = &progB->Parameters->Parameters[i]; if (p->Type == PROGRAM_STATE_VAR && p->StateIndexes[0] == STATE_INTERNAL && p->StateIndexes[1] == STATE_CURRENT_ATTRIB && (int) p->StateIndexes[2] == (int) VERT_ATTRIB_COLOR0) { progB_inputsRead |= FRAG_BIT_COL0; progB_colorFile = PROGRAM_STATE_VAR; progB_colorIndex = i; break; } } /* Connect color outputs of fprogA to color inputs of fprogB, via a * new temporary register. */ if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLOR)) && (progB_inputsRead & FRAG_BIT_COL0)) { GLint tempReg = _mesa_find_free_register(usedTemps, MAX_PROGRAM_TEMPS, firstTemp); if (tempReg < 0) { _mesa_problem(ctx, "No free temp regs found in " "_mesa_combine_programs(), using 31"); tempReg = 31; } firstTemp = tempReg + 1; /* replace writes to result.color[0] with tempReg */ replace_registers(newInst, lenA, PROGRAM_OUTPUT, FRAG_RESULT_COLOR, PROGRAM_TEMPORARY, tempReg); /* replace reads from the input color with tempReg */ replace_registers(newInst + lenA, lenB, progB_colorFile, progB_colorIndex, /* search for */ PROGRAM_TEMPORARY, tempReg /* replace with */ ); } /* compute combined program's InputsRead */ inputsB = progB_inputsRead; if (progA->OutputsWritten & (1 << FRAG_RESULT_COLOR)) { inputsB &= ~(1 << FRAG_ATTRIB_COL0); } newProg->InputsRead = progA->InputsRead | inputsB; newProg->OutputsWritten = progB->OutputsWritten; newProg->SamplersUsed = progA->SamplersUsed | progB->SamplersUsed; } else { /* vertex program */ assert(0); /* XXX todo */ } /* * Merge parameters (uniforms, constants, etc) */ newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters, progB->Parameters); adjust_param_indexes(newInst + lenA, lenB, numParamsA); return newProg; }