RetroArch/ps3/gcmgl/src/rgl_ps3_cg.cpp

5169 lines
183 KiB
C++

#include "rgl_cg.h"
static CGbool rglpSupportsVertexProgram( CGprofile p )
{
if ( p == CG_PROFILE_SCE_VP_RSX )
return CG_TRUE;
return CG_FALSE;
}
static CGbool rglpSupportsFragmentProgram( CGprofile p )
{
if ( CG_PROFILE_SCE_FP_RSX == p )
return CG_TRUE;
return CG_FALSE;
}
static CGprofile rglpGetLatestProfile( CGGLenum profile_type )
{
switch ( profile_type )
{
case CG_GL_VERTEX:
return CG_PROFILE_SCE_VP_RSX;
case CG_GL_FRAGMENT:
return CG_PROFILE_SCE_FP_RSX;
default:
break;
}
return CG_PROFILE_UNKNOWN;
}
static int rglGcmGenerateProgram (void *data, int profileIndex, const CgProgramHeader *programHeader, const void *ucode, const CgParameterTableHeader *parameterHeader,
const CgParameterEntry *parameterEntries, const char *stringTable, const float *defaultValues )
{
_CGprogram *program = (_CGprogram*)data;
CGprofile profile = ( CGprofile )programHeader->profile;
CellGcmContextData *thisContext = (CellGcmContextData*)gCellGcmCurrentContext;
int need_swapping = 0;
// if can't match a known profile, the data may be in wrong endianness
if ((( profile != CG_PROFILE_SCE_FP_RSX ) && ( profile != CG_PROFILE_SCE_VP_RSX ) ))
need_swapping = 1;
// check that this program block is of the right revision
// i.e. that the cgBinary.h header hasn't changed since it was
// compiled.
// validate the profile
int invalidProfile = 0;
switch ( ENDIAN_32( profile, need_swapping ) )
{
case CG_PROFILE_SCE_VP_RSX:
if ( profileIndex != VERTEX_PROFILE_INDEX )
invalidProfile = 1;
break;
case CG_PROFILE_SCE_FP_RSX:
if ( profileIndex != FRAGMENT_PROFILE_INDEX )
invalidProfile = 1;
break;
default:
invalidProfile = 1;
break;
}
if ( invalidProfile )
{
rglCgRaiseError( CG_UNKNOWN_PROFILE_ERROR );
return 0;
}
memcpy( &program->header, programHeader, sizeof( program->header ) );
program->ucode = ucode;
program->loadProgramId = GMM_ERROR;
program->loadProgramOffset = 0;
program->inLocalMemory = true;
size_t parameterSize = parameterHeader->entryCount * sizeof( CgRuntimeParameter );
void *memoryBlock;
if ( parameterSize )
memoryBlock = memalign( 16, parameterSize );
else
memoryBlock = NULL;
program->rtParametersCount = parameterHeader->entryCount;
program->runtimeParameters = ( CgRuntimeParameter* )memoryBlock;
if (!parameterEntries) // the param entry can be supplied if not right after parameterHeader in memory, it happens when there's a program copy
parameterEntries = ( CgParameterEntry* )( parameterHeader + 1 );
program->parametersEntries = parameterEntries;
program->parameterResources = ( char* )( program->parametersEntries + program->rtParametersCount );
program->resources = ( unsigned short* )(( char* )program->parametersEntries + ( parameterHeader->resourceTableOffset - sizeof( CgParameterTableHeader ) ) );
program->defaultValuesIndexCount = parameterHeader->defaultValueIndexCount;
program->defaultValuesIndices = ( CgParameterDefaultValue* )(( char* )program->parametersEntries + ( parameterHeader->defaultValueIndexTableOffset - sizeof( CgParameterTableHeader ) ) );
program->semanticCount = parameterHeader->semanticIndexCount;
program->semanticIndices = ( CgParameterSemantic* )( program->defaultValuesIndices + program->defaultValuesIndexCount );
program->defaultValues = NULL;
memset( program->runtimeParameters, 0, parameterHeader->entryCount*sizeof( CgRuntimeParameter ) );
//string table
program->stringTable = stringTable;
//default values
program->defaultValues = defaultValues;
rglCreatePushBuffer( program );
int count = program->defaultValuesIndexCount;
if ( profileIndex != FRAGMENT_PROFILE_INDEX )
{
/* modifies the push buffer */
for (int i = 0; i < count; i++)
{
int index = ( int )program->defaultValuesIndices[i].entryIndex;
CgRuntimeParameter *rtParameter = program->runtimeParameters + index;
int arrayCount = 1;
const CgParameterEntry *parameterEntry = rtParameter->parameterEntry;
bool isArray = false;
if ( parameterEntry->flags & CGP_ARRAY )
{
isArray = true;
const CgParameterArray *parameterArray = rglGetParameterArray( program, parameterEntry );
arrayCount = rglGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
parameterEntry++;
rtParameter++;
}
if ( rtParameter->pushBufferPointer ) //unreferenced might have default values
{
const CgParameterResource *parameterResource = rglGetParameterResource( program, parameterEntry );
const float *itemDefaultValues = program->defaultValues + program->defaultValuesIndices[i].defaultValueIndex;
int registerStride = isMatrix(( CGtype )parameterResource->type ) ? rglGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
if ( parameterEntry->flags & CGP_CONTIGUOUS )
memcpy( rtParameter->pushBufferPointer, itemDefaultValues, arrayCount * registerStride *4*sizeof( float ) );
else
{
unsigned int *pushBufferPointer = (( unsigned int * )rtParameter->pushBufferPointer );
for ( int j = 0;j < arrayCount;j++ )
{
unsigned int *pushBufferAddress = isArray ? ( *( unsigned int** )pushBufferPointer ) : pushBufferPointer;
memcpy( pushBufferAddress, itemDefaultValues, registerStride*4*sizeof( float ) );
pushBufferPointer += isArray ? 1 : 3 + registerStride * 4;
itemDefaultValues += 4 * registerStride;
}
}
}
}
}
// not loaded yet
program->loadProgramId = GMM_ERROR;
program->loadProgramOffset = 0;
if ( profileIndex == FRAGMENT_PROFILE_INDEX )
{
// always load fragment shaders.
// uploads the given fp shader to gpu memory. Allocates if needed.
// This also builds the shared constants push buffer if needed, since it depends on the load address
unsigned int ucodeSize = program->header.instructionCount * 16;
if ( program->loadProgramId == GMM_ERROR )
{
program->loadProgramId = gmmAlloc(ucodeSize);
program->loadProgramOffset = 0;
}
rglGcmSend(thisContext, program->loadProgramId, program->loadProgramOffset, 0, ( char* )program->ucode, ucodeSize );
}
program->programGroup = NULL;
program->programIndexInGroup = -1;
return 1;
}
CGprogram rglpCgUpdateProgramAtIndex( CGprogramGroup group, int index, int refcount )
{
if (index < ( int )group->programCount)
{
//index can be < 0 , in that case refcount update on the group only, used when destroying a copied program
if (index >= 0)
{
//if it has already been referenced duplicate instead of returning the same index, until the API offer a native support for
//group of programs ( //fixed bug 13007 )
if (refcount == 1 && group->programs[index].refCount == 1)
{
//it will handle the refcounting
CGprogram res = cgCopyProgram( group->programs[index].program );
return res;
}
group->programs[index].refCount += refcount;
}
group->refCount += refcount;
if (refcount < 0)
{
if (group->refCount == 0 && !group->userCreated)
rglCgDestroyProgramGroup( group );
return NULL;
}
else
return group->programs[index].program;
}
else
return NULL;
}
static CGprogramGroup rglCgCreateProgramGroupFromFile( CGcontext ctx, const char *group_file )
{
// check that file exists
FILE* fp = fopen( group_file, "rb" );
if (!fp)
return ( CGprogramGroup )NULL;
// find the file length
size_t file_size = 0;
fseek( fp, 0, SEEK_END );
file_size = ftell( fp );
rewind( fp );
// alloc memory for new binary program and read the data
char* ptr = ( char* )malloc( file_size + 1 );
if ( NULL == ptr )
{
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
fclose(fp);
return ( CGprogramGroup )NULL;
}
// read the entire file into memory then close the file
// TODO ********* just loading the file is a bit lame really. We can do better.
fread( ptr, file_size, 1, fp );
fclose( fp );
CGprogramGroup group = rglCgCreateProgramGroup( ctx, group_file, ptr, file_size );
if ( !group )
free( ptr );
return group;
}
CGprogramGroup rglCgCreateProgramGroup( CGcontext ctx, const char *name, void *ptr, int size )
{
_CGprogramGroup *group = NULL;
CGELFBinary elfBinary;
elfBinary.elfFile = NULL;
while ( 1 )
{
bool res = cgOpenElf( ptr, size, &elfBinary );
if ( !res )
break;
size_t elfConstTableSize = ( size_t )elfBinary.consttabSize;
size_t elfStringTableSize = ( size_t )elfBinary.strtabSize;
//first pass to get the size of each item ( could be faster if the embedded constants index table size was in the header
int programCount = elfBinary.shadertabSize / sizeof( CgProgramHeader );
int i;
//structure of the memory buffer storing the group
size_t nvProgramNamesOffset = rglPad( sizeof( _CGprogramGroup ), sizeof( _CGnamedProgram ) ); //program name offset
size_t nvDefaultValuesTableOffset = rglPad( nvProgramNamesOffset + programCount * sizeof( _CGnamedProgram ), 16 );//shared default value table
size_t nvStringTableOffset = nvDefaultValuesTableOffset + elfConstTableSize; //shared string table
size_t structureSize = nvStringTableOffset + elfStringTableSize;//total structure size
//create the program group
group = ( CGprogramGroup )malloc( structureSize );
if ( !group ) //out of memory
break;
//fill the group structure
group->ctx = ctx;
group->next = NULL;
group->programCount = ( unsigned int )programCount;
group->constantTable = ( unsigned int * )(( char* )group + nvDefaultValuesTableOffset );
group->stringTable = ( unsigned int * )(( char* )group + nvStringTableOffset );
group->programs = ( _CGnamedProgram * )(( char* )group + nvProgramNamesOffset );
group->userCreated = true;
group->refCount = 0;
group->filedata = ( char* )ptr;
if ( name )
{
int len = strlen( name );
group->name = ( char* )malloc( len + 1 );
if ( !group->name )//out of memory
break;
strcpy( group->name, name );
}
else
group->name = NULL;
//copy the default values
if ( elfConstTableSize )
memcpy(( char* )group + nvDefaultValuesTableOffset, elfBinary.consttab, elfConstTableSize );
//copy the string table
if ( elfStringTableSize )
memcpy(( char* )group + nvStringTableOffset, elfBinary.strtab, elfStringTableSize );
//add the group to the context:
_CGcontext *context = _cgGetContextPtr(ctx);
if ( !context->groupList )
context->groupList = group;
else
{
_CGprogramGroup *current = context->groupList;
while ( current->next )
current = current->next;
current->next = group;
}
//create all the shaders contained in the package and add them to the group
for ( i = 0;i < ( int )group->programCount;i++ )
{
CgProgramHeader *cgShader = ( CgProgramHeader* )elfBinary.shadertab + i;
//hack to counter removal of TypeC during beta
if ( cgShader->profile == ( CGprofile )7005 )
cgShader->profile = CG_PROFILE_SCE_VP_RSX;
if ( cgShader->profile == ( CGprofile )7006 )
cgShader->profile = CG_PROFILE_SCE_FP_RSX;
CGELFProgram elfProgram;
bool res = cgGetElfProgramByIndex( &elfBinary, i, &elfProgram );
if ( !res )
return false;
//I reference the buffer passed as parameter here, so it will have to stay around
CgProgramHeader *programHeader = cgShader;
char *ucode = ( char * )elfProgram.texttab;
CgParameterTableHeader *parameterHeader = ( CgParameterTableHeader * )elfProgram.paramtab;
const char *programName = getSymbolByIndexInPlace( elfBinary.symtab, elfBinary.symbolSize, elfBinary.symbolCount, elfBinary.symbolstrtab, i + 1 );
group->programs[i].name = programName;
group->programs[i].program = rglCgCreateProgram( ctx, ( CGprofile )cgShader->profile, programHeader, ucode, parameterHeader, ( const char* )group->stringTable, ( const float* )group->constantTable );
_CGprogram *cgProgram = _cgGetProgPtr( group->programs[i].program );
cgProgram->programGroup = group;
cgProgram->programIndexInGroup = i;
group->programs[i].refCount = 0;
}
break;
}
return group;
}
void rglCgDestroyProgramGroup( CGprogramGroup group )
{
_CGprogramGroup *_group = ( _CGprogramGroup * )group;
for ( int i = 0;i < ( int )_group->programCount;i++ )
{
//unlink the program
_CGprogram *cgProgram = _cgGetProgPtr( group->programs[i].program );
cgProgram->programGroup = NULL;
cgDestroyProgram( _group->programs[i].program );
}
free( _group->filedata );
if ( _group->name )
free( _group->name );
//remove the group from the group list
_CGcontext *context = _cgGetContextPtr( group->ctx );
_CGprogramGroup *current = context->groupList;
_CGprogramGroup *previous = NULL;
while ( current && current != group )
{
previous = current;
current = current->next;
}
if ( current )
{
if ( !previous )
context->groupList = current->next;
else
previous->next = current->next;
}
free( _group );
}
const char *rglCgGetProgramGroupName (CGprogramGroup group)
{
_CGprogramGroup *_group = ( _CGprogramGroup * )group;
return _group->name;
}
int rglCgGetProgramIndex (CGprogramGroup group, const char *name)
{
int i;
for ( i = 0;i < ( int )group->programCount;i++ )
if ( !strcmp( name, group->programs[i].name ) )
return i;
return -1;
}
void rglpProgramErase (void *data)
{
_CGprogram* platformProgram = (_CGprogram*)data;
_CGprogram* program = (_CGprogram*)platformProgram;
if ( program->loadProgramId != GMM_ERROR )
{
gmmFree( program->loadProgramId );
program->loadProgramId = GMM_ERROR;
program->loadProgramOffset = 0;
}
//free the runtime parameters
if ( program->runtimeParameters )
{
//need to erase all the program parameter "names"
int i;
int count = ( int )program->rtParametersCount;
for ( i = 0; i < count;i++ )
rglEraseName( &_CurrentContext->cgParameterNameSpace, (unsigned int)program->runtimeParameters[i].id );
free( program->runtimeParameters );
}
//free the push buffer block
if ( program->memoryBlock )
free( program->memoryBlock );
//free the samplers lookup tables
if ( program->samplerIndices )
{
free( program->samplerValuesLocation );
free( program->samplerIndices );
free( program->samplerUnits );
}
//free the "pointers" on the push buffer used for fast access
if ( program->constantPushBufferPointers )
free( program->constantPushBufferPointers );
}
//TODO: use a ref mechanism for the string table or duplicate it !
int rglpCopyProgram (void *src_data, void *dst_data)
{
_CGprogram *source = (_CGprogram*)src_data;
_CGprogram *destination = (_CGprogram*)dst_data;
//extract the layout of the parameter buffers from the source
CgParameterTableHeader parameterHeader;
parameterHeader.entryCount = source->rtParametersCount;
parameterHeader.resourceTableOffset = ( uintptr_t )(( char* )source->resources - ( char* )source->parametersEntries + sizeof( CgParameterTableHeader ) );
parameterHeader.defaultValueIndexCount = source->defaultValuesIndexCount;
parameterHeader.defaultValueIndexTableOffset = ( uintptr_t )(( char* )source->defaultValuesIndices - ( char* )source->parametersEntries + sizeof( CgParameterTableHeader ) );
parameterHeader.semanticIndexCount = source->semanticCount;
parameterHeader.semanticIndexTableOffset = ( uintptr_t )(( char* )source->defaultValuesIndices - ( char* )source->parametersEntries + sizeof( CgParameterTableHeader ) );
int profileIndex;
//allocate the copy of the program
switch ( source->header.profile )
{
case CG_PROFILE_SCE_VP_RSX:
profileIndex = VERTEX_PROFILE_INDEX;
break;
case CG_PROFILE_SCE_FP_RSX:
profileIndex = FRAGMENT_PROFILE_INDEX;
break;
default:
return 0;
}
return rglGcmGenerateProgram( destination, profileIndex, &source->header, source->ucode, &parameterHeader, source->parametersEntries, source->stringTable, source->defaultValues );
}
int rglpGenerateVertexProgram (void *data, const CgProgramHeader *programHeader,
const void *ucode, const CgParameterTableHeader *parameterHeader, const char *stringTable,
const float *defaultValues )
{
return rglGcmGenerateProgram( (_CGprogram*)data, VERTEX_PROFILE_INDEX, programHeader,
ucode, parameterHeader, NULL, stringTable, defaultValues );
}
int rglpGenerateFragmentProgram (void *data, const CgProgramHeader *programHeader, const void *ucode,
const CgParameterTableHeader *parameterHeader, const char *stringTable, const float *defaultValues )
{
return rglGcmGenerateProgram( (_CGprogram*)data, FRAGMENT_PROFILE_INDEX, programHeader, ucode, parameterHeader, NULL, stringTable, defaultValues );
}
/*============================================================
RTC CGC
============================================================ */
void cgRTCgcInit( void )
{
_cgRTCgcCompileProgramHook = &compile_program_from_string;
_cgRTCgcFreeCompiledProgramHook = &free_compiled_program;
}
void cgRTCgcFree( void )
{
_cgRTCgcCompileProgramHook = 0;
_cgRTCgcFreeCompiledProgramHook = 0;
}
/*============================================================
CG COMMON
============================================================ */
void rglCgRaiseError( CGerror error )
{
_CurrentContext->RGLcgLastError = error;
//_RGL_REPORT_EXTRA( RGL_REPORT_CG_ERROR, "Cg error:%s", cgGetErrorString( error ) );
if ( _CurrentContext->RGLcgErrorCallbackFunction )
_CurrentContext->RGLcgErrorCallbackFunction();
}
unsigned int rglCountFloatsInCgType( CGtype type )
{
int size = 0;
switch ( type )
{
case CG_FLOAT:
case CG_FLOAT1:
case CG_FLOAT1x1:
case CG_INT:
case CG_INT1:
case CG_INT1x1:
case CG_SAMPLER1D:
case CG_SAMPLER2D:
case CG_SAMPLER3D:
case CG_SAMPLERRECT:
case CG_SAMPLERCUBE:
case CG_BOOL:
case CG_HALF:
case CG_HALF1:
case CG_HALF1x1:
case CG_FIXED:
case CG_FIXED1:
case CG_FIXED1x1:
case CG_BOOL1:
case CG_BOOL1x1:
size = 1;
break;
case CG_FLOAT2:
case CG_FLOAT2x1:
case CG_FLOAT1x2:
case CG_HALF2:
case CG_HALF2x1:
case CG_HALF1x2:
case CG_INT2:
case CG_INT2x1:
case CG_INT1x2:
case CG_BOOL2:
case CG_BOOL2x1:
case CG_BOOL1x2:
case CG_FIXED2:
case CG_FIXED2x1:
case CG_FIXED1x2:
size = 2;
break;
case CG_FLOAT3:
case CG_FLOAT3x1:
case CG_FLOAT1x3:
case CG_HALF3:
case CG_HALF3x1:
case CG_HALF1x3:
case CG_INT3:
case CG_INT3x1:
case CG_INT1x3:
case CG_BOOL3:
case CG_BOOL3x1:
case CG_BOOL1x3:
case CG_FIXED3:
case CG_FIXED3x1:
case CG_FIXED1x3:
size = 3;
break;
case CG_FLOAT4:
case CG_FLOAT4x1:
case CG_FLOAT1x4:
case CG_FLOAT2x2:
case CG_HALF4:
case CG_HALF4x1:
case CG_HALF1x4:
case CG_HALF2x2:
case CG_INT4:
case CG_INT4x1:
case CG_INT1x4:
case CG_INT2x2:
case CG_BOOL4:
case CG_BOOL4x1:
case CG_BOOL1x4:
case CG_BOOL2x2:
case CG_FIXED4:
case CG_FIXED4x1:
case CG_FIXED1x4:
case CG_FIXED2x2:
size = 4;
break;
case CG_FLOAT2x3:
case CG_FLOAT3x2:
case CG_HALF2x3:
case CG_HALF3x2:
case CG_INT2x3:
case CG_INT3x2:
case CG_BOOL2x3:
case CG_BOOL3x2:
case CG_FIXED2x3:
case CG_FIXED3x2:
size = 6;
break;
case CG_FLOAT2x4:
case CG_FLOAT4x2:
case CG_HALF2x4:
case CG_HALF4x2:
case CG_INT2x4:
case CG_INT4x2:
case CG_BOOL2x4:
case CG_BOOL4x2:
case CG_FIXED2x4:
case CG_FIXED4x2:
size = 8;
break;
case CG_FLOAT3x3:
case CG_HALF3x3:
case CG_INT3x3:
case CG_BOOL3x3:
case CG_FIXED3x3:
size = 9;
break;
case CG_FLOAT3x4:
case CG_FLOAT4x3:
case CG_HALF3x4:
case CG_HALF4x3:
case CG_INT3x4:
case CG_INT4x3:
case CG_BOOL3x4:
case CG_BOOL4x3:
case CG_FIXED3x4:
case CG_FIXED4x3:
size = 12;
break;
case CG_FLOAT4x4:
case CG_HALF4x4:
case CG_INT4x4:
case CG_BOOL4x4:
case CG_FIXED4x4:
size = 16;
break;
default:
size = 0;
break;
}
return size;
}
void _cgRaiseInvalidParam (void *data, const void*v )
{
(void)data;
}
void _cgRaiseNotMatrixParam (void *data, const void*v )
{
(void)data;
}
void _cgIgnoreParamIndex (void *data, const void*v, const int index )
{
(void)data;
// nothing
}
CgRuntimeParameter* _cgGLTestTextureParameter( CGparameter param )
{
CgRuntimeParameter* ptr = rglCgGLTestParameter( param );
return ptr;
}
#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
nrows ,
static int _typesRowCount[] =
{
#include "Cg/cg_datatypes.h"
};
#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
ncols ,
static int _typesColCount[] =
{
#include "Cg/cg_datatypes.h"
};
unsigned int rglGetTypeRowCount( CGtype parameterType )
{
int typeIndex = parameterType - 1 - CG_TYPE_START_ENUM;
return _typesRowCount[typeIndex];
}
unsigned int rglGetTypeColCount( CGtype parameterType )
{
int typeIndex = parameterType - 1 - CG_TYPE_START_ENUM;
return _typesColCount[typeIndex];
}
/*============================================================
CG PARAMETER
============================================================ */
static const CGenum var_table[] = {CG_VARYING, CG_UNIFORM, CG_CONSTANT, CG_MIXED};
static const CGenum dir_table[] = {CG_IN, CG_OUT, CG_INOUT, CG_ERROR};
RGL_EXPORT CgparameterHookFunction _cgParameterCreateHook = NULL;
RGL_EXPORT CgparameterHookFunction _cgParameterDestroyHook = NULL;
static CGparameter rglAdvanceParameter( CGparameter param, int distance )
{
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( param );
if ( !rtParameter )
return NULL;
if ( distance == 0 )
return param;
if ( rtParameter > rtParameter->program->runtimeParameters )
{
CgRuntimeParameter *previousParameter = rtParameter - 1;
if (( previousParameter->parameterEntry->flags & CGP_ARRAY ) &&
!( previousParameter->parameterEntry->flags & CGP_UNROLLED ) )
{
int arrayIndex = CG_GETINDEX( param );
arrayIndex += distance;
const CgParameterArray *parameterArray = rglGetParameterArray( previousParameter->program, previousParameter->parameterEntry );
int arraySize = rglGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
if ( arrayIndex < arraySize )
{
int ret = ( int )rtParameter->id;
ret |= ( arrayIndex << CG_PARAMETERSIZE );
return ( CGparameter )ret;
}
else
{
return ( CGparameter )NULL;
}
}
}
//stop when the distance is 0
const CgParameterEntry *endEntry = rtParameter->program->parametersEntries + rtParameter->program->rtParametersCount;
const CgParameterEntry *paramEntry = rtParameter->parameterEntry;
while ( distance && paramEntry < endEntry )
{
switch ( paramEntry->flags & CGP_TYPE_MASK )
{
case CGP_ARRAY:
{
if ( paramEntry->flags & CGP_UNROLLED )
{
const CgParameterArray *parameterArray = rglGetParameterArray( rtParameter->program, paramEntry );
int arraySize = ( int )rglGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
paramEntry++;
distance--;
paramEntry++;
distance += arraySize;
}
else
{
paramEntry++;
distance--;
paramEntry++;
}
break;
}
case CGP_STRUCTURE:
{
const CgParameterStructure *parameterStructure = rglGetParameterStructure( rtParameter->program, paramEntry );
paramEntry++;
distance--;
distance += parameterStructure->memberCount;
break;
}
case CGP_INTRINSIC:
paramEntry++;
distance--;
break;
}
}
if ( paramEntry < endEntry )
{
size_t offset = ( paramEntry - rtParameter->parameterEntry );
CgRuntimeParameter *nextParameter = rtParameter + offset;
return nextParameter->id;
}
else
return ( CGparameter )NULL;
}
void rglCgDestroyContextParam( CgRuntimeParameter* ptr )
{
if (_cgParameterDestroyHook)
_cgParameterDestroyHook( ptr );
rglEraseName( &_CurrentContext->cgParameterNameSpace, (unsigned int)( ptr->id ) );
free( ptr );
}
static int rglGetSizeofSubArray( const short *dimensions, int count )
{
int res = 1;
for ( int i = 0;i < count;i++ )
res *= ( int )( *( dimensions++ ) );
return res;
}
static _CGparameter *_cgGetNamedParameter( _CGprogram* progPtr, const char* name, CGenum name_space, int *arrayIndex, const CgParameterEntry *_startEntry = NULL, int _entryCount = -1 )
{
if (!name)
return NULL;
*arrayIndex = -1;
int done = 0;
const char *structureEnd;
const char *structureStart = name;
int itemIndex = -1;
_CGprogram *program = progPtr;
const CgParameterEntry *currentEntry;
const CgParameterEntry *lastEntry;
int containerCount = -2;
if ( _startEntry && _entryCount != -1 )
{
currentEntry = _startEntry;
containerCount = _entryCount;
}
else
currentEntry = program->parametersEntries;
lastEntry = program->parametersEntries + program->rtParametersCount;
int isInArray = 0;
bool bWasUnrolled = false;
const char *prevStructureStart = structureStart; //for unrolled
while (( !done ) && ( *structureStart ) && ( containerCount != -1 ) )
{
structureEnd = strpbrk( structureStart, ".[" );
if (!structureEnd)
{
structureEnd = structureStart + strlen( structureStart );
done = 1;
}
if ( bWasUnrolled )
{
bWasUnrolled = false;
structureStart = prevStructureStart;
}
char structName[256];
int length = ( int )( structureEnd - structureStart );
strncpy( structName, structureStart, length );
structName[length] = '\0';
prevStructureStart = structureStart; //for unrolled
structureStart = structureEnd + 1;
bool found = false;
while ( !found && currentEntry < lastEntry && ( containerCount == -2 || containerCount > 0 ) )
{
if ( !strncmp( structName, program->stringTable + currentEntry->nameOffset, length )
&& ( name_space == 0 || ( name_space == CG_GLOBAL && ( currentEntry->flags & CGPF_GLOBAL ) )
|| ( name_space == CG_PROGRAM && !( currentEntry->flags & CGPF_GLOBAL ) ) ) )
{
if (( int )strlen( program->stringTable + currentEntry->nameOffset ) != length )
{
if ( !strcmp( name, program->stringTable + currentEntry->nameOffset ) )
{
found = true;
done = 1;
}
if ( !strncmp( name, program->stringTable + currentEntry->nameOffset, length ) &&
!strcmp( "[0]", program->stringTable + currentEntry->nameOffset + length ) )
{
found = true;
done = 1;
}
}
else
found = true;
}
if ( !found )
{
//we are skipping entries here, the search stays at the same level
int skipCount = 1;
while ( skipCount && currentEntry < lastEntry )
{
//skip as many entry as necessary
if ( currentEntry->flags & CGP_STRUCTURE )
{
const CgParameterStructure *parameterStructure = (const CgParameterStructure*)rglGetParameterStructure( program, currentEntry );
skipCount += parameterStructure->memberCount;
}
else if ( currentEntry->flags & CGP_ARRAY )
{
if ( currentEntry->flags & CGP_UNROLLED )
{
const CgParameterArray *parameterArray = (const CgParameterArray*)rglGetParameterArray( program, currentEntry );
skipCount += rglGetSizeofSubArray(( short* )parameterArray->dimensions, parameterArray->dimensionCount );
}
else
skipCount++; //the following item will be the type ( can't be a structure as they are always enrolled at the moment )
}
currentEntry++;
skipCount--;
}
}
if ( containerCount != -2 ) //denotes that we are in a structure or in an array and that we shouldn't look beyond
containerCount--;
}
//at that point we failed or we succeeded to find the entry at that level.
//if we have succeed we continue in the lower level if needed. if we have failed, we return not found.
if ( found )
{
switch ( currentEntry->flags & CGP_TYPE_MASK ) {
case 0:
itemIndex = ( int )( currentEntry - program->parametersEntries );
break;
case CGP_ARRAY:
{
const CgParameterEntry *arrayEntry = currentEntry;
const CgParameterArray *parameterArray = rglGetParameterArray( program, arrayEntry );
if ( *structureEnd == '\0' ) //if we are asked for the array parameter itself and not an array item:
{
itemIndex = ( int )( currentEntry - program->parametersEntries );
break;
}
//go inside:
currentEntry++;
if ( currentEntry->flags &CGP_STRUCTURE )
{
//continue to look into the list, but we should stop before the end of the array
bWasUnrolled = true;
containerCount = rglGetSizeofSubArray(( short* )parameterArray->dimensions, parameterArray->dimensionCount );
break;
}
else
{
//the array contains base types
isInArray = 1;
const char *arrayStart = structureEnd;
const char *arrayEnd = structureEnd;
int dimensionCount = 0;
int arrayCellIndex = 0;
while ( *arrayStart == '[' && dimensionCount < parameterArray->dimensionCount )
{
arrayEnd = strchr( arrayStart + 1, ']' );
int length = ( int )( arrayEnd - arrayStart - 1 );
char indexString[16];
strncpy( indexString, arrayStart + 1, length );
indexString[length] = '\0';
int index = atoi( indexString );
int rowSize = parameterArray->dimensions[dimensionCount];
if ( index >= rowSize )
{
//index out of range: not found
return NULL;
}
arrayCellIndex += index * rglGetSizeofSubArray(( short* )parameterArray->dimensions + dimensionCount, parameterArray->dimensionCount - dimensionCount - 1 );
arrayStart = arrayEnd + 1;
dimensionCount++;
}
structureEnd = arrayStart;
if ( *structureEnd == '\0' )
done = 1;
if ( done )
{
//found the item in the array
( *arrayIndex ) = arrayCellIndex;
itemIndex = ( int )( currentEntry - program->parametersEntries );
}
//else... don't know what to do, it's a array of structure not unrolled
}
}
break;
case CGP_STRUCTURE:
if ( done )
{
//the user is getting back a strut... not sure that all the checks are in place for such a return value
itemIndex = ( int )( currentEntry - program->parametersEntries );
}
else
{
const CgParameterStructure *parameterStructure = rglGetParameterStructure( program, currentEntry );
containerCount = parameterStructure->memberCount;
}
break;
default:
break;
}
}
if ( found )
{
if ( !bWasUnrolled )
currentEntry++;
}
else
break;//not found
}
//have we found it ?
if ( itemIndex != -1 )
return ( _CGparameter* )( program->runtimeParameters + itemIndex );
else
return NULL;
}
// API functions ----------------------------------------
CG_API CGparameter cgGetNamedParameter( CGprogram prog, const char* name )
{
// check program handle
if ( !CG_IS_PROGRAM( prog ) )
{
rglCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
return ( CGparameter )NULL;
}
_CGprogram* progPtr = _cgGetProgPtr( prog );
// 0 means don't care
int arrayIndex = -1;
CgRuntimeParameter *param = ( CgRuntimeParameter * )_cgGetNamedParameter( progPtr, name, ( CGenum )0, &arrayIndex );
if ( param )
{
int ret = ( int )param->id;
if ( arrayIndex != -1 )
ret |= ( arrayIndex << CG_PARAMETERSIZE );
return ( CGparameter )ret;
}
else
return ( CGparameter )NULL;
}
CG_API CGparameter cgGetFirstParameter( CGprogram prog, CGenum name_space )
{
// check program handle
if ( !CG_IS_PROGRAM( prog ) )
{
rglCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
return ( CGparameter )NULL;
}
_CGprogram* progPtr = _cgGetProgPtr( prog );
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )progPtr->runtimeParameters;
// find the first param of the requested namespace
for ( int i = 0; i < progPtr->rtParametersCount; ++i )
{
// check parameter handle
bool isGlobal = ( rtParameter->parameterEntry->flags & CGPF_GLOBAL ) == CGPF_GLOBAL;
if ( isGlobal && name_space == CG_GLOBAL )
{
return ( CGparameter )rtParameter->id;
}
if ( !isGlobal && name_space == CG_PROGRAM )
{
return ( CGparameter )rtParameter->id;
}
rtParameter++;
}
return ( CGparameter )NULL;
}
CG_API CGparameter cgGetNextParameter( CGparameter param )
{
//check parameter handle
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( param );
if ( !rtParameter )
return ( CGparameter )NULL;
// the case of the array element of a compact array is easy to solve
int arrayIndex = -1;
if ( rtParameter > rtParameter->program->runtimeParameters )
{
CgRuntimeParameter *previousParameter = rtParameter - 1;
if (( previousParameter->parameterEntry->flags & CGP_ARRAY ) &&
!( previousParameter->parameterEntry->flags & CGP_UNROLLED ) )
{
//we have an array item
arrayIndex = CG_GETINDEX( param );
const CgParameterArray *parameterArray = rglGetParameterArray( previousParameter->program, previousParameter->parameterEntry );
int arraySize = rglGetSizeofSubArray(( short* )parameterArray->dimensions, parameterArray->dimensionCount );
arrayIndex++;
if ( arrayIndex < arraySize )
{
int ret = ( int )rtParameter->id;
ret |= ( arrayIndex << CG_PARAMETERSIZE );
return ( CGparameter )ret;
}
else
return ( CGparameter )NULL;
}
}
//get the parent of the current as well as its location
const CgParameterEntry *paramEntry = rtParameter->parameterEntry;
const CgParameterEntry *firstEntry = rtParameter->program->parametersEntries;
int distance = 1;
paramEntry--;
bool bNextExists = false;
while ( paramEntry >= firstEntry && !bNextExists )
{
switch ( paramEntry->flags & CGP_TYPE_MASK )
{
case CGP_ARRAY:
distance--; // the array has one extra item, whether it's a structure or if it's an intrinsic type
//Ced: not it's not true, if it's unrolled there is no extra item
break;
case CGP_STRUCTURE:
{
const CgParameterStructure *parameterStructure = rglGetParameterStructure( rtParameter->program, paramEntry );
//+1 because of the structure element, there's structure
if ( distance >= ( parameterStructure->memberCount + 1 ) )
{
//the parameter is not in this structure, so I need to remove from the distance all the structure item
//so this structure will count for a distance of just one ( the distance is the level distance )
distance -= parameterStructure->memberCount;
}
else
{
//We are going to exit here
//ok so we were in this structure, so check if we have more item in the structure after the current one
if ( distance < parameterStructure->memberCount )
{
//we still have some items in the structure, take the next item
bNextExists = true;
break;
}
else
{
//no more items at this level return null
return ( CGparameter )NULL;
}
}
break;
}
case CGP_INTRINSIC:
break;
}
distance++;
paramEntry--;
}
//we have now elimated the case where we were in a struct and there were no more items, now if the item has a successor,
//this is what we are looking for
//we have already treated the current entry at that point, the loop starts on the previous one, the distance is 1
CGparameter nextParam = rglAdvanceParameter( rtParameter->id, 1 );
if ( nextParam )
{
CgRuntimeParameter *nextParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( nextParam );
//check that next param is still in the same namespace as param
bool isCurrentGlobal = ( rtParameter->parameterEntry->flags & CGPF_GLOBAL ) == CGPF_GLOBAL;
bool isNextGlobal = ( nextParameter->parameterEntry->flags & CGPF_GLOBAL ) == CGPF_GLOBAL;
if ( isNextGlobal != isCurrentGlobal )
{
//the next item doesn't have the same global flag... since they are grouped, it means we have quitted the current struct
return ( CGparameter )NULL;
}
else
return nextParam;
}
else
{
//case where we were at the root and there is no more items
return ( CGparameter )NULL;
}
}
CG_API const char* cgGetParameterSemantic( CGparameter param )
{
// check parameter handle
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( param );
if ( !rtParameter )
return NULL;
const CgParameterEntry *parameterEntry = rtParameter->parameterEntry;
unsigned short type = parameterEntry->flags & CGP_TYPE_MASK;
if ( type == CGP_STRUCTURE || CGP_STRUCTURE == CGP_ARRAY )
{
rglCgRaiseError( CG_INVALID_PARAMETER_ERROR );
return NULL;
}
size_t entryIndex = ( parameterEntry - rtParameter->program->parametersEntries );
//look for the parameter semantic in the semantic table for semantics set in the compiled source
int count = rtParameter->program->semanticCount;
int i;
for ( i = 0;i < count;i++ )
{
const CgParameterSemantic *semantic = rtParameter->program->semanticIndices + i;
if ( semantic->entryIndex == ( unsigned short )entryIndex )
return rtParameter->program->stringTable + semantic->semanticOffset; // found
}
//not found, we don't have the semantic for this parameter, returns empty strings
return "";
}
static bool rglPrependString( char *dst, const char *src, size_t size )
{
int len = strlen(src);
int previousLen = strlen(dst);
int spaceLeft = size - (previousLen + 1); //+1 for white space
if (spaceLeft < len)
return false;
memmove(dst + len, dst, previousLen + 1);
strncpy(dst, src, len);
return true;
}
CG_API CGenum cgGetParameterVariability( CGparameter param )
{
// check parameter handle
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( param );
if ( !rtParameter )
return ( CGenum )0;
else
{
unsigned int variability = rtParameter->parameterEntry->flags & CGPV_MASK;
if ( variability == CGPV_UNIFORM )
return CG_UNIFORM;
else if ( variability == CGPV_VARYING )
return CG_VARYING;
else if ( variability == CGPV_CONSTANT )
return CG_CONSTANT;
else if ( variability == CGPV_MIXED )
return CG_MIXED;
else
return ( CGenum )0;
}
}
CG_API CGenum cgGetParameterDirection( CGparameter param )
{
// check parameter handle
CgRuntimeParameter *rtParameter = ( CgRuntimeParameter* )rglCgGLTestParameter( param );
if ( !rtParameter )
return CG_ERROR;
else
{
unsigned int direction = rtParameter->parameterEntry->flags & CGPD_MASK;
if ( direction == CGPD_IN )
return CG_IN;
else if ( direction == CGPD_OUT )
return CG_OUT;
else if ( direction == CGPD_INOUT )
return CG_INOUT;
else
return CG_ERROR;
}
}
/*============================================================
CG TOKENS
============================================================ */
// ErrorFunctions ----------------------------
CG_API CGerror cgGetError( void )
{
CGerror err = _CurrentContext->RGLcgLastError;
_CurrentContext->RGLcgLastError = CG_NO_ERROR;
return err;
}
CG_API const char* cgGetErrorString( CGerror error )
{
return "cgGetErrorString not implemented.\n";
}
CG_API void cgSetErrorCallback( CGerrorCallbackFunc func )
{
_CurrentContext->RGLcgErrorCallbackFunction = func;
}
CG_API CGerrorCallbackFunc cgGetErrorCallback( void )
{
return _CurrentContext->RGLcgErrorCallbackFunction;
}
// Misc Functions -----------------------------
CG_API const char* cgGetString( CGenum sname )
{
if ( sname == CG_VERSION )
{
// this should return the version of the runtime or perhaps more meaninfully, the runtime compiler
static char versionstring[8];
sprintf( versionstring, "%d", CG_VERSION_NUM );
return versionstring;
}
rglCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
return NULL;
}
/*============================================================
CG CONTEXT
============================================================ */
RGL_EXPORT CgcontextHookFunction _cgContextCreateHook = NULL;
RGL_EXPORT CgcontextHookFunction _cgContextDestroyHook = NULL;
void rglCgContextPushFront( _CGcontext* ctx )
{
if ( _CurrentContext->RGLcgContextHead )
{
_CGcontext* head = _cgGetContextPtr( _CurrentContext->RGLcgContextHead );
// insert this context at the head of the list
ctx->next = head;
}
_CurrentContext->RGLcgContextHead = ctx->id;
}
static void destroy_context( _CGcontext*ctx )
{
if ( _cgContextDestroyHook ) _cgContextDestroyHook( ctx );
// free the id
rglEraseName( &_CurrentContext->cgContextNameSpace, (unsigned int)ctx->id );
// zero the memory
memset(ctx, 0, sizeof( *ctx));
ctx->compileType = CG_UNKNOWN;
// return context to free store
free( ctx );
}
// API functions ----------------------------------------
CG_API CGcontext cgCreateContext( void )
{
// create a context out of thin air and add it to the hidden global list.
_CGcontext* ptr = (_CGcontext*)malloc(sizeof(_CGcontext));
if ( NULL == ptr )
{
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
return ( CGcontext )NULL;
}
// initialise member variables
memset(ptr, 0, sizeof( *ptr));
ptr->compileType = CG_UNKNOWN;
// get a new id for the object
CGcontext result = ( CGcontext )rglCreateName( &_CurrentContext->cgContextNameSpace, ptr );
if ( !result ) { free( ptr ); return NULL; }
ptr->id = result;
ptr->defaultProgram.parentContext = ptr;
// insert context at head of global list
rglCgContextPushFront( ptr );
if ( _cgContextCreateHook ) _cgContextCreateHook( ptr );
return result;
}
CG_API void cgDestroyContext( CGcontext c )
{
// check if context is valid
if ( !CG_IS_CONTEXT( c ) )
{
rglCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
return;
}
_CGcontext* ctx = _cgGetContextPtr( c );
rglCgProgramErase( &ctx->defaultProgram );
// destroy all programs
rglCgProgramDestroyAll( ctx );
// unlink from global CGContext list
_CGcontext * const head = _cgGetContextPtr( _CurrentContext->RGLcgContextHead );
if ( head != ctx )
{
// node is not the head, find and erase it
// find the context that occurs before this one
_CGcontext* ptr = head;
while ( ptr->next != ctx )
ptr = ptr->next;
// relink
ptr->next = ctx->next;
destroy_context( ctx );
}
else
{
// node is the head, erase it
_CGcontext* second = head->next;
destroy_context( head );
// link to second element
if ( second )
_CurrentContext->RGLcgContextHead = second->id;
// nothing left
else
_CurrentContext->RGLcgContextHead = 0;
}
}
CG_API const char* cgGetLastListing( CGcontext c )
{
// check to see if context is a valid one
// NOTE: Cg API Reference docs omit this test for this API function.
if ( !CG_IS_CONTEXT( c ) )
{
rglCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
return NULL;
}
// currently does nothing
// TODO ****************************
return NULL;
}
/*============================================================
CG PROGRAM
============================================================ */
RGL_EXPORT CgprogramHookFunction _cgProgramCreateHook = NULL;
RGL_EXPORT CgprogramHookFunction _cgProgramDestroyHook = NULL;
RGL_EXPORT CgprogramCopyHookFunction _cgProgramCopyHook = NULL;
RGL_EXPORT cgRTCgcCompileHookFunction _cgRTCgcCompileProgramHook = NULL;
RGL_EXPORT cgRTCgcFreeHookFunction _cgRTCgcFreeCompiledProgramHook;
// Program list functions
// The context can contain many programs, but each one is a variable sized
// chunk of data produced by the CGC compiler and loaded at runtime. We cannot
// include intrusive "next" pointers in this data block so instead we'll use
// a non-intrusive list. The list is walked using cgGetFirstProgram() and
// cgGetNextProgram()
void rglCgProgramPushFront( _CGcontext* ctx, _CGprogram* prog )
{
// push the program to the context.
// updates the linked list and program count
// link the node into the head of the list
prog->next = ctx->programList;
ctx->programList = prog;
// update the parent context
prog->parentContext = ctx;
// update the program count
ctx->programCount++;
}
_CGprogram* rglCgProgramFindPrev( _CGcontext* ctx, _CGprogram* prog )
{
// get a pointer the the list element *before* prog in the list
// this allows us to unlink it safely.
_CGprogram* ptr = ctx->programList;
while (ptr && prog != ptr->next )
ptr = ptr->next;
return ptr;
}
inline void rglCgProgramEraseAndFree( _CGprogram* prog )
{
rglCgProgramErase( prog );
free( prog );
}
void rglCgProgramErase( _CGprogram* prog )
{
// remove the program from the linked list and deallocate it's storage.
if ( _cgProgramDestroyHook ) _cgProgramDestroyHook( prog );
switch ( prog->header.profile )
{
case CG_PROFILE_SCE_VP_RSX:
case CG_PROFILE_SCE_FP_RSX:
rglpProgramErase( prog );
break;
default:
// default program
break;
}
// return program and node to free store
if ( prog->id ) rglEraseName( &_CurrentContext->cgProgramNameSpace, (unsigned int)prog->id );
//free ELF data obtained from the runtime converter
if ( prog->runtimeElf )
free( prog->runtimeElf );
// zero out all pointers
memset( prog, 0, sizeof( _CGprogram ) );
}
void rglCgProgramEraseAfter( _CGprogram* prog )
{
// delete the list entry after the current pointer.
// (use in conjunction with find_prev for full effect!)
_CGprogram* next = prog->next;
if ( next )
{
prog->next = next->next;
rglCgProgramEraseAndFree( next );
}
}
void rglCgProgramDestroyAll( _CGcontext* ctx )
{
// destroy all programs in this context
while ( ctx->programList )
{
_CGprogram * p = ctx->programList;
ctx->programList = p->next;
// erase the program
rglCgProgramEraseAndFree( p );
}
}
static inline unsigned int getMatrixRowCount( CGtype type )
{
unsigned int rc = 0;
switch ( type )
{
case CG_FLOAT1x1:
case CG_FLOAT1x2:
case CG_FLOAT1x3:
case CG_FLOAT1x4:
case CG_HALF1x1:
case CG_HALF1x2:
case CG_HALF1x3:
case CG_HALF1x4:
case CG_INT1x1:
case CG_INT1x2:
case CG_INT1x3:
case CG_INT1x4:
case CG_BOOL1x1:
case CG_BOOL1x2:
case CG_BOOL1x3:
case CG_BOOL1x4:
case CG_FIXED1x1:
case CG_FIXED1x2:
case CG_FIXED1x3:
case CG_FIXED1x4:
rc = 1;
break;
case CG_FLOAT2x1:
case CG_FLOAT2x2:
case CG_FLOAT2x3:
case CG_FLOAT2x4:
case CG_HALF2x1:
case CG_HALF2x2:
case CG_HALF2x3:
case CG_HALF2x4:
case CG_INT2x1:
case CG_INT2x2:
case CG_INT2x3:
case CG_INT2x4:
case CG_BOOL2x1:
case CG_BOOL2x2:
case CG_BOOL2x3:
case CG_BOOL2x4:
case CG_FIXED2x1:
case CG_FIXED2x2:
case CG_FIXED2x3:
case CG_FIXED2x4:
rc = 2;
break;
case CG_FLOAT3x1:
case CG_FLOAT3x2:
case CG_FLOAT3x3:
case CG_FLOAT3x4:
case CG_HALF3x1:
case CG_HALF3x2:
case CG_HALF3x3:
case CG_HALF3x4:
case CG_INT3x1:
case CG_INT3x2:
case CG_INT3x3:
case CG_INT3x4:
case CG_BOOL3x1:
case CG_BOOL3x2:
case CG_BOOL3x3:
case CG_BOOL3x4:
case CG_FIXED3x1:
case CG_FIXED3x2:
case CG_FIXED3x3:
case CG_FIXED3x4:
rc = 3;
break;
case CG_FLOAT4x1:
case CG_FLOAT4x2:
case CG_FLOAT4x3:
case CG_FLOAT4x4:
case CG_HALF4x1:
case CG_HALF4x2:
case CG_HALF4x3:
case CG_HALF4x4:
case CG_INT4x1:
case CG_INT4x2:
case CG_INT4x3:
case CG_INT4x4:
case CG_BOOL4x1:
case CG_BOOL4x2:
case CG_BOOL4x3:
case CG_BOOL4x4:
case CG_FIXED4x1:
case CG_FIXED4x2:
case CG_FIXED4x3:
case CG_FIXED4x4:
rc = 4;
break;
default:
break;
}
return rc;
}
CGtype getMatrixRowType( CGtype type )
{
CGtype rt = ( CGtype )0;
switch ( type )
{
case CG_FLOAT1x1:
case CG_FLOAT2x1:
case CG_FLOAT3x1:
case CG_FLOAT4x1:
rt = CG_FLOAT1;
break;
case CG_FLOAT1x2:
case CG_FLOAT2x2:
case CG_FLOAT3x2:
case CG_FLOAT4x2:
rt = CG_FLOAT2;
break;
case CG_FLOAT1x3:
case CG_FLOAT2x3:
case CG_FLOAT3x3:
case CG_FLOAT4x3:
rt = CG_FLOAT3;
break;
case CG_FLOAT1x4:
case CG_FLOAT2x4:
case CG_FLOAT3x4:
case CG_FLOAT4x4:
rt = CG_FLOAT4;
break;
case CG_HALF1x1:
case CG_HALF2x1:
case CG_HALF3x1:
case CG_HALF4x1:
rt = CG_HALF1;
break;
case CG_HALF1x2:
case CG_HALF2x2:
case CG_HALF3x2:
case CG_HALF4x2:
rt = CG_HALF2;
break;
case CG_HALF1x3:
case CG_HALF2x3:
case CG_HALF3x3:
case CG_HALF4x3:
rt = CG_HALF3;
break;
case CG_HALF1x4:
case CG_HALF2x4:
case CG_HALF3x4:
case CG_HALF4x4:
rt = CG_HALF4;
break;
case CG_INT1x1:
case CG_INT2x1:
case CG_INT3x1:
case CG_INT4x1:
rt = CG_INT1;
break;
case CG_INT1x2:
case CG_INT2x2:
case CG_INT3x2:
case CG_INT4x2:
rt = CG_INT2;
break;
case CG_INT1x3:
case CG_INT2x3:
case CG_INT3x3:
case CG_INT4x3:
rt = CG_INT3;
break;
case CG_INT1x4:
case CG_INT2x4:
case CG_INT3x4:
case CG_INT4x4:
rt = CG_INT4;
break;
case CG_BOOL1x1:
case CG_BOOL2x1:
case CG_BOOL3x1:
case CG_BOOL4x1:
rt = CG_BOOL1;
break;
case CG_BOOL1x2:
case CG_BOOL2x2:
case CG_BOOL3x2:
case CG_BOOL4x2:
rt = CG_BOOL2;
break;
case CG_BOOL1x3:
case CG_BOOL2x3:
case CG_BOOL3x3:
case CG_BOOL4x3:
rt = CG_BOOL3;
break;
case CG_BOOL1x4:
case CG_BOOL2x4:
case CG_BOOL3x4:
case CG_BOOL4x4:
rt = CG_BOOL4;
break;
case CG_FIXED1x1:
case CG_FIXED2x1:
case CG_FIXED3x1:
case CG_FIXED4x1:
rt = CG_FIXED1;
break;
case CG_FIXED1x2:
case CG_FIXED2x2:
case CG_FIXED3x2:
case CG_FIXED4x2:
rt = CG_FIXED2;
break;
case CG_FIXED1x3:
case CG_FIXED2x3:
case CG_FIXED3x3:
case CG_FIXED4x3:
rt = CG_FIXED3;
break;
case CG_FIXED1x4:
case CG_FIXED2x4:
case CG_FIXED3x4:
case CG_FIXED4x4:
rt = CG_FIXED4;
break;
default:
break;
}
return rt;
}
// forward declaration for recursion
void AccumulateSizeForParams( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
unsigned int prefixLength, int elementResourceIndex,
size_t* nvParamSize, size_t* nvParamStringsSize, size_t* nvParamOffsetsSize,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount, size_t* nvParamDefaultsSize,
ELF_section_t* consttab );
void AccumulateSizeForParamResource( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
unsigned int prefixLength, int elementResourceIndex,
size_t* nvParamSize, size_t* nvParamStringsSize, size_t* nvParamOffsetsSize,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount, size_t* nvParamDefaultsSize,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterResource* paramResource = ( CgParameterResource* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
unsigned int rowCount = getMatrixRowCount(( CGtype )paramResource->type );
// param
*nvParamSize += sizeof( CgBinaryParameter );
// and for the row params of a matrix
*nvParamSize += rowCount * sizeof( CgBinaryParameter );
// name
*nvParamStringsSize += ( prefixLength + strlen( strtab->data + paramEntry->nameOffset ) + 1 );
// and for each row of a matrix which assumes just an extra 3 characters for [i]
*nvParamStringsSize += ( rowCount * ( prefixLength + strlen( strtab->data + paramEntry->nameOffset ) + 3 + 1 ) );
// semantic
// totally inefficient, but easiest implementation for now is to do the On^2 search
// in the semantic and default tables for the data associated with this parameter
unsigned int entryIndex = paramEntry - paramEntries;
// semantic
char* semantic = NULL;
for ( unsigned int sem = 0; sem < semanticIndexCount; sem++ )
{
CgParameterSemantic* semanticIndex = semanticIndexTable + sem;
if ( semanticIndex->entryIndex == entryIndex )
{
// found the semantic for this parameter
semantic = strtab->data + semanticIndex->semanticOffset;
break;
}
}
if ( semantic )
{
// increment the names size buffer
*nvParamStringsSize += strlen( semantic ) + 1;
}
// default
float* defaults = NULL;
for ( unsigned int def = 0; def < defaultIndexCount; def++ )
{
CgParameterDefaultValue* defaultIndex = defaultIndexTable + def;
if ( defaultIndex->entryIndex == entryIndex )
{
// found the default for this parameter
defaults = ( float* )consttab->data + defaultIndex->defaultValueIndex;
}
}
if (defaults)
{
// defaults are always padded to 4 floats per param
unsigned int defaultsCount = ( rowCount ? rowCount : 1 ) * 4;
*nvParamDefaultsSize += sizeof( float ) * defaultsCount;
}
// offsets
// fragment programs
// do we want this referenced param test???
if (nvParamOffsetsSize && paramEntry->flags & CGPF_REFERENCED )
{
// non varying params
if ( paramEntry->flags & CGPV_CONSTANT || paramEntry->flags & CGPV_UNIFORM )
{
// non sampler types
if ( !isSampler(( CGtype )paramResource->type ) )
{
unsigned short* resPtr = paramResourceTable + paramResource->resource;
if ( elementResourceIndex >= 0 )
{
//advance resPtr to the arrayIndex
int skipCount = elementResourceIndex;
while ( skipCount )
{
resPtr++;
int embeddedCount = *( resPtr++ );
resPtr += embeddedCount;
// if an array of matrices, must skip row parameter resources too
for ( int row = 1; row <= ( int )rowCount; row++ )
{
resPtr++;
int embeddedCount = *( resPtr++ );
resPtr += embeddedCount;
}
skipCount--;
}
//resPtr points to the resource of the arrayIndex
}
if ( !isMatrix(( CGtype )( paramResource->type ) ) )
{
// advance past register to second field that contains embedded count
resPtr++;
unsigned int embeddedConstantCount = *( resPtr++ );
*nvParamOffsetsSize += sizeof( CgBinaryEmbeddedConstant ) + sizeof( unsigned int ) * ( embeddedConstantCount - 1 );
}
else
{
for ( int row = 1; row <= ( int )rowCount; row++ )
{
// advance past register to second field that contains embedded count
resPtr++;
unsigned int embeddedConstantCount = *( resPtr++ );
*nvParamOffsetsSize += sizeof( CgBinaryEmbeddedConstant ) + sizeof( unsigned int ) * ( embeddedConstantCount - 1 );
}
}
}
}
}
// entry cursor
*nextEntry = paramEntry + 1;
}
void AccumulateSizeForParamArray( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
unsigned int prefixLength, int elementResourceIndex,
size_t* nvParamSize, size_t* nvParamStringsSize, size_t* nvParamOffsetsSize,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount, size_t* nvParamDefaultsSize,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterArray* paramArray = ( CgParameterArray* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
// this parameter's entry says it's an array
// this parameter's entry says its packing in the flags
// the next entry specifies its type...
// ... either a list of every element in the array
// ... or a single element in the array to represent all because they are the same
// ... or a single element to specify the layout of the parameter, but not every element has a resource
// this would give the number of intrinsics OR structs, but if base type is struct
// to get the number of parameters, must recurse over struct fields
unsigned int numElements = 1;
for ( unsigned int dim = 0; dim < paramArray->dimensionCount; dim++ )
numElements *= paramArray->dimensions[dim];
unsigned int prefixLengthIn = prefixLength;
// next entry will always be either an intrinsic or a struct
if ( !( paramEntry->flags & CGP_UNROLLED ) ) // one bit is not enough cause there are 3 representations!
{
// all elements are the same so have one entry to represent all of them
CgParameterEntry* elementEntry = paramEntry + 1;
// entries are same type, and packed, so just repeat same param
for ( unsigned int element = 0; element < numElements; element++ )
{
// name length for prefixIn + rest of array name for array + 16 bytes generously for [index]
prefixLength = prefixLengthIn + strlen( strtab->data + paramEntry->nameOffset ) + 16;
// right now i think this is always intrinsics for the not unrolled case, current limitation
AccumulateSizeForParamResource( elementEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, element,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
*nextEntry = *nextEntry - 1; // undo the increment on the next entry for this special case of shared entries
}
*nextEntry = *nextEntry + 1; // redo the increment upon exiting the loop, could be smarter, whatever
}
else if ( paramEntry->flags & CGP_UNROLLED )
{
// here everything is unrolled
CgParameterEntry* elementEntry = paramEntry + 1;
for ( unsigned int element = 0; element < numElements; element++ )
{
// name length for prefixIn + rest of array name for array + 16 bytes generously for [index]
prefixLength = prefixLengthIn + strlen( strtab->data + paramEntry->nameOffset ) + 16;
AccumulateSizeForParams( elementEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, -1,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
elementEntry = *nextEntry;
}
}
}
void AccumulateSizeForParamStruct( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
unsigned int prefixLength, int elementResourceIndex,
size_t* nvParamSize, size_t* nvParamStringsSize, size_t* nvParamOffsetsSize,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount, size_t* nvParamDefaultsSize,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterStructure* paramStruct = ( CgParameterStructure* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
// add prefix of struct name and .
// !!! but what about if we are inside an array??? then no struct name
if ( paramEntry->nameOffset > 0 ) // is this the right test???
{
prefixLength += strlen( strtab->data + paramEntry->nameOffset );
}
prefixLength += 1; // allow for the dot
unsigned short memberCount = paramStruct->memberCount;
CgParameterEntry* memberEntry = paramEntry + 1;
for ( unsigned int member = 0; member < memberCount; member++ )
{
AccumulateSizeForParams( memberEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, -1,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
memberEntry = *nextEntry;
}
}
void AccumulateSizeForParams( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
unsigned int prefixLength, int elementResourceIndex,
size_t* nvParamSize, size_t* nvParamStringsSize, size_t* nvParamOffsetsSize,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount, size_t* nvParamDefaultsSize,
ELF_section_t* consttab )
{
// expand for intrinsics, arrays, and structs
// flags hold which type of parameter it is (along with other information)
switch ( paramEntry->flags & CGP_TYPE_MASK )
{
case CGP_INTRINSIC:
{
//printf("resource --- %p\n", paramEntry);
AccumulateSizeForParamResource( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, elementResourceIndex,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
break;
}
case CGP_ARRAY:
{
//printf("array --- %p\n", paramEntry);
AccumulateSizeForParamArray( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, elementResourceIndex,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
break;
}
case CGP_STRUCTURE:
{
//printf("structure --- %p\n", paramEntry);
AccumulateSizeForParamStruct( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
prefixLength, elementResourceIndex,
nvParamSize, nvParamStringsSize, nvParamOffsetsSize,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount, nvParamDefaultsSize,
consttab );
break;
}
default:
{
break;
}
}
}
// forward declaration for recursion
void PopulateDataForParams( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
int elementResourceIndex, unsigned int* localParamNo, char* nvBinary,
CgBinaryParameter** nvParams, char** nvParamStrings,
CgBinaryEmbeddedConstant** nvParamOffsets, float** nvParamDefaults,
char* prefix, unsigned int prefixLength,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount,
ELF_section_t* consttab );
void PopulateDataForParamResource( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
int elementResourceIndex, unsigned int* localParamNo, char* nvBinary,
CgBinaryParameter** nvParams, char** nvParamStrings,
CgBinaryEmbeddedConstant** nvParamOffsets, float** nvParamDefaults,
char* prefix, unsigned int prefixLength,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterResource* paramResource = ( CgParameterResource* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
unsigned int rowCount = getMatrixRowCount(( CGtype )paramResource->type );
// type
( *nvParams )->type = ( CGtype )( paramResource->type );
// sub type for row params of matrix
for ( int row = 1; row <= ( int )rowCount; row++ )
( *nvParams + row )->type = getMatrixRowType(( CGtype )paramResource->type );
// varying parameters
if (( paramEntry->flags & CGPV_MASK ) == CGPV_VARYING )
{
( *nvParams )->res = ( CGresource )paramResource->resource;
( *nvParams )->resIndex = -1;
}
else if ((( paramEntry->flags & CGPV_MASK ) & CGPV_CONSTANT ) || (( paramEntry->flags & CGPV_MASK ) & CGPV_UNIFORM ) )
{
// sampler types
if ( isSampler(( CGtype )paramResource->type ) )
{
( *nvParams )->res = ( CGresource )paramResource->resource; //should be CG_TEXUNIT0 .. 15
( *nvParams )->resIndex = 0;
}
// nonsampler types
else
{
// CG_B requires support too
for ( int row = 0; row <= ( int )rowCount; row++ )
{
if ( paramResource->type == CGP_SCF_BOOL ) //(paramEntry->flags & CGP_SCFBOOL)
{
( *nvParams + row )->res = CG_B;
( *nvParams )->type = CG_BOOL;
}
else
( *nvParams + row )->res = CG_C;
}
// vertex programs
if (!nvParamOffsets)
{
// element of an array
if ( elementResourceIndex >= 0 )
{
if ( !isMatrix(( CGtype )( paramResource->type ) ) )
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + elementResourceIndex );
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
else if ( paramEntry->flags & CGP_CONTIGUOUS )
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + elementResourceIndex );
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
for ( int row = 1; row <= ( int )rowCount; row++ )
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + elementResourceIndex ) + row - 1;
( *nvParams + row )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
}
else
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + elementResourceIndex * rowCount );
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
for ( int row = 1; row <= ( int )rowCount; row++ )
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + elementResourceIndex * rowCount + row - 1 );
( *nvParams + row )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
}
}
// not array
else
{
if ( !isMatrix(( CGtype )( paramResource->type ) ) )
{
unsigned short tempResIndex = paramResource->resource;
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
else
{
if ( paramEntry->flags & CGP_CONTIGUOUS )
{
unsigned short tempResIndex = paramResource->resource;
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
for ( int row = 1; row <= ( int )rowCount; row++ )
{
unsigned short tempResIndex = paramResource->resource + row - 1;
( *nvParams + row )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
}
else
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource );
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
for ( int row = 1; row <= ( int )rowCount; row++ )
{
unsigned short tempResIndex = *( paramResourceTable + paramResource->resource + row - 1 );
( *nvParams + row )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
}
}
}
}
}
// fragment programs
else
{
unsigned short* resPtr = paramResourceTable + paramResource->resource;
if ( elementResourceIndex >= 0 )
{
// !!! matrices should be handled here too, i think
//advance resPtr to the arrayIndex
int skipCount = elementResourceIndex;
while ( skipCount )
{
resPtr++;
int embeddedCount = *( resPtr++ );
resPtr += embeddedCount;
// if an array of matrices, must skip row parameter resources too
for ( int row = 1; row <= ( int )rowCount; row++ )
{
resPtr++;
int embeddedCount = *( resPtr++ );
resPtr += embeddedCount;
}
skipCount--;
}
//resPtr points to the resource of the arrayIndex
}
if ( !isMatrix(( CGtype )( paramResource->type ) ) )
{
unsigned short tempResIndex = *( resPtr++ );
( *nvParams )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
unsigned int embeddedConstantCount = *( resPtr++ );
( *nvParamOffsets )->ucodeCount = embeddedConstantCount;
unsigned int ec = 0;
for ( ec = 0; ec < embeddedConstantCount; ec++ )
( *nvParamOffsets )->ucodeOffset[ec] = *( resPtr++ );
// set the field in the nv param
( *nvParams )->embeddedConst = ( char* )( *nvParamOffsets ) - nvBinary;
// forward pointer to nvParamOffsets
*nvParamOffsets = ( CgBinaryEmbeddedConstant* )( &(( *nvParamOffsets )->ucodeOffset[ec] ) );
}
else
{
for ( int row = 1; row <= ( int )rowCount; row++ )
{
unsigned short tempResIndex = *( resPtr++ );
( *nvParams + row )->resIndex = ( tempResIndex == 0xFFFF ) ? -1 : ( int )tempResIndex;
unsigned int embeddedConstantCount = *( resPtr++ );
( *nvParamOffsets )->ucodeCount = embeddedConstantCount;
unsigned int ec = 0;
for ( ec = 0; ec < embeddedConstantCount; ec++ )
( *nvParamOffsets )->ucodeOffset[ec] = *( resPtr++ );
// set the field in the nv param
( *nvParams + row )->embeddedConst = ( char* )( *nvParamOffsets ) - nvBinary;
// forward pointer to nvParamOffsets
*nvParamOffsets = ( CgBinaryEmbeddedConstant* )( &(( *nvParamOffsets )->ucodeOffset[ec] ) );
}
}
}
}
}
// RESOURCE DECODING ALGORTITHM END
// name
if ( paramEntry->nameOffset )
{
// copy the name and store its offset in the param
// i think prefix is already handled for this base case
strcpy( *nvParamStrings, strtab->data + paramEntry->nameOffset );
}
// set the name offset in the param, starting from the prefix
char* namebase = NULL;
if ( prefix )
{
( *nvParams )->name = prefix - nvBinary;
namebase = prefix;
}
else
{
( *nvParams )->name = *nvParamStrings - nvBinary;
namebase = *nvParamStrings;
}
// increment the names cursor
*nvParamStrings += strlen( *nvParamStrings ) + 1;
// and now handle the matrix rows
for ( int row = 1; row <= ( int )rowCount; row++ )
{
// copy the name and store its offset in the param
sprintf( *nvParamStrings, "%s[%d]", namebase, row - 1 );
// set the name offset in the param
( *nvParams + row )->name = *nvParamStrings - nvBinary;
// increment the names cursor
*nvParamStrings += strlen( *nvParamStrings ) + 1;
}
// totally inefficient, but easiest implementation for now is to do the On^2 search
// in the semantic and default tables for the data associated with this parameter
unsigned int entryIndex = paramEntry - paramEntries;
// semantic
char* semantic = NULL;
for ( unsigned int sem = 0; sem < semanticIndexCount; sem++ )
{
CgParameterSemantic* semanticIndex = semanticIndexTable + sem;
if ( semanticIndex->entryIndex == entryIndex )
{
// found the semantic for this parameter
semantic = strtab->data + semanticIndex->semanticOffset;
break;
}
}
if ( semantic )
{
// copy the name and store its offset in the param
strcpy( *nvParamStrings, semantic );
// set the name offset in the param (or params if a matrix)
for ( int row = 0; row <= ( int )rowCount; row++ )
( *nvParams )->semantic = *nvParamStrings - nvBinary;
// increment the names cursor
*nvParamStrings += strlen( semantic ) + 1;
}
// default
float* defaults = NULL;
for ( unsigned int def = 0; def < defaultIndexCount; def++ )
{
CgParameterDefaultValue* defaultIndex = defaultIndexTable + def;
if ( defaultIndex->entryIndex == entryIndex ) // found the default for this parameter
defaults = ( float* )consttab->data + defaultIndex->defaultValueIndex;
}
if ( defaults )
{
// defaults are always padded to 4 floats per param
unsigned int defaultsCount = ( rowCount ? rowCount : 1 ) * 4;
// copy the values into the right place in the buffer
memcpy( *nvParamDefaults, defaults, defaultsCount * sizeof( float ) );
// set the field in the param to point to them
if ( rowCount == 0 )
( *nvParams )->defaultValue = ( char* )( *nvParamDefaults ) - nvBinary;
else
{
for ( int row = 1; row <= ( int )rowCount; row++ )
( *nvParams + row )->defaultValue = ( char* )( *nvParamDefaults + 4 * ( row - 1 ) ) - nvBinary;
}
// forward the buffer pointer
*nvParamDefaults += defaultsCount;
}
// fill other fields, once for the matrix and once for every row
for ( int row = 0; row <= ( int )rowCount; row++ )
{
// var
unsigned int variability = paramEntry->flags & CGPV_MASK;
if ( variability == CGPV_VARYING )
( *nvParams )->var = CG_VARYING;
else if ( variability == CGPV_UNIFORM )
( *nvParams )->var = CG_UNIFORM;
else if ( variability == CGPV_CONSTANT )
( *nvParams )->var = CG_CONSTANT;
else if ( variability == CGPV_MIXED )
( *nvParams )->var = CG_MIXED;
else
( *nvParams )->var = ( CGenum )0;
//direction
unsigned int direction = paramEntry->flags & CGPD_MASK;
if ( direction == CGPD_IN )
( *nvParams )->direction = CG_IN;
else if ( direction == CGPD_OUT )
( *nvParams )->direction = CG_OUT;
else if ( direction == CGPD_INOUT )
( *nvParams )->direction = CG_INOUT;
else
( *nvParams )->direction = ( CGenum )0;
// paramno
if ( paramEntry->flags & CGPF_GLOBAL )
( *nvParams )->paramno = -1;
else if ( paramEntry->flags & CGP_INTERNAL )
( *nvParams )->paramno = -2;
else
{
( *nvParams )->paramno = *localParamNo;
if ( row == 0 )
*localParamNo += 1;
}
// isReferenced
( *nvParams )->isReferenced = (( paramEntry->flags & CGPF_REFERENCED ) != 0 );
// isShared
( *nvParams )->isShared = false;
// increment param cursor
*nvParams += 1;
}
// entry cursor
*nextEntry = paramEntry + 1;
}
void PopulateDataForParamArray( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
int elementResourceIndex, unsigned int* localParamNo, char* nvBinary,
CgBinaryParameter** nvParams, char** nvParamStrings,
CgBinaryEmbeddedConstant** nvParamOffsets, float** nvParamDefaults,
char* prefix, unsigned int prefixLength,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterArray* paramArray = ( CgParameterArray* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
// this would give the number of intrinsics OR structs, but if base type is struct
// to get the number of parameters, must recurse over struct fields
unsigned int numElements = 1;
for ( unsigned int dim = 0; dim < paramArray->dimensionCount; dim++ )
numElements *= paramArray->dimensions[dim];
// for name handling
char* prefixIn = prefix;
unsigned int prefixLengthIn = prefixLength;
if ( !( paramEntry->flags & CGP_UNROLLED ) ) // one bit is not enough cause there are 3 representations!
{
// all elements are the same so have one entry to represent all of them
CgParameterEntry* elementEntry = paramEntry + 1;
// entries are same type, and packed, so just repeat same param
for ( unsigned int element = 0; element < numElements; element++ )
{
// where is the array name??? in paramEntry or elementEntry???
// name
// copy prefix if it's not the first element whose prefix is already in place
if ( element > 0 )
{
strncpy( *nvParamStrings, prefixIn, prefixLengthIn );
prefix = *nvParamStrings;
*nvParamStrings += prefixLengthIn;
}
// rest of array name for array and index
sprintf( *nvParamStrings, "%s[%d]", ( strtab->data + paramEntry->nameOffset ), element );
if (!prefix)
prefix = *nvParamStrings;
*nvParamStrings += strlen( *nvParamStrings );
prefixLength = strlen( prefix );
PopulateDataForParamResource( elementEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
element, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
*nextEntry = *nextEntry - 1; // undo the increment on the next entry for this special case of shared entries
}
*nextEntry = *nextEntry + 1; // redo the increment on the next entry upon leaving the loop
}
else if ( paramEntry->flags & CGP_UNROLLED )
{
// here everything is unrolled
CgParameterEntry* elementEntry = paramEntry + 1;
for ( unsigned int element = 0; element < numElements; element++ )
{
// where is the array name??? in paramEntry or elementEntry???
// name
// copy prefix if it's not the first element whose prefix is already in place
if ( element > 0 )
{
strncpy( *nvParamStrings, prefixIn, prefixLengthIn );
prefix = *nvParamStrings;
*nvParamStrings += prefixLengthIn;
}
// rest of array name for array and index
sprintf( *nvParamStrings, "%s[%d]", ( strtab->data + paramEntry->nameOffset ), element );
*nvParamStrings += strlen( *nvParamStrings );
prefixLength = strlen( prefix );
PopulateDataForParams( elementEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
-1, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
elementEntry = *nextEntry;
}
}
}
void PopulateDataForParamStruct( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
int elementResourceIndex, unsigned int* localParamNo, char* nvBinary,
CgBinaryParameter** nvParams, char** nvParamStrings,
CgBinaryEmbeddedConstant** nvParamOffsets, float** nvParamDefaults,
char* prefix, unsigned int prefixLength,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount,
ELF_section_t* consttab )
{
// type index is actually the offset into the block of memory following the entries
CgParameterStructure* paramStruct = ( CgParameterStructure* )(( char* )( paramEntries + paramEntriesCount ) + paramEntry->typeIndex );
unsigned short memberCount = paramStruct->memberCount;
CgParameterEntry* memberEntry = paramEntry + 1;
// set the prefix pointer if it is not already set
if (!prefix)
prefix = *nvParamStrings;
// add prefix of struct name and .
// !!! but what about if we are inside an array??? then no struct name? or is . built in?
if ( paramEntry->nameOffset > 0 ) // is this the right test???
{
// name
unsigned int nameLength = strlen( strtab->data + paramEntry->nameOffset );
// copy the name and store its offset in the param
strcpy( *nvParamStrings, strtab->data + paramEntry->nameOffset );
// increment the names cursor but don't include null term
*nvParamStrings += nameLength;
prefixLength += nameLength;
}
// add the dot
strcpy( *nvParamStrings, "." );
*nvParamStrings += 1;
prefixLength += 1;
for ( unsigned int member = 0; member < memberCount; member++ )
{
// put the prefix in place for each subsequent member
if ( member > 0 )
{
strncpy( *nvParamStrings, prefix, prefixLength );
prefix = *nvParamStrings;
*nvParamStrings += prefixLength;
}
PopulateDataForParams( memberEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
-1, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
memberEntry = *nextEntry;
}
}
void PopulateDataForParams( CgParameterEntry* paramEntry, CgParameterEntry* paramEntries, unsigned int paramEntriesCount,
unsigned short* paramResourceTable, ELF_section_t* strtab, CgParameterEntry** nextEntry,
int elementResourceIndex, unsigned int* localParamNo, char* nvBinary,
CgBinaryParameter** nvParams, char** nvParamStrings,
CgBinaryEmbeddedConstant** nvParamOffsets, float** nvParamDefaults,
char* prefix, unsigned int prefixLength,
CgParameterSemantic* semanticIndexTable, unsigned int semanticIndexCount,
CgParameterDefaultValue* defaultIndexTable, unsigned int defaultIndexCount,
ELF_section_t* consttab )
{
// expand for intrinsics, arrays, and structs
// flags hold which type of parameter it is (along with other information)
switch ( paramEntry->flags & CGP_TYPE_MASK )
{
case CGP_INTRINSIC:
{
//printf("resource --- %p\n", paramEntry);
PopulateDataForParamResource( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
elementResourceIndex, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
break;
}
case CGP_ARRAY:
{
//printf("array --- %p\n", paramEntry);
PopulateDataForParamArray( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
elementResourceIndex, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
break;
}
case CGP_STRUCTURE:
{
//printf("structure --- %p\n", paramEntry);
PopulateDataForParamStruct( paramEntry, paramEntries, paramEntriesCount,
paramResourceTable, strtab, nextEntry,
elementResourceIndex, localParamNo, nvBinary,
nvParams, nvParamStrings, nvParamOffsets, nvParamDefaults,
prefix, prefixLength,
semanticIndexTable, semanticIndexCount,
defaultIndexTable, defaultIndexCount,
consttab );
break;
}
default:
{
break;
}
}
}
bool rglCgCreateProgramChecks( CGcontext ctx, CGprofile profile, CGenum program_type )
{
// check context
if ( !CG_IS_CONTEXT( ctx ) )
{
rglCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
return false;
}
// check the profile.
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
case CG_PROFILE_SCE_FP_RSX:
break;
default:
rglCgRaiseError( CG_UNKNOWN_PROFILE_ERROR );
return false;
}
// check the program_type
switch ( program_type )
{
case CG_BINARY:
case CG_SOURCE:
break;
default: // currently reject CG_OBJECT (assembly code in ASCII) and CG_UNDEFINED
rglCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
return false;
}
return true;
}
bool cgOpenElf( const void *ptr, size_t size, CGELFBinary *elfBinary )
{
while ( 1 )
{
size_t symbolSize;
size_t symbolCount;
const char *symbolstrtab;
const char *symtab = findSymbolSectionInPlace((const char *)ptr, &symbolSize, &symbolCount, &symbolstrtab );
if ( !symtab )
break;
size_t shadertabSize;
const char *shadertab = findSectionInPlace((const char*)ptr, ".shadertab", &shadertabSize );
if ( !shadertab )
break;
size_t strtabSize;
const char *strtab = findSectionInPlace((const char*)ptr, ".strtab", &strtabSize );
if ( !strtab )
break;
size_t consttabSize;
const char *consttab = findSectionInPlace((const char* )ptr, ".const", &consttabSize );
if ( !consttab )
break;
elfBinary->elfFile = ( const char* )ptr;
elfBinary->elfFileSize = size;
elfBinary->symtab = symtab;
elfBinary->symbolSize = symbolSize;
elfBinary->symbolCount = symbolCount;
elfBinary->symbolstrtab = symbolstrtab;
elfBinary->shadertab = shadertab;
elfBinary->shadertabSize = shadertabSize;
elfBinary->strtab = strtab;
elfBinary->strtabSize = strtabSize;
elfBinary->consttab = consttab;
elfBinary->consttabSize = consttabSize;
return true;
}
return false;
}
bool cgGetElfProgramByIndex( CGELFBinary *elfBinary, int index, CGELFProgram *elfProgram )
{
while ( true )
{
char sectionName[64];
sprintf( sectionName, ".text%04i", index );
size_t texttabSize;
const char *texttab = findSectionInPlace( elfBinary->elfFile, sectionName, &texttabSize );
if ( !texttab )
break;
sprintf( sectionName, ".paramtab%04i", index );
size_t paramtabSize;
const char *paramtab = findSectionInPlace( elfBinary->elfFile, sectionName, &paramtabSize );
if ( !paramtab )
break;
elfProgram->texttab = texttab;
elfProgram->texttabSize = texttabSize;
elfProgram->paramtab = paramtab;
elfProgram->paramtabSize = paramtabSize;
elfProgram->index = index;
return true;
}
return false;
}
static bool cgGetElfProgramByName( CGELFBinary *elfBinary, const char *name, CGELFProgram *elfProgram )
{
//if no name try to return the first program
int res;
if (!name || name[0] == '\0' )
res = 0;
else
res = lookupSymbolValueInPlace( elfBinary->symtab, elfBinary->symbolSize, elfBinary->symbolCount, elfBinary->symbolstrtab, name );
if ( res != -1 )
return cgGetElfProgramByIndex( elfBinary, res, elfProgram );
else
return false;
}
CGprogram rglCgCreateProgram( CGcontext ctx, CGprofile profile, const CgProgramHeader *programHeader, const void *ucode, const CgParameterTableHeader *parameterHeader, const char *stringTable, const float *defaultValues )
{
// Create the program structure.
// all the structural data is filled in here,
// as well as the profile.
// The parameters and the actual program are generated from the ABI specific calls.
_CGprogram* prog = ( _CGprogram* )malloc( sizeof( _CGprogram ) );
if ( NULL == prog )
{
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
return NULL;
}
// zero out the fields
memset( prog, 0, sizeof( _CGprogram ) );
// fill in the fields we know
prog->parentContext = _cgGetContextPtr( ctx );
prog->header.profile = profile;
int success = 0;
// create a name for the program and record it in the object
CGprogram id = ( CGprogram )rglCreateName( &_CurrentContext->cgProgramNameSpace, prog );
if ( !id )
{
free( prog );
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
return NULL;
}
prog->id = id;
//hack to counter removal of TypeC during beta
if ( profile == ( CGprofile )7005 )
profile = CG_PROFILE_SCE_VP_RSX;
if ( profile == ( CGprofile )7006 )
profile = CG_PROFILE_SCE_FP_RSX;
// load the binary into the program object
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
// TODO ************** need to include the entry symbol too
success = rglpGenerateVertexProgram( prog, programHeader, ucode, parameterHeader, stringTable, defaultValues );
break;
case CG_PROFILE_SCE_FP_RSX:
success = rglpGenerateFragmentProgram( prog, programHeader, ucode, parameterHeader, stringTable, defaultValues );
break;
default:
// should never reach here
break;
}
// if the creation failed, free all resources.
// the error was raised when the error was encoutered.
if ( success == 0 )
{
// free the program object
free( prog );
// release the id too
rglEraseName( &_CurrentContext->cgProgramNameSpace, (unsigned int)id );
return NULL;
}
// success! add the program to the program list in the context.
rglCgProgramPushFront( prog->parentContext, prog );
if ( _cgProgramCreateHook ) _cgProgramCreateHook( prog );
// everything worked.
return id;
}
CG_API CGprogram cgCreateProgram( CGcontext ctx,
CGenum program_type,
const char* program,
CGprofile profile,
const char* entry,
const char** args )
{
// Load a program from a memory pointer.
// NOTE: in our API all programs are pre-compiled binaries
// so entry point and compiler arguments are ignored.
//hack to counter removal of TypeC during beta
if ( profile == ( CGprofile )7005 )
profile = CG_PROFILE_SCE_VP_RSX;
if ( profile == ( CGprofile )7006 )
profile = CG_PROFILE_SCE_FP_RSX;
//hack to counter change of defines for program_type at r5294
// previously CG_BINARY was defined the same as CG_ROW_MAJOR
// if those values are passed in here, move them to the new values and remove this hack after we have
// an sdk that incorporates these changes so that prebuild libs (aka debugfont) can be used meanwhile
if ( program_type == CG_ROW_MAJOR )
program_type = CG_BINARY;
if ( !rglCgCreateProgramChecks( ctx, profile, program_type ) )
return NULL;
//data to extract from the buffer passed:
CgProgramHeader *programHeader = NULL;
const void *ucode = NULL;
CgParameterTableHeader *parameterHeader = NULL;
const char *stringTable = NULL;
const float *defaultValues = NULL;
//first step, compile any source file
const char *binaryBuffer = NULL;
char* compiled_program = NULL;
if ( program_type == CG_SOURCE )
{
if ( _cgRTCgcCompileProgramHook )
{
_cgRTCgcCompileProgramHook( program, (profile == CG_PROFILE_SCE_FP_RSX) ? "sce_fp_rsx" : "sce_vp_rsx", entry, args, &compiled_program );
if ( !compiled_program )
{
rglCgRaiseError( CG_COMPILER_ERROR );
return NULL;
}
binaryBuffer = compiled_program;
}
else
{
//_RGL_REPORT_EXTRA( RGL_REPORT_CG_ERROR, "The CG runtime compiler hasn't been setup. cgRTCgcInit() should be called prior to this function." );
rglCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
return NULL;
}
}
else
binaryBuffer = program;
bool bConvertedToElf = false;
//At that point we have a binary file which is either any ELF or an NV format file
const unsigned int ElfTag = 0x7F454C46; // == MAKEFOURCC(0x7F,'E','L','F');
if ( !( *( unsigned int* )binaryBuffer == ElfTag ) )
{
//we have an NV file, convert it to the runtime format
// if it was initially binary, throw warning about old format and recommend conversion to new with cgnv2elf
// don't throw the warning if it was source, cause clearly that would have been on purpose.
if ( program_type == CG_BINARY )
{
//_RGL_REPORT_EXTRA( RGL_REPORT_CG_ERROR, "A binary shader is being loaded using a deprecated binary format. Please use the cgnv2elf tool to convert to the new, memory-saving, faster-loading format." );
}
//convert from NV format to the runtime format
int compiled_program_size = 0;
std::vector<char> stringTableArray;
std::vector<float> defaultValuesArray;
CgBinaryProgram* nvProgram = ( CgBinaryProgram* )binaryBuffer;
char *runtimeElfShader = NULL;
//check the endianness
int totalSize;
if (( nvProgram->profile != ( CGprofile )7006 ) && ( nvProgram->profile != ( CGprofile )7005 ) &&
( nvProgram->profile != CG_PROFILE_SCE_FP_RSX ) && ( nvProgram->profile != CG_PROFILE_SCE_VP_RSX ) )
totalSize = endianSwapWord( nvProgram->totalSize );
else
totalSize = nvProgram->totalSize;
int res = convertNvToElfFromMemory( binaryBuffer, totalSize, 2, 0, ( void** ) & runtimeElfShader, &compiled_program_size, stringTableArray, defaultValuesArray );
if (res != 0)
{
//_RGL_REPORT_EXTRA( RGL_REPORT_CG_ERROR, "invalid CG binary program" );
rglCgRaiseError( CG_COMPILER_ERROR );
if ( compiled_program )
_cgRTCgcFreeCompiledProgramHook( compiled_program );
return NULL;
}
if ( compiled_program )
_cgRTCgcFreeCompiledProgramHook( compiled_program );
//TODO: remove all the unnecessary data shuffling, when this gets included in the compiler
//prepare the same buffer as the one that would be extracted from the binary file
size_t stringTableSize = stringTableArray.size() * sizeof( stringTable[0] );
size_t defaultTableSize = defaultValuesArray.size() * sizeof( defaultValues[0] );
int paddedSize = rglPad( compiled_program_size, 4 );
//the following pointer will be remembered in the _CGprogram structure to be freed when the
//program will be deleted, the ucode and the param info are used in place at runtime
char *runtimeElf = ( char* )memalign( 16, paddedSize + stringTableSize + defaultTableSize );
if ( !runtimeElf )
{
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
return NULL;
}
bConvertedToElf = true; //to free the memalign
//program header, ucode and parameters
memcpy( runtimeElf, runtimeElfShader, compiled_program_size );
//we can free the intermediate buffer used to receive the converted program //may be we should keep it rather than reallocating ?
convertNvToElfFreeBinaryShader( runtimeElfShader );
//default values
//the pointer is stored right after the program data ( padded to 4 ) and after the string table pointer
float* pDefaultValues = ( float* )(( char* )runtimeElf + paddedSize );
defaultValues = pDefaultValues;
if ( defaultTableSize )
memcpy( pDefaultValues, &defaultValuesArray[0], defaultTableSize );
//string table
//the pointer is stored right after the program data ( padded to 4 )
char *pStringTable = ( char* )runtimeElf + paddedSize + defaultTableSize;
stringTable = pStringTable;
if ( stringTableSize )
memcpy( pStringTable, &stringTableArray[0], stringTableSize );
//success
programHeader = ( CgProgramHeader* )runtimeElf;
size_t elfUcodeSize = programHeader->instructionCount * 16;
size_t ucodeOffset = rglPad( sizeof( CgProgramHeader ), 16 );
size_t parameterOffset = rglPad( ucodeOffset + elfUcodeSize, 16 );
ucode = ( char* )runtimeElf + ucodeOffset;
parameterHeader = ( CgParameterTableHeader* )(( char* )runtimeElf + parameterOffset );
}
else
{
//we have an ELF file, it will be used in place ( case where the elf file has been passed as a memory pointer )
CGELFBinary elfBinary;
CGELFProgram elfProgram;
/* CG binary not aligned to 16 bytes, needed for ucode section */
if ((( intptr_t )binaryBuffer ) & 15 )
{
rglCgRaiseError( CG_PROGRAM_LOAD_ERROR );
return NULL;
}
bool res = cgOpenElf( binaryBuffer, 0, &elfBinary );
/* Not a valid ELF file */;
if ( !res )
{
rglCgRaiseError( CG_PROGRAM_LOAD_ERROR );
return NULL;
}
/* Couldn't find the shader entry in the CG binary */
if ( !cgGetElfProgramByName( &elfBinary, entry, &elfProgram ) )
return NULL;
programHeader = ( CgProgramHeader* )elfBinary.shadertab + elfProgram.index;
ucode = ( char* )elfProgram.texttab;
parameterHeader = ( CgParameterTableHeader* )elfProgram.paramtab;
stringTable = elfBinary.strtab;
defaultValues = ( float* )elfBinary.consttab;
}
CGprogram prog = rglCgCreateProgram( ctx, profile, programHeader, ucode, parameterHeader, stringTable, defaultValues );
//if we used the runtime compiler/converter we need to delete the buffer on exit
if ( bConvertedToElf )
{
_CGprogram* ptr = _cgGetProgPtr( prog );
ptr->runtimeElf = programHeader;
}
return prog;
}
CG_API CGprogram cgCreateProgramFromFile( CGcontext ctx,
CGenum program_type,
const char* program_file,
CGprofile profile,
const char* entry,
const char** args )
{
//hack to counter removal of TypeC during beta
if ( profile == ( CGprofile )7005 )
profile = CG_PROFILE_SCE_VP_RSX;
if ( profile == ( CGprofile )7006 )
profile = CG_PROFILE_SCE_FP_RSX;
//hack to counter change of defines for program_type at r5294
// previously CG_BINARY was defined the same as CG_ROW_MAJOR
// if those values are passed in here, move them to the new values and remove this hack after we have
// an sdk that incorporates these changes so that prebuild libs (aka debugfont) can be used meanwhile
if ( program_type == CG_ROW_MAJOR )
program_type = CG_BINARY;
if ( !rglCgCreateProgramChecks( ctx, profile, program_type ) )
return NULL;
// load a program from a file
// NOTE: in our API all programs are pre-compiled binaries
// so profile and compiler arguments are ignored.
FILE* fp = NULL;
if ( RGL_LIKELY( program_type == CG_BINARY ) )
{
CGprogram ret = NULL;
_CGcontext *context = _cgGetContextPtr( ctx );
CGprogramGroup group = NULL;
//can we find it in the groups already loaded ?
group = context->groupList;
while ( group )
{
//check the group name
const char *groupName = rglCgGetProgramGroupName( group );
if ( groupName && !strcmp( groupName, program_file ) )
{
int index;
if (!entry)
index = 0;
else
index = rglCgGetProgramIndex( group, entry );
if ( index >= 0 )
{
ret = rglpCgUpdateProgramAtIndex( group, index, 1 );
break;
}
else
{
//we couldn't find the entry in the group which has the right name
return ( CGprogram )NULL;
}
}
group = group->next;
}
if ( ret )
return ret;
else
{
//do we have an elf file ?
//read file tag:
// check that file exists
fp = fopen( program_file, "rb" );
if (!fp)
return ( CGprogram )NULL;
unsigned int filetag = 0;
int res = fread( &filetag, sizeof( filetag ), 1, fp );
if ( !res )
{
fclose( fp );
rglCgRaiseError( CG_FILE_READ_ERROR );
return ( CGprogram )NULL;
}
const unsigned int ElfTag = 0x7F454C46; // == MAKEFOURCC(0x7F,'E','L','F');
if ( filetag == ElfTag )
{
fclose( fp );
group = rglCgCreateProgramGroupFromFile( ctx, program_file );
if ( group )
{
_CGprogramGroup *_group = ( _CGprogramGroup * )group;
_group->userCreated = false;
if (!entry)
{
if ( group->programCount == 1 )
{
ret = rglpCgUpdateProgramAtIndex( group, 0, 1 );
}
}
else
{
int index = rglCgGetProgramIndex( group, entry );
if ( index == -1 )
{
//_RGL_REPORT_EXTRA( RGL_REPORT_CG_ERROR, "couldn't find the shader entry in the CG binary" );
}
else
{
ret = rglpCgUpdateProgramAtIndex( group, index, 1 );
}
}
}
return ret;
}
}
}
//we have a NV file or a CG source:
if ( !fp )
{
fp = fopen( program_file, "rb" );
if (!fp)
return ( CGprogram )NULL;
}
// find the file length
size_t file_size = 0;
fseek( fp, 0, SEEK_END );
file_size = ftell( fp );
rewind( fp );
// alloc memory for the file
char* ptr = ( char* )malloc( file_size + 1 );
if (!ptr)
{
fclose(fp);
return (CGprogram)NULL;
}
// read the entire file into memory then close the file
// TODO ********* just loading the file is a bit lame really. We can do better.
fread( ptr, file_size, 1, fp );
fclose( fp );
if ( program_type == CG_SOURCE )
{
ptr[file_size] = '\0';
}
// call the CreateProgram API to do the rest of the job.
CGprogram ret = cgCreateProgram( ctx, program_type, ptr, profile, entry, args );
// free the memory for the file, we're done with it
free( ptr );
return ret;
}
CG_API CGprogram cgCopyProgram( CGprogram program )
{
// check input parameter
if ( !CG_IS_PROGRAM( program ) )
return NULL;
_CGprogram* prog = _cgGetProgPtr( program );
if (!prog)
return ( CGprogram )NULL;
_CGprogram* newprog;
size_t paddedProgramSize = 0;
size_t ucodeSize = 0;
if (prog->header.profile == CG_PROFILE_SCE_FP_RSX)
{
paddedProgramSize = rglPad( sizeof( _CGprogram ), 16);
ucodeSize = prog->header.instructionCount * 16;
newprog = ( _CGprogram* )malloc( paddedProgramSize + ucodeSize );
}
else
{
newprog = ( _CGprogram* )malloc( sizeof( _CGprogram ) );
}
if ( NULL == newprog )
{
rglCgRaiseError( CG_MEMORY_ALLOC_ERROR );
return ( CGprogram )NULL;
}
memset( newprog, 0, sizeof( _CGprogram ) );
// copy information from the old program
newprog->header.profile = prog->header.profile;
newprog->parentContext = prog->parentContext;
// generate a new id
newprog->id = ( CGprogram )rglCreateName( &_CurrentContext->cgProgramNameSpace, newprog );
// copy all the parameter information
// copy the binary program information
// TODO ******** copy the entire elf here, not just the executable.
int success = 0;
switch ( prog->header.profile )
{
case CG_PROFILE_SCE_VP_RSX:
case CG_PROFILE_SCE_FP_RSX:
success = rglpCopyProgram( prog, newprog );
break;
default:
rglCgRaiseError( CG_UNKNOWN_PROFILE_ERROR );
success = 0;
break;
}
if ( success == 0 )
{
// we failed to create a new program object, clean up
free( newprog );
rglEraseName( &_CurrentContext->cgProgramNameSpace, (unsigned int)newprog->id );
return ( CGprogram )NULL;
}
if (prog->header.profile == CG_PROFILE_SCE_FP_RSX)
{
newprog->ucode = (char*)newprog + paddedProgramSize;
memcpy((char*)newprog->ucode, (char*)prog->ucode, ucodeSize);
}
//handle refcounting for string table and default values
if ( prog->programGroup )
{
//refcount for the string and the default table
newprog->programGroup = prog->programGroup;
newprog->programIndexInGroup = -1;
rglpCgUpdateProgramAtIndex( newprog->programGroup, -1, 1 );
}
// add the new program object to the program list in the context
rglCgProgramPushFront( newprog->parentContext, newprog );
if ( _cgProgramCopyHook ) _cgProgramCopyHook( newprog, prog );
return newprog->id;
}
CG_API void cgDestroyProgram( CGprogram program )
{
// remove the program from the program list
// check the program input
if ( !CG_IS_PROGRAM( program ) )
{
rglCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
return;
}
_CGprogram* ptr = _cgGetProgPtr( program );
if ( NULL == ptr )
{
rglCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
return;
}
if ( ptr->programGroup )
{
if ( !ptr->programGroup->userCreated )
{
if ( ptr->programIndexInGroup != -1 && ptr->programGroup->programs[ptr->programIndexInGroup].refCount == 0 )
{
//not a valid handle, the refcount is 0, this handle has already been freed
rglCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
return;
}
else
{
bool isGroupMember = ( ptr->programIndexInGroup != -1 );
rglpCgUpdateProgramAtIndex( ptr->programGroup, ptr->programIndexInGroup, -1 );
if ( isGroupMember )
return;
//else continue, it needs to be deleted here, the group won't delete it.
//such program which have a programGroup but are not member of the group are
//clone of a program coming from a group. but those program still need the group
//because the group holds their stringtable and their default table.
}
}
}
// get the context that this program belongs to.
_CGcontext* ctx = ptr->parentContext;
// find and unlink the program from the program list
if ( ptr == ctx->programList )
{
// node is the head of the list, so unlink it.
_CGprogram* p = ctx->programList;
ctx->programList = p->next;
// erase the program
rglCgProgramEraseAndFree( p );
}
else
{
// node not the head, so use find_previous and delete_after
// NOTE: if the program is not found in the list, returns silently.
_CGprogram* p = rglCgProgramFindPrev( ctx, ptr );
rglCgProgramEraseAfter( p );
}
return;
}
/*============================================================
CG GL
============================================================ */
/******************************************************************************
*** Profile Functions
*****************************************************************************/
CGGL_API CGbool cgGLIsProfileSupported( CGprofile profile )
{
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
return ( CGbool ) rglpSupportsVertexProgram( profile );
case CG_PROFILE_SCE_FP_RSX:
return ( CGbool ) rglpSupportsFragmentProgram( profile );
default:
return CG_FALSE;
}
}
CGGL_API void cgGLEnableProfile( CGprofile profile )
{
// this is a logical extension to glEnable
RGLcontext* LContext = _CurrentContext;
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
LContext->VertexProgram = GL_TRUE;
LContext->needValidate |= RGL_VALIDATE_VERTEX_PROGRAM;
break;
case CG_PROFILE_SCE_FP_RSX:
{
LContext->FragmentProgram = GL_TRUE;
struct _CGprogram* current = LContext->BoundFragmentProgram;
if ( current )
{
for ( GLuint i = 0; i < current->samplerCount; ++i )
{
int unit = current->samplerUnits[i];
rglTextureImageUnit *unit_ptr = (rglTextureImageUnit*)&_CurrentContext->TextureImageUnits[unit];
unit_ptr->currentTexture = rglGetCurrentTexture( unit_ptr, unit_ptr->fragmentTarget );
}
}
LContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;
}
break;
default:
rglCgRaiseError( CG_INVALID_PROFILE_ERROR );
break;
}
}
CGGL_API void cgGLDisableProfile( CGprofile profile )
{
// this is a logical extension to glDisable
RGLcontext* LContext = _CurrentContext;
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
LContext->VertexProgram = GL_FALSE;
// no need to invalidate textures because they are only available on programmable pipe.
LContext->needValidate |= RGL_VALIDATE_VERTEX_PROGRAM ;
break;
case CG_PROFILE_SCE_FP_RSX:
LContext->FragmentProgram = GL_FALSE;
break;
default:
rglCgRaiseError( CG_INVALID_PROFILE_ERROR );
break;
}
}
CGGL_API CGprofile cgGLGetLatestProfile( CGGLenum profile_type )
{
switch ( profile_type )
{
case CG_GL_VERTEX:
case CG_GL_FRAGMENT:
return rglpGetLatestProfile( profile_type );
default:
rglCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
return CG_PROFILE_UNKNOWN;
}
}
CGGL_API void cgGLSetOptimalOptions( CGprofile profile )
{
// This does nothing because we don't compile at run-time.
}
/******************************************************************************
*** Program Managment Functions
*****************************************************************************/
CGGL_API void cgGLLoadProgram( CGprogram program )
{
// noop since there is integration of cg in js.
}
CGGL_API CGbool cgGLIsProgramLoaded( CGprogram program )
{
// TODO: for now a valid program is always loaded.
return CG_TRUE;
}
CGGL_API void cgGLBindProgram( CGprogram program )
{
_CGprogram* ptr = _cgGetProgPtr( program );
// now do the binding.
switch ( ptr->header.profile )
{
case 7005:
case CG_PROFILE_SCE_VP_RSX:
// the program is a vertex program, just update the GL state
_CurrentContext->BoundVertexProgram = ptr;
// and inform the GL state to re-upload the vertex program
_CurrentContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM;
break;
case 7006:
case CG_PROFILE_SCE_FP_RSX:
_CurrentContext->BoundFragmentProgram = ptr;
// need to revalidate the textures in order to update which targets to fetch from
_CurrentContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;
// TODO: push texture state
// Needs to be done per profile. Can't use glPushAttrib.
// deal with the texture parameters now.
for ( GLuint index = 0; index < ptr->samplerCount; ++index )
{
// walk the array of sampler parameters
CgRuntimeParameter *rtParameter = ptr->runtimeParameters + ptr->samplerIndices[index];
CgParameterResource *parameter = ( CgParameterResource * )( ptr->parameterResources + rtParameter->parameterEntry->typeIndex );
// find out which texture unit this parameter has been assigned to
unsigned int unit = parameter->resource - CG_TEXUNIT0;
_CurrentContext->TextureImageUnits[unit].fragmentTarget = rtParameter->glType;
rglTextureImageUnit *unit_ptr = (rglTextureImageUnit*)&_CurrentContext->TextureImageUnits[unit];
unit_ptr->currentTexture = rglGetCurrentTexture( unit_ptr, unit_ptr->fragmentTarget );
}
break;
default:
rglCgRaiseError( CG_INVALID_PROFILE_ERROR );
return;
}
}
CGGL_API void cgGLUnbindProgram( CGprofile profile )
{
switch ( profile )
{
case CG_PROFILE_SCE_VP_RSX:
_CurrentContext->BoundVertexProgram = NULL;
_CurrentContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM;
// no need to invalidate textures because they are only available on programmable pipe.
break;
case CG_PROFILE_SCE_FP_RSX:
_CurrentContext->BoundFragmentProgram = NULL;
break;
default:
rglCgRaiseError( CG_INVALID_PROFILE_ERROR );
return;
}
}
/******************************************************************************
*** Parameter Managment Functions
*****************************************************************************/
CGGL_API void cgGLSetParameter1f( CGparameter param, float x )
{
// check to see if the parameter is a good one
CgRuntimeParameter *ptr = rglCgGLTestParameter( param );
// otherwise apply the values to the parameter
float v[4] = {x, x, x, x};
ptr->setterIndex( ptr, v, CG_GETINDEX( param ) );
}
CGGL_API void cgGLSetParameter2f( CGparameter param, float x, float y )
{
CgRuntimeParameter *ptr = rglCgGLTestParameter( param );
// otherwise apply the values to the parameter
float v[4] = {x, y, y, y};
ptr->setterIndex( ptr, v, CG_GETINDEX( param ) );
}
CGGL_API void cgGLSetParameterPointer( CGparameter param,
GLint fsize,
GLenum type,
GLsizei stride,
const GLvoid *pointer )
{
CgRuntimeParameter *_ptr = rglCgGLTestParameter( param );
const CgParameterResource *parameterResource = rglGetParameterResource( _ptr->program, _ptr->parameterEntry );
GLuint index = ( GLuint )( parameterResource->resource - CG_ATTR0 );
rglVertexAttribPointerNV(
index, // attribute index
fsize, // element size
type, // GL_FLOAT
( _ptr->parameterEntry->flags & CGP_NORMALIZE ) ? 1 : 0, // from the semantic of the param
stride, // element to element in bytes
pointer ); // data pointer
}
CGGL_API void cgGLEnableClientState( CGparameter param )
{
CgRuntimeParameter *_ptr = rglCgGLTestParameter( param );
const CgParameterResource *parameterResource = rglGetParameterResource( _ptr->program, _ptr->parameterEntry );
GLuint index = ( GLuint )( parameterResource->resource - CG_ATTR0 );
rglEnableVertexAttribArrayNV( index );
}
CGGL_API void cgGLDisableClientState( CGparameter param )
{
CgRuntimeParameter *_ptr = rglCgGLTestParameter( param );
const CgParameterResource *parameterResource = rglGetParameterResource( _ptr->program, _ptr->parameterEntry );
GLuint index = ( GLuint )( parameterResource->resource - CG_ATTR0 );
rglDisableVertexAttribArrayNV( index );
}
/******************************************************************************
*** Matrix Parameter Managment Functions
*****************************************************************************/
CGGL_API void cgGLSetMatrixParameterfc( CGparameter param, const float *matrix )
{
CgRuntimeParameter* ptr = rglCgGLTestParameter( param );
ptr->settercIndex( ptr, matrix, CG_GETINDEX( param ) );
}
CGGL_API void cgGLSetTextureParameter( CGparameter param, GLuint texobj )
{
// According to the cg implementation from nvidia, set just stores the obj.
// Enable does the actual work.
CgRuntimeParameter* ptr = _cgGLTestTextureParameter( param );
ptr->samplerSetter( ptr, &texobj, 0 );
}
CGGL_API GLuint cgGLGetTextureParameter( CGparameter param )
{
CgRuntimeParameter* ptr = _cgGLTestTextureParameter( param );
if (!ptr)
return 0;
if (!(ptr->parameterEntry->flags & CGPF_REFERENCED))
{
rglCgRaiseError( CG_INVALID_PARAMETER_ERROR );
return 0;
}
return *( GLuint* )ptr->pushBufferPointer;
}
CGGL_API void cgGLEnableTextureParameter( CGparameter param )
{
CgRuntimeParameter* ptr = _cgGLTestTextureParameter( param );
ptr->samplerSetter( ptr, NULL, 0 );
}
CGGL_API void cgGLDisableTextureParameter( CGparameter param )
{
if ( _cgGLTestTextureParameter( param ) )
{
// this does not do anything on nvidia's implementation
}
}
/*============================================================
CG NV2ELF
============================================================ */
using namespace cgc::bio;
class CgBaseType
{
public:
CgBaseType() {_type = 0;}
virtual ~CgBaseType() {}
unsigned short _type; //index of the type in the types array of the program if >= STRUCT, else it's a BASETYPE
short _resourceIndex; //index of the type in the types array of the program if >= STRUCT, else it's a BASETYPE
unsigned short _resource; //index of the type in the types array of the program if >= STRUCT, else it's a BASETYPE
};
//array
class CgArrayType : public CgBaseType
{
public:
CgArrayType():_elementType(NULL) { _type = (unsigned char)(CG_ARRAY + 128); }
virtual ~CgArrayType()
{
if (_elementType)
free(_elementType);
}
CgBaseType *_elementType;
unsigned char _dimensionCount;
unsigned short _dimensionItemCountsOffset;
};
//structure
class CgStructureType : public CgBaseType
{
public:
//definitions
class CgStructuralElement
{
public:
char _name[256];
char _semantic[256];
CgBaseType *_type;
unsigned short _flags; //in,out,global,varying,const,uniform,shared, hasDefaults(base types only) -> consecutive reg allocation flag
unsigned short _index; //helper index ?
};
//implementation
CgStructureType()
{
_type = (unsigned char)(CG_STRUCT + 128);
_root = false;
}
virtual ~CgStructureType()
{
int i=0;
int count = (int)_elements.size();
for (i = 0;i < count; i++)
{
if (_elements[i]._type)
free(_elements[i]._type);
}
}
//members
std::vector<CgStructuralElement> _elements;
bool _insideArray; //modifies the way the resources are stored, they should be sent directly to the resources,
//the array is keeping track of the resource offset in that case
bool _root;
};
typedef struct
{
const char *name;
int resIndex;
} BINDLOCATION;
//////////////////////////////////////////////////////////////////////////////
// static arrays containing type information
#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
classname ,
static int classnames[] = {
#include "Cg/cg_datatypes.h"
};
#define CG_DATATYPE_MACRO(name, compiler_name, enum_name, base_enum, nrows, ncols,classname) \
nrows ,
static int rows[] = {
#include "Cg/cg_datatypes.h"
};
typedef float _float4[4];
//////////////////////////////////////////////////////////////////////////////
// static buffers containing the information extracted from the shaders
typedef struct
{
std::vector<short> _resources;
std::vector<unsigned short> _defaultValuesIndices;
std::vector<unsigned short> _elfDefaultsIndices;
std::vector<short> _dimensions;
std::vector<CgParameterSemantic> _semanticIndices;
} _CGNVCONTAINERS;
static bool bIsVertexProgram = true;
//////////////////////////////////////////////////////////////////////////////
// forward declarations
static int getStride(CgBaseType *type);
static int getSizeofSubArray(_CGNVCONTAINERS &containers, int dimensionIndex, int dimensionCount, int endianness);
static unsigned int stringTableFind( std::vector<char> &stringTable, const char* str )
{
const char* data = &stringTable[0];
size_t size = stringTable.size();
const char *end = data + size;
size_t length = strlen(str);
if (length+1 > size)
return 0;
data += length;
const char *p = (char*)memchr(data,'\0',end-data);
while (p && (end-data)>0)
{
if (!memcmp(p - length, str, length))
return (unsigned int)(p - length - &stringTable[0]);
data = p+1;
p = (char*)memchr(data,'\0',end-data);
}
return 0;
}
static unsigned int stringTableAdd( std::vector<char> &stringTable, const char* str )
{
unsigned int ret = (unsigned int)stringTable.size();
if ( ret == 0 )
{
stringTable.push_back('\0'); //add the empty string
ret = 1;
}
size_t stringLength = strlen(str) + 1;
stringTable.resize(ret + stringLength);
memcpy(&stringTable[0] + ret,str,stringLength);
return ret;
}
static unsigned int stringTableAddUnique( std::vector<char> &stringTable, const char* str )
{
if ( stringTable.size() == 0 )
stringTable.push_back('\0'); //add the empty string
unsigned int ret = stringTableFind(stringTable, str);
if (ret == 0 && str[0] != '\0')
ret = stringTableAdd(stringTable, str);
return ret;
}
static unsigned short getFlags(CGenum var, CGenum dir, int no, bool is_referenced, bool is_shared, int paramIndex);
//////////////////////////////////////////////////////////////////////////////
// implementation
static void fillStructureItems(_CGNVCONTAINERS &containers, CgStructureType *structure,
int endianness,
std::vector<CgParameterEntry> &parameterEntries,
std::vector<char> &parameterResources, std::vector<char> &stringTable, unsigned short *arrayResourceIndex,
unsigned short *arrayDefaultValueIndex);
#define swap16(v) ((v>>16) | (v<<16))
int convertNvToElfFromMemory(const void *sourceData, size_t size, int endianness, int constTableOffset, void **binaryShader, int *binarySize,
std::vector<char> &stringTable, std::vector<float> &defaultValues)
{
_CGNVCONTAINERS containers;
unsigned char elfEndianness = endianness; //used in the macro CNVEND
nvb_reader* nvbr = 0;
bin_io::instance()->new_nvb_reader( &nvbr );
CGBIO_ERROR err = nvbr->loadFromString((const char*)sourceData,size);
if (err != CGBIO_ERROR_NO_ERROR)
return -1;
//flag which tells if we have to swap data coming from nv and going to elf
bool doSwap = !(nvbr->endianness() == (HOST_ENDIANNESS)elfEndianness);
CGprofile NVProfile = nvbr->profile();
if (NVProfile == CG_PROFILE_SCE_VP_RSX)
bIsVertexProgram = true;
else if (NVProfile == CG_PROFILE_SCE_FP_RSX)
bIsVertexProgram = false;
else
{
//RGL_ASSERT2(0,("error: unknown shader profile\n"));
return -1;
}
//Fill the shader header structure and save it into the shadertab
CgProgramHeader cgShader;
memset(&cgShader,0,sizeof(CgProgramHeader));
cgShader.profile = CNV2END((unsigned short) NVProfile); //ok cnv2end here we go from platform endiannes to elf endiannes
cgShader.compilerVersion = 0;//TODO
if (bIsVertexProgram)
{
const CgBinaryVertexProgram *nvVertex = nvbr->vertex_program();
if (doSwap) //here we go directly from nv to elf endiannes without going to the platform endianness
{
cgShader.instructionCount = ENDSWAP(nvVertex->instructionCount);
cgShader.attributeInputMask = ENDSWAP(nvVertex->attributeInputMask);
//vertex program specific
cgShader.vertexProgram.instructionSlot = ENDSWAP(nvVertex->instructionSlot);
cgShader.vertexProgram.registerCount = ENDSWAP(nvVertex->registerCount);
cgShader.vertexProgram.attributeOutputMask = ENDSWAP(nvVertex->attributeOutputMask);
}
else
{
cgShader.instructionCount = nvVertex->instructionCount;
cgShader.attributeInputMask = nvVertex->attributeInputMask;
//vertex program specific
cgShader.vertexProgram.instructionSlot = nvVertex->instructionSlot;
cgShader.vertexProgram.registerCount = nvVertex->registerCount;
cgShader.vertexProgram.attributeOutputMask = nvVertex->attributeOutputMask;
}
}
else
{
const CgBinaryFragmentProgram *nvFragment = nvbr->fragment_program();
unsigned char flags;
if (doSwap)
{
cgShader.instructionCount = ENDSWAP(nvFragment->instructionCount);
cgShader.attributeInputMask = ENDSWAP(nvFragment->attributeInputMask);
//fragment program specific
cgShader.fragmentProgram.partialTexType = ENDSWAP(nvFragment->partialTexType);
cgShader.fragmentProgram.texcoordInputMask = ENDSWAP(nvFragment->texCoordsInputMask);
cgShader.fragmentProgram.texcoord2d = ENDSWAP(nvFragment->texCoords2D);
cgShader.fragmentProgram.texcoordCentroid = ENDSWAP(nvFragment->texCoordsCentroid);
cgShader.fragmentProgram.registerCount = ENDSWAP(nvFragment->registerCount);
flags =
(nvFragment->outputFromH0 ? CGF_OUTPUTFROMH0 : 0) |
(nvFragment->depthReplace ? CGF_DEPTHREPLACE : 0) |
(nvFragment->pixelKill ? CGF_PIXELKILL : 0);
}
else
{
cgShader.instructionCount = nvFragment->instructionCount;
cgShader.attributeInputMask = nvFragment->attributeInputMask;
//fragment program specific
cgShader.fragmentProgram.partialTexType = nvFragment->partialTexType;
cgShader.fragmentProgram.texcoordInputMask = nvFragment->texCoordsInputMask;
cgShader.fragmentProgram.texcoord2d = nvFragment->texCoords2D;
cgShader.fragmentProgram.texcoordCentroid = nvFragment->texCoordsCentroid;
cgShader.fragmentProgram.registerCount = nvFragment->registerCount;
flags =
(nvFragment->outputFromH0 ? CGF_OUTPUTFROMH0 : 0) |
(nvFragment->depthReplace ? CGF_DEPTHREPLACE : 0) |
(nvFragment->pixelKill ? CGF_PIXELKILL : 0);
}
cgShader.fragmentProgram.flags = CNV2END(flags);
}
//shader specific info ( the shader header is in the shader tab )
//ucode
uint32_t *tmp = (uint32_t *)nvbr->ucode();
const char *ucode;
uint32_t *buffer = NULL;
if (doSwap)
{
int size = (int)nvbr->ucode_size()/sizeof(uint32_t);
buffer = (uint32_t*)malloc(size * sizeof(uint32_t));
for (int i = 0; i < size; i++)
{
uint32_t val = ENDSWAP(tmp[i]);
if (!bIsVertexProgram)
val = swap16(val);
buffer[i] = val;
}
ucode = (const char*)buffer;
}
else
{
ucode = (const char*)tmp;
// !!!xxx this is to workaround what appears to be a linux platform specific bug
// that manifests as a memory overwrite in properly allocated memory during a std::vector resize
int size = (int)nvbr->ucode_size()/sizeof(uint32_t);
buffer = (uint32_t*)malloc(size * sizeof(uint32_t));
for (int i = 0; i < size; i++)
buffer[i] = tmp[i];
ucode = (const char*)buffer;
// end workaround
}
int ucodeSize = nvbr->ucode_size();
//ucode, ucodeSize;
//RGL_ASSERT2(CNV2END(cgShader.instructionCount)*16 ==nvbr->ucode_size(),("%i, %i",CNV2END(cgShader.instructionCount)*16,nvbr->ucode_size()));
//parameters
CgStructureType root;
root._insideArray = false;
root._root = true;
//we don't have the names of the user structures, so to share structures we will have to rely on the
//layout of the data, we might do mistakes, but it's not important, since what we want to represent is
//the structure layout.
//parameters
//the parameters represents a hierarchical structure, I am reconstructing this structure
int paramIndex = -1;
//just for sanity check purpose: it's use to detect when there's a change of structural element to reset the embedded constant counter
CgStructureType::CgStructuralElement *current = NULL;
int embeddedConstants = 0;
bool rootChildDefaultValues = false;
int rootChildIndex = -1;
char currentRefStructureName[256];
currentRefStructureName[0] = '\0';
int i;
for (i = 0; i < (int)nvbr->number_of_params(); i++)
{
CGtype type;
CGresource res;
CGenum var;
int rin;
const char *name;
std::vector<float> dv;
std::vector<unsigned int> ec;
const char *sem;
CGenum dir;
int no;
bool is_referenced;
bool is_shared;
nvbr->get_param( i, type, res, var, rin, &name, dv, ec, &sem, dir, no, is_referenced, is_shared );
const char *parameterName = name;
const char *structureEnd = NULL;
CgStructureType *container = &root;
int done = 0;
const char * structureStart = parameterName;
while (!done && *structureStart)
{
// process the next structural element
structureEnd = strpbrk(structureStart, ".[");
if (structureEnd)
{
//there's a special case for sampler, we don't support arrays of sampler, so unrolled the array here
//and don't even register the structure as being an array
if (*structureEnd == '[' && type >= CG_SAMPLER1D && type <= CG_SAMPLERCUBE)
{
//do we have an array of samplers and are we at the end ?
const char *closed = strchr(structureEnd, ']');
//RGL_ASSERT2(closed,("name error for parameter %s",parameterName));
const char *somethingElse = strpbrk(closed, ".[");
if (!somethingElse)
structureEnd = NULL; //so we are at the end we are in an array of samplers
}
}
// test if we are done finding structural information
if (!structureEnd)
{
//set structureEnd correctly so that the rest of the function performs correctly
structureEnd = structureStart + strlen(structureStart);
//set the done flag to exit the loop
done = 1;
}
//extract the current structural element name
char structName[256];
int length = (int)(structureEnd - structureStart);
strncpy(structName, structureStart, length);
structName[length] = '\0';
//do we already have it in the structure ?
CgStructureType::CgStructuralElement *structuralElement = NULL;
int j=0;
int elementCount = (int)container->_elements.size();
for (j=0;j<elementCount;j++)
{
structuralElement = &container->_elements[j];
if (!strcmp(structuralElement->_name,structName))
{
//same name, we need to check that this is the same scope ( program versus global )
if ( (no == -1 && (structuralElement->_flags & CGPF_GLOBAL)) ||
(no != -1 && !(structuralElement->_flags & CGPF_GLOBAL)))
break;
}
}
if (j==elementCount)
{
if (container == &root)
{
if (rootChildIndex != j)
{
rootChildDefaultValues = false;
rootChildIndex = j;
}
}
//we don't have it yet, add it
container->_elements.resize(elementCount+1);
structuralElement = &container->_elements[elementCount];
strncpy(structuralElement->_name,structName,sizeof(structuralElement->_name));
structuralElement->_name[sizeof(structuralElement->_name)-1] = '\0';
structuralElement->_flags = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
int dimensionCount = 0;
if (strncmp(sem, "COLOR", 5) == 0 || strncmp(sem, "NORMAL", 6) == 0)
structuralElement->_flags |= CGP_NORMALIZE;
//check if we have a structure or an array of structure or an array of basetypes
//then:
// - create an array type if we have an array
// - update the container if we have a structure or an array of structure
int isStructure = (*structureEnd == '.');
if (*structureEnd == '[')
{
dimensionCount++;
const char *arrayEnd = strchr(structureEnd,']')+1;
while (*arrayEnd == '[')
{
arrayEnd = strchr(arrayEnd,']')+1;
dimensionCount++;
}
if (*arrayEnd == '.')
isStructure = true;
}
//we have an array, create an array type
if (dimensionCount)
{
CgArrayType *arrayType = new CgArrayType;
arrayType->_dimensionCount = dimensionCount;
arrayType->_dimensionItemCountsOffset = (unsigned short)containers._dimensions.size();
//initialize the dimension count;
int k;
for (k=0;k<dimensionCount;k++)
containers._dimensions.push_back(CNV2END((short)0));
structuralElement->_type = arrayType;
if (isStructure)
{
//we have a structure inside the array
container = new CgStructureType;
container->_insideArray = true;
arrayType->_elementType = container;
}
else
{
//we have a base type inside the array
arrayType->_elementType = new CgBaseType;
arrayType->_elementType->_type = type - CG_TYPE_START_ENUM;
}
arrayType->_elementType->_resource = res;
arrayType->_elementType->_resourceIndex = -1;
structuralElement->_type->_resourceIndex = (int)containers._resources.size();
structuralElement->_type->_resource = res;
}
else
{
//we have a structure
if (isStructure)
{
bool insideArray = container->_insideArray;
container = new CgStructureType;
container->_insideArray = insideArray;
structuralElement->_type = container;
}
else
{
//assign the basetype and resource
structuralElement->_type = new CgBaseType;
structuralElement->_type->_type = type - CG_TYPE_START_ENUM;
structuralElement->_type->_resource = res;
if (classnames[structuralElement->_type->_type-1] == CG_PARAMETERCLASS_MATRIX)
{
//TODO, Cedric: for vertexprogram assume the matrices are always fully allocated and that they have consecuttive assignements
if (bIsVertexProgram)
{
structuralElement->_type->_resourceIndex = (short)rin;
}
else
structuralElement->_type->_resourceIndex = (int)containers._resources.size();
}
else
{
if (!container->_insideArray)
{
//vertex program
if (bIsVertexProgram)
structuralElement->_type->_resourceIndex = rin;
else
{
//fragment program
if (structuralElement->_flags & CGPV_VARYING)
structuralElement->_type->_resourceIndex = -1;
else
{
structuralElement->_type->_resourceIndex = (int)containers._resources.size();
containers._resources.push_back(CNV2END((unsigned short)rin));
int size = (int)ec.size();
containers._resources.push_back(CNV2END((unsigned short)size));
int k;
for (k=0;k<size;k++)
containers._resources.push_back(CNV2END((unsigned short)ec[k]));
}
}
}
else
{
//will be done later
structuralElement->_type->_resourceIndex = (short)-1;
structuralElement->_type->_resource = (unsigned short)res;
}
}
}
}
}
else
{
if (structuralElement->_type->_type == CG_STRUCT+128)
{
container = (CgStructureType*)structuralElement->_type;
}
else if (structuralElement->_type->_type == CG_ARRAY+128)
{
CgArrayType *arrayType = (CgArrayType *)structuralElement->_type;
if (arrayType->_elementType->_type >128 )
{
if (arrayType->_elementType->_type != CG_STRUCT+128) //we can't have arrays of arrays
{
//RGL_ASSERT2(0,("arrays of arrays not supported"));
}
container = (CgStructureType*)arrayType->_elementType;
}
}
}
//default values should only be at the root of the hierarchical parameter structure
//RGL_ASSERT2(!rootChildDefaultValues || dv.size(),("all the parameter below a root child should have a default value"));
//if (dv.size() && !rootChildDefaultValues)
if (dv.size())
{
int size = (int)containers._defaultValuesIndices.size();
if (!size || (containers._defaultValuesIndices[size-2] != CNV2END((unsigned short)(rootChildIndex))))
{
//assert for default values on structures:
//now supported:
/*RGL_ASSERT2(structuralElement->_type->_type != CG_STRUCT + 128,("default values on structure not yet supported"));
RGL_ASSERT2(structuralElement->_type->_type != CG_ARRAY + 128 ||
((CgArrayType *)structuralElement->_type)->_elementType->_type != CG_STRUCT + 128,
("default values on arrays of structures not yet supported"));*/
//push the index of the parameter in the global structure
containers._defaultValuesIndices.push_back(CNV2END((unsigned short)(rootChildIndex)));
//push the index of the default value ( could add the size if necessary )
containers._defaultValuesIndices.push_back(CNV2END((unsigned short)defaultValues.size()));
}
}
//this case is for when we have item within a struct which is in an array, we don't duplicate the struct
//but we still need to save the resources at the array level, so with this code when we reach the end of the parameter
//name, we only need to save the resources
if (container->_insideArray && done)
{
//the reason of the reset of the flag is because the first item of the array might unreferenced
//so it doesn't has the correct resource type
if (is_referenced)
{
structuralElement->_flags = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
}
if (bIsVertexProgram)
containers._resources.push_back(CNV2END((unsigned short)rin));
else
{
//fragment program
if (structuralElement->_flags & CGPV_VARYING)
containers._resources.push_back(CNV2END((unsigned short)rin));
else
{
containers._resources.push_back(CNV2END((unsigned short)rin));
int size = (int)ec.size();
containers._resources.push_back(CNV2END((unsigned short)size));
int k;
for (k=0;k<size;k++)
containers._resources.push_back(CNV2END((unsigned short)ec[k]));
}
}
}
//if we have an array we need to update the item counts and skip the array for the parsing of the name of the structure
CgArrayType *arrayType = NULL;
if (*structureEnd == '[')
{
int arrayCellIndex = 0;
const char *arrayStart = structureEnd;
const char *arrayEnd = structureEnd;
CgBaseType *itemType = structuralElement->_type;
if (itemType->_type >= 128)
{
arrayType = (CgArrayType *)itemType;
int dimensionCount = 0;
while (*arrayStart == '[' && dimensionCount<arrayType->_dimensionCount)
{
arrayEnd = strchr(arrayStart+1,']');
int length =(int)(arrayEnd - arrayStart - 1);
char indexString[16];
strncpy(indexString,arrayStart+1,length);
indexString[length] = '\0';
int index = atoi(indexString);
int dim = CNV2END(containers._dimensions[arrayType->_dimensionItemCountsOffset + dimensionCount]);
if ((index+1) > dim)
containers._dimensions[arrayType->_dimensionItemCountsOffset + dimensionCount] = CNV2END((short)(index+1));
arrayCellIndex += index*getSizeofSubArray(containers,arrayType->_dimensionItemCountsOffset + dimensionCount,arrayType->_dimensionCount - dimensionCount -1,endianness);
arrayStart = arrayEnd+1;
dimensionCount++;
}
structureEnd = arrayStart;
itemType = arrayType->_elementType;
if (itemType->_type<128)
{
int rowCount = rows[itemType->_type-1];
if (!rowCount) //no matrix here
{
containers._resources.push_back(CNV2END((unsigned short)rin));
//fragment program
if (!bIsVertexProgram)
{
int size = (int)ec.size();
containers._resources.push_back(CNV2END((unsigned short)size));
int k;
for (k=0;k<size;k++)
containers._resources.push_back(CNV2END((unsigned short)ec[k]));
}
//RGL_ASSERT2(*arrayStart == '\0',("internal error: matrix type expected"));
done = 1;
}
}
if (*arrayStart == '\0')
{
done = 1;
}
}
//if we still have some [ , it means we have a matrix
if (*structureEnd == '[')
{
int dimensionCount = 0;
while (*arrayStart == '[')
{
arrayEnd = strchr(arrayStart+1,']');
if (itemType->_type <128)//matrix
{
if (structuralElement != current)
{
embeddedConstants = 0;
current = structuralElement;
}
int length =(int)(arrayEnd - arrayStart - 1);
char indexString[16];
strncpy(indexString,arrayStart+1,length);
indexString[length] = '\0';
//TODO: Cedric: for vertex program and matrices not in array, I am not adding any resource:
if (bIsVertexProgram)
{
if (arrayType)
{
//add the resource index ... scary
containers._resources.push_back(CNV2END((unsigned short)rin));
}
}
else
{
containers._resources.push_back(CNV2END((unsigned short)rin));
int size = (int)ec.size();
containers._resources.push_back(CNV2END((unsigned short)size));
int k;
for (k=0;k<size;k++)
containers._resources.push_back(CNV2END((unsigned short)ec[k]));
embeddedConstants += k+1;
}
//RGL_ASSERT2(*(arrayEnd+1) == '\0',("internal error: matrix type expected, the parameter specifier is longer than expected"));
done = 1;
}
arrayStart = arrayEnd+1;
dimensionCount++;
}
structureEnd = arrayEnd;
}
// matrix single or array or array of matrix: the flags should be the same for all the referenced items
if (is_referenced)
{
unsigned short flag = getFlags(var,dir,no,is_referenced,is_shared,paramIndex);
structuralElement->_flags = flag;
structuralElement->_type->_resource = res;
//if array
if (arrayType)
arrayType->_elementType->_resource = res;
}
}
if (done && dv.size())
{
//very difficult to check that there is no mistake done... should do a diff with
//containers._defaultValuesIndices[containers._defaultValuesIndices.size()-1] and the current parameter position in
//the structure
for ( int jj = 0; jj < (int)dv.size(); ++jj )
defaultValues.push_back(dv[jj]);
}
if (done)
{
if (strlen(sem))
strncpy(structuralElement->_semantic,sem,sizeof(structuralElement->_semantic));
else
structuralElement->_semantic[0] = '\0';
}
structureStart = structureEnd+1;
}
}
//we are done with the reader
nvbr->release();
//release singleton
bin_io::delete_instance();
//print the structure that we have extracted
//transform the extracted structure into the file format
std::vector<CgParameterEntry> parameterEntries;
std::vector<char> parameterResources;
fillStructureItems(containers,&root,endianness,parameterEntries,parameterResources,stringTable, NULL, NULL);
//save it
CgParameterTableHeader header;
memset(&header,0,sizeof(CgParameterTableHeader));
header.entryCount = (unsigned short)parameterEntries.size();
header.resourceTableOffset = sizeof(CgParameterTableHeader) + (unsigned short)(parameterEntries.size()*sizeof(parameterEntries[0]) + parameterResources.size()*sizeof(parameterResources[0]));
header.defaultValueIndexTableOffset = header.resourceTableOffset + (unsigned short)containers._resources.size() * sizeof(containers._resources[0]);
header.defaultValueIndexCount = (unsigned short)containers._elfDefaultsIndices.size()/2;
header.semanticIndexTableOffset = header.defaultValueIndexTableOffset + (unsigned short)containers._elfDefaultsIndices.size() * sizeof (containers._elfDefaultsIndices[0]);
header.semanticIndexCount = (unsigned short)containers._semanticIndices.size();
header.entryCount = CNV2END(header.entryCount);
header.resourceTableOffset = CNV2END(header.resourceTableOffset);
header.defaultValueIndexTableOffset = CNV2END(header.defaultValueIndexTableOffset);
header.defaultValueIndexCount = CNV2END(header.defaultValueIndexCount);
header.semanticIndexTableOffset = CNV2END(header.semanticIndexTableOffset);
header.semanticIndexCount = CNV2END(header.semanticIndexCount);
//fill the parameter section
size_t parameterTableSize = sizeof(CgParameterTableHeader);
parameterTableSize += (unsigned int)parameterEntries.size() * sizeof(parameterEntries[0]);
parameterTableSize += (unsigned int)parameterResources.size() * sizeof(parameterResources[0]);
parameterTableSize += (unsigned int)containers._resources.size() * sizeof(containers._resources[0]);
//parameterTableSize += (unsigned int)containers._defaultValuesIndices.size() * sizeof(containers._defaultValuesIndices[0]);
parameterTableSize += (unsigned int)containers._elfDefaultsIndices.size() * sizeof(containers._elfDefaultsIndices[0]);
parameterTableSize += (unsigned int)containers._semanticIndices.size() * sizeof(containers._semanticIndices[0]);
//Allocate the binary file
int ucodeOffset = (((sizeof(CgProgramHeader)-1)/16)+1)*16;
size_t programSize = ucodeOffset + ucodeSize + parameterTableSize;
char *program = (char*)malloc(programSize * sizeof(char));
//header
memcpy(program,&cgShader,sizeof(CgProgramHeader));
if (ucodeOffset-sizeof(CgProgramHeader))
memset(program+sizeof(CgProgramHeader),0,ucodeOffset-sizeof(CgProgramHeader));
//ucode
memcpy(program + ucodeOffset,ucode,ucodeSize);
if (buffer)
free(buffer);
//parameters
char *parameterOffset = program + ucodeOffset + ucodeSize;
size_t datasize;
memcpy(parameterOffset,&header,sizeof(CgParameterTableHeader));
parameterOffset += sizeof(CgParameterTableHeader);
datasize = parameterEntries.size() * sizeof(parameterEntries[0]);
memcpy(parameterOffset, &parameterEntries[0], datasize);
parameterOffset += datasize;
datasize = parameterResources.size() * sizeof(parameterResources[0]);
memcpy(parameterOffset, &parameterResources[0], datasize);
parameterOffset += datasize;
datasize = containers._resources.size() * sizeof(containers._resources[0]);
memcpy(parameterOffset, &containers._resources[0], datasize);
parameterOffset += datasize;
datasize = containers._elfDefaultsIndices.size() * sizeof(containers._elfDefaultsIndices[0]);
memcpy(parameterOffset, &containers._elfDefaultsIndices[0], datasize);
parameterOffset += datasize;
datasize = containers._semanticIndices.size() * sizeof(containers._semanticIndices[0]);
memcpy(parameterOffset, &containers._semanticIndices[0], datasize);
parameterOffset += datasize;
//RGL_ASSERT2(parameterOffset == program + programSize,("error\n"));
//set the return values
*binarySize = (int)programSize;
*binaryShader = program;
return 0;
}
int convertNvToElfFreeBinaryShader(void *binaryShader)
{
char *program = (char *)binaryShader;
if (program)
free(program);
return 0;
}
static void pushbackUnsignedShort(std::vector<char> &parameterResources, unsigned short value)
{
size_t size = parameterResources.size();
parameterResources.resize(size + 2);
*((unsigned short*)&parameterResources[size]) = value;
}
//This function fill the parameter tables that will be written to the files
static void fillStructureItems(_CGNVCONTAINERS &containers, CgStructureType *structure, int endianness,
std::vector<CgParameterEntry> &parameterEntries,std::vector<char> &parameterResources,
std::vector<char> &stringTable, unsigned short *arrayResourceIndex, unsigned short *arrayDefaultValueIndex)
{
unsigned char elfEndianness = endianness; //used in the macro CNVEND
int currentDefaultIndex = 0;
int count = (int)structure->_elements.size();
int i;
for (i=0;i<count;i++)
{
CgStructureType::CgStructuralElement *structuralElement = &structure->_elements[i];
size_t size = parameterEntries.size();
parameterEntries.resize(size+1);
CgParameterEntry *parameterEntry = &parameterEntries[size];
parameterEntry->nameOffset = CNV2END((int)stringTableAddUnique(stringTable, structuralElement->_name));
if (structuralElement->_semantic[0])
{
CgParameterSemantic semantic;
semantic.entryIndex = CNV2END((unsigned short)size);
semantic.reserved = 0;
semantic.semanticOffset = CNV2END((int)stringTableAddUnique(stringTable, structuralElement->_semantic));
containers._semanticIndices.push_back(semantic);
}
parameterEntry->flags = CNV2END(structuralElement->_flags);
//prepare the typeIndex
unsigned short typeIndex = ((unsigned short)parameterResources.size());
//RGL_ASSERT2((typeIndex&3) == 0,("typeindex must be aligned on 4 bytes"));
parameterEntry->typeIndex = CNV2END(typeIndex);
CgBaseType *itemType = structuralElement->_type;
unsigned short _resource = itemType->_resource;
unsigned short _resourceIndex = itemType->_resourceIndex;
//will contain the parameterEntry for this item, for simple type arrays the value will be corrected
//parameterEntry.size() is not parameterEntryIndex in that case.
int parameterEntryIndex;
if (itemType->_type-128 == CG_ARRAY)
{
CgArrayType *arrayType = (CgArrayType *)structuralElement->_type;
int arraySize = getSizeofSubArray(containers,arrayType->_dimensionItemCountsOffset,arrayType->_dimensionCount,endianness);
itemType = arrayType->_elementType;
//RGL_ASSERT2(!(itemType->_type +CG_TYPE_START_ENUM >= CG_SAMPLER1D && itemType->_type +CG_TYPE_START_ENUM<= CG_SAMPLERCUBE),("array of samplers not yet supported"));
unsigned short arrayFlag = CGP_ARRAY;
parameterEntry->flags |= CNV2END(arrayFlag);
parameterResources.resize(typeIndex+sizeof(CgParameterArray));
CgParameterArray *parameterArray = (CgParameterArray *)(&parameterResources[typeIndex]);
if (itemType->_type-128 == CG_STRUCT )
parameterArray->arrayType = CNV2END((unsigned short)CG_STRUCT);
else
parameterArray->arrayType = CNV2END(itemType->_type+128);
parameterArray->dimensionCount = CNV2END((unsigned short)arrayType->_dimensionCount);
int j;
for (j=0;j<arrayType->_dimensionCount;j++)
{
pushbackUnsignedShort(parameterResources,(unsigned short)containers._dimensions[arrayType->_dimensionItemCountsOffset+j]); //already endian-swapped
}
//padding
if (arrayType->_dimensionCount&1)
pushbackUnsignedShort(parameterResources,CNV2END((unsigned short)0));
//unroll the array of structure here, create one structure for each item of the array
if (itemType->_type-128 == CG_STRUCT )
{
unsigned short unrolledFlag = CGP_UNROLLED;
parameterEntry->flags |= CNV2END(unrolledFlag);
CgStructureType *structureType = (CgStructureType*)itemType;
int k;
//those lines won't work for arrays of structures containing arrays
unsigned short _arrayResourceIndex = (unsigned short)(arrayType->_resourceIndex);
unsigned short _arrayDefaultValueIndex = 0;
bool hasDefaults = false;
if (structure->_root && containers._defaultValuesIndices.size())
{
if (containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
{
hasDefaults = true;
_arrayDefaultValueIndex = containers._defaultValuesIndices[currentDefaultIndex*2+1];
//containers._defaultValuesIndices[currentDefaultIndex*2] = CNV2END((unsigned short)(parameterEntries.size() - 1));
//currentDefaultIndex++;
}
}
for (k=0;k<arraySize;k++)
{
size_t size = parameterEntries.size();
parameterEntries.resize(size+1);
CgParameterEntry &parameterArrayEntry = parameterEntries[size];
char buffer[256];
sprintf(buffer,"%s[%i]",structuralElement->_name,k);
parameterArrayEntry.nameOffset = CNV2END((int)stringTableAddUnique(stringTable, buffer));
parameterArrayEntry.flags = CNV2END(structuralElement->_flags);
unsigned short structureFlag = CGP_STRUCTURE;
parameterArrayEntry.flags |= CNV2END(structureFlag);
unsigned short arrayEntryTypeIndex = (unsigned short)parameterResources.size();
//RGL_ASSERT2((arrayEntryTypeIndex&3) == 0,("typeindex must be aligned on 4 bytes"));
parameterResources.resize(arrayEntryTypeIndex+sizeof(CgParameterStructure));
parameterArrayEntry.typeIndex = CNV2END(arrayEntryTypeIndex);
CgParameterStructure *parameterStructure = (CgParameterStructure*)(&parameterResources[arrayEntryTypeIndex]);
parameterStructure->memberCount = CNV2END((unsigned short)structureType->_elements.size());
parameterStructure->reserved = CNV2END((unsigned short)0);
if (hasDefaults)
fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable,&_arrayResourceIndex,&_arrayDefaultValueIndex);
else
fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable,&_arrayResourceIndex, NULL);
}
if (hasDefaults)
{
//should check this value, but it's not really easy, need the entire size of the complex structure in registers
//unsigned short ArrayBaseTypeCount = _arrayDefaultValueIndex -containers._defaultValuesIndices[currentDefaultIndex*2+1];
currentDefaultIndex++;
}
//containers._defaultValuesIndices[currentDefaultIndex*2] = CNV2END((unsigned short)(parameterEntries.size() - 1));
//currentDefaultIndex++;
//default value expansion: we have an array of struct: we have one default value index for the array
//since the array is unrolled we don't give any resource to the array, but instead we give the resource to each item of each structure
//those items can be arrays or structure themselves
//we have already output the content of the array continue to the next structure item
continue;
}
else
{
//create the type item following the array , the type section will fill typeindex value
size_t size = parameterEntries.size();
parameterEntries.resize(size+1);
parameterEntry = &parameterEntries[size];
parameterEntry->nameOffset = CNV2END(0);
parameterEntry->flags = CNV2END(structuralElement->_flags);
//prepare a typeIndex for this type
typeIndex = ((unsigned short)parameterResources.size());
//RGL_ASSERT2((typeIndex&3) == 0,("typeindex must be aligned on 4 bytes"));
parameterEntry->typeIndex = CNV2END(typeIndex);
//keep the parameterEntryIndex to assign the default values to the proper item
parameterEntryIndex = (int)size - 1;
}
}
else
{
unsigned short contiguousFlag = CGP_CONTIGUOUS;
parameterEntry->flags |= CNV2END(contiguousFlag);
size_t size = parameterEntries.size();
parameterEntryIndex = (int)size - 1;
}
if (itemType->_type<128)
{
parameterResources.resize(typeIndex+sizeof(CgParameterResource));
CgParameterResource *parameterResource = (CgParameterResource*)(&parameterResources[typeIndex]);
if (itemType->_type + CG_TYPE_START_ENUM == CG_BOOL) // checking _resource == CG_B only works if referenced
{
parameterResource->type = CNV2END((unsigned short)CGP_SCF_BOOL);
}
else
{
parameterResource->type = CNV2END((unsigned short)(itemType->_type + CG_TYPE_START_ENUM));
}
if ((structuralElement->_flags & CGPV_MASK) == CGPV_UNIFORM || (structuralElement->_flags & CGPV_MASK) == CGPV_CONSTANT)
{
//use the resource index of the array if we are in an array: pbl with arrays containing samplers...
if (itemType->_type +CG_TYPE_START_ENUM >= CG_SAMPLER1D && itemType->_type +CG_TYPE_START_ENUM<= CG_SAMPLERCUBE)
parameterResource->resource = CNV2END(_resource); //sampler we need the texture unit
else
{
if (arrayResourceIndex)
{
unsigned short tmp = *arrayResourceIndex;
unsigned short localflags = CNV2END(parameterEntry->flags);
if (!bIsVertexProgram)
{
parameterResource->resource = CNV2END(tmp);
int embeddedConstantCount = CNV2END(containers._resources[tmp+1]);
(*arrayResourceIndex) = tmp+1+1+embeddedConstantCount;
if (embeddedConstantCount == 0 && (CNV2END(containers._resources[tmp]) == 0))
{
//we are unrolling an array of struct, so we get individual items
//we should mark if they are referenced individually
if (parameterResource->resource == 0xffff)
localflags &= ~CGPF_REFERENCED;
}
}
else
{
parameterResource->resource = containers._resources[tmp];//_resources is already converted
(*arrayResourceIndex) = tmp+1;
//we are unrolling an array of struct, so we get individual items
//we should mark if they are referenced individually
if (parameterResource->resource == 0xffff)
localflags &= ~CGPF_REFERENCED;
}
//update the flags
parameterEntry->flags = CNV2END(localflags);
}
else
parameterResource->resource = CNV2END(_resourceIndex); //uniform not a sampler we need the register
}
}
else
{
//RGL_ASSERT2((structuralElement->_flags & CGPV_MASK) == CGPV_VARYING,("assumed varying"));
//RGL_ASSERT2(arrayResourceIndex == NULL,("varying within array of structures not yet supported"));
parameterResource->resource = CNV2END(itemType->_resource); //we need the bind location for the varying
}
if (containers._defaultValuesIndices.size())
{
if (structure->_root)
{
//TODO: what about default values on structures at the root level, the structure will flatten out so the default should go on the individual items
//but they are not at root so the indices cannot be represented right now I guess.
//semantics and default values indices need to be updated:
//we are trying to find the index in the sorted array of default value indices, if we are already at the end of the array we stop here
if (currentDefaultIndex < (int)(containers._defaultValuesIndices.size()/2) && containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
{
containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
containers._elfDefaultsIndices.push_back(containers._defaultValuesIndices[currentDefaultIndex*2+1]);
//containers._defaultValuesIndices[currentDefaultIndex*2] = CNV2END((unsigned short)(parameterEntries.size() - 1));
currentDefaultIndex++;
}
}
else if (arrayDefaultValueIndex)
{
//we are in a struct or in an array of struct which has a top level default, each time we have a basic type we need to increase
//the index
containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
containers._elfDefaultsIndices.push_back(*arrayDefaultValueIndex);
//increment the default values index by the number of defaultValues slot used by this type
int typeRegisterCount = getStride(itemType);
*arrayDefaultValueIndex = CNV2END( (unsigned short)((CNV2END((*arrayDefaultValueIndex)))+typeRegisterCount*4));
//no handling of the currentDefaultIndex, which is only valid at root.
}
}
}
else if (itemType->_type == CG_STRUCT + 128)
{
//RGL_ASSERT2(arrayResourceIndex == NULL,("struct within array of structures not yet supported"));
unsigned short structureFlag = CGP_STRUCTURE;
parameterEntry->flags |= CNV2END(structureFlag);
CgStructureType *structureType = (CgStructureType*)itemType;
parameterResources.resize(typeIndex+sizeof(CgParameterStructure));
CgParameterStructure *parameterStructure = (CgParameterStructure*)(&parameterResources[typeIndex]);
parameterStructure->memberCount = CNV2END((unsigned short)structureType->_elements.size());
parameterStructure->reserved = CNV2END((unsigned short)0);
fillStructureItems(containers,structureType,endianness,parameterEntries,parameterResources,stringTable, NULL, NULL);
if (containers._defaultValuesIndices.size() && structure->_root)
{
if (currentDefaultIndex < (int)(containers._defaultValuesIndices.size()/2) && containers._defaultValuesIndices[currentDefaultIndex*2] == CNV2END((unsigned short)i))
{
containers._elfDefaultsIndices.push_back(CNV2END((unsigned short)(parameterEntryIndex)));
containers._elfDefaultsIndices.push_back(containers._defaultValuesIndices[currentDefaultIndex*2+1]);
currentDefaultIndex++;
}
}
}
}
//RGL_ASSERT2(!structure->_root || currentDefaultIndex == (int)containers._defaultValuesIndices.size()/2,("error\n"));
}
static int getStride(CgBaseType *type)
{
if (type->_type <128)
{
if (classnames[type->_type-1] == CG_PARAMETERCLASS_MATRIX)
return rows[type->_type-1];
else
return 1;
}
else
{
if (type->_type == CG_STRUCT + 128)
{
CgStructureType *structureType = (CgStructureType *)type;
int res = 0;
int i;
int count = (int)structureType->_elements.size();
for (i=0;i<count;i++)
res += getStride(structureType->_elements[i]._type);
return res;
}
else
{
//RGL_ASSERT2(0,("arrays of arrays not supported"));
return -9999999;
}
//if array ??
}
}
static int getSizeofSubArray(_CGNVCONTAINERS &containers, int dimensionIndex, int dimensionCount, int endianness)
{
unsigned char elfEndianness = endianness; //used in the macro CNVEND
int res = 1;
int i;
for ( i=0; i < dimensionCount; i++)
res *= (int)CNV2END(containers._dimensions[dimensionIndex + i]);
return res;
}
unsigned short getFlags(CGenum var, CGenum dir, int no, bool is_referenced, bool is_shared, int paramIndex)
{
//the following is error prone, use 2 enum array to do the matching instead
//variability
uint16_t flags = 0;
if (var == CG_VARYING)
flags |= CGPV_VARYING;
else if (var == CG_UNIFORM)
flags |= CGPV_UNIFORM;
else if (var == CG_CONSTANT)
flags |= CGPV_CONSTANT;
else if (var == CG_MIXED)
flags |= CGPV_MIXED;
//direction
if (dir == CG_IN)
flags |= CGPD_IN;
else if (dir == CG_OUT)
flags |= CGPD_OUT;
else if (dir == CG_INOUT)
flags |= CGPD_INOUT;
//boolean
if (is_referenced)
flags |= CGPF_REFERENCED;
//is it a global parameter ?
if (no == -1)
flags |= CGPF_GLOBAL;
else if (no == -2)
flags |= CGP_INTERNAL;
else
{
//RGL_ASSERT2(no>=0,("error\n"));
/*if ( no == paramIndex ) //means that a previous parameter already had the same param index, it's an aggregated type
flags |= CGP_AGGREGATED;*/
paramIndex = no;
}
return flags;
}
/*============================================================
CGBIO
============================================================ */
namespace cgc {
namespace bio {
bin_io* bin_io::instance_ = 0;
bin_io::bin_io()
{
}
bin_io::bin_io( const bin_io& )
{
}
const bin_io*
bin_io::instance()
{
if (instance_ == 0)
instance_ = new bin_io;
return instance_;
}
void
bin_io::delete_instance()
{
if (instance_ != 0)
{
free(instance_);
instance_ = 0;
}
}
const char*
bin_io::error_string( CGBIO_ERROR error ) const
{
switch ( error )
{
case CGBIO_ERROR_NO_ERROR:
return "No error";
case CGBIO_ERROR_LOADED:
return "Binary file has been loaded earlier";
case CGBIO_ERROR_FILEIO:
return "File input output error";
case CGBIO_ERROR_FORMAT:
return "File format error";
case CGBIO_ERROR_INDEX:
return "Index is out of range";
case CGBIO_ERROR_MEMORY:
return "Can't allocate memory";
case CGBIO_ERROR_RELOC:
return "Relocation error";
case CGBIO_ERROR_SYMBOL:
return "Symbol error";
case CGBIO_ERROR_UNKNOWN_TYPE:
return "Uknown type";
default:
return "Unknown error";
}
return "Unknown error";
}
CGBIO_ERROR
bin_io::new_nvb_reader( nvb_reader** obj ) const
{
CGBIO_ERROR ret = CGBIO_ERROR_NO_ERROR;
*obj = new nvb_reader_impl;
if (*obj == 0)
ret = CGBIO_ERROR_MEMORY;
return ret;
}
} // bio namespace
} // cgc namespace
using std::fill_n;
/*============================================================
NVBI IMPLEMENTATION
============================================================ */
namespace cgc {
namespace bio {
nvb_reader_impl::nvb_reader_impl()
{
ref_count_ = 1;
offset_ = 0;
image_ = 0;
owner_ = false;
loaded_ = false;
endianness_ = host_endianness();
std::fill_n( (char*)( &header_ ), sizeof( header_ ), '\0' );
}
nvb_reader_impl::~nvb_reader_impl()
{
if (image_ != 0)
free(image_);
}
ptrdiff_t
nvb_reader_impl::reference() const
{
return ++ref_count_;
}
ptrdiff_t
nvb_reader_impl::release() const
{
ptrdiff_t ret = --ref_count_;
if (ref_count_ == 0)
delete this;
return ret;
}
CGBIO_ERROR
nvb_reader_impl::loadFromString( const char* source, size_t length)
{
if ( loaded_ )
return CGBIO_ERROR_LOADED;
CGBIO_ERROR ret = CGBIO_ERROR_NO_ERROR;
while (1)
{
if (length < sizeof( header_ ))
{
ret = CGBIO_ERROR_FORMAT;
break;
}
memcpy(&header_ ,source,sizeof( header_ ));
if ( CG_BINARY_FORMAT_REVISION != header_.binaryFormatRevision )
{
endianness_ = ( CGBIODATALSB == endianness_ ) ? CGBIODATAMSB : CGBIODATALSB;
int binaryRevision = convert_endianness( header_.binaryFormatRevision, endianness_ );
if ( CG_BINARY_FORMAT_REVISION != binaryRevision )
{
ret = CGBIO_ERROR_FORMAT;
break;
}
}
image_ = (char*)malloc(length * sizeof(char));
memcpy(image_, source , length);
loaded_ = true;
ret = CGBIO_ERROR_NO_ERROR;
break;
}
return ret;
}
bool
nvb_reader_impl::is_loaded() const
{
return loaded_;
}
unsigned char
nvb_reader_impl::endianness() const
{
return endianness_;
}
CGprofile
nvb_reader_impl::profile() const
{
return (CGprofile) convert_endianness( (unsigned int) header_.profile, endianness() );
}
unsigned int
nvb_reader_impl::revision() const
{
return convert_endianness( header_.binaryFormatRevision, endianness() );
}
unsigned int
nvb_reader_impl::size() const
{
return convert_endianness( header_.totalSize, endianness() );
}
unsigned int
nvb_reader_impl::number_of_params() const
{
return convert_endianness( header_.parameterCount, endianness() );
}
unsigned int
nvb_reader_impl::ucode_size() const
{
return convert_endianness( header_.ucodeSize, endianness() );
}
const char*
nvb_reader_impl::ucode() const
{
if (image_ == 0 || ucode_size() == 0)
return 0;
return ( image_ + convert_endianness( header_.ucode, endianness() ) );
}
const CgBinaryFragmentProgram*
nvb_reader_impl::fragment_program() const
{
if (image_ == 0)
return 0;
return (CgBinaryFragmentProgram*)( &image_[convert_endianness( header_.program, endianness_ )] );
}
const CgBinaryVertexProgram*
nvb_reader_impl::vertex_program() const
{
if (image_ == 0)
return 0;
return (CgBinaryVertexProgram*)( &image_[convert_endianness( header_.program, endianness_ )] );
}
CGBIO_ERROR
nvb_reader_impl::get_param_name( unsigned int index, const char **name, bool& is_referenced ) const
{
if (index >= number_of_params())
return CGBIO_ERROR_INDEX;
if (image_ == 0)
return CGBIO_ERROR_NO_ERROR;
const CgBinaryParameter* params = (CgBinaryParameter*)( &image_[convert_endianness( header_.parameterArray, endianness_ )] );
const CgBinaryParameter& pp = params[index];
is_referenced = convert_endianness( pp.isReferenced, endianness() ) != 0;
CgBinaryStringOffset nm_offset = convert_endianness( pp.name,endianness() );
if (nm_offset != 0)
*name = &image_[nm_offset];
else
*name = "";
return CGBIO_ERROR_NO_ERROR;
}
CGBIO_ERROR
nvb_reader_impl::get_param( unsigned int index,
CGtype& type,
CGresource& resource,
CGenum& variability,
int& resource_index,
const char ** name,
std::vector<float>& default_value,
std::vector<unsigned int>& embedded_constants,
const char ** semantic,
CGenum& direction,
int& paramno,
bool& is_referenced,
bool& is_shared ) const
{
if (index >= number_of_params())
return CGBIO_ERROR_INDEX;
if (image_ == 0)
return CGBIO_ERROR_NO_ERROR;
const CgBinaryParameter* params = (CgBinaryParameter*)( &image_[convert_endianness( header_.parameterArray, endianness_ )] );
const CgBinaryParameter& pp = params[index];
type = static_cast<CGtype>( convert_endianness( static_cast<unsigned int>( pp.type ), endianness() ) );
resource = static_cast<CGresource>( convert_endianness( static_cast<unsigned int>( pp.res ), endianness() ) );
variability = static_cast<CGenum>( convert_endianness( static_cast<unsigned int>( pp.var ), endianness() ) );
resource_index = convert_endianness( pp.resIndex, endianness() );
direction = static_cast<CGenum>( convert_endianness( static_cast<unsigned int>( pp.direction ), endianness() ) );
paramno = convert_endianness( pp.paramno, endianness() );
is_referenced = convert_endianness( pp.isReferenced, endianness() ) != 0;
is_shared = false;
CgBinaryStringOffset nm_offset = convert_endianness( pp.name, endianness() );
CgBinaryFloatOffset dv_offset = convert_endianness( pp.defaultValue, endianness() );
CgBinaryEmbeddedConstantOffset ec_offset = convert_endianness( pp.embeddedConst, endianness() );
CgBinaryStringOffset sm_offset = convert_endianness( pp.semantic, endianness() );
if (nm_offset != 0)
*name = &image_[nm_offset];
else
*name = "";
if (sm_offset != 0)
*semantic = &image_[sm_offset];
else
*semantic = "";
if (dv_offset != 0)
{
char *vp = &image_[dv_offset];
for (int ii = 0; ii < 4; ++ii)
{
int tmp;
memcpy(&tmp,vp+4*ii,4);
tmp = convert_endianness(tmp,endianness());
float tmp2;
memcpy(&tmp2,&tmp,4);
default_value.push_back( tmp2 );
}
}
if (ec_offset != 0)
{
void *vp = &image_[ec_offset];
CgBinaryEmbeddedConstant& ec = *(static_cast<CgBinaryEmbeddedConstant*>( vp ));
for (unsigned int ii = 0; ii < convert_endianness( ec.ucodeCount, endianness() ); ++ii)
{
unsigned int offset = convert_endianness( ec.ucodeOffset[ii], endianness() );
embedded_constants.push_back( offset );
}
}
return CGBIO_ERROR_NO_ERROR;
}
}
}