Пример #1
0
inline void cgfxVaryingParameter::addRecursive(
    CGparameter parameter,
    cgfxVaryingParameter**& nextParameter
)
{
	if( cgGetParameterVariability( parameter) == CG_VARYING)
	{
		if( cgGetParameterType( parameter) == CG_STRUCT)
		{
			CGparameter input = cgGetFirstStructParameter( parameter);
			while( input)
			{
				addRecursive( input, nextParameter);
				input = cgGetNextParameter( input);
			}
		}
		else if( cgIsParameterReferenced( parameter))
		{
			*nextParameter = new cgfxVaryingParameter( parameter);
			nextParameter = &(*nextParameter)->fNext;
		}
	}
}
Пример #2
0
static inline CGparameter find_param_from_semantic(CGparameter param, const std::string &sem)
{
   while (param)
   {
      if (cgGetParameterType(param) == CG_STRUCT)
      {
         CGparameter ret = find_param_from_semantic(cgGetFirstStructParameter(param), sem);
         if (ret)
            return ret;
      }
      else
      {
         if (cgGetParameterSemantic(param) &&
               sem == cgGetParameterSemantic(param) &&
               cgGetParameterDirection(param) == CG_IN &&
               cgGetParameterVariability(param) == CG_VARYING &&
               validate_param_name(cgGetParameterName(param)))
            return param;
      }
      param = cgGetNextParameter(param);
   }

   return NULL;
}
Пример #3
0
static void set_program_base_attrib(unsigned i)
{
   CGparameter param = cgGetFirstParameter(prg[i].vprg, CG_PROGRAM);
   for (; param; param = cgGetNextParameter(param))
   {
      if (cgGetParameterDirection(param) != CG_IN || cgGetParameterVariability(param) != CG_VARYING)
         continue;

      const char *semantic = cgGetParameterSemantic(param);
      if (!semantic)
         continue;

      RARCH_LOG("CG: Found semantic \"%s\" in prog #%u.\n", semantic, i);

      if (strcmp(semantic, "TEXCOORD") == 0 || strcmp(semantic, "TEXCOORD0") == 0)
         prg[i].tex = param;
      else if (strcmp(semantic, "COLOR") == 0 || strcmp(semantic, "COLOR0") == 0)
         prg[i].color = param;
      else if (strcmp(semantic, "POSITION") == 0)
         prg[i].vertex = param;
      else if (strcmp(semantic, "TEXCOORD1") == 0)
         prg[i].lut_tex = param;
   }
}
Пример #4
0
	//---------------------------------------------------------------------
	void CgProgram::recurseParams(CGparameter parameter, size_t contextArraySize)
	{
		while (parameter != 0)
		{
			// Look for uniform parameters only
			// Don't bother enumerating unused parameters, especially since they will
			// be optimised out and therefore not in the indexed versions
			CGtype paramType = cgGetParameterType(parameter);

			if (cgGetParameterVariability(parameter) == CG_UNIFORM &&
				paramType != CG_SAMPLER1D &&
				paramType != CG_SAMPLER2D &&
				paramType != CG_SAMPLER3D &&
				paramType != CG_SAMPLERCUBE &&
				paramType != CG_SAMPLERRECT &&
				cgGetParameterDirection(parameter) != CG_OUT && 
				cgIsParameterReferenced(parameter))
			{
				int arraySize;

				switch(paramType)
				{
				case CG_STRUCT:
					recurseParams(cgGetFirstStructParameter(parameter));
					break;
				case CG_ARRAY:
					// Support only 1-dimensional arrays
					arraySize = cgGetArraySize(parameter, 0);
					recurseParams(cgGetArrayParameter(parameter, 0), (size_t)arraySize);
					break;
				default:
					// Normal path (leaf)
					String paramName = cgGetParameterName(parameter);
					size_t logicalIndex = cgGetParameterResourceIndex(parameter);

					// Get the parameter resource, to calculate the physical index
					CGresource res = cgGetParameterResource(parameter);
					bool isRegisterCombiner = false;
					size_t regCombinerPhysicalIndex = 0;
					switch (res)
					{
					case CG_COMBINER_STAGE_CONST0:
						// register combiner, const 0
						// the index relates to the texture stage; store this as (stage * 2) + 0
						regCombinerPhysicalIndex = logicalIndex * 2;
						isRegisterCombiner = true;
						break;
					case CG_COMBINER_STAGE_CONST1:
						// register combiner, const 1
						// the index relates to the texture stage; store this as (stage * 2) + 1
						regCombinerPhysicalIndex = (logicalIndex * 2) + 1;
						isRegisterCombiner = true;
						break;
					default:
						// normal constant
						break;
					}

					// Trim the '[0]' suffix if it exists, we will add our own indexing later
					if (StringUtil::endsWith(paramName, "[0]", false))
					{
						paramName.erase(paramName.size() - 3);
					}


					GpuConstantDefinition def;
					def.arraySize = contextArraySize;
					mapTypeAndElementSize(paramType, isRegisterCombiner, def);

					if (def.constType == GCT_UNKNOWN)
					{
						LogManager::getSingleton().logMessage(
							"Problem parsing the following Cg Uniform: '"
							+ paramName + "' in file " + mName);
						// next uniform
						parameter = cgGetNextParameter(parameter);
						continue;
					}
					if (isRegisterCombiner)
					{
						def.physicalIndex = regCombinerPhysicalIndex;
					}
					else
					{
						// base position on existing buffer contents
						if (def.isFloat())
						{
							def.physicalIndex = mFloatLogicalToPhysical->bufferSize;
						}
						else
						{
							def.physicalIndex = mIntLogicalToPhysical->bufferSize;
						}
					}

					def.logicalIndex = logicalIndex;
					if( mParametersMap.find(paramName) == mParametersMap.end())
					{
						mParametersMap.insert(GpuConstantDefinitionMap::value_type(paramName, def));
						mParametersMapSizeAsBuffer += sizeof(size_t);
						mParametersMapSizeAsBuffer += paramName.size();
						mParametersMapSizeAsBuffer += sizeof(GpuConstantDefinition);
					}

					// Record logical / physical mapping
					if (def.isFloat())
					{
											OGRE_LOCK_MUTEX(mFloatLogicalToPhysical->mutex);
						mFloatLogicalToPhysical->map.insert(
							GpuLogicalIndexUseMap::value_type(def.logicalIndex, 
								GpuLogicalIndexUse(def.physicalIndex, def.arraySize * def.elementSize, GPV_GLOBAL)));
						mFloatLogicalToPhysical->bufferSize += def.arraySize * def.elementSize;
					}
					else
					{
											OGRE_LOCK_MUTEX(mIntLogicalToPhysical->mutex);
						mIntLogicalToPhysical->map.insert(
							GpuLogicalIndexUseMap::value_type(def.logicalIndex, 
								GpuLogicalIndexUse(def.physicalIndex, def.arraySize * def.elementSize, GPV_GLOBAL)));
						mIntLogicalToPhysical->bufferSize += def.arraySize * def.elementSize;
					}

					break;
				}                   
			}

			// now handle uniform samplers. This is needed to fix their register positions
			// if delegating to a GLSL shader.
			if (mDelegate && cgGetParameterVariability(parameter) == CG_UNIFORM && (
				paramType == CG_SAMPLER1D ||
				paramType == CG_SAMPLER2D ||
				paramType == CG_SAMPLER3D ||
				paramType == CG_SAMPLERCUBE ||
				paramType == CG_SAMPLERRECT) &&
				cgGetParameterDirection(parameter) != CG_OUT && 
				cgIsParameterReferenced(parameter))
			{
				String paramName = cgGetParameterName(parameter);
				CGresource res = cgGetParameterResource(parameter);
				int pos = -1;
				switch (res)
				{
				case CG_TEXUNIT0: pos = 0; break;
				case CG_TEXUNIT1: pos = 1; break;
				case CG_TEXUNIT2: pos = 2; break;
				case CG_TEXUNIT3: pos = 3; break;
				case CG_TEXUNIT4: pos = 4; break;
				case CG_TEXUNIT5: pos = 5; break;
				case CG_TEXUNIT6: pos = 6; break;
				case CG_TEXUNIT7: pos = 7; break;
				case CG_TEXUNIT8: pos = 8; break;
				case CG_TEXUNIT9: pos = 9; break;
				case CG_TEXUNIT10: pos = 10; break;
				case CG_TEXUNIT11: pos = 11; break;
				case CG_TEXUNIT12: pos = 12; break;
				case CG_TEXUNIT13: pos = 13; break;
				case CG_TEXUNIT14: pos = 14; break;
				case CG_TEXUNIT15: pos = 15; break;
#if(CG_VERSION_NUM >= 3000)
				case CG_TEXUNIT16: pos = 16; break;
				case CG_TEXUNIT17: pos = 17; break;
				case CG_TEXUNIT18: pos = 18; break;
				case CG_TEXUNIT19: pos = 19; break;
				case CG_TEXUNIT20: pos = 20; break;
				case CG_TEXUNIT21: pos = 21; break;
				case CG_TEXUNIT22: pos = 22; break;
				case CG_TEXUNIT23: pos = 23; break;
				case CG_TEXUNIT24: pos = 24; break;
				case CG_TEXUNIT25: pos = 25; break;
				case CG_TEXUNIT26: pos = 26; break;
				case CG_TEXUNIT27: pos = 27; break;
				case CG_TEXUNIT28: pos = 28; break;
				case CG_TEXUNIT29: pos = 29; break;
				case CG_TEXUNIT30: pos = 30; break;
				case CG_TEXUNIT31: pos = 31; break;
#endif
				default:
					break;
				}
				if (pos != -1)
				{
					mSamplerRegisterMap.insert(std::make_pair(paramName, pos));
				}
			}

			// Get next
			parameter = cgGetNextParameter(parameter);
		}

		
	}
