added _mesa_combine_parameter_lists()
cherry-picked from gallium-0.1
This commit is contained in:
@@ -603,6 +603,39 @@ _mesa_clone_parameter_list(const struct gl_program_parameter_list *list)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@@ -78,6 +78,16 @@ _mesa_free_parameter_list(struct gl_program_parameter_list *paramList);
|
||||
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,
|
||||
|
@@ -526,6 +526,155 @@ _mesa_insert_instructions(struct gl_program *prog, GLuint start, GLuint count)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@@ -115,6 +115,14 @@ _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog);
|
||||
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
|
||||
*/
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include "context.h"
|
||||
#include "prog_parameter.h"
|
||||
#include "prog_statevars.h"
|
||||
#include "program.h"
|
||||
#include "programopt.h"
|
||||
#include "prog_instruction.h"
|
||||
|
||||
@@ -102,7 +103,7 @@ _mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
|
||||
_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;
|
||||
@@ -275,7 +276,7 @@ _mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
|
||||
inst++;
|
||||
|
||||
/* free old instructions */
|
||||
_mesa_free(fprog->Base.Instructions);
|
||||
_mesa_free_instructions(fprog->Base.Instructions, origLen);
|
||||
|
||||
/* install new instructions */
|
||||
fprog->Base.Instructions = newInst;
|
||||
@@ -365,3 +366,94 @@ _mesa_count_texture_instructions(struct gl_program *prog)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,5 +39,7 @@ _mesa_count_texture_indirections(struct gl_program *prog);
|
||||
extern void
|
||||
_mesa_count_texture_instructions(struct gl_program *prog);
|
||||
|
||||
extern void
|
||||
_mesa_remove_varying_reads(struct gl_program *prog);
|
||||
|
||||
#endif /* PROGRAMOPT_H */
|
||||
|
Reference in New Issue
Block a user