Cg 1.2 Runtime Api Additions
Version 1.2 of the Cg runtime adds a number of new capabilities to the
existing set of functionality from previous releases. These new features
include functionality that make it possible to write programs that can run
more efficiently on the GPU, techniques that help hide some of the inherent
limitations of some Cg profiles on the GPU, and entrypoints that support
new language functionality in the Cg 1.2 release.
Parameter Literalization
The 1.2 Cg runtime makes it possible to denote some of the parameters to a
program as having a fixed constant value. This feature can lead to
substantially more efficient programs in a number of cases. For example,
a program might have a block of code that implements functionality that is
only used some of the time:
float4 main(uniform float enableDazzle, ...) : COLOR {
if (enableDazzle) {
// do lengthy computation
}
else {
// do basic computation
}
}
Some hardware profiles don't directly support branching (this includes all
of the fragment program profiles supported in this release), and have to
handle code like the program by effectively following both sides of the
if() test. (They still compute the correct result in the end, just not
very efficiently.)
However, if the "enableDazzle" parameter is marked as a literal parameter
and a value is provided for it, the compiler can generate an optimized
version of the program with the knowledge of "enableDazzle"'s value, just
generating GPU code for one of the two cases. This can lead to substantial
performance improvments. This feature also makes it easier to write
general purpose shaders with a wide variety of supported functionality,
while only paying the runtime cost for the functionality provided.
This feature is also useful for parameters with numeric values. For
example, consider a shader that implements a diffuse reflection model:
float4 main(uniform float3 lightPos, uniform float3 lightColor,
uniform float3 Kd, float3 pos : TEXCOORD0,
float3 normal : TEXCOORD1) : COLOR
{
return Kd*lightColor*max(0., dot(normalize(lightPos-pos), normal));
}
If the "lightColor" and "Kd" parameters are set to literals, it is possible
for the compiler to compute the product "Kd * lightColor" once, rather than
once each time the program executes.
Given a parameter handle, the cgSetParameterVariability() entrypoint sets
the variability of a parameter:
void cgSetParameterVariability(CGparameter param, CGenum vary);
To set it to a literal parameter, the CG_LITERAL enumerant should be passed
as the second parameter.
After a parameter has set to be a literal, the following routines should be
used to set the parameter's value.
void cgSetParameter1f(CGparameter param, float x);
void cgSetParameter2f(CGparameter param, float x, float y);
void cgSetParameter3f(CGparameter param, float x, float y, float z);
void cgSetParameter4f(CGparameter param, float x, float y, float z,
float w);
void cgSetParameter1d(CGparameter param, double x);
void cgSetParameter2d(CGparameter param, double x, double y);
void cgSetParameter3d(CGparameter param, double x, double y, double z);
void cgSetParameter4d(CGparameter param, double x, double y, double z,
double w);
void cgSetParameter1fv(CGparameter param, const float *v);
void cgSetParameter2fv(CGparameter param, const float *v);
void cgSetParameter3fv(CGparameter param, const float *v);
void cgSetParameter4fv(CGparameter param, const float *v);
void cgSetParameter1dv(CGparameter param, const double *v);
void cgSetParameter2dv(CGparameter param, const double *v);
void cgSetParameter3dv(CGparameter param, const double *v);
void cgSetParameter4dv(CGparameter param, const double *v);
void cgSetMatrixParameterdr(CGparameter param, const double *matrix);
void cgSetMatrixParameterfr(CGparameter param, const float *matrix);
void cgSetMatrixParameterdc(CGparameter param, const double *matrix);
void cgSetMatrixParameterfc(CGparameter param, const float *matrix);
After a parameter has been set to be a literal, or after the value of a
literal parameter has been changed, the program must be compiled and loaded
into the GPU, regardless of whether it had already been compiled. This
issue is discussed further in the section on program recompilation below.
Array Size Specification
The Cg 1.2 language also adds support for ``unsized array'' variables;
programs can be written to take parameters that are arrays with an
indeterminate size. The actual size of these arrays is then set via the Cg
runtime. This feature is useful for writing general-purpose shaders
with a minimal performance penalty.
For example, consider a shader that computes shading given some number of
light sources. If the information about each light source is stored in a
struct LightInfo, the shader might be written as:
float4 main(LightInfo lights[], ...) : COLOR
{
float4 color = float4(0,0,0,1);
for (i = 0; i < lights.length; ++i) {
// add lights[i]'s contribution to color
}
return color;
}
The runtime can then be used to set the length of the lights[] array (and
then to initialize the values of the LightInfo structures.) As with
literal parameters, the program must be recompiled and reloaded after a
parameter's array size is set or changes.
These two entrypoints set the size of an unsized array parameter referenced
by the given parameter handle. To set the size of a multidimensional
unsized array, all of the dimensions' sizes must be set simultaneously, by
providing them all via the pointer to an array of integer values.
void cgSetArraySize(CGparameter param, int size);
void cgSetMultiDimArraySize(CGparameter param, const int *sizes);
XXX what happens if these are called with an already-sized array?? XXX
To get the size of an array parameter, the cgGetArraySize() entrypoint can
be used.
int cgGetArraySize(CGparameter param, int dimension);
Program Recompilation At Runtime
The Cg 1.2 runtime environment will allow automatic and manual recompilation
of programs. This functionality is useful for multiple reasons :
- Changing variability of parameters
-
Parameters may be changed from uniform variability to literal variability
as described above.
- Changing value of literal parameters
-
Changing the value of a literal parameter will require recompilation since
the value is used at compile time.
- Resizing parameter arrays
-
Changing the length of a parameter array may require recompilation depending
on the capabilities of the profile of the program.
- Binding sub-shader parameters
-
Sub-shader parameters are structures that overload methods that need to be
provided at compile time; they are described below. Binding such
parameters to program parameters will require recompilation.
Recompilation can be executed manually by the application using the runtime
or automatically by the runtime.
The entry point:
void cgCompileProgram(CGprogram program);
causes the given program to be recompiled, and the function:
CGbool cgIsProgramCompiled(CGprogram program);
reurns a boolean value indicating whether the current program needs
recompilation.
By default, programs are automatically compiled when cgCreateProgram() or
cgCreateProgramFromFile() is called. This behavior can be controled with the
entry point :
void cgSetAutoCompile(CGcontext ctx, CGenum flag);
Where flag is one of the following three enumerants :
- CG_COMPILE_MANUAL
-
With this method the application is responsible for manually recompiling
a program. It may check to see if a program requires recompilation with
the entry point cgIsProgramCompiled().
cgCompileProgram() can then
be used to force compilation.
- CG_COMPILE_IMMEDIATE
-
CG_COMPILE_IMMEDIATE will force recompilation automatically and
immediately when a program enters an uncompiled state.
- CG_COMPILE_LAZY
-
This method is similar to CG_COMPILE_IMMEDIATE but will delay
program recompilation until the program object code is needed. The
advantage of this method is the reduction of extraneous recompilations.
The disadvantage is that compile time errors will not be encountered
when the program is enters the uncompiled state but will instead be
encountered at some later time.
For programs that use features like unsized arrays that can not be compiled until
their array sizes are set, it is good practice to change the default behavior
of compilation to CG_COMPILE_MANUAL so that cgCreateProgram() or
cgCreateProgramFromFile() do not unnecessarily encounter and report
compilation errors.
Shared Parameters (Context Global Parameters)
Version 1.2 of the runtime introduces parameters that may be shared across
programs in the same context via a new binding mechanism. Once shared
parameters are constructed and bound to program parameters, setting the
value of the shared parameter will automatically set the value of all of
the program parameters they are bound to.
Shared parameters belong to a CGcontext instead of a CGprogram.
They may be created with the following new entry points :
CGparameter cgCreateParameter(CGcontext ctx, CGtype type);
CGparameter cgCreateParameterArray(CGcontext ctx, CGtype type,
int length);
CGparameter cgCreateParameterMultiDimArray(CGcontext ctx, CGtype type,
int dim, const int *lengths);
They may be deleted with :
void cgDestroyParameter(CGparameter param);
After a parameter has been created, its value should be set with the
cgSetParameter*() routines described in the literalization section above.
Once a shared parameter is created it may be associated with any number of program
parameters with the call:
void cgConnectParameter(CGparameter from, CGparameter to);
where ``from'' is a parameter created with one of the cgCreateParameter()
calls, and ``to'' is a program parameter.
Given a program parameter, the handle to the shared parameter that is bound
to it (if any) can be found with the call:
CGparameter cgGetConnectedParameter(CGparameter param);
It returns NULL if no shared parameter has been connected to ``param''.
There are also calls that make it possible to find the set of program
parameters to which a given shared parameter has been connected to. The
entry point:
int cgGetNumConnectedToParameters(CGparameter param);
returns the total number of program parameters that ``param'' has been
conencted to, and the entry point:
CGparameter cgGetConnectedToParameter(CGparameter param, int index);
can be used to get CGparameter handles for each of the program parameters
to which a shared parameter is connected.
A shared parameter can be unbound from a program parameter with :
void cgDisconnectParameter(CGparameter param);
The context in which a shared parameter was created can be returned with:
CGcontext cgGetParameterContext(CGparameter param);
And the entrypoint:
CGbool cgIsParameterGlobal(CGparameter param);
can be used to determine if a parameter is a shared (global) parameter.
Shader Interface Support
From the runtime's perspective, shader interfaces are simply struct parameters that have
a CGtype associated with them. For example, if the following Cg code is included
in some program source compiled in the runtime :
interface FooInterface
{
float SomeMethod(float x);
}
struct FooStruct : FooInterface
{
float SomeMethod(float x);
{
return(Scale * x);
}
float Scale;
};
The named types FooInterface and FooStruct will be added to the context.
Each one will have a unique CGtype associated with it. The CGtype can
be retrieved with :
CGtype cgGetNamedUserType(CGprogram program, const char *name);
int cgGetNumUserTypes(CGprogram program);
CGtype cgGetUserType(CGprogram program, int index);
CGbool cgIsParentType(CGtype parent, CGtype child);
CGbool cgIsInterfaceType(CGtype type);
Once the CGtype has been retrieved, it may be used to construct an instance
of the struct using cgCreateParameter. It may then
be connected to a program parameter of the parent type (in the above example this
would be FooInterface) using cgConnectParameter.
Calling cgGetParameterType on such a parameter will
return the CG_STRUCT to keep backwards compatibility with code that recurses
parameter trees. In order to obtain the enumerant of the named type the
following entry point should be used :
CGtype cgGetParameterNamedType(CGparameter param);
The parent types of a given named type may be obtained with the following
entry points :
int cgGetNumParentTypes(CGtype type);
CGtype cgGetParentType(CGtype type, int index);
If Cg source modules with differing definitions of named types are added to the
same context, an error will be thrown.
XXX update for new scoping/context/program local definitions stuff XXX
Updated Parameter Management Routines
XXX where should these go?
Some entrypoints from before have been updated in backwards compatible ways
CGparameter cgGetFirstParameter(CGprogram program, CGenum name_space);
CGparameter cgGetFirstLeafParameter(CGprogram program, CGenum name_space);
like cgGetNamedParameter, but limits search to the given name_space
(CG_PROGRAM or CG_GLOBAL)...
CGparameter cgGetNamedProgramParameter(CGprogram program, CGenum name_space,
const char *name);
|