
s/FRAG_RESULT_DEPR/FRAG_RESULT_DEPTH/ s/FRAG_RESULT_COLR/FRAG_RESULT/COLOR/ Remove FRAG_RESULT_COLH (NV half-precision) output since we never used it. Next, we might merge the COLOR and DATA outputs (COLOR0, COLOR1, etc).
461 lines
16 KiB
C
461 lines
16 KiB
C
/*
|
|
* Mesa 3-D graphics library
|
|
* Version: 6.5.3
|
|
*
|
|
* Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* \file programopt.c
|
|
* Vertex/Fragment program optimizations and transformations for program
|
|
* options, etc.
|
|
*
|
|
* \author Brian Paul
|
|
*/
|
|
|
|
|
|
#include "main/glheader.h"
|
|
#include "main/context.h"
|
|
#include "prog_parameter.h"
|
|
#include "prog_statevars.h"
|
|
#include "program.h"
|
|
#include "programopt.h"
|
|
#include "prog_instruction.h"
|
|
|
|
|
|
/**
|
|
* This function inserts instructions for coordinate modelview * projection
|
|
* into a vertex program.
|
|
* May be used to implement the position_invariant option.
|
|
*/
|
|
void
|
|
_mesa_insert_mvp_code(GLcontext *ctx, struct gl_vertex_program *vprog)
|
|
{
|
|
struct prog_instruction *newInst;
|
|
const GLuint origLen = vprog->Base.NumInstructions;
|
|
const GLuint newLen = origLen + 4;
|
|
GLuint i;
|
|
|
|
/*
|
|
* Setup state references for the modelview/projection matrix.
|
|
* XXX we should check if these state vars are already declared.
|
|
*/
|
|
static const gl_state_index mvpState[4][STATE_LENGTH] = {
|
|
{ STATE_MVP_MATRIX, 0, 0, 0, 0 }, /* state.matrix.mvp.row[0] */
|
|
{ STATE_MVP_MATRIX, 0, 1, 1, 0 }, /* state.matrix.mvp.row[1] */
|
|
{ STATE_MVP_MATRIX, 0, 2, 2, 0 }, /* state.matrix.mvp.row[2] */
|
|
{ STATE_MVP_MATRIX, 0, 3, 3, 0 }, /* state.matrix.mvp.row[3] */
|
|
};
|
|
GLint mvpRef[4];
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
mvpRef[i] = _mesa_add_state_reference(vprog->Base.Parameters,
|
|
mvpState[i]);
|
|
}
|
|
|
|
/* Alloc storage for new instructions */
|
|
newInst = _mesa_alloc_instructions(newLen);
|
|
if (!newInst) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY,
|
|
"glProgramString(inserting position_invariant code)");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Generated instructions:
|
|
* newInst[0] = DP4 result.position.x, mvp.row[0], vertex.position;
|
|
* newInst[1] = DP4 result.position.y, mvp.row[1], vertex.position;
|
|
* newInst[2] = DP4 result.position.z, mvp.row[2], vertex.position;
|
|
* newInst[3] = DP4 result.position.w, mvp.row[3], vertex.position;
|
|
*/
|
|
_mesa_init_instructions(newInst, 4);
|
|
for (i = 0; i < 4; i++) {
|
|
newInst[i].Opcode = OPCODE_DP4;
|
|
newInst[i].DstReg.File = PROGRAM_OUTPUT;
|
|
newInst[i].DstReg.Index = VERT_RESULT_HPOS;
|
|
newInst[i].DstReg.WriteMask = (WRITEMASK_X << i);
|
|
newInst[i].SrcReg[0].File = PROGRAM_STATE_VAR;
|
|
newInst[i].SrcReg[0].Index = mvpRef[i];
|
|
newInst[i].SrcReg[0].Swizzle = SWIZZLE_NOOP;
|
|
newInst[i].SrcReg[1].File = PROGRAM_INPUT;
|
|
newInst[i].SrcReg[1].Index = VERT_ATTRIB_POS;
|
|
newInst[i].SrcReg[1].Swizzle = SWIZZLE_NOOP;
|
|
}
|
|
|
|
/* Append original instructions after new instructions */
|
|
_mesa_copy_instructions (newInst + 4, vprog->Base.Instructions, origLen);
|
|
|
|
/* free old instructions */
|
|
_mesa_free_instructions(vprog->Base.Instructions, origLen);
|
|
|
|
/* install new instructions */
|
|
vprog->Base.Instructions = newInst;
|
|
vprog->Base.NumInstructions = newLen;
|
|
vprog->Base.InputsRead |= VERT_BIT_POS;
|
|
vprog->Base.OutputsWritten |= (1 << VERT_RESULT_HPOS);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Append extra instructions onto the given fragment program to implement
|
|
* the fog mode specified by fprog->FogOption.
|
|
* The fragment.fogcoord input is used to compute the fog blend factor.
|
|
*
|
|
* XXX with a little work, this function could be adapted to add fog code
|
|
* to vertex programs too.
|
|
*/
|
|
void
|
|
_mesa_append_fog_code(GLcontext *ctx, struct gl_fragment_program *fprog)
|
|
{
|
|
static const gl_state_index fogPStateOpt[STATE_LENGTH]
|
|
= { STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED, 0, 0, 0 };
|
|
static const gl_state_index fogColorState[STATE_LENGTH]
|
|
= { STATE_FOG_COLOR, 0, 0, 0, 0};
|
|
struct prog_instruction *newInst, *inst;
|
|
const GLuint origLen = fprog->Base.NumInstructions;
|
|
const GLuint newLen = origLen + 5;
|
|
GLuint i;
|
|
GLint fogPRefOpt, fogColorRef; /* state references */
|
|
GLuint colorTemp, fogFactorTemp; /* temporary registerss */
|
|
|
|
if (fprog->FogOption == GL_NONE) {
|
|
_mesa_problem(ctx, "_mesa_append_fog_code() called for fragment program"
|
|
" with FogOption == GL_NONE");
|
|
return;
|
|
}
|
|
|
|
/* Alloc storage for new instructions */
|
|
newInst = _mesa_alloc_instructions(newLen);
|
|
if (!newInst) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY,
|
|
"glProgramString(inserting fog_option code)");
|
|
return;
|
|
}
|
|
|
|
/* Copy orig instructions into new instruction buffer */
|
|
_mesa_copy_instructions(newInst, fprog->Base.Instructions, origLen);
|
|
|
|
/* PARAM fogParamsRefOpt = internal optimized fog params; */
|
|
fogPRefOpt
|
|
= _mesa_add_state_reference(fprog->Base.Parameters, fogPStateOpt);
|
|
/* PARAM fogColorRef = state.fog.color; */
|
|
fogColorRef
|
|
= _mesa_add_state_reference(fprog->Base.Parameters, fogColorState);
|
|
|
|
/* TEMP colorTemp; */
|
|
colorTemp = fprog->Base.NumTemporaries++;
|
|
/* TEMP fogFactorTemp; */
|
|
fogFactorTemp = fprog->Base.NumTemporaries++;
|
|
|
|
/* Scan program to find where result.color is written */
|
|
inst = newInst;
|
|
for (i = 0; i < fprog->Base.NumInstructions; i++) {
|
|
if (inst->Opcode == OPCODE_END)
|
|
break;
|
|
if (inst->DstReg.File == PROGRAM_OUTPUT &&
|
|
inst->DstReg.Index == FRAG_RESULT_COLOR) {
|
|
/* change the instruction to write to colorTemp w/ clamping */
|
|
inst->DstReg.File = PROGRAM_TEMPORARY;
|
|
inst->DstReg.Index = colorTemp;
|
|
inst->SaturateMode = SATURATE_ZERO_ONE;
|
|
/* don't break (may be several writes to result.color) */
|
|
}
|
|
inst++;
|
|
}
|
|
assert(inst->Opcode == OPCODE_END); /* we'll overwrite this inst */
|
|
|
|
_mesa_init_instructions(inst, 5);
|
|
|
|
/* emit instructions to compute fog blending factor */
|
|
if (fprog->FogOption == GL_LINEAR) {
|
|
/* MAD fogFactorTemp.x, fragment.fogcoord.x, fogPRefOpt.x, fogPRefOpt.y; */
|
|
inst->Opcode = OPCODE_MAD;
|
|
inst->DstReg.File = PROGRAM_TEMPORARY;
|
|
inst->DstReg.Index = fogFactorTemp;
|
|
inst->DstReg.WriteMask = WRITEMASK_X;
|
|
inst->SrcReg[0].File = PROGRAM_INPUT;
|
|
inst->SrcReg[0].Index = FRAG_ATTRIB_FOGC;
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
|
|
inst->SrcReg[1].File = PROGRAM_STATE_VAR;
|
|
inst->SrcReg[1].Index = fogPRefOpt;
|
|
inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
|
|
inst->SrcReg[2].File = PROGRAM_STATE_VAR;
|
|
inst->SrcReg[2].Index = fogPRefOpt;
|
|
inst->SrcReg[2].Swizzle = SWIZZLE_YYYY;
|
|
inst->SaturateMode = SATURATE_ZERO_ONE;
|
|
inst++;
|
|
}
|
|
else {
|
|
ASSERT(fprog->FogOption == GL_EXP || fprog->FogOption == GL_EXP2);
|
|
/* fogPRefOpt.z = d/ln(2), fogPRefOpt.w = d/sqrt(ln(2) */
|
|
/* EXP: MUL fogFactorTemp.x, fogPRefOpt.z, fragment.fogcoord.x; */
|
|
/* EXP2: MUL fogFactorTemp.x, fogPRefOpt.w, fragment.fogcoord.x; */
|
|
inst->Opcode = OPCODE_MUL;
|
|
inst->DstReg.File = PROGRAM_TEMPORARY;
|
|
inst->DstReg.Index = fogFactorTemp;
|
|
inst->DstReg.WriteMask = WRITEMASK_X;
|
|
inst->SrcReg[0].File = PROGRAM_STATE_VAR;
|
|
inst->SrcReg[0].Index = fogPRefOpt;
|
|
inst->SrcReg[0].Swizzle
|
|
= (fprog->FogOption == GL_EXP) ? SWIZZLE_ZZZZ : SWIZZLE_WWWW;
|
|
inst->SrcReg[1].File = PROGRAM_INPUT;
|
|
inst->SrcReg[1].Index = FRAG_ATTRIB_FOGC;
|
|
inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
|
|
inst++;
|
|
if (fprog->FogOption == GL_EXP2) {
|
|
/* MUL fogFactorTemp.x, fogFactorTemp.x, fogFactorTemp.x; */
|
|
inst->Opcode = OPCODE_MUL;
|
|
inst->DstReg.File = PROGRAM_TEMPORARY;
|
|
inst->DstReg.Index = fogFactorTemp;
|
|
inst->DstReg.WriteMask = WRITEMASK_X;
|
|
inst->SrcReg[0].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[0].Index = fogFactorTemp;
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
|
|
inst->SrcReg[1].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[1].Index = fogFactorTemp;
|
|
inst->SrcReg[1].Swizzle = SWIZZLE_XXXX;
|
|
inst++;
|
|
}
|
|
/* EX2_SAT fogFactorTemp.x, -fogFactorTemp.x; */
|
|
inst->Opcode = OPCODE_EX2;
|
|
inst->DstReg.File = PROGRAM_TEMPORARY;
|
|
inst->DstReg.Index = fogFactorTemp;
|
|
inst->DstReg.WriteMask = WRITEMASK_X;
|
|
inst->SrcReg[0].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[0].Index = fogFactorTemp;
|
|
inst->SrcReg[0].NegateBase = NEGATE_XYZW;
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
|
|
inst->SaturateMode = SATURATE_ZERO_ONE;
|
|
inst++;
|
|
}
|
|
/* LRP result.color.xyz, fogFactorTemp.xxxx, colorTemp, fogColorRef; */
|
|
inst->Opcode = OPCODE_LRP;
|
|
inst->DstReg.File = PROGRAM_OUTPUT;
|
|
inst->DstReg.Index = FRAG_RESULT_COLOR;
|
|
inst->DstReg.WriteMask = WRITEMASK_XYZ;
|
|
inst->SrcReg[0].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[0].Index = fogFactorTemp;
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_XXXX;
|
|
inst->SrcReg[1].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[1].Index = colorTemp;
|
|
inst->SrcReg[1].Swizzle = SWIZZLE_NOOP;
|
|
inst->SrcReg[2].File = PROGRAM_STATE_VAR;
|
|
inst->SrcReg[2].Index = fogColorRef;
|
|
inst->SrcReg[2].Swizzle = SWIZZLE_NOOP;
|
|
inst++;
|
|
/* MOV result.color.w, colorTemp.x; # copy alpha */
|
|
inst->Opcode = OPCODE_MOV;
|
|
inst->DstReg.File = PROGRAM_OUTPUT;
|
|
inst->DstReg.Index = FRAG_RESULT_COLOR;
|
|
inst->DstReg.WriteMask = WRITEMASK_W;
|
|
inst->SrcReg[0].File = PROGRAM_TEMPORARY;
|
|
inst->SrcReg[0].Index = colorTemp;
|
|
inst->SrcReg[0].Swizzle = SWIZZLE_NOOP;
|
|
inst++;
|
|
/* END; */
|
|
inst->Opcode = OPCODE_END;
|
|
inst++;
|
|
|
|
/* free old instructions */
|
|
_mesa_free_instructions(fprog->Base.Instructions, origLen);
|
|
|
|
/* install new instructions */
|
|
fprog->Base.Instructions = newInst;
|
|
fprog->Base.NumInstructions = inst - newInst;
|
|
fprog->Base.InputsRead |= FRAG_BIT_FOGC;
|
|
/* XXX do this? fprog->FogOption = GL_NONE; */
|
|
}
|
|
|
|
|
|
|
|
static GLboolean
|
|
is_texture_instruction(const struct prog_instruction *inst)
|
|
{
|
|
switch (inst->Opcode) {
|
|
case OPCODE_TEX:
|
|
case OPCODE_TXB:
|
|
case OPCODE_TXD:
|
|
case OPCODE_TXL:
|
|
case OPCODE_TXP:
|
|
case OPCODE_TXP_NV:
|
|
return GL_TRUE;
|
|
default:
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Count the number of texure indirections in the given program.
|
|
* The program's NumTexIndirections field will be updated.
|
|
* See the GL_ARB_fragment_program spec (issue 24) for details.
|
|
* XXX we count texture indirections in texenvprogram.c (maybe use this code
|
|
* instead and elsewhere).
|
|
*/
|
|
void
|
|
_mesa_count_texture_indirections(struct gl_program *prog)
|
|
{
|
|
GLuint indirections = 1;
|
|
GLbitfield tempsOutput = 0x0;
|
|
GLbitfield aluTemps = 0x0;
|
|
GLuint i;
|
|
|
|
for (i = 0; i < prog->NumInstructions; i++) {
|
|
const struct prog_instruction *inst = prog->Instructions + i;
|
|
|
|
if (is_texture_instruction(inst)) {
|
|
if (((inst->SrcReg[0].File == PROGRAM_TEMPORARY) &&
|
|
(tempsOutput & (1 << inst->SrcReg[0].Index))) ||
|
|
((inst->Opcode != OPCODE_KIL) &&
|
|
(inst->DstReg.File == PROGRAM_TEMPORARY) &&
|
|
(aluTemps & (1 << inst->DstReg.Index))))
|
|
{
|
|
indirections++;
|
|
tempsOutput = 0x0;
|
|
aluTemps = 0x0;
|
|
}
|
|
}
|
|
else {
|
|
GLuint j;
|
|
for (j = 0; j < 3; j++) {
|
|
if (inst->SrcReg[j].File == PROGRAM_TEMPORARY)
|
|
aluTemps |= (1 << inst->SrcReg[j].Index);
|
|
}
|
|
if (inst->DstReg.File == PROGRAM_TEMPORARY)
|
|
aluTemps |= (1 << inst->DstReg.Index);
|
|
}
|
|
|
|
if ((inst->Opcode != OPCODE_KIL) && (inst->DstReg.File == PROGRAM_TEMPORARY))
|
|
tempsOutput |= (1 << inst->DstReg.Index);
|
|
}
|
|
|
|
prog->NumTexIndirections = indirections;
|
|
}
|
|
|
|
|
|
/**
|
|
* Count number of texture instructions in given program and update the
|
|
* program's NumTexInstructions field.
|
|
*/
|
|
void
|
|
_mesa_count_texture_instructions(struct gl_program *prog)
|
|
{
|
|
GLuint i;
|
|
prog->NumTexInstructions = 0;
|
|
for (i = 0; i < prog->NumInstructions; i++) {
|
|
prog->NumTexInstructions += is_texture_instruction(prog->Instructions + i);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Scan/rewrite program to remove reads of custom (output) registers.
|
|
* The passed type has to be either PROGRAM_OUTPUT or PROGRAM_VARYING
|
|
* (for vertex shaders).
|
|
* In GLSL shaders, varying vars can be read and written.
|
|
* 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, enum register_file type)
|
|
{
|
|
GLuint i;
|
|
GLint outputMap[VERT_RESULT_MAX];
|
|
GLuint numVaryingReads = 0;
|
|
|
|
assert(type == PROGRAM_VARYING || type == PROGRAM_OUTPUT);
|
|
assert(prog->Target == GL_VERTEX_PROGRAM_ARB || type != PROGRAM_VARYING);
|
|
|
|
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 == 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(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 == 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 < VERT_RESULT_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++;
|
|
}
|
|
}
|
|
}
|
|
}
|