static void unalias_temporary(struct nqssadce_state* s, GLuint oldindex) { GLuint newindex = _mesa_find_free_register(s->Program, PROGRAM_TEMPORARY); int ip; for(ip = 0; ip < s->IP; ++ip) { struct prog_instruction* inst = s->Program->Instructions + ip; if (inst->DstReg.File == PROGRAM_TEMPORARY && inst->DstReg.Index == oldindex) inst->DstReg.Index = newindex; unalias_srcregs(inst, oldindex, newindex); } unalias_srcregs(s->Program->Instructions + s->IP, oldindex, newindex); }
static struct prog_instruction* track_used_srcreg(struct nqssadce_state* s, struct prog_instruction *inst, GLint src, GLuint sourced) { int i; GLuint deswz_source = 0; for(i = 0; i < 4; ++i) { if (GET_BIT(sourced, i)) { GLuint swz = GET_SWZ(inst->SrcReg[src].Swizzle, i); deswz_source |= 1 << swz; } else { inst->SrcReg[src].Swizzle &= ~(7 << (3*i)); inst->SrcReg[src].Swizzle |= SWIZZLE_NIL << (3*i); } } if (!s->Descr->IsNativeSwizzle(inst->Opcode, inst->SrcReg[src])) { struct prog_dst_register dstreg = inst->DstReg; dstreg.File = PROGRAM_TEMPORARY; dstreg.Index = _mesa_find_free_register(s->Program, PROGRAM_TEMPORARY); dstreg.WriteMask = sourced; s->Descr->BuildSwizzle(s, dstreg, inst->SrcReg[src]); inst = s->Program->Instructions + s->IP; inst->SrcReg[src].File = PROGRAM_TEMPORARY; inst->SrcReg[src].Index = dstreg.Index; inst->SrcReg[src].Swizzle = 0; inst->SrcReg[src].NegateBase = 0; inst->SrcReg[src].Abs = 0; inst->SrcReg[src].NegateAbs = 0; for(i = 0; i < 4; ++i) { if (GET_BIT(sourced, i)) inst->SrcReg[src].Swizzle |= i << (3*i); else inst->SrcReg[src].Swizzle |= SWIZZLE_NIL << (3*i); } deswz_source = sourced; } struct register_state *regstate = get_reg_state(s, inst->SrcReg[src].File, inst->SrcReg[src].Index); if (regstate) regstate->Sourced |= deswz_source & 0xf; return inst; }
/** * Transform the program to support fragment.position. * * Introduce a small fragment at the start of the program that will be * the only code that directly reads the FRAG_ATTRIB_WPOS input. * All other code pieces that reference that input will be rewritten * to read from a newly allocated temporary. * * \todo if/when r5xx supports the radeon_program architecture, this is a * likely candidate for code sharing. */ static void insert_WPOS_trailer(struct r300_fragment_program_compiler *compiler) { GLuint InputsRead = compiler->fp->mesa_program.Base.InputsRead; if (!(InputsRead & FRAG_BIT_WPOS)) return; static gl_state_index tokens[STATE_LENGTH] = { STATE_INTERNAL, STATE_R300_WINDOW_DIMENSION, 0, 0, 0 }; struct prog_instruction *fpi; GLuint window_index; int i = 0; GLuint tempregi = _mesa_find_free_register(compiler->program, PROGRAM_TEMPORARY); _mesa_insert_instructions(compiler->program, 0, 3); fpi = compiler->program->Instructions; /* perspective divide */ fpi[i].Opcode = OPCODE_RCP; fpi[i].DstReg.File = PROGRAM_TEMPORARY; fpi[i].DstReg.Index = tempregi; fpi[i].DstReg.WriteMask = WRITEMASK_W; fpi[i].DstReg.CondMask = COND_TR; fpi[i].SrcReg[0].File = PROGRAM_INPUT; fpi[i].SrcReg[0].Index = FRAG_ATTRIB_WPOS; fpi[i].SrcReg[0].Swizzle = SWIZZLE_WWWW; i++; fpi[i].Opcode = OPCODE_MUL; fpi[i].DstReg.File = PROGRAM_TEMPORARY; fpi[i].DstReg.Index = tempregi; fpi[i].DstReg.WriteMask = WRITEMASK_XYZ; fpi[i].DstReg.CondMask = COND_TR; fpi[i].SrcReg[0].File = PROGRAM_INPUT; fpi[i].SrcReg[0].Index = FRAG_ATTRIB_WPOS; fpi[i].SrcReg[0].Swizzle = SWIZZLE_XYZW; fpi[i].SrcReg[1].File = PROGRAM_TEMPORARY; fpi[i].SrcReg[1].Index = tempregi; fpi[i].SrcReg[1].Swizzle = SWIZZLE_WWWW; i++; /* viewport transformation */ window_index = _mesa_add_state_reference(compiler->program->Parameters, tokens); fpi[i].Opcode = OPCODE_MAD; fpi[i].DstReg.File = PROGRAM_TEMPORARY; fpi[i].DstReg.Index = tempregi; fpi[i].DstReg.WriteMask = WRITEMASK_XYZ; fpi[i].DstReg.CondMask = COND_TR; fpi[i].SrcReg[0].File = PROGRAM_TEMPORARY; fpi[i].SrcReg[0].Index = tempregi; fpi[i].SrcReg[0].Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_ZERO); fpi[i].SrcReg[1].File = PROGRAM_STATE_VAR; fpi[i].SrcReg[1].Index = window_index; fpi[i].SrcReg[1].Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_ZERO); fpi[i].SrcReg[2].File = PROGRAM_STATE_VAR; fpi[i].SrcReg[2].Index = window_index; fpi[i].SrcReg[2].Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_ZERO); i++; for (; i < compiler->program->NumInstructions; ++i) { int reg; for (reg = 0; reg < 3; reg++) { if (fpi[i].SrcReg[reg].File == PROGRAM_INPUT && fpi[i].SrcReg[reg].Index == FRAG_ATTRIB_WPOS) { fpi[i].SrcReg[reg].File = PROGRAM_TEMPORARY; fpi[i].SrcReg[reg].Index = tempregi; } } } }
/** * 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; }
/** * Scan/rewrite program to remove reads of custom (output) registers. * The passed type has to be PROGRAM_OUTPUT. * On some hardware, trying to read an output register causes trouble. * So, rewrite the program to use a temporary register in this case. */ void _mesa_remove_output_reads(struct gl_program *prog, gl_register_file type) { GLuint i; GLint outputMap[VARYING_SLOT_MAX]; GLuint numVaryingReads = 0; GLboolean usedTemps[MAX_PROGRAM_TEMPS]; GLuint firstTemp = 0; _mesa_find_used_registers(prog, PROGRAM_TEMPORARY, usedTemps, MAX_PROGRAM_TEMPS); assert(type == PROGRAM_OUTPUT); for (i = 0; i < VARYING_SLOT_MAX; i++) outputMap[i] = -1; /* look for instructions which read from varying vars */ for (i = 0; i < prog->NumInstructions; i++) { struct prog_instruction *inst = prog->Instructions + i; const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode); GLuint j; for (j = 0; j < numSrc; j++) { if (inst->SrcReg[j].File == type) { /* replace the read with a temp reg */ const GLuint var = inst->SrcReg[j].Index; if (outputMap[var] == -1) { numVaryingReads++; outputMap[var] = _mesa_find_free_register(usedTemps, MAX_PROGRAM_TEMPS, firstTemp); firstTemp = outputMap[var] + 1; } inst->SrcReg[j].File = PROGRAM_TEMPORARY; inst->SrcReg[j].Index = outputMap[var]; } } } if (numVaryingReads == 0) return; /* nothing to be done */ /* look for instructions which write to the varying vars identified above */ for (i = 0; i < prog->NumInstructions; i++) { struct prog_instruction *inst = prog->Instructions + i; if (inst->DstReg.File == type && outputMap[inst->DstReg.Index] >= 0) { /* change inst to write to the temp reg, instead of the varying */ inst->DstReg.File = PROGRAM_TEMPORARY; inst->DstReg.Index = outputMap[inst->DstReg.Index]; } } /* insert new instructions to copy the temp vars to the varying vars */ { struct prog_instruction *inst; GLint endPos, var; /* Look for END instruction and insert the new varying writes */ endPos = -1; for (i = 0; i < prog->NumInstructions; i++) { struct prog_instruction *inst = prog->Instructions + i; if (inst->Opcode == OPCODE_END) { endPos = i; _mesa_insert_instructions(prog, i, numVaryingReads); break; } } assert(endPos >= 0); /* insert new MOV instructions here */ inst = prog->Instructions + endPos; for (var = 0; var < VARYING_SLOT_MAX; var++) { if (outputMap[var] >= 0) { /* MOV VAR[var], TEMP[tmp]; */ inst->Opcode = OPCODE_MOV; inst->DstReg.File = type; inst->DstReg.Index = var; inst->SrcReg[0].File = PROGRAM_TEMPORARY; inst->SrcReg[0].Index = outputMap[var]; inst++; } } } }
/** * 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; }