}
+/**
+ * Return a new parameter list which is listA + listB.
+ */
+struct gl_program_parameter_list *
+_mesa_combine_parameter_lists(const struct gl_program_parameter_list *listA,
+ const struct gl_program_parameter_list *listB)
+{
+ struct gl_program_parameter_list *list;
+
+ if (listA) {
+ list = _mesa_clone_parameter_list(listA);
+ if (list && listB) {
+ GLuint i;
+ for (i = 0; i < listB->NumParameters; i++) {
+ struct gl_program_parameter *param = listB->Parameters + i;
+ _mesa_add_parameter(list, param->Type, param->Name, param->Size,
+ param->DataType,
+ listB->ParameterValues[i],
+ param->StateIndexes);
+ }
+ }
+ }
+ else if (listB) {
+ list = _mesa_clone_parameter_list(listB);
+ }
+ else {
+ list = NULL;
+ }
+ return list;
+}
+
+
+
/**
* Find longest name of all uniform parameters in list.
*/
extern struct gl_program_parameter_list *
_mesa_clone_parameter_list(const struct gl_program_parameter_list *list);
+extern struct gl_program_parameter_list *
+_mesa_combine_parameter_lists(const struct gl_program_parameter_list *a,
+ const struct gl_program_parameter_list *b);
+
+static INLINE GLuint
+_mesa_num_parameters(const struct gl_program_parameter_list *list)
+{
+ return list ? list->NumParameters : 0;
+}
+
extern GLint
_mesa_add_parameter(struct gl_program_parameter_list *paramList,
enum register_file type, const char *name,
}
+/**
+ * Search instructions for registers that match (oldFile, oldIndex),
+ * replacing them with (newFile, newIndex).
+ */
+static void
+replace_registers(struct prog_instruction *inst, GLuint numInst,
+ GLuint oldFile, GLuint oldIndex,
+ GLuint newFile, GLuint newIndex)
+{
+ GLuint i, j;
+ for (i = 0; i < numInst; i++) {
+ for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
+ if (inst[i].SrcReg[j].File == oldFile &&
+ inst[i].SrcReg[j].Index == oldIndex) {
+ inst[i].SrcReg[j].File = newFile;
+ inst[i].SrcReg[j].Index = newIndex;
+ }
+ }
+ }
+}
+
+
+/**
+ * Search instructions for references to program parameters. When found,
+ * increment the parameter index by 'offset'.
+ * Used when combining programs.
+ */
+static void
+adjust_param_indexes(struct prog_instruction *inst, GLuint numInst,
+ GLuint offset)
+{
+ GLuint i, j;
+ for (i = 0; i < numInst; i++) {
+ for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
+ GLuint f = inst[i].SrcReg[j].File;
+ if (f == PROGRAM_CONSTANT ||
+ f == PROGRAM_UNIFORM ||
+ f == PROGRAM_STATE_VAR) {
+ inst[i].SrcReg[j].Index += offset;
+ }
+ }
+ }
+}
+
+
+/**
+ * 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,
+ struct gl_program *progA, 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;
+ 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) {
+ /* connect color outputs/inputs */
+ if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) &&
+ (progB->InputsRead & (1 << FRAG_ATTRIB_COL0))) {
+ replace_registers(newInst + lenA, lenB,
+ PROGRAM_INPUT, FRAG_ATTRIB_COL0,
+ PROGRAM_OUTPUT, FRAG_RESULT_COLR);
+ }
+
+ newProg->InputsRead = progA->InputsRead;
+ newProg->InputsRead |= (progB->InputsRead & ~(1 << FRAG_ATTRIB_COL0));
+ newProg->OutputsWritten = progB->OutputsWritten;
+ }
+ 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 the given program to find a free register of the given type.
+ * \param regFile - PROGRAM_INPUT, PROGRAM_OUTPUT or PROGRAM_TEMPORARY
+ */
+GLint
+_mesa_find_free_register(const struct gl_program *prog, GLuint regFile)
+{
+ GLboolean used[MAX_PROGRAM_TEMPS];
+ GLuint i, k;
+
+ assert(regFile == PROGRAM_INPUT ||
+ regFile == PROGRAM_OUTPUT ||
+ regFile == PROGRAM_TEMPORARY);
+
+ _mesa_memset(used, 0, sizeof(used));
+
+ for (i = 0; i < prog->NumInstructions; i++) {
+ const struct prog_instruction *inst = prog->Instructions + i;
+ const GLuint n = _mesa_num_inst_src_regs(inst->Opcode);
+
+ for (k = 0; k < n; k++) {
+ if (inst->SrcReg[k].File == regFile) {
+ used[inst->SrcReg[k].Index] = GL_TRUE;
+ }
+ }
+ }
+
+ for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
+ if (!used[i])
+ return i;
+ }
+
+ return -1;
+}
+
+
+
/**
* Mixing ARB and NV vertex/fragment programs can be tricky.
* Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV
extern GLboolean
_mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count);
+extern struct gl_program *
+_mesa_combine_programs(GLcontext *ctx,
+ struct gl_program *progA, struct gl_program *progB);
+
+extern GLint
+_mesa_find_free_register(const struct gl_program *prog, GLuint regFile);
+
+
/*
* API functions common to ARB/NV_vertex/fragment_program
*/
#include "context.h"
#include "prog_parameter.h"
#include "prog_statevars.h"
+#include "program.h"
#include "programopt.h"
#include "prog_instruction.h"
_mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen);
/* free old instructions */
- _mesa_free(vprog->Base.Instructions);
+ _mesa_free_instructions(vprog->Base.Instructions, origLen);
/* install new instructions */
vprog->Base.Instructions = newInst;
inst++;
/* free old instructions */
- _mesa_free(fprog->Base.Instructions);
+ _mesa_free_instructions(fprog->Base.Instructions, origLen);
/* install new instructions */
fprog->Base.Instructions = newInst;
}
}
+
+/**
+ * Scan/rewrite program to remove reads of varying (output) registers.
+ * In GLSL vertex shaders, varying vars can be read and written.
+ * Normally, vertex varying vars are implemented as output registers.
+ * 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_varying_reads(struct gl_program *prog)
+{
+ GLuint i;
+ GLint outputMap[VERT_RESULT_MAX];
+ GLuint numVaryingReads = 0;
+
+ assert(prog->Target == GL_VERTEX_PROGRAM_ARB);
+
+ for (i = 0; i < VERT_RESULT_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 == PROGRAM_VARYING) {
+ /* 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(prog,
+ PROGRAM_TEMPORARY);
+ }
+ 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;
+ const GLuint numSrc = _mesa_num_inst_src_regs(inst->Opcode);
+ GLuint j;
+ for (j = 0; j < numSrc; j++) {
+ if (inst->DstReg.File == PROGRAM_VARYING &&
+ 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 < VERT_RESULT_MAX; var++) {
+ if (outputMap[var] >= 0) {
+ /* MOV VAR[var], TEMP[tmp]; */
+ inst->Opcode = OPCODE_MOV;
+ inst->DstReg.File = PROGRAM_VARYING;
+ inst->DstReg.Index = var;
+ inst->SrcReg[0].File = PROGRAM_TEMPORARY;
+ inst->SrcReg[0].Index = outputMap[var];
+ inst++;
+ }
+ }
+ }
+}
extern void
_mesa_count_texture_instructions(struct gl_program *prog);
+extern void
+_mesa_remove_varying_reads(struct gl_program *prog);
#endif /* PROGRAMOPT_H */