Пример #5
0
static bool AddPass(CGtechnique technique, JSON &json, CGpass pass, UniformsMap &uniformRemapping)
{
    bool success = true;
    json.AddObject(NULL);

    const char * const passName = cgGetPassName(pass);
    if (NULL != passName)
    {
        json.AddString("name", passName);
    }

    bool firstParameter = true;

#if CG_VERSION_NUM >= 3000
    const int CG_NUMBER_OF_DOMAINS = (CG_TESSELLATION_EVALUATION_DOMAIN + 1);
#endif

    for (int domain = CG_FIRST_DOMAIN; domain < CG_NUMBER_OF_DOMAINS; domain++)
    {
        const CGprogram program = cgGetPassProgram(pass, (CGdomain)domain);
        if (NULL != program)
        {
            const char * const programString = cgGetProgramString(program, CG_COMPILED_PROGRAM);

            CGparameter param = cgGetFirstParameter(program, CG_GLOBAL);
            while (NULL != param)
            {
                if (cgIsParameterUsed(param, program) &&
                    CG_UNIFORM == cgGetParameterVariability(param))
                {
                    if (firstParameter)
                    {
                        firstParameter = false;
                        json.AddArray("parameters", true);
                        json.BeginData(true);
                    }
                    const char * const paramName = cgGetParameterName(param);
                    AddMappedParameter(json, paramName, programString, uniformRemapping);
                }
                param = cgGetNextParameter(param);
            }

            param = cgGetFirstParameter(program, CG_PROGRAM);
            while (NULL != param)
            {
                if (cgIsParameterUsed(param, program) &&
                    CG_UNIFORM == cgGetParameterVariability(param))
                {
                    if (firstParameter)
                    {
                        firstParameter = false;
                        json.AddArray("parameters", true);
                        json.BeginData(true);
                    }
                    const char * const paramName = cgGetParameterName(param);
                    AddMappedParameter(json, paramName, programString, uniformRemapping);
                }
                param = cgGetNextParameter(param);
            }
        }
    }

    if (!firstParameter)
    {
        json.EndData();
        json.CloseArray(true); // parameters
    }


    json.AddArray("semantics", true);
    json.BeginData(true);

    CGprogram vertexProgram = cgGetPassProgram(pass, CG_VERTEX_DOMAIN);
    CGparameter vertexInputParameter = cgGetFirstLeafParameter(vertexProgram, CG_PROGRAM);
    while (NULL != vertexInputParameter)
    {
        const CGenum variability = cgGetParameterVariability(vertexInputParameter);
        if (CG_VARYING == variability)
        {
            const CGenum direction = cgGetParameterDirection(vertexInputParameter);
            if (CG_IN == direction ||
                CG_INOUT == direction)
            {
                const char * const semantic = cgGetParameterSemantic(vertexInputParameter);
                json.AddData(semantic, strlen(semantic));
            }
        }
        vertexInputParameter = cgGetNextLeafParameter(vertexInputParameter);
    }

    json.EndData();
    json.CloseArray(true); // semantics


    json.AddObject("states");

    CGstateassignment state = cgGetFirstStateAssignment(pass);
    if (NULL != state)
    {
        do
        {
            success &= AddState(json, state);
            state = cgGetNextStateAssignment(state);
        }
        while (NULL != state);
    }

    json.CloseObject(); // states


    json.AddArray("programs", true);
    json.BeginData(true);

    for (int domain = CG_FIRST_DOMAIN; domain < CG_NUMBER_OF_DOMAINS; domain++)
    {
        const CGprogram program = cgGetPassProgram(pass, (CGdomain)domain);
        if (NULL != program)
        {
            const char * const entryPoint = cgGetProgramString(program, CG_PROGRAM_ENTRY);
            json.AddData(entryPoint, strlen(entryPoint));
        }
        else if (domain == CG_VERTEX_DOMAIN)
        {

            ErrorMessage("%s : No vertex program.", cgGetTechniqueName(technique));
            success = false;
        }
        else if(domain == CG_FRAGMENT_DOMAIN)
        {
            ErrorMessage("%s : No fragment program.", cgGetTechniqueName(technique));
            success = false;
        }
    }

    json.EndData();
    json.CloseArray(true); // programs

    json.CloseObject(); // pass

    return success;
}
Пример #6
0
	//---------------------------------------------------------------------
	void CgProgram::recurseParams(CGparameter parameter, size_t contextArraySize) const
	{
		while (parameter != 0)
        {
            // Look for uniform (non-sampler) parameters only
            // Don't bother enumerating unused parameters, especially since they will
            // be optimised out and therefore not in the indexed versions
            CGtype paramType = cgGetParameterType(parameter);

            if (cgGetParameterVariability(parameter) == CG_UNIFORM &&
                paramType != CG_SAMPLER1D &&
                paramType != CG_SAMPLER2D &&
                paramType != CG_SAMPLER3D &&
                paramType != CG_SAMPLERCUBE &&
                paramType != CG_SAMPLERRECT &&
                cgGetParameterDirection(parameter) != CG_OUT && 
                cgIsParameterReferenced(parameter))
            {
				int arraySize;

				switch(paramType)
				{
				case CG_STRUCT:
					recurseParams(cgGetFirstStructParameter(parameter));
					break;
				case CG_ARRAY:
					// Support only 1-dimensional arrays
					arraySize = cgGetArraySize(parameter, 0);
					recurseParams(cgGetArrayParameter(parameter, 0), (size_t)arraySize);
					break;
				default:
					// Normal path (leaf)
					String paramName = cgGetParameterName(parameter);
					size_t logicalIndex = cgGetParameterResourceIndex(parameter);

					// Get the parameter resource, to calculate the physical index
					CGresource res = cgGetParameterResource(parameter);
					bool isRegisterCombiner = false;
					size_t regCombinerPhysicalIndex = 0;
					switch (res)
					{
					case CG_COMBINER_STAGE_CONST0:
						// register combiner, const 0
						// the index relates to the texture stage; store this as (stage * 2) + 0
						regCombinerPhysicalIndex = logicalIndex * 2;
						isRegisterCombiner = true;
						break;
					case CG_COMBINER_STAGE_CONST1:
						// register combiner, const 1
						// the index relates to the texture stage; store this as (stage * 2) + 1
						regCombinerPhysicalIndex = (logicalIndex * 2) + 1;
						isRegisterCombiner = true;
						break;
					default:
						// normal constant
						break;
					}

					// Trim the '[0]' suffix if it exists, we will add our own indexing later
					if (StringUtil::endsWith(paramName, "[0]", false))
					{
						paramName.erase(paramName.size() - 3);
					}


					GpuConstantDefinition def;
					def.arraySize = contextArraySize;
					mapTypeAndElementSize(paramType, isRegisterCombiner, def);

					if (def.constType == GCT_UNKNOWN)
					{
						LogManager::getSingleton().logMessage(
							"Problem parsing the following Cg Uniform: '"
							+ paramName + "' in file " + mName);
						// next uniform
						continue;
					}
					if (isRegisterCombiner)
					{
						def.physicalIndex = regCombinerPhysicalIndex;
					}
					else
					{
						// base position on existing buffer contents
						if (def.isFloat())
						{
							def.physicalIndex = mFloatLogicalToPhysical.bufferSize;
						}
						else
						{
							def.physicalIndex = mIntLogicalToPhysical.bufferSize;
						}
					}


					mConstantDefs.map.insert(GpuConstantDefinitionMap::value_type(paramName, def));

					// Record logical / physical mapping
					if (def.isFloat())
					{
						OGRE_LOCK_MUTEX(mFloatLogicalToPhysical.mutex)
						mFloatLogicalToPhysical.map.insert(
							GpuLogicalIndexUseMap::value_type(logicalIndex, 
								GpuLogicalIndexUse(def.physicalIndex, def.arraySize * def.elementSize)));
						mFloatLogicalToPhysical.bufferSize += def.arraySize * def.elementSize;
						mConstantDefs.floatBufferSize = mFloatLogicalToPhysical.bufferSize;
					}
					else
					{
						OGRE_LOCK_MUTEX(mIntLogicalToPhysical.mutex)
						mIntLogicalToPhysical.map.insert(
							GpuLogicalIndexUseMap::value_type(logicalIndex, 
								GpuLogicalIndexUse(def.physicalIndex, def.arraySize * def.elementSize)));
						mIntLogicalToPhysical.bufferSize += def.arraySize * def.elementSize;
						mConstantDefs.intBufferSize = mIntLogicalToPhysical.bufferSize;
					}

					// Deal with array indexing
					mConstantDefs.generateConstantDefinitionArrayEntries(paramName, def);

					break;
		
				}
					
            }
            // Get next
            parameter = cgGetNextParameter(parameter);
        }

        
    }
