#include "../glNV.h" #include #include "rc1.0_general.h" #include "nvparse_errors.h" #include "nvparse_externs.h" void GeneralCombinersStruct::Validate(int numConsts, ConstColorStruct *pcc) { GLint maxGCs; glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &maxGCs); if (num > maxGCs) { char buffer[256]; sprintf(buffer, "%d general combiners specified, only %d supported", num, maxGCs); errors.set(buffer); num = maxGCs; } if (0 == num) { // Setup a "fake" general combiner 0 general[0].ZeroOut(); num = 1; } localConsts = 0; int i; for (i = 0; i < num; i++) localConsts += general[i].numConsts; if (localConsts > 0) if (NULL == qglCombinerStageParameterfvNV) errors.set("local constant(s) specified, but not supported -- ignored"); else for (i = 0; i < num; i++) general[i].SetUnusedLocalConsts(numConsts, pcc); for (i = 0; i < num; i++) general[i].Validate(i); for (; i < maxGCs; i++) general[i].ZeroOut(); } void GeneralCombinersStruct::Invoke() { qglCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, num); int i; for (i = 0; i < num; i++) general[i].Invoke(i); if (NULL != qglCombinerStageParameterfvNV) { if (localConsts > 0) glEnable(GL_PER_STAGE_CONSTANTS_NV); else glDisable(GL_PER_STAGE_CONSTANTS_NV); } } void GeneralCombinerStruct::ZeroOut() { numPortions = 2; numConsts = 0; portion[0].ZeroOut(); portion[0].designator = RCP_RGB; portion[1].ZeroOut(); portion[1].designator = RCP_ALPHA; } void GeneralCombinerStruct::SetUnusedLocalConsts(int numGlobalConsts, ConstColorStruct *globalCCs) { int i; for (i = 0; i < numGlobalConsts; i++) { bool constUsed = false; int j; for (j = 0; j < numConsts; j++) constUsed |= (cc[j].reg.bits.name == globalCCs[i].reg.bits.name); if (!constUsed) cc[numConsts++] = globalCCs[i]; } } void GeneralCombinerStruct::Validate(int stage) { if (2 == numConsts && cc[0].reg.bits.name == cc[1].reg.bits.name) errors.set("local constant set twice"); switch (numPortions) { case 0: portion[0].designator = RCP_RGB; // Fallthru case 1: portion[1].designator = ((RCP_RGB == portion[0].designator) ? RCP_ALPHA : RCP_RGB); // Fallthru case 2: if (portion[0].designator == portion[1].designator) errors.set("portion declared twice"); break; } int i; for (i = 0; i < numPortions; i++) portion[i].Validate(stage); for (; i < 2; i++) portion[i].ZeroOut(); } void GeneralCombinerStruct::Invoke(int stage) { int i; if (NULL != qglCombinerStageParameterfvNV) for (i = 0; i < numConsts; i++) qglCombinerStageParameterfvNV(GL_COMBINER0_NV + stage, cc[i].reg.bits.name, &(cc[i].v[0])); for (i = 0; i < 2; i++) portion[i].Invoke(stage); } void GeneralPortionStruct::Validate(int stage) { gf.Validate(stage, designator); } void GeneralPortionStruct::Invoke(int stage) { gf.Invoke(stage, designator, bs); } void GeneralPortionStruct::ZeroOut() { gf.ZeroOut(); bs.word = RCP_SCALE_BY_ONE; } void GeneralFunctionStruct::ZeroOut() { // Create mapped registers for zero and discard MappedRegisterStruct unsignedZero; RegisterEnum zero; zero.word = RCP_ZERO; unsignedZero.Init(zero); MappedRegisterStruct unsignedDiscard; RegisterEnum discard; discard.word = RCP_DISCARD; unsignedDiscard.Init(discard); numOps = 3; op[0].op = RCP_MUL; op[0].reg[0] = unsignedDiscard; op[0].reg[1] = unsignedZero; op[0].reg[2] = unsignedZero; op[1].op = RCP_MUL; op[1].reg[0] = unsignedDiscard; op[1].reg[1] = unsignedZero; op[1].reg[2] = unsignedZero; op[2].op = RCP_SUM; op[2].reg[0] = unsignedDiscard; } void GeneralFunctionStruct::Validate(int stage, int portion) { int i; for (i = 0; i < numOps; i++) op[i].Validate(stage, portion); // Check if multiple ops are writing to same register (and it's not DISCARD) if (numOps > 1 && op[0].reg[0].reg.bits.name == op[1].reg[0].reg.bits.name && GL_DISCARD_NV != op[0].reg[0].reg.bits.name) errors.set("writing to same register twice"); if (numOps > 2 && (op[0].reg[0].reg.bits.name == op[2].reg[0].reg.bits.name || op[1].reg[0].reg.bits.name == op[2].reg[0].reg.bits.name) && GL_DISCARD_NV != op[2].reg[0].reg.bits.name) errors.set("writing to same register twice"); // Set unused outputs to discard, unused inputs to zero/unsigned_identity if (numOps < 2) { // Set C input to zero op[1].reg[1].reg.bits.name = GL_ZERO; op[1].reg[1].map = GL_UNSIGNED_IDENTITY_NV; op[1].reg[1].reg.bits.channel = portion; // Set D input to zero op[1].reg[2].reg.bits.name = GL_ZERO; op[1].reg[2].map = GL_UNSIGNED_IDENTITY_NV; op[1].reg[2].reg.bits.channel = portion; // Discard CD output op[1].op = false; op[1].reg[0].reg.bits.name = GL_DISCARD_NV; } if (numOps < 3) { // Discard muxSum output op[2].reg[0].reg.bits.name = GL_DISCARD_NV; op[2].op = RCP_SUM; } } void GeneralFunctionStruct::Invoke(int stage, int portion, BiasScaleEnum bs) { GLenum portionEnum = (RCP_RGB == portion) ? GL_RGB : GL_ALPHA; qglCombinerInputNV(GL_COMBINER0_NV + stage, portionEnum, GL_VARIABLE_A_NV, op[0].reg[1].reg.bits.name, op[0].reg[1].map, MAP_CHANNEL(op[0].reg[1].reg.bits.channel)); qglCombinerInputNV(GL_COMBINER0_NV + stage, portionEnum, GL_VARIABLE_B_NV, op[0].reg[2].reg.bits.name, op[0].reg[2].map, MAP_CHANNEL(op[0].reg[2].reg.bits.channel)); qglCombinerInputNV(GL_COMBINER0_NV + stage, portionEnum, GL_VARIABLE_C_NV, op[1].reg[1].reg.bits.name, op[1].reg[1].map, MAP_CHANNEL(op[1].reg[1].reg.bits.channel)); qglCombinerInputNV(GL_COMBINER0_NV + stage, portionEnum, GL_VARIABLE_D_NV, op[1].reg[2].reg.bits.name, op[1].reg[2].map, MAP_CHANNEL(op[1].reg[2].reg.bits.channel)); qglCombinerOutputNV(GL_COMBINER0_NV + stage, portionEnum, op[0].reg[0].reg.bits.name, op[1].reg[0].reg.bits.name, op[2].reg[0].reg.bits.name, bs.bits.scale, bs.bits.bias, op[0].op, op[1].op, (op[2].op == RCP_MUX) ? true : false); } // This helper function assigns a channel to an undesignated input register static void ConvertRegister(RegisterEnum& reg, int portion) { if (RCP_NONE == reg.bits.channel) { reg.bits.channel = portion; if (GL_FOG == reg.bits.name && RCP_ALPHA == portion) // Special case where fog alpha is final only, but RGB is not reg.bits.finalOnly = true; } } void OpStruct::Validate(int stage, int portion) { int args = 1; if (RCP_DOT == op || RCP_MUL == op) args = 3; else args = 1; if (reg[0].reg.bits.readOnly) errors.set("writing to a read-only register"); if (RCP_ALPHA == portion && RCP_DOT == op) errors.set("dot used in alpha portion"); int i; for (i = 0; i < args; i++) { ConvertRegister(reg[i].reg, portion); if (reg[i].reg.bits.finalOnly) errors.set("final register used in general combiner"); if (RCP_RGB == portion && RCP_BLUE == reg[i].reg.bits.channel) errors.set("blue register used in rgb portion"); if (RCP_ALPHA == portion && RCP_RGB == reg[i].reg.bits.channel) errors.set("rgb register used in alpha portion"); if (i > 0 && GL_DISCARD_NV == reg[i].reg.bits.name) errors.set("reading from discard"); } }