Пример #7
0
void CCgMaterialRenderer::getUniformList()
{
	for(unsigned int i = 0; i < UniformInfo.size(); ++i)
		delete UniformInfo[i];

	UniformInfo.clear();

	for(unsigned int i = 0; i < 2; ++i)
	{
		CGenum Space = CG_GLOBAL;
		bool IsGlobal = 1;

		if(i == 1)
		{
			Space = CG_PROGRAM;
			IsGlobal = 0;
		}

		for(unsigned int j = 0; j < 3; ++j)
		{
			CGprogram* Program = 0;

			switch(j)
			{
			case 0:
				Program = &VertexProgram;
				break;
			case 1:
				Program = &FragmentProgram;
				break;
			case 2:
				Program = &GeometryProgram;
				break;
			}

			if(*Program)
			{
				CGparameter Parameter = cgGetFirstParameter(*Program, Space);

				while(Parameter)
				{
					if(cgGetParameterVariability(Parameter) == CG_UNIFORM && cgGetParameterDirection(Parameter) == CG_IN)
					{
						CCgUniform* Uniform = 0;

						CGtype Type = cgGetParameterType(Parameter);

						switch(Type)
						{
						case CG_FLOAT:
						case CG_FLOAT1:
							Uniform = new CCgUniform1f(Parameter, IsGlobal);
							break;
						case CG_FLOAT2:
							Uniform = new CCgUniform2f(Parameter, IsGlobal);
							break;
						case CG_FLOAT3:
							Uniform = new CCgUniform3f(Parameter, IsGlobal);
							break;
						case CG_FLOAT4:
							Uniform = new CCgUniform4f(Parameter, IsGlobal);
							break;
						case CG_INT:
						case CG_INT1:
							Uniform = new CCgUniform1i(Parameter, IsGlobal);
							break;
						case CG_INT2:
							Uniform = new CCgUniform2i(Parameter, IsGlobal);
							break;
						case CG_INT3:
							Uniform = new CCgUniform3i(Parameter, IsGlobal);
							break;
						case CG_INT4:
							Uniform = new CCgUniform4i(Parameter, IsGlobal);
							break;
						case CG_FLOAT4x4:
							Uniform = new CCgUniform4x4f(Parameter, IsGlobal);
							break;
						case CG_SAMPLER2D:
							Uniform = new CCgUniformSampler2D(Parameter, IsGlobal);
							break;
						}

						if(Uniform)
							UniformInfo.push_back(Uniform);
					}

					Parameter = cgGetNextParameter(Parameter);
				}
			}
		}
	}
}
Пример #8
0
bool csShaderGLCGCommon::DefaultLoadProgram (iShaderProgramCG* cgResolve,
  const char* programStr, ProgramType _type,
  const ProfileLimitsPair& customLimitsPair, uint flags)
{
  if (!programStr || !*programStr) return false;

  const ProfileLimits& customLimits = (_type == progVP) ?
    customLimitsPair.vp : customLimitsPair.fp;
  CGGLenum type = (_type == progVP) ? CG_GL_VERTEX : CG_GL_FRAGMENT;

  size_t i;
  csString augmentedProgramStr = GetAugmentedProgram (programStr,
    (flags & loadFlagUnusedV2FForInit) != 0);
    
  programStr = augmentedProgramStr;
  CGprofile profile = customLimits.profile;
  CS::PluginCommon::ShaderProgramPluginGL::HardwareVendor vendor =
    customLimits.vendor;

  if (shaderPlug->doVerbose || shaderPlug->doVerbosePrecache)
  {
    shaderPlug->Report (CS_REPORTER_SEVERITY_NOTIFY,
      "Cg %s program %s: using profile %s[%d]", GetProgramType(),
      CS::Quote::Single (description.GetData ()),
      cgGetProfileString (profile), profile);
  }

  ArgumentArray args;
  shaderPlug->GetProfileCompilerArgs (GetProgramType(), profile, 
    customLimitsPair,
    vendor,
    (flags & loadIgnoreConfigProgramOpts) ? csGLShader_CG::argsNoConfig
      : csGLShader_CG::argsAll, args);
  for (i = 0; i < compilerArgs.GetSize(); i++) 
    args.Push (compilerArgs[i]);
  programPositionInvariant = false;
  for (i = 0; i < args.GetSize(); ) 
  {
    if (strcmp (args[i], "-posinv") == 0)
    {
      if (profile >= CG_PROFILE_GPU_VP)
      {
	/* Work around Cg 2.0 and above (including 3.0) bug: it emits
	   "OPTION ARB_position_invariant;" AND computes result.position in
	   the VP - doing both is verboten.
	   Affected are the GP4VP and higher profiles.
	   Remedy: remove -posinv argument 
	 */
	args.DeleteIndex (i);
	continue;
      }
      programPositionInvariant = true;
    }
    i++;
  }
  customLimits.ToCgOptions (args);
  args.Push (0);
 
  if (program)
  {
    cgDestroyProgram (program);
  }
  shaderPlug->SetCompiledSource (programStr);
  shaderPlug->SetIgnoreErrors (true);
  program = cgCreateProgram (shaderPlug->context, 
    CG_SOURCE, programStr, 
    profile, !entrypoint.IsEmpty() ? entrypoint : "main", args.GetArray());
  shaderPlug->SetIgnoreErrors (false);
  
  if (!(flags & loadIgnoreErrors)) shaderPlug->PrintAnyListing();

  if (!program)
  {
    shaderPlug->SetCompiledSource (0);
    /*if (shaderPlug->debugDump)
    {
      EnsureDumpFile();
      WriteAdditionalDumpInfo ("Failed program source", programStr);
    }*/
    return false;
  }
  programProfile = cgGetProgramProfile (program);

  GetParamsFromVmapConstants();
  if (flags & loadApplyVmap)
    GetParamsFromVmap();

  if (flags & loadIgnoreErrors) shaderPlug->SetIgnoreErrors (true);
  cgCompileProgram (program);
  if (flags & loadIgnoreErrors)
    shaderPlug->SetIgnoreErrors (false);
  else
    shaderPlug->PrintAnyListing();
  
  if (flags & loadApplyVmap)
    GetPostCompileParamProps ();

  if (flags & loadLoadToGL)
  {
    cgGetError(); // Clear error
    cgGLLoadProgram (program);
    if (!(flags & loadIgnoreErrors)) shaderPlug->PrintAnyListing();
    if ((cgGetError() != CG_NO_ERROR)
      || !cgGLIsProgramLoaded (program)) 
    {
      if (shaderPlug->debugDump)
	DoDebugDump();

      if (shaderPlug->doVerbose
	  && (((type == CG_GL_VERTEX) && (profile >= CG_PROFILE_ARBVP1))
	    || ((type == CG_GL_FRAGMENT) && (profile >= CG_PROFILE_ARBFP1))))
      {
	csString err = (char*)glGetString (GL_PROGRAM_ERROR_STRING_ARB);
	if (!err.IsEmpty())
	  shaderPlug->Report (CS_REPORTER_SEVERITY_WARNING,
	    "OpenGL error string: %s", err.GetData());
      }

      shaderPlug->SetCompiledSource (0);
      return false;
    }
  }

  if (shaderPlug->debugDump)
    DoDebugDump();
  
  shaderPlug->SetCompiledSource (0);

  bool result = true;
  if (programType == progFP)
  {
    int numVaryings = 0;
    CGparameter param = cgGetFirstLeafParameter (program, CG_PROGRAM);
    while (param)
    {
      if (cgIsParameterUsed (param, program)
	&& cgIsParameterReferenced (param))
      {
	const CGenum var = cgGetParameterVariability (param);
	if (var == CG_VARYING)
	  numVaryings++;
      }
  
      param = cgGetNextLeafParameter (param);
    }
    
    /* WORKAROUND: Even NVs G80 doesn't support passing more than 16 attribs
       into an FP, yet Cg happily generates code that uses more (and GL falls 
       back to SW).
       So manually check the number of varying inputs and reject more than 16.
       
       @@@ This should be at least configurable
     */
    const int maxNumVaryings = 16;
    if (numVaryings > maxNumVaryings)
    {
      if (shaderPlug->doVerbose || shaderPlug->doVerbosePrecache)
      {
	shaderPlug->Report (CS_REPORTER_SEVERITY_NOTIFY,
	  "Discarding compiled program for having too much varyings "
	  "(%d, limit is %d)",
	  numVaryings, maxNumVaryings);
      }
      cgDestroyProgram (program);
      program = 0;
      result = false;
    }
  }
  if (!result && !debugFN.IsEmpty())
  {
    csRef<iVFS> vfs = csQueryRegistry<iVFS> (objectReg);
    vfs->DeleteFile (debugFN);
  }
  return result;
}