#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <float.h>

#include <cell/gcm.h>

#include <sys/sys_time.h>

#include "rgl.h"
#include "private.h"
#include <ppu_intrinsics.h>

#include "cg.h"
#include <Cg/cgc.h>
#include <Cg/cgGL.h>
#include "readelf.h"
#include "cgnv2rt.h"

#include <cell/sysmodule.h>

#include "../../../compat/strl.h"
#include "../../../general.h"

#define RGL_ALIGN_FAST_TRANSFER 128

#define ENDIAN_32(X, F) ((F) ? endianSwapWord(X) : (X))
#define SWAP_IF_BIG_ENDIAN(arg) endianSwapWordByHalf(arg)

#define ROW_MAJOR 0
#define COL_MAJOR 1

#define pad(x, pad) (((x) + (pad) - 1 ) / (pad) * (pad))

#define gmmAddressToOffset(address, isMain) ((isMain) ? (address)-pGmmMainAllocator->memoryBase : (address)-pGmmLocalAllocator->memoryBase)

#define GL_UNSIGNED_SHORT_8_8		GL_UNSIGNED_SHORT_8_8_SCE
#define GL_UNSIGNED_SHORT_8_8_REV	GL_UNSIGNED_SHORT_8_8_REV_SCE
#define GL_UNSIGNED_INT_16_16		GL_UNSIGNED_INT_16_16_SCE
#define GL_UNSIGNED_INT_16_16_REV	GL_UNSIGNED_INT_16_16_REV_SCE

#define DECLARE_C_TYPES \
DECLARE_TYPE(GL_BYTE,GLbyte,127.f) \
DECLARE_TYPE(GL_UNSIGNED_BYTE,GLubyte,255.f) \
DECLARE_TYPE(GL_SHORT,GLshort,32767.f) \
DECLARE_TYPE(GL_UNSIGNED_SHORT,GLushort,65535.f) \
DECLARE_TYPE(GL_INT,GLint,2147483647.f) \
DECLARE_TYPE(GL_UNSIGNED_INT,GLuint,4294967295.0) \
DECLARE_TYPE(GL_FIXED,GLfixed,65535.f)

#define DECLARE_UNPACKED_TYPES \
DECLARE_UNPACKED_TYPE(GL_BYTE) \
DECLARE_UNPACKED_TYPE(GL_UNSIGNED_BYTE) \
DECLARE_UNPACKED_TYPE(GL_SHORT) \
DECLARE_UNPACKED_TYPE(GL_UNSIGNED_SHORT) \
DECLARE_UNPACKED_TYPE(GL_INT) \
DECLARE_UNPACKED_TYPE(GL_UNSIGNED_INT) \
DECLARE_UNPACKED_TYPE(GL_HALF_FLOAT_ARB) \
DECLARE_UNPACKED_TYPE(GL_FLOAT) \
DECLARE_UNPACKED_TYPE(GL_FIXED)


#define DECLARE_PACKED_TYPES \
DECLARE_PACKED_TYPE_AND_REV_2(UNSIGNED_BYTE,4,4) \
DECLARE_PACKED_TYPE_AND_REV_2(UNSIGNED_BYTE,6,2) \
DECLARE_PACKED_TYPE_AND_REV_3(UNSIGNED_BYTE,3,3,2) \
DECLARE_PACKED_TYPE_AND_REV_4(UNSIGNED_BYTE,2,2,2,2) \
DECLARE_PACKED_TYPE_AND_REV_2(UNSIGNED_SHORT,12,4) \
DECLARE_PACKED_TYPE_AND_REV_2(UNSIGNED_SHORT,8,8) \
DECLARE_PACKED_TYPE_AND_REV_3(UNSIGNED_SHORT,5,6,5) \
DECLARE_PACKED_TYPE_AND_REV_4(UNSIGNED_SHORT,4,4,4,4) \
DECLARE_PACKED_TYPE_AND_REV_4(UNSIGNED_SHORT,5,5,5,1) \
DECLARE_PACKED_TYPE_AND_REV_2(UNSIGNED_INT,16,16) \
DECLARE_PACKED_TYPE_AND_REV_4(UNSIGNED_INT,8,8,8,8) \
DECLARE_PACKED_TYPE_AND_REV_4(UNSIGNED_INT,10,10,10,2)

#define DECLARE_FORMATS \
DECLARE_FORMAT(GL_RGB,3) \
DECLARE_FORMAT(GL_BGR,3) \
DECLARE_FORMAT(GL_RGBA,4) \
DECLARE_FORMAT(GL_BGRA,4) \
DECLARE_FORMAT(GL_ABGR,4) \
DECLARE_FORMAT(GL_ARGB_SCE,4) \
DECLARE_FORMAT(GL_RED,1) \
DECLARE_FORMAT(GL_GREEN,1) \
DECLARE_FORMAT(GL_BLUE,1) \
DECLARE_FORMAT(GL_ALPHA,1)

enum
{
   M10 = 1, M20, M30,
   M01, M11, M21, M31,
   M02, M12, M22, M32,
   M03, M13, M23, M33
};

static int _RGLInitCompleted = 0;
static char *_RGLVendorString = "Retro Arch";
static char *_RGLRendererString = "RGL";
static char *_RGLExtensionsString = "";
static char *_RGLVersionNumber = "1.0";

PSGLcontext* _CurrentContext = NULL;
RGLcontextHookFunction _RGLContextCreateHook = NULL;
RGLcontextHookFunction _RGLContextDestroyHook = NULL;

GmmAllocator *pGmmLocalAllocator = NULL;
GmmAllocator *pGmmMainAllocator = NULL;
static volatile uint32_t *pLock = NULL;
static uint32_t cachedLockValue = 0;
static GmmFixedAllocData *pGmmFixedAllocData = NULL;
GLuint nvFenceCounter = 0;

#define CAPACITY_INCR 16
#define NAME_INCREMENT 4

#define DECLARE_TYPE(TYPE,CTYPE,MAXVAL) \
typedef CTYPE type_##TYPE; \
static inline type_##TYPE _RGLFloatTo_##TYPE(float v) { return (type_##TYPE)(_RGLClampf(v)*MAXVAL); } \
static inline float _RGLFloatFrom_##TYPE(type_##TYPE v) { return ((float)v)/MAXVAL; }
DECLARE_C_TYPES
#undef DECLARE_TYPE

typedef GLfloat type_GL_FLOAT;

static inline type_GL_FLOAT _RGLFloatTo_GL_FLOAT(float v)
{
   return v;
}

static inline float _RGLFloatFrom_GL_FLOAT(type_GL_FLOAT v)
{
   return v;
}

typedef GLhalfARB type_GL_HALF_FLOAT_ARB;

static const char *_getStringTable(const Elf32_Ehdr *ehdr)
{
   const char *sectionHeaderStart = (const char*)ehdr + ehdr->e_shoff;
   const Elf32_Shdr *shstrtabHeader = (const Elf32_Shdr*)sectionHeaderStart + ehdr->e_shstrndx;
   return (const char*)ehdr + shstrtabHeader->sh_offset;
}

const char *findSectionInPlace(const char* memory,unsigned int /*size*/,const char *name, size_t *sectionSize)
{
   const Elf32_Ehdr *ehdr = (const Elf32_Ehdr*)memory;
	
   const char *shstrtab = _getStringTable(ehdr);

   size_t sectionCount = ehdr->e_shnum;
   const char *sectionHeaderStart = (const char*)ehdr + ehdr->e_shoff;

   for(size_t i=0; i < sectionCount; i++)
   {
      const Elf32_Shdr *sectionHeader = (const Elf32_Shdr *)sectionHeaderStart + i;
      const char *sectionName = shstrtab + sectionHeader->sh_name;
      if (!strcmp(name,sectionName))
      {
         *sectionSize = sectionHeader->sh_size;
	 return (const char*)ehdr + sectionHeader->sh_offset;
      }
   }
   return NULL;
}

const char *findSymbolSectionInPlace(const char *memory, unsigned int /*size*/, size_t *symbolSize, size_t *symbolCount, const char **symbolstrtab)
{
   const Elf32_Ehdr *ehdr = (const Elf32_Ehdr*)memory;
	
   size_t sectionCount = ehdr->e_shnum;
   const char *sectionHeaderStart = (const char*)ehdr + ehdr->e_shoff;

   for(size_t i = 0; i<sectionCount; i++)
   {
      const Elf32_Shdr *sectionHeader = (const Elf32_Shdr *)sectionHeaderStart + i;
      if (sectionHeader->sh_type == SHT_SYMTAB)
      {
         *symbolSize = sectionHeader->sh_entsize;
	 *symbolCount = sectionHeader->sh_size / sectionHeader->sh_entsize;

	 const Elf32_Shdr *symbolStrHeader = (const Elf32_Shdr *)sectionHeaderStart + sectionHeader->sh_link;
	 *symbolstrtab = (const char*)ehdr + symbolStrHeader->sh_offset;
	 return (const char*)ehdr + sectionHeader->sh_offset;
      }
   }
   return NULL;
}


int lookupSymbolValueInPlace(const char* symbolSection, size_t symbolSize, size_t symbolCount, const char *symbolstrtab, const char *name)
{
   for (size_t i = 0; i < symbolCount; i++)
   {
      Elf32_Sym* elf_sym = (Elf32_Sym*)symbolSection;

      if (!strcmp(symbolstrtab + elf_sym->st_name, name))
         return elf_sym->st_value;

      symbolSection += symbolSize;
   }
   return -1;
}

const char *getSymbolByIndexInPlace(const char* symbolSection, size_t symbolSize, size_t symbolCount,  const char *symbolstrtab, int index)
{
   Elf32_Sym* elf_sym = (Elf32_Sym*)symbolSection + index;
   return symbolstrtab + elf_sym->st_name;
}

static inline type_GL_HALF_FLOAT_ARB _RGLFloatTo_GL_HALF_FLOAT_ARB( float x )
{
   jsIntAndFloat V = {f: x};
   unsigned int S = ( V.i >> 31 ) & 1;
   int E = (( V.i >> 23 ) & 0xff ) - 0x7f;
   unsigned int M = V.i & 0x007fffff;
   if (( E == 0x80 ) && ( M ) ) return 0x7fff;
   else if ( E >= 15 ) return( S << 15 ) | 0x7c00;
   else if ( E <= -14 ) return( S << 15 ) | (( 0x800000 + M ) >> ( -14 - E ) );
   else return( S << 15 ) | ((( E + 15 )&0x1f ) << 10 ) | ( M >> 13 );
}

static inline float _RGLFloatFrom_GL_HALF_FLOAT_ARB( type_GL_HALF_FLOAT_ARB x )
{
    unsigned int S = x >> 15;
    unsigned int E = ( x & 0x7C00 ) >> 10;
    unsigned int M = x & 0x03ff;
    float f;
    if ( E == 31 )
    {
        if ( M == 0 ) f = _RGLInfinity.f;
        else f = _RGLNan.f;
    }
    else if ( E == 0 )
    {
        if ( M == 0 ) f = 0.f;
        else f = M * 1.f / ( 1 << 24 );
    }
    else f = ( 0x400 + M ) * 1.f / ( 1 << 25 ) * ( 1 << E );
    return S ? -f : f;
}

#define DECLARE_PACKED_TYPE_AND_REV_2(REALTYPE,S1,S2) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S1##_##S2,2,S1,S2,0,0,) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S2##_##S1##_REV,2,S2,S1,0,0,_REV)

#define DECLARE_PACKED_TYPE_AND_REV_3(REALTYPE,S1,S2,S3) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S1##_##S2##_##S3,3,S1,S2,S3,0,) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S3##_##S2##_##S1##_REV,3,S3,S2,S1,0,_REV)

#define DECLARE_PACKED_TYPE_AND_REV_4(REALTYPE,S1,S2,S3,S4) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S1##_##S2##_##S3##_##S4,4,S1,S2,S3,S4,) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,GL_##REALTYPE##_##S4##_##S3##_##S2##_##S1##_REV,4,S4,S3,S2,S1,_REV)

#define DECLARE_PACKED_TYPE_AND_REALTYPE(REALTYPE,N,S1,S2,S3,S4,REV) \
DECLARE_PACKED_TYPE(GL_##REALTYPE,PACKED_TYPE(REALTYPE,N,S1,S2,S3,S4,REV),N,S1,S2,S3,S4,REV)

#define INDEX(N,X) (X)
#define INDEX_REV(N,X) (N-1-X)

#define GET_BITS(to,from,first,count) if ((count)>0) to=((GLfloat)(((from)>>(first))&((1<<(count))-1)))/(GLfloat)((1<<((count==0)?1:count))-1)
#define PUT_BITS(from,to,first,count) if ((count)>0) to|=((unsigned int)((from)*((GLfloat)((1<<((count==0)?1:count))-1))))<<(first);

static inline void _RGLFifoGlVertexAttribPointer
(
    GLuint          index,
    GLint           size,
    RGLEnum       type,
    GLboolean       normalized,
    GLsizei         stride,
    GLushort        frequency,
    GLboolean       isMain,
    GLuint          offset
)
{
    switch ( size )
    {
        case 0:
            stride = 0;
            normalized = 0;
            type = RGL_FLOAT;
            offset = 0;
            break;
        case 1:
        case 2:
        case 3:
        case 4:
            break;
        default:
            break;
    }

    uint8_t gcmType = 0;
    switch ( type )
    {
        case RGL_UNSIGNED_BYTE:
            if (normalized)
                gcmType = CELL_GCM_VERTEX_UB;
            else
                gcmType = CELL_GCM_VERTEX_UB256;
            break;

        case RGL_SHORT:
            gcmType = normalized ? CELL_GCM_VERTEX_S1 : CELL_GCM_VERTEX_S32K;
            break;

        case RGL_FLOAT:
            gcmType = CELL_GCM_VERTEX_F;
            break;

        case RGL_HALF_FLOAT:
            gcmType = CELL_GCM_VERTEX_SF;
            break;

        case RGL_CMP:
            size = 1;
            gcmType = CELL_GCM_VERTEX_CMP;
            break;

        default:
            break;
    }

    uint8_t location = CELL_GCM_LOCATION_LOCAL;

    if ( isMain )
        location = CELL_GCM_LOCATION_MAIN;

    cellGcmSetVertexDataArrayInline( &_RGLState.fifo, index, frequency, stride, size, gcmType, location, offset );
}

static void _RGLResetAttributeState( jsAttributeState* as )
{
    for ( int i = 0; i < _RGL_MAX_VERTEX_ATTRIBS; ++i )
    {
        as->attrib[i].clientSize = 4;
        as->attrib[i].clientType = GL_FLOAT;
        as->attrib[i].clientStride = 16;
        as->attrib[i].clientData = NULL;

        as->attrib[i].value[0] = 0.0f;
        as->attrib[i].value[1] = 0.0f;
        as->attrib[i].value[2] = 0.0f;
        as->attrib[i].value[3] = 1.0f;

        as->attrib[i].normalized = GL_FALSE;
        as->attrib[i].frequency = 1;

        as->attrib[i].arrayBuffer = 0;
    }

    as->attrib[_RGL_ATTRIB_PRIMARY_COLOR_INDEX].value[0] = 1.0f;
    as->attrib[_RGL_ATTRIB_PRIMARY_COLOR_INDEX].value[1] = 1.0f;
    as->attrib[_RGL_ATTRIB_PRIMARY_COLOR_INDEX].value[2] = 1.0f;
    as->attrib[_RGL_ATTRIB_PRIMARY_COLOR_INDEX].value[3] = 1.0f;

    as->attrib[_RGL_ATTRIB_SECONDARY_COLOR_INDEX].value[0] = 1.0f;
    as->attrib[_RGL_ATTRIB_SECONDARY_COLOR_INDEX].value[1] = 1.0f;
    as->attrib[_RGL_ATTRIB_SECONDARY_COLOR_INDEX].value[2] = 1.0f;
    as->attrib[_RGL_ATTRIB_SECONDARY_COLOR_INDEX].value[3] = 1.0f;

    as->attrib[_RGL_ATTRIB_NORMAL_INDEX].value[0] = 0.f;
    as->attrib[_RGL_ATTRIB_NORMAL_INDEX].value[1] = 0.f;
    as->attrib[_RGL_ATTRIB_NORMAL_INDEX].value[2] = 1.f;

    as->DirtyMask = ( 1 << _RGL_MAX_VERTEX_ATTRIBS ) - 1;
    as->EnabledMask = 0;
    as->NeedsConversionMask = 0;
    as->HasVBOMask = 0;
    as->ModuloMask = 0;
}

static jsAttribSet* _RGLCreateAttribSet( void )
{
   jsAttribSet* attribSet = ( jsAttribSet * )memalign( 16, sizeof( jsAttribSet ) );

   _RGLResetAttributeState( &attribSet->attribs );

   attribSet->dirty = GL_TRUE;
   attribSet->beenUpdatedMask = 0;
   attribSet->cmdBuffer = NULL;
   attribSet->cmdNumWords = 0;

   return attribSet;
}

static void _RGLDestroyAttribSet( jsAttribSet* attribSet )
{
   if ( attribSet->cmdBuffer )
      free( attribSet->cmdBuffer );
   free( attribSet );
}

static inline jsBufferObject *_RGLGetBufferObject( PSGLcontext *LContext, GLuint name )
{
   return ( jsBufferObject * )LContext->bufferObjectNameSpace.data[name];
}

static void _RGLAttribSetDeleteBuffer( PSGLcontext *LContext, GLuint buffName )
{
   jsBufferObject *bufferObject = _RGLGetBufferObject( LContext, buffName );
   GLuint attrSetCount = bufferObject->attribSets.getCount();
   if ( attrSetCount == 0 ) return;
   for ( unsigned int i = 0;i < attrSetCount;++i )
   {
      jsAttribSet *attribSet = bufferObject->attribSets[i];

      for ( GLuint j = 0; j < _RGL_MAX_VERTEX_ATTRIBS; ++j )
         if ( attribSet->attribs.attrib[j].arrayBuffer == buffName )
            attribSet->attribs.attrib[j].arrayBuffer = 0;

      attribSet->dirty = GL_TRUE;
   }
   LContext->attribSetDirty = GL_TRUE;
   bufferObject->attribSets.clear();
}

static inline jsAttribSet* _RGLGetAttribSet( GLuint name )
{
   PSGLcontext* LContext = _CurrentContext;
   return ( jsAttribSet* ) LContext->attribSetNameSpace.data[name];
}

static void _RGLAttribSetMarkDirty (void)
{
   PSGLcontext* LContext = _CurrentContext;
   jsAttribSet* attribSet = _RGLGetAttribSet( LContext->attribSetName );
   attribSet->dirty = GL_TRUE;
   LContext->attribSetDirty = GL_TRUE;
}

static jsBufferObject *_RGLCreateBufferObject (void)
{
   GLuint size = sizeof( jsBufferObject ) + sizeof( RGLBufferObject);
   jsBufferObject *buffer = ( jsBufferObject * )malloc( size );

   if( !buffer )
      return NULL;

   memset( buffer, 0, size );

   buffer->refCount = 1;
   new( &buffer->textureReferences ) RGL::Vector<jsTexture *>();
   new( &buffer->attribSets ) RGL::Vector<jsAttribSet *>();

   return buffer;
}

static void _RGLPlatformDestroyBufferObject( jsBufferObject* bufferObject )
{
   RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

   switch ( jsBuffer->pool )
   {
      case _RGL_SURFACE_POOL_SYSTEM:
      case _RGL_SURFACE_POOL_LINEAR:
         gmmFree( jsBuffer->bufferId );
	 break;
      case _RGL_SURFACE_POOL_NONE:
         break;
      default:
	 break;
   }

   jsBuffer->pool = _RGL_SURFACE_POOL_NONE;
   jsBuffer->bufferId = GMM_ERROR;
}

static void _RGLFreeBufferObject( jsBufferObject *buffer )
{
   if ( --buffer->refCount == 0 )
   {
      _RGLPlatformDestroyBufferObject( buffer );
      buffer->textureReferences.~Vector<jsTexture *>();
      buffer->attribSets.~Vector<jsAttribSet *>();
      if(buffer != NULL)
         free( buffer );
   }
}

static void _RGLUnbindBufferObject( PSGLcontext *LContext, GLuint name )
{
    if ( LContext->ArrayBuffer == name ) LContext->ArrayBuffer = 0;
    if ( LContext->PixelUnpackBuffer == name ) LContext->PixelUnpackBuffer = 0;
    for ( int i = 0;i < _RGL_MAX_VERTEX_ATTRIBS;++i )
    {
        if ( LContext->attribs->attrib[i].arrayBuffer == name )
        {
            LContext->attribs->attrib[i].arrayBuffer = 0;
            LContext->attribs->HasVBOMask &= ~( 1 << i );
        }
    }
    _RGLAttribSetDeleteBuffer( LContext, name );
}

GLAPI void APIENTRY glBindBuffer( GLenum target, GLuint name )
{
    PSGLcontext *LContext = _CurrentContext;

    if(name)
       _RGLTexNameSpaceCreateNameLazy( &LContext->bufferObjectNameSpace, name );

    switch ( target )
    {
        case GL_ARRAY_BUFFER:
            LContext->ArrayBuffer = name;
            break;
        case GL_ELEMENT_ARRAY_BUFFER:
            break;
        case GL_PIXEL_PACK_BUFFER_ARB:
            break;
        case GL_PIXEL_UNPACK_BUFFER_ARB:
            LContext->PixelUnpackBuffer = name;
            break;
        case GL_TEXTURE_REFERENCE_BUFFER_SCE:
            LContext->TextureBuffer = name;
            break;
        default:
            break;
    }
}

GLAPI void APIENTRY glDeleteBuffers(GLsizei n, const GLuint *buffers)
{
   PSGLcontext *LContext = _CurrentContext;
   for(int i = 0; i < n; ++i)
   {
      if ( !_RGLTexNameSpaceIsName( &LContext->bufferObjectNameSpace, buffers[i] ) )
         continue;

      if ( buffers[i] )
         _RGLUnbindBufferObject( LContext, buffers[i] );
   }
   _RGLTexNameSpaceDeleteNames( &LContext->bufferObjectNameSpace, n, buffers );
}

GLAPI void APIENTRY glGenBuffers(GLsizei n, GLuint *buffers)
{
   PSGLcontext *LContext = _CurrentContext;
   _RGLTexNameSpaceGenNames( &LContext->bufferObjectNameSpace, n, buffers );
}

static inline jsFramebuffer *_RGLGetFramebuffer( PSGLcontext *LContext, GLuint name )
{
   return ( jsFramebuffer * )LContext->framebufferNameSpace.data[name];
}

static inline void _RGLTextureTouchFBOs( jsTexture *texture )
{
   PSGLcontext *LContext = _CurrentContext;
   if(!LContext)
      return;

   GLuint fbCount = texture->framebuffers.getCount();
   if(fbCount > 0)
   {
      jsFramebuffer *contextFramebuffer = LContext->framebuffer ? _RGLGetFramebuffer( LContext, LContext->framebuffer ) : NULL;
      for ( GLuint i = 0;i < fbCount;++i )
      {
         jsFramebuffer* framebuffer = texture->framebuffers[i];
	 framebuffer->needValidate = GL_TRUE;
	 if ( RGL_UNLIKELY( framebuffer == contextFramebuffer ) ) LContext->needValidate |= PSGL_VALIDATE_FRAMEBUFFER;
      }
   }
}

static void _RGLAllocateBuffer( jsBufferObject* bufferObject )
{
   RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

   _RGLPlatformDestroyBufferObject( bufferObject );

   jsBuffer->pool = _RGL_SURFACE_POOL_LINEAR;
   jsBuffer->bufferId = gmmAlloc(0, jsBuffer->bufferSize);
   jsBuffer->pitch = 0;

   if ( jsBuffer->bufferId == GMM_ERROR )
      jsBuffer->pool = _RGL_SURFACE_POOL_NONE;

   GLuint referenceCount = bufferObject->textureReferences.getCount();
   if ( referenceCount > 0 )
   {
      for ( GLuint i = 0;i < referenceCount;++i )
      {
         jsTexture *texture = bufferObject->textureReferences[i];
	 RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
	 gcmTexture->gpuAddressId = jsBuffer->bufferId;
	 gcmTexture->gpuAddressIdOffset = texture->offset;
	 texture->revalidate |= _RGL_TEXTURE_REVALIDATE_PARAMETERS;
	 _RGLTextureTouchFBOs( texture );
      }
      _CurrentContext->needValidate |= PSGL_VALIDATE_TEXTURES_USED | PSGL_VALIDATE_VERTEX_TEXTURES_USED ;
   }
}

static void _RGLMemcpy( const GLuint dstId, unsigned dstOffset, unsigned int pitch, const GLuint srcId, unsigned int size )
{
   pitch = pitch ? : 64;
   const GLuint dstOffsetAlign = dstOffset % pitch;
   GLuint srcOffset = 0;

   if ( dstOffsetAlign )
   {
      const GLuint firstBytes = MIN( pitch - dstOffsetAlign, size );

      _RGLTransferDataVidToVid(
		      dstId,
		      0,
		      pitch,
		      dstOffsetAlign / 2, dstOffset / pitch,
		      srcId,
		      srcOffset,
		      pitch,
		      0, 0,
		      firstBytes / 2, 1,
		      2 );
      dstOffset += firstBytes;
      srcOffset += firstBytes;
      size -= firstBytes;
   }

   const GLuint fullLines = size / pitch;
   const GLuint extraBytes = size % pitch;
   if ( fullLines )
      _RGLTransferDataVidToVid(
		      dstId,
		      0,
		      pitch,
		      0, dstOffset / pitch,
		      srcId,
		      srcOffset,
		      pitch,
		      0, 0,
		      pitch / 2, fullLines,
		      2 );
   if ( extraBytes )
      _RGLTransferDataVidToVid(
		      dstId,
		      0,
		      pitch,
		      0, fullLines + dstOffset / pitch,
		      srcId,
		      srcOffset,
		      pitch,
		      0, fullLines,
		      extraBytes / 2, 1,
		      2 );
}

static void _RGLPlatformBufferObjectSetData( jsBufferObject* bufferObject, GLintptr offset, GLsizeiptr size, const GLvoid *data, GLboolean tryImmediateCopy )
{
   RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

    if ( size == bufferObject->size && tryImmediateCopy )
        memcpy( gmmIdToAddress( jsBuffer->bufferId ) + offset, data, size );
    else
        if ( size >= bufferObject->size )
        {
            jsBuffer->bufferSize = _RGLPad( size, _RGL_BUFFER_OBJECT_BLOCK_SIZE );
            _RGLAllocateBuffer( bufferObject );

            switch ( jsBuffer->pool )
            {
                case _RGL_SURFACE_POOL_NONE:
                    _RGLSetError( GL_OUT_OF_MEMORY );
                    return;
                default:
                    memcpy( gmmIdToAddress( jsBuffer->bufferId ), data, size );
                    break;
            }
        }
        else
        {
            if ( tryImmediateCopy )
                memcpy( gmmIdToAddress( jsBuffer->bufferId ) + offset, data, size );
            else
	    {
		    unsigned int dstId = jsBuffer->bufferId;
		    unsigned int pitch = jsBuffer->pitch;
		    const char *src = (const char *)data;
		    switch ( bufferObject->usage )
		    {
			    case GL_STREAM_DRAW:
			    case GL_STREAM_READ:
			    case GL_STREAM_COPY:
			    case GL_DYNAMIC_DRAW:
			    case GL_DYNAMIC_READ:
			    case GL_DYNAMIC_COPY:
				    {
					    GLuint id = gmmAlloc(0, size);

					    memcpy( gmmIdToAddress(id), src, size );
					    _RGLMemcpy( dstId, offset, pitch, id, size );

					    gmmFree( id );
				    }
				    break;
			    default:
				    cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
				    _RGLFifoFinish( &_RGLState.fifo );
				    memcpy( gmmIdToAddress( dstId ) + offset, src, size );
				    break;
		    };
	    }
        }

    ((RGLDriver *)_CurrentDevice->rasterDriver)->invalidateVertexCache = GL_TRUE;
}

static GLboolean _RGLPlatformCreateBufferObject( jsBufferObject* bufferObject )
{
   RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

   jsBuffer->pool = _RGL_SURFACE_POOL_NONE;
   jsBuffer->bufferId = GMM_ERROR;
   jsBuffer->mapCount = 0;
   jsBuffer->mapAccess = GL_NONE;
   jsBuffer->bufferSize = _RGLPad( bufferObject->size, _RGL_BUFFER_OBJECT_BLOCK_SIZE );

   _RGLAllocateBuffer( bufferObject );

   return jsBuffer->bufferId != GMM_ERROR;
}

GLAPI void APIENTRY glBufferData( GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage )
{
   PSGLcontext *LContext = _CurrentContext;

   GLuint name = 0;
   switch(target)
   {
      case GL_ARRAY_BUFFER:
         name = LContext->ArrayBuffer;
	 break;
      case GL_ELEMENT_ARRAY_BUFFER:
	 break;
      case GL_PIXEL_PACK_BUFFER_ARB:
	 break;
      case GL_PIXEL_UNPACK_BUFFER_ARB:
	 name = LContext->PixelUnpackBuffer;
	 break;
      case GL_TEXTURE_REFERENCE_BUFFER_SCE:
	 name = LContext->TextureBuffer;
	 break;
      default:
	 _RGLSetError(GL_INVALID_ENUM);
	 return;
   }

   jsBufferObject* bufferObject = _RGLGetBufferObject( LContext, name );

   if(bufferObject->refCount > 1)
   {
      _RGLTexNameSpaceDeleteNames( &LContext->bufferObjectNameSpace, 1, &name );
      _RGLTexNameSpaceCreateNameLazy( &LContext->bufferObjectNameSpace, name );
      bufferObject = _RGLGetBufferObject( LContext, name );
   }

   if (bufferObject->size > 0)
      _RGLPlatformDestroyBufferObject( bufferObject );

   bufferObject->usage = usage;
   bufferObject->size = size;
   bufferObject->width = 0;
   bufferObject->height = 0;
   bufferObject->internalFormat = GL_NONE;

   if (size > 0)
   {
      GLboolean created = _RGLPlatformCreateBufferObject( bufferObject );
      if ( !created )
      {
         _RGLSetError( GL_OUT_OF_MEMORY );
	 return;
      }
      if ( data )
         _RGLPlatformBufferObjectSetData( bufferObject, 0, size, data, GL_TRUE );
   }

   GLuint attrSetCount = bufferObject->attribSets.getCount();
   if ( attrSetCount == 0 )
      return;

   for(unsigned int i = 0; i < attrSetCount; ++i)
   {
      jsAttribSet *attribSet = bufferObject->attribSets[i];
      attribSet->dirty = GL_TRUE;
   }

   LContext->attribSetDirty = GL_TRUE;
}

static GLvoid _RGLPlatformBufferObjectCopyData( jsBufferObject* bufferObjectDst,
    jsBufferObject* bufferObjectSrc )
{

    RGLBufferObject* dst = ( RGLBufferObject* )bufferObjectDst->platformBufferObject;
    RGLBufferObject* src = ( RGLBufferObject* )bufferObjectSrc->platformBufferObject;

    switch ( dst->pool )
    {
        case _RGL_SURFACE_POOL_LINEAR:
            _RGLMemcpy( dst->bufferId, 0, dst->pitch, src->bufferId, src->bufferSize );
            break;
        case _RGL_SURFACE_POOL_SYSTEM:
	    cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
	    _RGLFifoFinish( &_RGLState.fifo );
            memcpy( gmmIdToAddress( dst->bufferId ), gmmIdToAddress( src->bufferId ),
            src->bufferSize );
            break;
    }

    ((RGLDriver *)_CurrentDevice->rasterDriver)->invalidateVertexCache = GL_TRUE;
}

GLAPI void APIENTRY glBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data )
{
    PSGLcontext *LContext = _CurrentContext;
    GLuint name = 0;
    switch ( target )
    {
        case GL_ARRAY_BUFFER:
            name = LContext->ArrayBuffer;
            break;
        case GL_ELEMENT_ARRAY_BUFFER:
            break;
        case GL_PIXEL_PACK_BUFFER_ARB:
            break;
        case GL_PIXEL_UNPACK_BUFFER_ARB:
            name = LContext->PixelUnpackBuffer;
            break;
        case GL_TEXTURE_REFERENCE_BUFFER_SCE:
            name = LContext->TextureBuffer;
            break;
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }

    jsBufferObject* bufferObject = _RGLGetBufferObject( LContext, name );


    if ( bufferObject->refCount > 1 )
    {
        jsBufferObject* oldBufferObject = bufferObject;

        _RGLTexNameSpaceDeleteNames( &LContext->bufferObjectNameSpace, 1, &name );
        _RGLTexNameSpaceCreateNameLazy( &LContext->bufferObjectNameSpace, name );

        bufferObject = _RGLGetBufferObject( LContext, name );
        bufferObject->size = oldBufferObject->size;
        bufferObject->usage = oldBufferObject->usage;

        GLboolean created = _RGLPlatformCreateBufferObject( bufferObject );
        if ( !created )
        {
            _RGLSetError( GL_OUT_OF_MEMORY );
            return;
        }
        _RGLPlatformBufferObjectCopyData( bufferObject, oldBufferObject );
    }
    _RGLPlatformBufferObjectSetData( bufferObject, offset, size, data, GL_FALSE );
}


static inline jsTexture* _RGLGetTexture( PSGLcontext *LContext, GLuint name )
{
   return ( jsTexture* )LContext->textureNameSpace.data[name];
}

static inline jsTexture* _RGLGetTextureSafe( PSGLcontext *LContext, GLuint name )
{
   return _RGLTexNameSpaceIsName( &LContext->textureNameSpace, name ) ? ( jsTexture* )LContext->textureNameSpace.data[name] : NULL;
}

static void _RGLFramebufferGetAttachmentTexture(
    const jsFramebufferAttachment* attachment,
    jsTexture** texture,
    GLuint* face )
{
   PSGLcontext* LContext = _CurrentContext;
    switch ( attachment->type )
    {
        case _RGL_FRAMEBUFFER_ATTACHMENT_NONE:
            *texture = NULL;
            *face = 0;
            break;
        case _RGL_FRAMEBUFFER_ATTACHMENT_RENDERBUFFER:
            break;
        case _RGL_FRAMEBUFFER_ATTACHMENT_TEXTURE:
            *texture = _RGLGetTextureSafe( LContext, attachment->name );
            *face = 0;
            break;
        default:
            *face = 0;
            *texture = NULL;
	    break;
    }
}

static GLboolean _RGLTextureIsValid( const jsTexture* texture )
{
    if ( texture->imageCount < 1 )
        return GL_FALSE;
    if ( !texture->image )
        return GL_FALSE;
    const jsImage* image = texture->image;

    int width = image->width;
    int height = image->height;
    GLenum format = image->format;
    GLenum type = image->type;
    GLenum internalFormat = image->internalFormat;
    if (( internalFormat == 0 ) || ( format == 0 ) || ( type == 0 ) )
        return GL_FALSE;

    if ( !image->isSet )
	    return GL_FALSE;
    if ( width != image->width )
	    return GL_FALSE;
    if ( height != image->height )
	    return GL_FALSE;
    if ( format != image->format )
	    return GL_FALSE;
    if ( type != image->type )
	    return GL_FALSE;
    if ( internalFormat != image->internalFormat )
	    return GL_FALSE;

    return GL_TRUE;
}

static GLenum _RGLPlatformFramebufferCheckStatus( jsFramebuffer* framebuffer )
{
   GLuint nBuffers = 0;

   jsImage* image[_RGL_MAX_COLOR_ATTACHMENTS + 2] = {0};

   GLuint colorFormat = 0;
   for ( int i = 0; i < _RGL_MAX_COLOR_ATTACHMENTS; ++i )
   {
      jsTexture* colorTexture = NULL;
      GLuint colorFace = 0;
      _RGLFramebufferGetAttachmentTexture(&framebuffer->color[i], &colorTexture, &colorFace );

      if ( colorTexture != NULL )
      {
         if ( !_RGLTextureIsValid( colorTexture ) )
	 {
            RARCH_ERR("Framebuffer color attachment texture is not complete.\n");
	    return GL_FRAMEBUFFER_UNSUPPORTED_OES;
	 }

         image[nBuffers] = colorTexture->image;

	 if ( colorFormat && colorFormat != image[nBuffers]->internalFormat )
	 {
            RARCH_ERR("Framebuffer attachments have inconsistent color formats.\n" );
	    return GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES;
	 }
	 colorFormat = image[nBuffers]->internalFormat;

	 ++nBuffers;
      }
   }

   if ( nBuffers && colorFormat != RGL_ARGB8)
   {
      RARCH_ERR("Color attachment to framebuffer must be a supported drawable format (GL_ARGB_SCE)\n");
      return GL_FRAMEBUFFER_UNSUPPORTED_OES;
   }

   if ( nBuffers == 0 )
      return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES;

   for ( GLuint i = 0; i < nBuffers; ++i )
      for ( GLuint j = i + 1; j < nBuffers; ++j )
         if ( image[i] == image[j] )
            return GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_OES;

   return GL_FRAMEBUFFER_COMPLETE_OES;
}

enum _RGLTextureStrategy {
   _RGL_TEXTURE_STRATEGY_END,
   _RGL_TEXTURE_STRATEGY_UNTILED_ALLOC,
   _RGL_TEXTURE_STRATEGY_UNTILED_CLEAR,
};

static enum _RGLTextureStrategy linearGPUStrategy[] =
{
   _RGL_TEXTURE_STRATEGY_UNTILED_ALLOC,
   _RGL_TEXTURE_STRATEGY_UNTILED_CLEAR,
   _RGL_TEXTURE_STRATEGY_UNTILED_ALLOC,
   _RGL_TEXTURE_STRATEGY_END,
};

static void _RGLImageAllocCPUStorage( jsImage *image )
{
    if (( image->storageSize > image->mallocStorageSize ) || ( !image->mallocData ) )
    {
        if ( image->mallocData ) free( image->mallocData );
        image->mallocData = ( char * )malloc( image->storageSize + 128 );
        image->mallocStorageSize = image->storageSize;
    }
    image->data = _RGLPadPtr( image->mallocData, 128 );
}

static inline int _RGLGetComponentCount( GLenum format )
{
    switch ( format )
    {
#define DECLARE_FORMAT(FORMAT,COUNT) \
		case FORMAT: \
			return COUNT;
            DECLARE_FORMATS
#undef DECLARE_FORMAT
        default:
            return 0;
    }
}

typedef void( GetComponentsFunction_t )( const unsigned char *bytes, GLfloat *values, int count );
typedef void( PutComponentsFunction_t )( unsigned char *bytes, GLfloat *values, int count );
typedef void( ColorConvertFunction_t )( jsColorRGBAf *color, GLfloat *values );


#define DECLARE_UNPACKED_TYPE(TYPE) \
static void _RGLGetComponents_##TYPE(const unsigned char *bytes, GLfloat *values, int count) \
{ \
	int i; \
	for (i=0;i<count;++i) \
	{ \
		const type_##TYPE data=*(const type_##TYPE *)bytes; \
		values[i]=_RGLFloatFrom_##TYPE(data); \
		bytes+=sizeof(type_##TYPE); \
	} \
}
DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
static void _RGLGetComponents_##TYPE(const unsigned char *bytes, GLfloat *values, int count) \
{ \
	const type_##REALTYPE data=*(const type_##REALTYPE *)bytes; \
	GET_BITS(values[INDEX##REV(N,0)],data,S2+S3+S4,S1); \
	GET_BITS(values[INDEX##REV(N,1)],data,S3+S4,S2); \
	GET_BITS(values[INDEX##REV(N,2)],data,S4,S3); \
	GET_BITS(values[INDEX##REV(N,3)],data,0,S4); \
}
DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

#define DECLARE_UNPACKED_TYPE(TYPE) \
static void _RGLPutComponents_##TYPE(unsigned char *bytes, GLfloat *values, int count) \
{ \
	int i; \
	for (i=0;i<count;++i) \
	{ \
		type_##TYPE *data=(type_##TYPE *)bytes; \
		*data=_RGLFloatTo_##TYPE(values[i]); \
		bytes+=sizeof(type_##TYPE); \
	} \
}
DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
static void _RGLPutComponents_##TYPE(unsigned char *bytes, GLfloat *values, int count) \
{ \
	type_##REALTYPE *data=(type_##REALTYPE *)bytes; \
	*data=0; \
	PUT_BITS(values[INDEX##REV(N,0)],*data,S2+S3+S4,S1); \
	PUT_BITS(values[INDEX##REV(N,1)],*data,S3+S4,S2); \
	PUT_BITS(values[INDEX##REV(N,2)],*data,S4,S3); \
	PUT_BITS(values[INDEX##REV(N,3)],*data,0,S4); \
}
DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

static inline GetComponentsFunction_t *_RGLFindGetComponentsFunction( GLenum type )
{
    switch ( type )
    {

#define DECLARE_UNPACKED_TYPE(TYPE) \
		case TYPE: \
			return &_RGLGetComponents_##TYPE;
            DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
		case TYPE: \
			return &_RGLGetComponents_##TYPE;
            DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

        default:
            return NULL;
    }
}

static inline PutComponentsFunction_t *_RGLFindPutComponentsFunction( GLenum type )
{
    switch ( type )
    {
#define DECLARE_UNPACKED_TYPE(TYPE) \
		case TYPE: \
			return &_RGLPutComponents_##TYPE;
            DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
		case TYPE: \
			return &_RGLPutComponents_##TYPE;
            DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

        default:
            return NULL;
    }
}

static void _RGLValuesToColor_GL_RGB( jsColorRGBAf *c, GLfloat *v ) { c->R = v[0]; c->G = v[1]; c->B = v[2]; c->A = 1.f; }
static void _RGLValuesToColor_GL_BGR( jsColorRGBAf *c, GLfloat *v ) { c->B = v[0]; c->G = v[1]; c->R = v[2]; c->A = 1.f; }
static void _RGLValuesToColor_GL_RGBA( jsColorRGBAf *c, GLfloat *v ) { c->R = v[0]; c->G = v[1]; c->B = v[2]; c->A = v[3]; }
static void _RGLValuesToColor_GL_BGRA( jsColorRGBAf *c, GLfloat *v ) { c->B = v[0]; c->G = v[1]; c->R = v[2]; c->A = v[3]; }
static void _RGLValuesToColor_GL_ABGR( jsColorRGBAf *c, GLfloat *v ) { c->A = v[0]; c->B = v[1]; c->G = v[2]; c->R = v[3]; }
static void _RGLValuesToColor_GL_ARGB_SCE( jsColorRGBAf *c, GLfloat *v ) { c->A = v[0]; c->R = v[1]; c->G = v[2]; c->B = v[3]; }
static void _RGLValuesToColor_GL_RED( jsColorRGBAf *c, GLfloat *v ) { c->R = v[0]; c->G = 0.f; c->B = 0.f; c->A = 1.f; }
static void _RGLValuesToColor_GL_GREEN( jsColorRGBAf *c, GLfloat *v ) { c->R = 0.f; c->G = v[0]; c->B = 0.f; c->A = 1.f; }
static void _RGLValuesToColor_GL_BLUE( jsColorRGBAf *c, GLfloat *v ) { c->R = 0.f; c->G = 0.f; c->B = v[0]; c->A = 1.f; }
static void _RGLValuesToColor_GL_ALPHA( jsColorRGBAf *c, GLfloat *v ) { c->R = 0.f; c->G = 0.f; c->B = 0.f; c->A = v[0]; }

static void _RGLColorToValues_GL_RGB( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->R; v[1] = c->G; v[2] = c->B; }
static void _RGLColorToValues_GL_BGR( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->B; v[1] = c->G; v[2] = c->R; }
static void _RGLColorToValues_GL_RGBA( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->R; v[1] = c->G; v[2] = c->B; v[3] = c->A; }
static void _RGLColorToValues_GL_BGRA( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->B; v[1] = c->G; v[2] = c->R; v[3] = c->A; }
static void _RGLColorToValues_GL_ABGR( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->A; v[1] = c->B; v[2] = c->G; v[3] = c->R; }
static void _RGLColorToValues_GL_ARGB_SCE( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->A; v[1] = c->R; v[2] = c->G; v[3] = c->B; }
static void _RGLColorToValues_GL_RED( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->R; }
static void _RGLColorToValues_GL_GREEN( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->G; }
static void _RGLColorToValues_GL_BLUE( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->B; }
static void _RGLColorToValues_GL_ALPHA( jsColorRGBAf *c, GLfloat *v ) { v[0] = c->A; }

static inline ColorConvertFunction_t *_RGLFindValuesToColorFunction( GLenum format )
{
    switch ( format )
    {
#define DECLARE_FORMAT(FORMAT,COUNT) \
		case FORMAT: \
			return &_RGLValuesToColor_##FORMAT;
            DECLARE_FORMATS
#undef DECLARE_FORMAT
        default:
            return NULL;
    }
}

static inline ColorConvertFunction_t *_RGLFindColorToValuesFunction( GLenum format )
{
    switch ( format )
    {
#define DECLARE_FORMAT(FORMAT,COUNT) \
		case FORMAT: \
			return &_RGLColorToValues_##FORMAT;
            DECLARE_FORMATS
#undef DECLARE_FORMAT
        default:
            return NULL;
    }
}

static void _RGLRasterToImage(const jsRaster* raster, jsImage* image)
{
   const int srcComponents = _RGLGetComponentCount( raster->format );
   const int dstComponents = _RGLGetComponentCount( image->format );
   GetComponentsFunction_t* getComponents = _RGLFindGetComponentsFunction( raster->type );
   PutComponentsFunction_t* putComponents = _RGLFindPutComponentsFunction( image->type );
   ColorConvertFunction_t* valuesToColor = _RGLFindValuesToColorFunction( raster->format );
   ColorConvertFunction_t* colorToValues = _RGLFindColorToValuesFunction( image->format );

   for ( int j = 0; j < raster->height; ++j )
   {
      const unsigned char *src = ( const unsigned char * )raster->data + j * raster->ystride;
      unsigned char *dst = ( unsigned char * )image->data + (j) * image->ystride;

      for ( int k = 0; k < raster->width; ++k )
      {
         GLfloat values[4];
	 jsColorRGBAf color;
	 getComponents( src, values, srcComponents );
	 valuesToColor( &color, values );
	 colorToValues( &color, values );

	 if (image->type!=GL_FLOAT && image->type!=GL_HALF_FLOAT_ARB)
	 {
            values[0]= MAX( MIN( values[0], 1.f ), 0.f );
	    values[1]= MAX( MIN( values[1], 1.f ), 0.f );
	    values[2]= MAX( MIN( values[2], 1.f ), 0.f );
	    values[3]= MAX( MIN( values[3], 1.f ), 0.f );
	 }

	 putComponents( dst, values, dstComponents );

	 src += raster->xstride;
	 dst += image->xstride;
      }
   }
}

static void _RGLPlatformCopyGPUTexture( jsTexture* texture )
{
   RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;

   if ( gcmTexture->gpuAddressId == GMM_ERROR )
      return;

   RGLTextureLayout *layout = &gcmTexture->gpuLayout;

   jsImage* image = texture->image;

   if ( image->dataState == _RGL_IMAGE_DATASTATE_GPU )
   {
      _RGLImageAllocCPUStorage( image );

      cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
      _RGLFifoFinish( &_RGLState.fifo );

      char* gpuData = gmmIdToAddress(gcmTexture->gpuAddressId) + gcmTexture->gpuAddressIdOffset;

      jsRaster raster = 
      {
         format : image->format,
	 type   : image->type,
	 width  : image->width,
	 height : image->height,
	 xstride : image->xstride,
	 ystride : layout->pitch,
	 data    : gpuData
      };

      _RGLRasterToImage( &raster, image);

      image->dataState = _RGL_IMAGE_DATASTATE_HOST;
   }
}

static void _RGLPlatformFreeGcmTexture( jsTexture* texture )
{
    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
    switch ( gcmTexture->pool )
    {
        case _RGL_SURFACE_POOL_LINEAR:
        case _RGL_SURFACE_POOL_SYSTEM:
            gmmFree( gcmTexture->gpuAddressId );
        case _RGL_SURFACE_POOL_NONE:
            break;
        default:
		break;
    }

    gcmTexture->gpuAddressId = GMM_ERROR;
    gcmTexture->gpuAddressIdOffset = 0;
    gcmTexture->gpuSize = 0;
}

void _RGLPlatformDropTexture( jsTexture *texture )
{
   RGLTexture * gcmTexture = (RGLTexture *)texture->platformTexture;
   if(gcmTexture->pbo != NULL)
   {
      _RGLPlatformCopyGPUTexture(texture);
      _RGLFreeBufferObject(gcmTexture->pbo);
      gcmTexture->pbo = NULL;
      gcmTexture->gpuAddressId = GMM_ERROR;
      gcmTexture->gpuAddressIdOffset = 0;
      gcmTexture->pool = _RGL_SURFACE_POOL_NONE;
      gcmTexture->gpuSize = 0;
   }

   if(gcmTexture->pool != _RGL_SURFACE_POOL_NONE)
   {
      _RGLPlatformCopyGPUTexture( texture );
      _RGLPlatformFreeGcmTexture( texture );
   }

   gcmTexture->pool = _RGL_SURFACE_POOL_NONE;
   gcmTexture->gpuAddressId = GMM_ERROR;
   gcmTexture->gpuAddressIdOffset = 0;
   gcmTexture->gpuSize = 0;
   texture->revalidate |= _RGL_TEXTURE_REVALIDATE_IMAGES;
   _RGLTextureTouchFBOs(texture);
}

int _RGLGetPixelSize( GLenum format, GLenum type )
{
    int componentSize;

    switch ( type )
    {

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
		case TYPE: \
			return sizeof(type_##REALTYPE);
            DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

#define DECLARE_UNPACKED_TYPE(TYPE) \
		case TYPE: \
			componentSize=sizeof(type_##TYPE); \
			break;
            DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

        default:
            return 0;
   }

   return _RGLGetComponentCount( format )*componentSize;
}

static inline int _RGLGetStorageSize( GLenum format, GLenum type, GLsizei width, GLsizei height, GLsizei depth )
{
   return _RGLGetPixelSize( format, type )*width*height*depth;
}

static void _RGLPlatformChooseGPUFormatAndLayout(
    const jsTexture* texture,
    GLboolean forceLinear,
    GLuint pitch,
    RGLTextureLayout* newLayout )
{
   jsImage *image = texture->image;

   newLayout->baseWidth = image->width;
   newLayout->baseHeight = image->height;
   newLayout->internalFormat = ( RGLEnum )image->internalFormat;
   newLayout->pixelBits = _RGLPlatformGetBitsPerPixel( newLayout->internalFormat );
   newLayout->pitch = pitch ? pitch : _RGLPad( _RGLGetStorageSize( texture->image->format, texture->image->type, texture->image->width, 1, 1 ), 64 );
}

void _RGLPlatformDropUnboundTextures( GLenum pool )
{
   PSGLcontext* LContext = _CurrentContext;
   GLuint i, j;
   for (i = 0; i < LContext->textureNameSpace.capacity; ++i)
   {
      GLboolean bound = GL_FALSE;
      jsTexture *texture = ( jsTexture * )LContext->textureNameSpace.data[i];
      if ( !texture || ( texture->referenceBuffer != 0 ) ) continue;

      for (j = 0; j < _RGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; ++j)
      {
         if (LContext->VertexTextureImages[j] == texture)
	 {
            bound = GL_TRUE;
	    break;
	 }
      }

      for ( j = 0; j < _RGL_MAX_TEXTURE_IMAGE_UNITS; ++j)
      {
         jsTextureImageUnit *tu = LContext->TextureImageUnits + j;
	 if (tu->bound2D == i)
	 {
            bound = GL_TRUE;
	    break;
	 }
      }

      if ( bound )
         continue;

      RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
      if ( gcmTexture->pbo != NULL && gcmTexture->pbo->refCount > 1 )
         continue;

      if ( pool != _RGL_SURFACE_POOL_NONE && pool != gcmTexture->pool )
         continue;

      _RGLPlatformDropTexture( texture );
   }
}

static void _RGLPlatformReallocateGcmTexture( jsTexture* texture )
{
    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;

    enum _RGLTextureStrategy *step = linearGPUStrategy;

    GLuint size = 0;
    GLuint id = GMM_ERROR;

    const RGLTextureLayout currentLayout = gcmTexture->gpuLayout;
    const GLuint currentSize = gcmTexture->gpuSize;

    // process strategy
    GLboolean done = GL_FALSE;
    while ( !done )
    {
        RGLTextureLayout newLayout;

        switch ( *step++ )
        {
            case _RGL_TEXTURE_STRATEGY_UNTILED_ALLOC:
                _RGLPlatformChooseGPUFormatAndLayout( texture, GL_TRUE, 0, &newLayout );
		size = _RGLPad( newLayout.baseHeight * newLayout.pitch, 1);

                if ( gcmTexture->pool == _RGL_SURFACE_POOL_LINEAR )
                {
                    if ( currentSize >= size && newLayout.pitch == currentLayout.pitch )
                    {
                        gcmTexture->gpuLayout = newLayout;
                        done = GL_TRUE;
                    }
                    else
                        _RGLPlatformDropTexture( texture );
                }

                if ( !done )
                {
		    id = gmmAlloc(0, size);
                    if ( id != GMM_ERROR )
                    {
                        if ( gcmTexture->pool != _RGL_SURFACE_POOL_NONE )
                            _RGLPlatformDropTexture( texture );

                        gcmTexture->pool = _RGL_SURFACE_POOL_LINEAR;
                        gcmTexture->gpuAddressId = id;
                        gcmTexture->gpuAddressIdOffset = 0;
                        gcmTexture->gpuSize = size;
                        gcmTexture->gpuLayout = newLayout;

                        done = GL_TRUE;
                    }
                }
                break;
            case _RGL_TEXTURE_STRATEGY_UNTILED_CLEAR:
                _RGLPlatformDropUnboundTextures( _RGL_SURFACE_POOL_LINEAR );
                break;
            case _RGL_TEXTURE_STRATEGY_END:
                _RGLSetError( GL_OUT_OF_MEMORY );
                done = GL_TRUE;
                break;
            default:
	    	break;
        }
    }
    _RGLTextureTouchFBOs( texture );
}

static void _RGLImageFreeCPUStorage( jsImage *image )
{
   if (!image->mallocData)
      return;

   if (image->mallocData != NULL)
      free( image->mallocData );

   image->mallocStorageSize = 0;
   image->data = NULL;
   image->mallocData = NULL;
   image->dataState &= ~_RGL_IMAGE_DATASTATE_HOST;
}

static void _RGLPlatformValidateTextureResources( jsTexture *texture )
{
    if ( RGL_UNLIKELY( !_RGLTextureIsValid( texture ) ) )
    {
        texture->isComplete = GL_FALSE;
        return;
    }
    texture->isComplete = GL_TRUE;

    if ( texture->revalidate & _RGL_TEXTURE_REVALIDATE_IMAGES || texture->revalidate & _RGL_TEXTURE_REVALIDATE_LAYOUT )
    {
	    _RGLPlatformReallocateGcmTexture( texture );
	    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
	    RGLTextureLayout *layout = &gcmTexture->gpuLayout;

	    const GLuint pixelBytes = layout->pixelBits / 8;

	    RGLSurface src = {
source:		_RGL_SURFACE_SOURCE_TEMPORARY,
		width:		0,
		height:		0,
		bpp:		pixelBytes,
		pitch:		0,
		format:		layout->internalFormat,
		pool:		_RGL_SURFACE_POOL_LINEAR,
		ppuData:     NULL,
		dataId:      GMM_ERROR,
		dataIdOffset:0,
	    };

	    RGLSurface dst = {
source:		_RGL_SURFACE_SOURCE_TEXTURE,
		width:		0,
		height:		0,
		bpp:		pixelBytes,
		pitch:		layout->pitch,
		format:		layout->internalFormat,
		pool:		_RGL_SURFACE_POOL_SYSTEM,
		ppuData:     NULL,
		dataId:      GMM_ERROR,
		dataIdOffset:0,
	    };

	    GLuint bounceBufferId = GMM_ERROR;
	    jsImage *image = texture->image;
	    if ( image->dataState == _RGL_IMAGE_DATASTATE_HOST )
	    {
		    src.ppuData = image->data;

		    if ( bounceBufferId == GMM_ERROR)
			    bounceBufferId = gmmAlloc(0, gcmTexture->gpuSize);

		    if ( bounceBufferId != GMM_ERROR )
		    {
			    src.dataId = bounceBufferId;
			    src.dataIdOffset = 0;

			    memcpy( gmmIdToAddress( src.dataId ), image->data, 
					    image->storageSize );
		    }

		    src.width = image->width;
		    src.height = image->height;
		    src.pitch = pixelBytes * src.width;

		    dst.width = src.width;
		    dst.height = image->height;
		    dst.dataId = gcmTexture->gpuAddressId;
		    dst.dataIdOffset = gcmTexture->gpuAddressIdOffset;

		    _RGLTransferDataVidToVid( dst.dataId, dst.dataIdOffset, dst.pitch ? dst.pitch : (dst.bpp * dst.width), 0, 0, src.dataId, src.dataIdOffset, src.pitch ? src.pitch : (src.bpp * src.width), 0, 0, src.width, src.height, src.bpp );

		    _RGLImageFreeCPUStorage( image );
		    image->dataState |= _RGL_IMAGE_DATASTATE_GPU;
	    }

	    if ( bounceBufferId != GMM_ERROR )
		    gmmFree( bounceBufferId );

	    cellGcmSetInvalidateTextureCacheInline( &_RGLState.fifo, CELL_GCM_INVALIDATE_TEXTURE);
    }

    RGLTexture *platformTexture = ( RGLTexture * )texture->platformTexture;
    RGLTextureLayout *layout = &platformTexture->gpuLayout;

    GLuint minFilter = texture->minFilter;
    GLuint magFilter = texture->magFilter;

    platformTexture->gcmMethods.filter.min = _RGLMapMinTextureFilter( minFilter );
    platformTexture->gcmMethods.filter.mag = _RGLMapMagTextureFilter( magFilter );
    platformTexture->gcmMethods.filter.bias = ( GLint )(( -.26f ) * 256.0f );

    GLuint gamma = 0;
    GLuint remap = texture->gammaRemap;
    gamma |= ( remap & RGL_GAMMA_REMAP_RED_BIT ) 	? CELL_GCM_TEXTURE_GAMMA_R : 0;
    gamma |= ( remap & RGL_GAMMA_REMAP_GREEN_BIT )	? CELL_GCM_TEXTURE_GAMMA_G : 0;
    gamma |= ( remap & RGL_GAMMA_REMAP_BLUE_BIT ) 	? CELL_GCM_TEXTURE_GAMMA_B : 0;
    gamma |= ( remap & RGL_GAMMA_REMAP_ALPHA_BIT ) 	? CELL_GCM_TEXTURE_GAMMA_A : 0;

    platformTexture->gcmMethods.address.gamma = gamma;

    GLuint internalFormat = layout->internalFormat;

    _RGLMapTextureFormat( internalFormat,
       platformTexture->gcmTexture.format, platformTexture->gcmTexture.remap );

    if ( layout->pitch )
        platformTexture->gcmTexture.format += 0x20;

    platformTexture->gcmTexture.width = layout->baseWidth;
    platformTexture->gcmTexture.height = layout->baseHeight;
    platformTexture->gcmTexture.depth = 1;
    platformTexture->gcmTexture.pitch = layout->pitch;
    platformTexture->gcmTexture.mipmap = 1;
    platformTexture->gcmTexture.cubemap = CELL_GCM_FALSE;
    platformTexture->gcmTexture.dimension = CELL_GCM_TEXTURE_DIMENSION_2;

    if ( gmmIdIsMain(platformTexture->gpuAddressId) )
        platformTexture->gcmTexture.location = CELL_GCM_LOCATION_MAIN;
    else
        platformTexture->gcmTexture.location = CELL_GCM_LOCATION_LOCAL;

    texture->revalidate = 0;
}

static void jsPlatformFramebuffer_validate( jsPlatformFramebuffer * fb, PSGLcontext *LContext )
{
   fb->complete = ( _RGLPlatformFramebufferCheckStatus(fb) == GL_FRAMEBUFFER_COMPLETE_OES );

   if(!fb->complete)
      return;

   GLuint width = CELL_GCM_MAX_RT_DIMENSION;
   GLuint height = CELL_GCM_MAX_RT_DIMENSION;

   fb->rt.colorBufferCount = 0;
   fb->rt.colorFormat = RGL_NONE;
   fb->colorBufferMask = 0x0;
   GLuint defaultPitch = 0;
   GLuint defaultId = GMM_ERROR;
   GLuint defaultIdOffset = 0;
   for ( int i = 0; i < RGL_SETRENDERTARGET_MAXCOUNT; ++i )
   {
      jsTexture* colorTexture = NULL;
      GLuint face = 0;
      _RGLFramebufferGetAttachmentTexture(&fb->color[i], &colorTexture, &face );

      if(colorTexture == NULL)
         continue;

      RGLTexture* nvTexture = ( RGLTexture * )colorTexture->platformTexture;

      if ( !colorTexture->isRenderTarget )
      {
         colorTexture->isRenderTarget = GL_TRUE;
	 colorTexture->revalidate |= _RGL_TEXTURE_REVALIDATE_LAYOUT;
      }
      _RGLPlatformValidateTextureResources( colorTexture );
      colorTexture->image->dataState = _RGL_IMAGE_DATASTATE_GPU;

      fb->rt.colorId[i] = nvTexture->gpuAddressId;
      fb->rt.colorIdOffset[i] = nvTexture->gpuAddressIdOffset;
      fb->rt.colorPitch[i] = nvTexture->gpuLayout.pitch ? nvTexture->gpuLayout.pitch : nvTexture->gpuLayout.pixelBits * nvTexture->gpuLayout.baseWidth / 8;
      fb->colorBufferMask |= 1 << i;

      width = MIN( width, nvTexture->gpuLayout.baseWidth );
      height = MIN( height, nvTexture->gpuLayout.baseHeight );
      fb->rt.colorFormat = nvTexture->gpuLayout.internalFormat;
      fb->rt.colorBufferCount = i + 1;
      defaultId = fb->rt.colorId[i];
      defaultIdOffset = fb->rt.colorIdOffset[i];
      defaultPitch = fb->rt.colorPitch[i];

      if ( !( fb->colorBufferMask & ( 1 << i ) ) )
      {
         fb->rt.colorId[i] = defaultId;
	 fb->rt.colorIdOffset[i] = defaultIdOffset;
	 fb->rt.colorPitch[i] = defaultPitch;
      }
   }

    fb->rt.width = width;
    fb->rt.height = height;

    fb->rt.yInverted = CELL_GCM_FALSE;
    fb->rt.xOffset = 0;
    fb->rt.yOffset = 0;
    fb->needValidate = GL_FALSE;
}

static void _RGLValidateFramebuffer( void )
{
    PSGLdevice *LDevice = _CurrentDevice;
    RGLDevice *gcmDevice = ( RGLDevice * )LDevice->platformDevice;

    PSGLcontext* LContext = _CurrentContext;
    RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;

    driver->rtValid = GL_FALSE;

    if(LContext->framebuffer)
    {
        jsPlatformFramebuffer* framebuffer = static_cast<jsPlatformFramebuffer *>( _RGLGetFramebuffer( LContext, LContext->framebuffer ) );

        if ( framebuffer->needValidate )
           jsPlatformFramebuffer_validate( framebuffer, LContext );

	driver->rt = framebuffer->rt;
        driver->colorBufferMask = framebuffer->colorBufferMask;
    }
    else
    {
	driver->rt = gcmDevice->rt;
        driver->colorBufferMask = 0x1;
    }

    driver->rtValid = GL_TRUE;

    _RGLFifoGlSetRenderTarget( &driver->rt );

    LContext->needValidate &= ~PSGL_VALIDATE_FRAMEBUFFER;
    _RGLFifoGlViewport(LContext->ViewPort.X, LContext->ViewPort.Y, 
    LContext->ViewPort.XSize, LContext->ViewPort.YSize, 0.0f, 1.0f);
}

GLAPI void APIENTRY glClear( GLbitfield mask )
{
   PSGLcontext *LContext = _CurrentContext;
   RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;
   RGLFifo * fifo = &_RGLState.fifo;

   if ( LContext->needValidate & PSGL_VALIDATE_FRAMEBUFFER )
      _RGLValidateFramebuffer();

   if ( !driver->rtValid )
      return;

   GLbitfield newmask = 0;
   if (driver->rt.colorBufferCount ) newmask |= RGL_COLOR_BUFFER_BIT;

   if ( !newmask )
      return;

   GLbitfield clearMask = newmask;

   if ( driver->rt.colorBufferCount > 1 )
	   clearMask &= ~RGL_COLOR_BUFFER_BIT;

   if ( clearMask )
   {
      GLuint hwColor;
      RGL_CALC_COLOR_LE_ARGB8( &hwColor, RGL_CLAMPF_01(LContext->ClearColor.R), RGL_CLAMPF_01(LContext->ClearColor.G), RGL_CLAMPF_01(LContext->ClearColor.B), RGL_CLAMPF_01(LContext->ClearColor.A) );

      cellGcmSetClearColorInline( &_RGLState.fifo, hwColor);
      cellGcmSetClearSurfaceInline ( &_RGLState.fifo, CELL_GCM_CLEAR_R | CELL_GCM_CLEAR_G | CELL_GCM_CLEAR_B | CELL_GCM_CLEAR_A);
      newmask &= ~clearMask;
   }

   if ( newmask )
   {
      cellGcmSetScissorInline( &_RGLState.fifo, 0, 0, 4095, 4095);
      cellGcmSetDepthTestEnableInline( &_RGLState.fifo, CELL_GCM_FALSE);
      cellGcmSetStencilTestEnableInline( &_RGLState.fifo, CELL_GCM_FALSE);

      static float _RGLClearVertexBuffer[12] __attribute__(( aligned( RGL_ALIGN_FAST_TRANSFER ) ) ) =
      {
         -1.f, -1.f, 0.f,
	 -1.f, 1.f, 0.f,
	 1.f, -1.f, 0.f,
	 1.f, 1.f, 0.f,
      };

      _RGLClearVertexBuffer[2] = 2.f - 1.f;
      _RGLClearVertexBuffer[5] = 2.f - 1.f;
      _RGLClearVertexBuffer[8] = 2.f - 1.f;
      _RGLClearVertexBuffer[11] = 2.f - 1.f;

      GLuint bufferId = gmmAlloc(0, sizeof(_RGLClearVertexBuffer));
      memcpy( gmmIdToAddress(bufferId), _RGLClearVertexBuffer, sizeof( _RGLClearVertexBuffer ) );
      _RGLFifoGlVertexAttribPointer( 0, 3, RGL_FLOAT, CELL_GCM_FALSE, 3*sizeof( GLfloat ), 1, 0, gmmIdToOffset(bufferId) );
      RGLBIT_TRUE( LContext->attribs->DirtyMask, 0 );

      for(int i = 1; i < _RGL_MAX_VERTEX_ATTRIBS; ++i)
      {
         _RGLFifoGlVertexAttribPointer( i, 0, RGL_FLOAT, 0, 0, 0, 0, 0 );
	 RGLBIT_TRUE( LContext->attribs->DirtyMask, i );
      }
      cellGcmSetVertexData4fInline( &_RGLState.fifo, _RGL_ATTRIB_PRIMARY_COLOR_INDEX, (GLfloat*)&LContext->ClearColor);

      LContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM;

      gmmFree( bufferId );
   }

   cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
   _RGLFifoFlush( fifo );
}

GLAPI void APIENTRY glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha )
{
    PSGLcontext *LContext = _CurrentContext;

    LContext->ClearColor.R = _RGLClampf( red );
    LContext->ClearColor.G = _RGLClampf( green );
    LContext->ClearColor.B = _RGLClampf( blue );
    LContext->ClearColor.A = _RGLClampf( alpha );
}

GLAPI void APIENTRY glBlendEquation( GLenum mode )
{
    PSGLcontext *LContext = _CurrentContext;

    LContext->BlendEquationRGB = LContext->BlendEquationAlpha = mode;
    LContext->needValidate |= PSGL_VALIDATE_BLENDING;
}

GLAPI void APIENTRY glBlendColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha )
{
    PSGLcontext*	LContext = _CurrentContext;
    LContext->BlendColor.R = _RGLClampf( red );
    LContext->BlendColor.G = _RGLClampf( green );
    LContext->BlendColor.B = _RGLClampf( blue );
    LContext->BlendColor.A = _RGLClampf( alpha );

    LContext->needValidate |= PSGL_VALIDATE_BLENDING;
}


GLAPI void APIENTRY glBlendFunc( GLenum sfactor, GLenum dfactor )
{
    PSGLcontext *LContext = _CurrentContext;

    LContext->BlendFactorSrcRGB = sfactor;
    LContext->BlendFactorSrcAlpha = sfactor;
    LContext->BlendFactorDestRGB = dfactor;
    LContext->BlendFactorDestAlpha = dfactor;
    LContext->needValidate |= PSGL_VALIDATE_BLENDING;
}


jsFramebufferAttachment* _RGLFramebufferGetAttachment( jsFramebuffer *framebuffer, GLenum attachment )
{
    switch ( attachment )
    {
        case GL_COLOR_ATTACHMENT0_EXT:
        case GL_COLOR_ATTACHMENT1_EXT:
        case GL_COLOR_ATTACHMENT2_EXT:
        case GL_COLOR_ATTACHMENT3_EXT:
            return &framebuffer->color[attachment - GL_COLOR_ATTACHMENT0_EXT];
        case GL_DEPTH_ATTACHMENT_OES:
        case GL_STENCIL_ATTACHMENT_OES:
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return NULL;
    }
}

GLAPI void APIENTRY glBindFramebufferOES( GLenum target, GLuint framebuffer )
{
    PSGLcontext* LContext = _CurrentContext;

    if ( framebuffer )
        _RGLTexNameSpaceCreateNameLazy( &LContext->framebufferNameSpace, framebuffer );

    LContext->framebuffer = framebuffer;
    LContext->needValidate |= PSGL_VALIDATE_FRAMEBUFFER;
}

GLAPI void APIENTRY glDeleteFramebuffersOES( GLsizei n, const GLuint *framebuffers )
{

    PSGLcontext *LContext = _CurrentContext;

    for(int i = 0; i < n; ++i)
    {
        if ( framebuffers[i] && framebuffers[i] == LContext->framebuffer )
           glBindFramebufferOES( GL_FRAMEBUFFER_OES, 0 );
    }

    _RGLTexNameSpaceDeleteNames( &LContext->framebufferNameSpace, n, framebuffers );
}

GLAPI void APIENTRY glGenFramebuffersOES( GLsizei n, GLuint *framebuffers )
{
   PSGLcontext *LContext = _CurrentContext;
   _RGLTexNameSpaceGenNames( &LContext->framebufferNameSpace, n, framebuffers );
}


GLAPI GLenum APIENTRY glCheckFramebufferStatusOES( GLenum target )
{
    PSGLcontext *LContext = _CurrentContext;

    if ( LContext->framebuffer )
    {
        jsFramebuffer* framebuffer = _RGLGetFramebuffer( LContext, LContext->framebuffer );
        return _RGLPlatformFramebufferCheckStatus( framebuffer );
    }

    return GL_FRAMEBUFFER_COMPLETE_OES;
}

GLAPI void APIENTRY glFramebufferTexture2DOES( GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level )
{
   PSGLcontext* LContext = _CurrentContext;

   jsFramebuffer* framebuffer = _RGLGetFramebuffer( LContext, LContext->framebuffer );

   jsFramebufferAttachment* attach = _RGLFramebufferGetAttachment( framebuffer, GL_COLOR_ATTACHMENT0_EXT );

   if ( !attach )
      return;

   jsTexture *textureObject = NULL;
   GLuint face;
   _RGLFramebufferGetAttachmentTexture(attach, &textureObject, &face );

   if ( textureObject )
      textureObject->framebuffers.removeElement( framebuffer );

   if ( texture )
   {
      attach->type = _RGL_FRAMEBUFFER_ATTACHMENT_TEXTURE;
      textureObject = _RGLGetTexture( LContext, texture );
      textureObject->framebuffers.pushBack( framebuffer );
   }
   else
      attach->type = _RGL_FRAMEBUFFER_ATTACHMENT_NONE;

   attach->name = texture;
   attach->textureTarget = GL_TEXTURE_2D;

   framebuffer->needValidate = GL_TRUE;
   LContext->needValidate |= PSGL_VALIDATE_FRAMEBUFFER;
}

void cgRTCgcInit( void )
{
    _cgRTCgcCompileProgramHook = &compile_program_from_string;
    _cgRTCgcFreeCompiledProgramHook = &free_compiled_program;
}

void cgRTCgcFree( void )
{
    _cgRTCgcCompileProgramHook = 0;
    _cgRTCgcFreeCompiledProgramHook = 0;
}

void _RGLInitNameSpace( jsNameSpace * name )
{
   name->data = NULL;
   name->firstFree = NULL;
   name->capacity = 0;
}

void _RGLFreeNameSpace( jsNameSpace * ns )
{
   if ( ns->data )
      free( ns->data );

   ns->data = NULL;
   ns->capacity = 0;
   ns->firstFree = NULL;
}

jsName _RGLCreateName( jsNameSpace * ns, void* object )
{
   if ( ns->firstFree == NULL )
   {
      int newCapacity = ns->capacity + NAME_INCREMENT;

      void** newData = ( void** )malloc( newCapacity * sizeof( void* ) );
      if ( newData == NULL )
      {
         _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
	 return 0;
      }
      memcpy( newData, ns->data, ns->capacity * sizeof( void* ) );

      if ( ns->data != NULL )
         free( ns->data );

      ns->data = newData;

      for ( int index = ns->capacity; index < newCapacity - 1; ++index )
         ns->data[index] = ns->data + index + 1;

      ns->data[newCapacity - 1] = NULL;
      ns->firstFree = ns->data + ns->capacity;
      ns->capacity = newCapacity;
   }
   jsName result = ns->firstFree - ns->data;

   ns->firstFree = ( void** ) * ns->firstFree;
   ns->data[result] = object;

   return result + 1;
}

unsigned int _RGLIsName( jsNameSpace* ns, jsName name )
{
   if ( RGL_UNLIKELY( name == 0 ) )
      return 0;

   --name;

   if ( RGL_UNLIKELY( name >= ns->capacity ) )
      return 0;

   void** value = ( void** )ns->data[name];

   if ( RGL_UNLIKELY( value == NULL ||
   ( value >= ns->data && value < ns->data + ns->capacity ) ) )
      return 0;

   return 1;
}

void _RGLEraseName( jsNameSpace* ns, jsName name )
{
   if ( _RGLIsName( ns, name ) )
   {
      --name;
      ns->data[name] = ns->firstFree;
      ns->firstFree = ns->data + name;
   }
}

void _RGLTexNameSpaceInit( jsTexNameSpace *ns, jsTexNameSpaceCreateFunction create, jsTexNameSpaceDestroyFunction destroy )
{
    ns->capacity = CAPACITY_INCR; 
    ns->data = ( void ** )malloc( ns->capacity * sizeof( void* ) );
    memset( ns->data, 0, ns->capacity*sizeof( void* ) );
    ns->create = create;
    ns->destroy = destroy;
}

void _RGLTexNameSpaceFree( jsTexNameSpace *ns )
{
   for ( GLuint i = 1;i < ns->capacity;++i )
      if ( ns->data[i] ) ns->destroy( ns->data[i] );

   if(ns->data != NULL)
      free( ns->data );

   ns->data = NULL;
}

void _RGLTexNameSpaceResetNames( jsTexNameSpace *ns )
{
   for ( GLuint i = 1;i < ns->capacity;++i )
   {
      if ( ns->data[i] )
      {
         ns->destroy( ns->data[i] );
	 ns->data[i] = NULL;
      }
   }
}

GLuint _RGLTexNameSpaceGetFree( jsTexNameSpace *ns )
{
   GLuint i;

   for(i = 1; i < ns->capacity; ++i)
      if ( !ns->data[i] )
         break;

   return i;
}

GLboolean _RGLTexNameSpaceCreateNameLazy( jsTexNameSpace *ns, GLuint name )
{
    if ( name >= ns->capacity )
    {
        int newCapacity = name >= ns->capacity + CAPACITY_INCR ? name + 1 : ns->capacity + CAPACITY_INCR;
        void **newData = ( void ** )realloc( ns->data, newCapacity * sizeof( void * ) );
        memset( newData + ns->capacity, 0, ( newCapacity - ns->capacity )*sizeof( void * ) );
        ns->data = newData;
        ns->capacity = newCapacity;
    }
    if ( !ns->data[name] )
    {
        ns->data[name] = ns->create();
        if ( ns->data[name] ) return GL_TRUE;
    }
    return GL_FALSE;
}

GLboolean _RGLTexNameSpaceIsName( jsTexNameSpace *ns, GLuint name )
{
   if (( name > 0 ) && (name < ns->capacity))
      return( ns->data[name] != 0 );
   else
      return GL_FALSE;
}

void _RGLTexNameSpaceGenNames( jsTexNameSpace *ns, GLsizei n, GLuint *names )
{
    for (int i = 0; i < n; ++i)
    {
        GLuint name = _RGLTexNameSpaceGetFree( ns );
        names[i] = name;

        if(name)
           _RGLTexNameSpaceCreateNameLazy( ns, name );
    }
}

void _RGLTexNameSpaceDeleteNames( jsTexNameSpace *ns, GLsizei n, const GLuint *names )
{
   for ( int i = 0;i < n;++i )
   {
      GLuint name = names[i];

      if(!_RGLTexNameSpaceIsName(ns, name))
         continue;

      ns->destroy( ns->data[name] );
      ns->data[name] = NULL;
   }
}

static inline unsigned int endianSwapWordByHalf( unsigned int v )
{
   return (v & 0xffff ) << 16 | v >> 16;
}

static uint32_t gmmInitFixedAllocator (void)
{
   pGmmFixedAllocData = (GmmFixedAllocData *)malloc(sizeof(GmmFixedAllocData));

   if (pGmmFixedAllocData == NULL)
      return GMM_ERROR;

   memset(pGmmFixedAllocData, 0, sizeof(GmmFixedAllocData));

   for (int i = 0; i < 2; i++)
   {
      int blockCount = (i==0) ? GMM_BLOCK_COUNT : GMM_TILE_BLOCK_COUNT;
      int blockSize  = (i==0) ? sizeof(GmmBlock): sizeof(GmmTileBlock);

      pGmmFixedAllocData->ppBlockList[i] = (char **)malloc(sizeof(char *));

      if (pGmmFixedAllocData->ppBlockList[i] == NULL)
         return GMM_ERROR;

      pGmmFixedAllocData->ppBlockList[i][0] = (char *)malloc(blockSize * blockCount);

      if (pGmmFixedAllocData->ppBlockList[i][0] == NULL)
         return GMM_ERROR;

      pGmmFixedAllocData->ppFreeBlockList[i] = (uint16_t **)malloc(sizeof(uint16_t *));

      if (pGmmFixedAllocData->ppFreeBlockList[i] == NULL)
         return GMM_ERROR;

      pGmmFixedAllocData->ppFreeBlockList[i][0] = (uint16_t *)malloc(sizeof(uint16_t) * blockCount);

      if (pGmmFixedAllocData->ppFreeBlockList[i][0] == NULL)
         return GMM_ERROR;

      pGmmFixedAllocData->pBlocksUsed[i] = (uint16_t *)malloc(sizeof(uint16_t));

      if (pGmmFixedAllocData->pBlocksUsed[i] == NULL)
         return GMM_ERROR;

      for (int j=0; j<blockCount; j++)
         pGmmFixedAllocData->ppFreeBlockList[i][0][j] = j; 

      pGmmFixedAllocData->pBlocksUsed[i][0] = 0;
      pGmmFixedAllocData->BlockListCount[i] = 1;
   }

   return CELL_OK;
}

static void gmmRemovePendingFree(
    GmmAllocator *pAllocator,
    GmmBlock *pBlock
)
{
    if (pBlock == pAllocator->pPendingFreeHead)
        pAllocator->pPendingFreeHead = pBlock->pNextFree;

    if (pBlock == pAllocator->pPendingFreeTail)
        pAllocator->pPendingFreeTail = pBlock->pPrevFree;

    if (pBlock->pNextFree)
        pBlock->pNextFree->pPrevFree = pBlock->pPrevFree;

    if (pBlock->pPrevFree)
        pBlock->pPrevFree->pNextFree = pBlock->pNextFree;
}

static uint8_t gmmSizeToFreeIndex(uint32_t size)
{
    if (size >= GMM_FREE_BIN_0 && size < GMM_FREE_BIN_1)
        return 0;
    else if (size >= GMM_FREE_BIN_1 && size < GMM_FREE_BIN_2)
        return 1;
    else if (size >= GMM_FREE_BIN_2 && size < GMM_FREE_BIN_3)
        return 2;
    else if (size >= GMM_FREE_BIN_3 && size < GMM_FREE_BIN_4)
        return 3;
    else if (size >= GMM_FREE_BIN_4 && size < GMM_FREE_BIN_5)
        return 4;
    else if (size >= GMM_FREE_BIN_5 && size < GMM_FREE_BIN_6)
        return 5;
    else if (size >= GMM_FREE_BIN_6 && size < GMM_FREE_BIN_7)
        return 6;
    else if (size >= GMM_FREE_BIN_7 && size < GMM_FREE_BIN_8)
        return 7;
    else if (size >= GMM_FREE_BIN_8 && size < GMM_FREE_BIN_9)
        return 8;
    else if (size >= GMM_FREE_BIN_9 && size < GMM_FREE_BIN_10)
        return 9;
    else if (size >= GMM_FREE_BIN_10 && size < GMM_FREE_BIN_11)
        return 10;
    else if (size >= GMM_FREE_BIN_11 && size < GMM_FREE_BIN_12)
        return 11;
    else if (size >= GMM_FREE_BIN_12 && size < GMM_FREE_BIN_13)
        return 12;
    else if (size >= GMM_FREE_BIN_13 && size < GMM_FREE_BIN_14)
        return 13;
    else if (size >= GMM_FREE_BIN_14 && size < GMM_FREE_BIN_15)
        return 14;
    else if (size >= GMM_FREE_BIN_15 && size < GMM_FREE_BIN_16)
        return 15;
    else if (size >= GMM_FREE_BIN_16 && size < GMM_FREE_BIN_17)
        return 16;
    else if (size >= GMM_FREE_BIN_17 && size < GMM_FREE_BIN_18)
        return 17;
    else if (size >= GMM_FREE_BIN_18 && size < GMM_FREE_BIN_19)
        return 18;
    else if (size >= GMM_FREE_BIN_19 && size < GMM_FREE_BIN_20)
        return 19;
    else if (size >= GMM_FREE_BIN_20 && size < GMM_FREE_BIN_21)
        return 20;
    else
        return 21;
}

static void gmmAddFree(GmmAllocator *pAllocator, GmmBlock *pBlock)
{
    uint8_t freeIndex = gmmSizeToFreeIndex(pBlock->base.size);


    if (pAllocator->pFreeHead[freeIndex])
    {
        GmmBlock *pInsertBefore = pAllocator->pFreeHead[freeIndex];

        while (pInsertBefore && pInsertBefore->base.size < pBlock->base.size)
        {
            pInsertBefore = pInsertBefore->pNextFree;
        }

        if (pInsertBefore == NULL)
        {
            pBlock->pNextFree = NULL;
            pBlock->pPrevFree = pAllocator->pFreeTail[freeIndex];
            pAllocator->pFreeTail[freeIndex]->pNextFree = pBlock;
            pAllocator->pFreeTail[freeIndex] = pBlock;
        }
        else if (pInsertBefore == pAllocator->pFreeHead[freeIndex])
        {
            pBlock->pNextFree = pInsertBefore;
            pBlock->pPrevFree = pInsertBefore->pPrevFree;
            pInsertBefore->pPrevFree = pBlock;
            pAllocator->pFreeHead[freeIndex] = pBlock;
        }
        else
        {
            pBlock->pNextFree = pInsertBefore;
            pBlock->pPrevFree = pInsertBefore->pPrevFree;
            pInsertBefore->pPrevFree->pNextFree = pBlock;
            pInsertBefore->pPrevFree = pBlock;
        }
    }
    else
    {
        pBlock->pNextFree = NULL;
        pBlock->pPrevFree = NULL;
        pAllocator->pFreeHead[freeIndex] = pBlock;
        pAllocator->pFreeTail[freeIndex] = pBlock;
    }
}

void gmmUpdateFreeList (const uint8_t location)
{
    GmmAllocator    *pAllocator;
    const uint32_t  fence = _RGLState.semaphores->userSemaphores[_RGL_SEMA_FENCE].val;
    GmmBlock        *pBlock = NULL;
    GmmBlock        *pTemp = NULL;

    pAllocator = (location == CELL_GCM_LOCATION_LOCAL) ? 
                 pGmmLocalAllocator : 
                 pGmmMainAllocator;
    

    pBlock = pAllocator->pPendingFreeHead;

    while (pBlock)
    {
        pTemp = pBlock->pNextFree;

        if ( !(( fence - pBlock->fence ) & 0x80000000 ) )
        {
            gmmRemovePendingFree(pAllocator, pBlock);
            gmmAddFree(pAllocator, pBlock);
        }

        pBlock = pTemp;
    }

}

static void *gmmAllocFixed(uint8_t isTile)
{
    int blockCount = isTile ? GMM_TILE_BLOCK_COUNT : GMM_BLOCK_COUNT;
    int blockSize  = isTile ? sizeof(GmmTileBlock) : sizeof(GmmBlock);
    int listCount  = pGmmFixedAllocData->BlockListCount[isTile];

    for (int i=0; i<listCount; i++)
    {
        if (pGmmFixedAllocData->pBlocksUsed[isTile][i] < blockCount)
        {
            return pGmmFixedAllocData->ppBlockList[isTile][i] + 
                   (pGmmFixedAllocData->ppFreeBlockList[isTile][i][pGmmFixedAllocData->pBlocksUsed[isTile][i]++] * 
                    blockSize);
        }
    }

    char **ppBlockList = 
        (char **)realloc(pGmmFixedAllocData->ppBlockList[isTile],
                         (listCount + 1) * sizeof(char *));
    if (ppBlockList == NULL)
        return NULL;

    pGmmFixedAllocData->ppBlockList[isTile] = ppBlockList;

    pGmmFixedAllocData->ppBlockList[isTile][listCount] = 
        (char *)malloc(blockSize * blockCount);
    if (pGmmFixedAllocData->ppBlockList[isTile][listCount] == NULL)
        return NULL;

    uint16_t **ppFreeBlockList = 
        (uint16_t **)realloc(pGmmFixedAllocData->ppFreeBlockList[isTile],
                            (listCount + 1) * sizeof(uint16_t *));
    if (ppFreeBlockList == NULL)
        return NULL;

    pGmmFixedAllocData->ppFreeBlockList[isTile] = ppFreeBlockList;

    pGmmFixedAllocData->ppFreeBlockList[isTile][listCount] = 
        (uint16_t *)malloc(sizeof(uint16_t) * blockCount);
    if (pGmmFixedAllocData->ppFreeBlockList[isTile][listCount] == NULL)
        return NULL;

    uint16_t *pBlocksUsed = 
        (uint16_t *)realloc(pGmmFixedAllocData->pBlocksUsed[isTile],
                            (listCount + 1) * sizeof(uint16_t));
    if (pBlocksUsed == NULL)
        return NULL;

    pGmmFixedAllocData->pBlocksUsed[isTile] = pBlocksUsed;
                                
    for (int i=0; i<blockCount; i++)
       pGmmFixedAllocData->ppFreeBlockList[isTile][listCount][i] = i; 

    pGmmFixedAllocData->pBlocksUsed[isTile][listCount] = 0;
    pGmmFixedAllocData->BlockListCount[isTile]++;

    return pGmmFixedAllocData->ppBlockList[isTile][listCount] + 
           (pGmmFixedAllocData->ppFreeBlockList[isTile][listCount][pGmmFixedAllocData->pBlocksUsed[isTile][listCount]++] * 
            blockSize);
}

static void gmmFreeFixed(uint8_t isTile, void *pBlock)
{
   int blockCount = isTile ? GMM_TILE_BLOCK_COUNT : GMM_BLOCK_COUNT;
   int blockSize  = isTile ? sizeof(GmmTileBlock) : sizeof(GmmBlock);

   for (int i=0; i<pGmmFixedAllocData->BlockListCount[isTile]; i++)
   {
      if (pBlock >= pGmmFixedAllocData->ppBlockList[isTile][i] &&
      pBlock < (pGmmFixedAllocData->ppBlockList[isTile][i] + blockSize * blockCount))
      {
         int index = ((char *)pBlock - pGmmFixedAllocData->ppBlockList[isTile][i]) / blockSize;
	 pGmmFixedAllocData->ppFreeBlockList[isTile][i][--pGmmFixedAllocData->pBlocksUsed[isTile][i]] = index;
      }
   }
}

uint32_t gmmInit(
    const void *localMemoryBase,
    const void *localStartAddress,
    const uint32_t localSize,
    const void *mainMemoryBase,
    const void *mainStartAddress,
    const uint32_t mainSize
)
{
    GmmAllocator *pAllocator[2];
    uint32_t alignedLocalSize, alignedMainSize;
    uint32_t localEndAddress = (uint32_t)localStartAddress + localSize;
    uint32_t mainEndAddress = (uint32_t)mainStartAddress + mainSize;

    localEndAddress = (localEndAddress / GMM_TILE_ALIGNMENT) * GMM_TILE_ALIGNMENT;
    mainEndAddress = (mainEndAddress / GMM_TILE_ALIGNMENT) * GMM_TILE_ALIGNMENT;

    alignedLocalSize = localEndAddress - (uint32_t)localStartAddress;
    alignedMainSize = mainEndAddress - (uint32_t)mainStartAddress;


    pAllocator[0] = (GmmAllocator *)malloc(2*sizeof(GmmAllocator));
    pAllocator[1] = pAllocator[0] + 1;

    if (pAllocator[0] == NULL)
        return GMM_ERROR;

    memset(pAllocator[0], 0, 2*sizeof(GmmAllocator));

    if (pAllocator[0])
    {
        pAllocator[0]->memoryBase = (uint32_t)localMemoryBase;
        pAllocator[1]->memoryBase = (uint32_t)mainMemoryBase;
        pAllocator[0]->startAddress = (uint32_t)localStartAddress;
        pAllocator[1]->startAddress = (uint32_t)mainStartAddress;
        pAllocator[0]->size = alignedLocalSize;
        pAllocator[1]->size = alignedMainSize;
        pAllocator[0]->freeAddress = pAllocator[0]->startAddress;
        pAllocator[1]->freeAddress = pAllocator[1]->startAddress;
        pAllocator[0]->tileStartAddress = ((uint32_t)localStartAddress) + alignedLocalSize;
        pAllocator[1]->tileStartAddress = ((uint32_t)mainStartAddress) + alignedMainSize;
        pAllocator[0]->totalSize = alignedLocalSize;
        pAllocator[1]->totalSize = alignedMainSize;

        pGmmLocalAllocator = pAllocator[0];
        pGmmMainAllocator = pAllocator[1];
    }
    else
        return GMM_ERROR;

    pLock = cellGcmGetLabelAddress(GMM_PPU_WAIT_INDEX);
    *pLock = 0;
    cachedLockValue = 0;

    return gmmInitFixedAllocator();
}

void gmmSetTileAttrib(const uint32_t id, const uint32_t tag, void *pData)
{
   GmmTileBlock *pTileBlock = (GmmTileBlock *)id;

   pTileBlock->tileTag = tag;
   pTileBlock->pData = pData;
}

uint32_t gmmIdToOffset(const uint32_t id)
{
   GmmBaseBlock *pBaseBlock = (GmmBaseBlock *)id;
   return gmmAddressToOffset(pBaseBlock->address, pBaseBlock->isMain);
}

char *gmmIdToAddress(const uint32_t id)
{
    GmmBaseBlock *pBaseBlock = (GmmBaseBlock *)id;

    do
    {
       if (cachedLockValue == 0)
          break;

       cachedLockValue = *pLock;

       if (cachedLockValue == 0)
          break;
    }while(1);

    return (char *)pBaseBlock->address;
}

static GmmBlock *gmmAllocBlock(GmmAllocator *pAllocator, uint32_t size)
{
    uint32_t    address;
    GmmBlock    *pNewBlock = NULL;
    GmmBlock    *pBlock = pAllocator->pTail;

    address = pAllocator->freeAddress;

    if (UINT_MAX - address >= size && 
        address + size <= pAllocator->startAddress + pAllocator->size)
    {
        pNewBlock = (GmmBlock *)gmmAllocFixed(0);
        if (pNewBlock == NULL)
            return NULL;

        memset(pNewBlock, 0, sizeof(GmmBlock));

        pNewBlock->base.address = address;
        pNewBlock->base.isMain = (pAllocator == pGmmMainAllocator);
        pNewBlock->base.size = size;
        pAllocator->freeAddress = address + size;

        if (pBlock)
        {
            pNewBlock->pPrev = pBlock;
            pBlock->pNext = pNewBlock;
            pAllocator->pTail = pNewBlock;
        }
        else
        {
            pAllocator->pHead = pNewBlock;
            pAllocator->pTail = pNewBlock;
        }
    }

    return pNewBlock;
}

static GmmTileBlock *gmmFindFreeTileBlock(GmmAllocator *pAllocator, const uint32_t size)
{
    GmmTileBlock    *pBlock = pAllocator->pTileHead;
    GmmTileBlock    *pBestAfterBlock = NULL;
    GmmTileBlock    *pNewBlock = NULL;
    uint32_t        bestSize = 0;
    uint32_t        freeSize = 0;

    while (pBlock && pBlock->pNext)
    {
        freeSize = pBlock->pNext->base.address - pBlock->base.address - pBlock->base.size;

        if (freeSize >= size &&
            (pBestAfterBlock == NULL || freeSize < bestSize) &&
            (pBlock->pNext == NULL ||
             pBlock->pData != pBlock->pNext->pData))
        {
            pBestAfterBlock = pBlock;
            bestSize = freeSize;
        }

        pBlock = pBlock->pNext;
    }

    if (pBestAfterBlock)
    {
        pNewBlock = (GmmTileBlock *)gmmAllocFixed(1);

        if (pNewBlock == NULL)
            return NULL;

        memset(pNewBlock, 0, sizeof(GmmTileBlock));

        pNewBlock->base.address = pBestAfterBlock->base.address + pBestAfterBlock->base.size;
        pNewBlock->base.isMain = (pAllocator == pGmmMainAllocator);
        pNewBlock->base.isTile = 1;
        pNewBlock->base.size = size;

        pNewBlock->pNext = pBestAfterBlock->pNext;
        pNewBlock->pPrev = pBestAfterBlock;
        pNewBlock->pPrev->pNext = pNewBlock;
        pNewBlock->pNext->pPrev = pNewBlock;

        return pNewBlock;
    }
    else
        return NULL;
}

static GmmTileBlock *gmmCreateTileBlock(GmmAllocator *pAllocator, const uint32_t size)
{
    GmmTileBlock    *pNewBlock;
    uint32_t        address;

    address = pAllocator->tileStartAddress - size;

    if (address > pAllocator->startAddress + pAllocator->size)
        return NULL;

    if (pAllocator->pTail &&
        pAllocator->pTail->base.address + pAllocator->pTail->base.size > address)
        return NULL;

    pAllocator->size = address - pAllocator->startAddress;
    pAllocator->tileSize = pAllocator->tileStartAddress + pAllocator->tileSize - address;
    pAllocator->tileStartAddress = address;

    pNewBlock = (GmmTileBlock *)gmmAllocFixed(1);
    if (pNewBlock == NULL)
        return NULL;

    memset(pNewBlock, 0, sizeof(GmmTileBlock));

    pNewBlock->base.address = address;
    pNewBlock->base.isMain = (pAllocator == pGmmMainAllocator);
    pNewBlock->base.isTile = 1;
    pNewBlock->base.size = size;
    pNewBlock->pNext = pAllocator->pTileHead;

    if (pAllocator->pTileHead)
        pAllocator->pTileHead->pPrev = pNewBlock;
    else
        pAllocator->pTileTail = pNewBlock;

    pAllocator->pTileHead = pNewBlock;

    return pNewBlock;
}

static void gmmFreeTileBlock(GmmTileBlock *pTileBlock)
{
    GmmAllocator    *pAllocator;

    if (pTileBlock->pPrev)
        pTileBlock->pPrev->pNext = pTileBlock->pNext;

    if (pTileBlock->pNext)
        pTileBlock->pNext->pPrev = pTileBlock->pPrev;

    if (pTileBlock->base.isMain)
        pAllocator = pGmmMainAllocator;
    else
        pAllocator = pGmmLocalAllocator;

    if (pAllocator->pTileHead == pTileBlock)
    {
        pAllocator->pTileHead = pTileBlock->pNext;

        if (pAllocator->pTileHead)
            pAllocator->pTileHead->pPrev = NULL;

        pAllocator->size = pAllocator->pTileHead ? 
                           pAllocator->pTileHead->base.address - pAllocator->startAddress :
                           pAllocator->totalSize;
        pAllocator->tileSize = pAllocator->totalSize - pAllocator->size;
        pAllocator->tileStartAddress = pAllocator->pTileHead ?
                                       pAllocator->pTileHead->base.address :
                                       pAllocator->startAddress + pAllocator->size;
    }

    if (pAllocator->pTileTail == pTileBlock)
    {
        pAllocator->pTileTail = pTileBlock->pPrev;
        
        if (pAllocator->pTileTail)
            pAllocator->pTileTail->pNext = NULL;
    }

    gmmFreeFixed(1, pTileBlock);
}

uint32_t gmmAllocExtendedTileBlock(const uint32_t size, const uint32_t tag)
{
    GmmAllocator    *pAllocator =  pGmmLocalAllocator;
    uint32_t        retId = 0;
    uint32_t        newSize;
    uint8_t         resizeSucceed = 1;

    newSize = pad(size, GMM_TILE_ALIGNMENT);

    GmmTileBlock    *pBlock = pAllocator->pTileTail;

    while (pBlock)
    {
        if (pBlock->tileTag == tag)
        {
            GLuint address, tileSize;
            _RGLGetTileRegionInfo(pBlock->pData, &address, &tileSize);

            if ((pBlock->pNext && pBlock->pNext->base.address-pBlock->base.address-pBlock->base.size >= newSize) ||
                (pBlock->pPrev && pBlock->base.address-pBlock->pPrev->base.address-pBlock->pPrev->base.size >= newSize))
            {
                GmmTileBlock *pNewBlock = (GmmTileBlock *)gmmAllocFixed(1);
                if (pNewBlock == NULL)
                    break;

                retId = (uint32_t)pNewBlock;

                memset(pNewBlock, 0, sizeof(GmmTileBlock));

                pNewBlock->base.isMain = (pAllocator == pGmmMainAllocator);
                pNewBlock->base.isTile = 1;
                pNewBlock->base.size = newSize;

                if (pBlock->pNext && pBlock->pNext->base.address-pBlock->base.address-pBlock->base.size >= newSize)
                {
                    pNewBlock->base.address = pBlock->base.address+pBlock->base.size;
                    pNewBlock->pNext = pBlock->pNext;
                    pNewBlock->pPrev = pBlock;
                    pBlock->pNext->pPrev = pNewBlock;
                    pBlock->pNext = pNewBlock;

                    if (pNewBlock->pPrev->pData != pNewBlock->pNext->pData)
                    {
                        resizeSucceed = _RGLTryResizeTileRegion( address, tileSize+newSize, pBlock->pData );
                    }
                }
                else
                {
                    pNewBlock->base.address = pBlock->base.address-newSize;
                    pNewBlock->pNext = pBlock;
                    pNewBlock->pPrev = pBlock->pPrev;
                    pBlock->pPrev->pNext = pNewBlock;
                    pBlock->pPrev = pNewBlock;

                    if (pNewBlock->pPrev->pData != pNewBlock->pNext->pData)
                    {
                        resizeSucceed = _RGLTryResizeTileRegion( (GLuint)gmmIdToOffset((uint32_t)pNewBlock), tileSize+newSize, pBlock->pData );
                    }
                }
                gmmSetTileAttrib( retId, tag, pBlock->pData );
                break;
            }

            if (pBlock == pAllocator->pTileHead)
            {
                retId = (uint32_t)gmmCreateTileBlock(pAllocator, newSize); 
                if (retId == 0)
                    break;

                resizeSucceed = _RGLTryResizeTileRegion( (GLuint)gmmIdToOffset(retId), tileSize+newSize, pBlock->pData );
                gmmSetTileAttrib( retId, tag, pBlock->pData );
                break;
            }
        }

        pBlock = pBlock->pPrev;
    }

    if (retId == 0)
        return GMM_ERROR;

    if (!resizeSucceed)
    {
        gmmFreeTileBlock((GmmTileBlock *)retId);
        return GMM_ERROR;
    }

    return retId;
}

static GmmTileBlock *gmmAllocTileBlock(
    GmmAllocator *pAllocator,
    const uint32_t size
)
{
    GmmTileBlock    *pBlock = gmmFindFreeTileBlock(pAllocator, size); 

    if (pBlock == NULL)
        pBlock = gmmCreateTileBlock(pAllocator, size);

    return pBlock;
}

static void gmmFreeBlock(GmmBlock *pBlock)
{
    GmmAllocator    *pAllocator;

    if (pBlock->pPrev)
        pBlock->pPrev->pNext = pBlock->pNext;

    if (pBlock->pNext)
        pBlock->pNext->pPrev = pBlock->pPrev;
   
    if (pBlock->base.isMain)
        pAllocator = pGmmMainAllocator;
    else
        pAllocator = pGmmLocalAllocator;

    if (pAllocator->pHead == pBlock)
    {
        pAllocator->pHead = pBlock->pNext;

        if (pAllocator->pHead)
            pAllocator->pHead->pPrev = NULL;
    }

    if (pAllocator->pTail == pBlock)
    {
        pAllocator->pTail = pBlock->pPrev;
        
        if (pAllocator->pTail)
            pAllocator->pTail->pNext = NULL;
    }

    if (pBlock->pPrev == NULL)
        pAllocator->pSweepHead = pAllocator->pHead;
    else if (pBlock->pPrev &&
             (pAllocator->pSweepHead == NULL || 
              (pAllocator->pSweepHead &&
               pAllocator->pSweepHead->base.address > pBlock->pPrev->base.address)))
    {
        pAllocator->pSweepHead = pBlock->pPrev;
    }

    pAllocator->freedSinceSweep += pBlock->base.size;
    
    gmmFreeFixed(0, pBlock);
}

static void gmmAddPendingFree(GmmBlock *pBlock)
{
    GmmAllocator    *pAllocator;

    if (pBlock->base.isMain)
        pAllocator = pGmmMainAllocator;
    else
        pAllocator = pGmmLocalAllocator;

    if (pAllocator->pPendingFreeTail)
    {
        pBlock->pNextFree = NULL;
        pBlock->pPrevFree = pAllocator->pPendingFreeTail;
        pAllocator->pPendingFreeTail->pNextFree = pBlock;
        pAllocator->pPendingFreeTail = pBlock;
    }
    else
    {
        pBlock->pNextFree = NULL;
        pBlock->pPrevFree = NULL;
        pAllocator->pPendingFreeHead = pBlock;
        pAllocator->pPendingFreeTail = pBlock;
    }

    pBlock->isPinned = 0;

    GLuint* ref = &pBlock->fence;
    ++nvFenceCounter;
    cellGcmSetWriteBackEndLabelInline( &_RGLState.fifo, _RGL_SEMA_FENCE, nvFenceCounter);
    *ref = nvFenceCounter;
}



uint32_t gmmFree(const uint32_t freeId)
{
    GmmBaseBlock    *pBaseBlock = (GmmBaseBlock *)freeId;
    
    if (pBaseBlock->isTile)
    {
        GmmTileBlock *pTileBlock = (GmmTileBlock *)pBaseBlock;

        if (pTileBlock->pPrev &&
            pTileBlock->pNext &&
            pTileBlock->pPrev->pData == pTileBlock->pNext->pData)
        {
        }
        else if (pTileBlock->pPrev && pTileBlock->pPrev->pData == pTileBlock->pData)
        {
            GLuint address, size;

            _RGLGetTileRegionInfo(pTileBlock->pData, &address, &size);
            if ( !_RGLTryResizeTileRegion(address, (size-pTileBlock->base.size), pTileBlock->pData) )
            {
                _RGLTryResizeTileRegion(address, 0, pTileBlock->pData);
                if ( !_RGLTryResizeTileRegion(address, (size-pTileBlock->base.size), pTileBlock->pData) )
                {
                }
            }
        }
        else if (pTileBlock->pNext && pTileBlock->pNext->pData == pTileBlock->pData)
        {
            GLuint address, size;

            _RGLGetTileRegionInfo(pTileBlock->pData, &address, &size);
            if ( !_RGLTryResizeTileRegion((address+pTileBlock->base.size), (size-pTileBlock->base.size), pTileBlock->pData) )
            {
                _RGLTryResizeTileRegion(address, 0, pTileBlock->pData);
                if ( !_RGLTryResizeTileRegion((address+pTileBlock->base.size), (size-pTileBlock->base.size), pTileBlock->pData) )
                {
                }
            }
        }
        else
        {
            if ( !_RGLTryResizeTileRegion( (GLuint)gmmIdToOffset(freeId), 0, ((GmmTileBlock *)freeId)->pData ) )
            {
            }
        }

        gmmFreeTileBlock(pTileBlock);
    }
    else
    {
        GmmBlock *pBlock = (GmmBlock *)pBaseBlock;

        gmmAddPendingFree(pBlock);
    }

    return CELL_OK;
}

static inline void gmmLocalMemcpy(
    const uint32_t dstOffset,
    const uint32_t srcOffset,
    const uint32_t moveSize
)
{
	CellGcmContextData *thisContext = &_RGLState.fifo;
    int32_t offset = 0;
    int32_t sizeLeft = moveSize;
    int32_t dimension = 4096;

    while (sizeLeft)
    {
        while(sizeLeft >= dimension*dimension*4)
        {
            cellGcmSetTransferImage(thisContext,
                                    CELL_GCM_TRANSFER_LOCAL_TO_LOCAL,
                                    dstOffset+offset,
                                    dimension*4,
                                    0,
                                    0,
                                    srcOffset+offset,
                                    dimension*4,
                                    0,
                                    0,
                                    dimension,
                                    dimension,
                                    4);

            offset = offset + dimension*dimension*4;
            sizeLeft = sizeLeft - (dimension*dimension*4);
        }

        dimension = dimension >> 1;

        if (dimension == 32)
            break;
    }

    if (sizeLeft)
    {
        cellGcmSetTransferImage(thisContext, 
                                CELL_GCM_TRANSFER_LOCAL_TO_LOCAL,
                                dstOffset+offset,
                                sizeLeft,
                                0,
                                0,
                                srcOffset+offset,
                                sizeLeft,
                                0,
                                0,
                                sizeLeft/4,
                                1,
                                4);
    }
}

static inline void gmmMemcpy(const uint32_t dstOffset, const uint32_t srcOffset, const uint32_t moveSize)
{
   if (dstOffset + moveSize <= srcOffset)
      gmmLocalMemcpy(dstOffset, srcOffset, moveSize);
   else
   {
      uint32_t moveBlockSize = srcOffset-dstOffset;
      uint32_t iterations = (moveSize+moveBlockSize-1)/moveBlockSize;

      for (uint32_t i = 0; i < iterations; i++)
         gmmLocalMemcpy(dstOffset+(i*moveBlockSize), srcOffset+(i*moveBlockSize), moveBlockSize);
   }
}

static uint8_t gmmInternalSweep (void)
{
    GmmAllocator    *pAllocator  = pGmmLocalAllocator;
    GmmBlock        *pBlock;       
    GmmBlock        *pSrcBlock;
    GmmBlock        *pTempBlock;
    GmmBlock        *pTempBlockNext;
    uint32_t        dstAddress, srcAddress;
    uint32_t        srcOffset, dstOffset;
    uint32_t        prevEndAddress;
    uint32_t        moveSize, moveDistance;
    uint8_t         ret = 0;
    uint32_t        totalMoveSize = 0;

    pBlock = pAllocator->pSweepHead;
    srcAddress = 0;
    dstAddress = 0;
    prevEndAddress = 0;
    pSrcBlock = pBlock;

    while (pBlock != NULL)
    {
        if (pBlock->isPinned == 0)
        {
            if (pBlock->pPrev)
                prevEndAddress = pBlock->pPrev->base.address + pBlock->pPrev->base.size;
            else
                prevEndAddress = pAllocator->startAddress;

            if (pBlock->base.address > prevEndAddress)
            {
                dstAddress = prevEndAddress;
                srcAddress = pBlock->base.address;
                pSrcBlock = pBlock;
            }

            moveSize = pBlock->base.address + pBlock->base.size - srcAddress;

            if (srcAddress > dstAddress &&
                (pBlock->pNext == NULL ||
                 pBlock->pNext->base.address > pBlock->base.address + pBlock->base.size ||
                 pBlock->pNext->isPinned))
            {
                dstOffset = gmmAddressToOffset(dstAddress, 0);
                srcOffset = gmmAddressToOffset(srcAddress, 0);

                totalMoveSize += moveSize;

                gmmMemcpy(dstOffset, srcOffset, moveSize);

                pTempBlock = pSrcBlock;

                moveDistance = srcOffset - dstOffset;

                while (pTempBlock != pBlock->pNext)
                {
                    pTempBlock->base.address -= moveDistance;
                    pTempBlock = pTempBlock->pNext;
                }
            }
        }
        else
        {
            uint32_t availableSize;

            srcAddress = 0;
            dstAddress = 0;

            if (pBlock->pPrev == NULL)
                availableSize = pBlock->base.address - pAllocator->startAddress;
            else
                availableSize = pBlock->base.address - (pBlock->pPrev->base.address + pBlock->pPrev->base.size);

            pTempBlock = pBlock->pNext;

            while (availableSize >= GMM_ALIGNMENT && pTempBlock)
            {
                pTempBlockNext = pTempBlock->pNext;

                if (pTempBlock->isPinned == 0 && pTempBlock->base.size <= availableSize)
                {
                    uint32_t pinDstAddress = (pBlock->pPrev == NULL) ?
                                             pAllocator->startAddress :
                                             pBlock->pPrev->base.address + pBlock->pPrev->base.size;
                    uint32_t pinSrcAddress = pTempBlock->base.address;

                    dstOffset = gmmAddressToOffset(pinDstAddress, 0);
                    srcOffset = gmmAddressToOffset(pinSrcAddress, 0);

                    totalMoveSize += pTempBlock->base.size;

                    gmmMemcpy(dstOffset, srcOffset, pTempBlock->base.size);

                    pTempBlock->base.address = pinDstAddress;

                    if (pTempBlock == pAllocator->pTail)
                    {
                        if (pTempBlock->pNext)
                            pAllocator->pTail = pTempBlock->pNext;
                        else
                            pAllocator->pTail = pTempBlock->pPrev;
                    }

                    if (pTempBlock->pNext)
                        pTempBlock->pNext->pPrev = pTempBlock->pPrev;

                    if (pTempBlock->pPrev)
                        pTempBlock->pPrev->pNext = pTempBlock->pNext;

                    if (pBlock->pPrev)
                        pBlock->pPrev->pNext = pTempBlock;
                    else
                        pAllocator->pHead = pTempBlock;

                    pTempBlock->pPrev = pBlock->pPrev;
                    pTempBlock->pNext = pBlock;
                    pBlock->pPrev = pTempBlock;
                }

                if (pBlock->pPrev)
                    availableSize = pBlock->base.address - (pBlock->pPrev->base.address + pBlock->pPrev->base.size);

                pTempBlock = pTempBlockNext;
            }

            if (availableSize)
            {
                GmmBlock *pNewBlock = (GmmBlock *)gmmAllocFixed(0);

                if (pNewBlock)
                {
                    memset(pNewBlock, 0, sizeof(GmmBlock));
                    pNewBlock->base.address = pBlock->base.address - availableSize;
                    pNewBlock->base.isMain = pBlock->base.isMain;
                    pNewBlock->base.size = availableSize;
                    pNewBlock->pNext = pBlock;
                    pNewBlock->pPrev = pBlock->pPrev;

                    if (pBlock->pPrev)
                        pBlock->pPrev->pNext = pNewBlock;

                    pBlock->pPrev = pNewBlock;

                    if (pBlock == pAllocator->pHead)
                        pAllocator->pHead = pNewBlock;

                    gmmAddFree(pAllocator, pNewBlock);

                    ret = 1;
                }
            }
        }

        pBlock = pBlock->pNext;
    }

    uint32_t newFreeAddress = pAllocator->pTail ? 
                              pAllocator->pTail->base.address + pAllocator->pTail->base.size :
                              pAllocator->startAddress;

    if (pAllocator->freeAddress != newFreeAddress)
    {
        pAllocator->freeAddress = newFreeAddress;
        ret = 1;
    }

    pAllocator->freedSinceSweep = 0;
    pAllocator->pSweepHead = NULL;


    return ret;
}


static void gmmFreeAll (void)
{
    GmmAllocator    *pAllocator = pGmmLocalAllocator;
    GmmBlock        *pBlock;
    GmmBlock        *pTemp;

    pBlock = pAllocator->pPendingFreeHead;
    while (pBlock)
    {
        pTemp = pBlock->pNextFree;
        gmmFreeBlock(pBlock);
        pBlock = pTemp;
    }
    pAllocator->pPendingFreeHead = NULL;
    pAllocator->pPendingFreeTail = NULL;

    for (int i=0; i<GMM_NUM_FREE_BINS; i++)
    {
        pBlock = pAllocator->pFreeHead[i];
        while (pBlock)
        {
            pTemp = pBlock->pNextFree;
            gmmFreeBlock(pBlock);
            pBlock = pTemp;
        }
        pAllocator->pFreeHead[i] = NULL;
        pAllocator->pFreeTail[i] = NULL;
    }
}

static void gmmRemoveFree(
    GmmAllocator *pAllocator,
    GmmBlock *pBlock,
    uint8_t freeIndex
)
{

    if (pBlock == pAllocator->pFreeHead[freeIndex])
        pAllocator->pFreeHead[freeIndex] = pBlock->pNextFree;

    if (pBlock == pAllocator->pFreeTail[freeIndex])
        pAllocator->pFreeTail[freeIndex] = pBlock->pPrevFree;

    if (pBlock->pNextFree)
        pBlock->pNextFree->pPrevFree = pBlock->pPrevFree;

    if (pBlock->pPrevFree)
        pBlock->pPrevFree->pNextFree = pBlock->pNextFree;
}

static uint32_t gmmFindFreeBlock(
    GmmAllocator    *pAllocator,
    uint32_t        size
)
{
    uint32_t        retId = GMM_ERROR;
    GmmBlock        *pBlock;
    uint8_t         found = 0;
    uint8_t         freeIndex = gmmSizeToFreeIndex(size);

    pBlock = pAllocator->pFreeHead[freeIndex];

    while (freeIndex < GMM_NUM_FREE_BINS)
    {
        if (pBlock)
        {
            if (pBlock->base.size >= size)
            {
                found = 1;
                break;
            }
            pBlock = pBlock->pNextFree;
        }
        else if (++freeIndex < GMM_NUM_FREE_BINS)
            pBlock = pAllocator->pFreeHead[freeIndex];
    }

    if (found)
    {
        if (pBlock->base.size != size)
        {
            // create a new block here
            GmmBlock *pNewBlock = (GmmBlock *)gmmAllocFixed(0);
            if (pNewBlock == NULL)
                return GMM_ERROR;

            memset(pNewBlock, 0, sizeof(GmmBlock));
            pNewBlock->base.address = pBlock->base.address + size;
            pNewBlock->base.isMain = pBlock->base.isMain;
            pNewBlock->base.size = pBlock->base.size - size;
            pNewBlock->pNext = pBlock->pNext;
            pNewBlock->pPrev = pBlock;

            if (pBlock->pNext)
                pBlock->pNext->pPrev = pNewBlock;

            pBlock->pNext = pNewBlock;

            if (pBlock == pAllocator->pTail)
                pAllocator->pTail = pNewBlock;

            gmmAddFree(pAllocator, pNewBlock);
        }
        pBlock->base.size = size;
        gmmRemoveFree(pAllocator, pBlock, freeIndex);
        retId = (uint32_t)pBlock;
    }

    return retId;
}

uint32_t gmmAlloc(const uint8_t isTile, const uint32_t size)
{
   CellGcmContextData *thisContext = (CellGcmContextData*)&_RGLState.fifo;
   GmmAllocator    *pAllocator;
   uint32_t        retId;
   uint32_t        newSize;

   if (__builtin_expect((size == 0),0))
      return GMM_ERROR;

   pAllocator = pGmmLocalAllocator;

   if (!isTile)
   {
      newSize = pad(size, GMM_ALIGNMENT);
      retId = gmmFindFreeBlock(pAllocator, newSize);
   }
   else
   {
      newSize = pad(size, GMM_TILE_ALIGNMENT);
      retId = GMM_ERROR;
   }

   if (retId == GMM_ERROR)
   {
      if (isTile)
         retId = (uint32_t)gmmAllocTileBlock(pAllocator, newSize);
      else
         retId = (uint32_t)gmmAllocBlock(pAllocator, newSize);

      if (retId == RGL_MEMORY_ALLOC_ERROR)
      {
         gmmFreeAll();

	 if (gmmInternalSweep())
	 {
            *pLock = 1;
	    cachedLockValue = 1;
	    cellGcmSetWriteBackEndLabel(thisContext, GMM_PPU_WAIT_INDEX, 0);

	    cellGcmFlush(thisContext);
	 }

	 if (isTile)
            retId = (uint32_t)gmmAllocTileBlock(pAllocator, newSize);
	 else
            retId = (uint32_t)gmmAllocBlock(pAllocator, newSize);

	 if (!isTile && retId == RGL_MEMORY_ALLOC_ERROR)
            retId = gmmFindFreeBlock(pAllocator, newSize);
      }
   }

   return retId;
}


static int _RGLLoadFPShader( _CGprogram *program )
{
   unsigned int ucodeSize = program->header.instructionCount * 16;

   if ( program->loadProgramId == GMM_ERROR )
   {
      program->loadProgramId = gmmAlloc(0, ucodeSize);
      program->loadProgramOffset = 0;
   }

   unsigned int dstId = program->loadProgramId;
   unsigned dstOffset = program->loadProgramOffset;
   const char *src = (char*)program->ucode;

   GLuint id = gmmAlloc(0, ucodeSize);

   memcpy( gmmIdToAddress(id), src, ucodeSize );
   _RGLMemcpy( dstId, dstOffset, 0, id, ucodeSize );

   gmmFree( id );
   return GL_TRUE;
}

static void _RGLUnloadFPShader( _CGprogram *program )
{
   if ( program->loadProgramId != GMM_ERROR )
   {
      gmmFree( program->loadProgramId );
      program->loadProgramId = GMM_ERROR;
      program->loadProgramOffset = 0;
   }
}

typedef struct RGLparamUIDRec
{
   char * string;
   GLboolean global;
} RGLparamUID;

typedef struct RGLparamUIDTableRec
{
   int count;
   RGLparamUID* uids;
} RGLparamUIDTable;


RGLparamUID getParamUIDByIndex( const RGLparamUIDTable* st, int index )
{
   return st->uids[index];
}

void _RGLPlatformSetVertexRegister4fv( unsigned int reg, const float * __restrict v ) {}
void _RGLPlatformSetVertexRegisterBlock( unsigned int reg, unsigned int count, const float * __restrict v ) {}
void _RGLPlatformSetFragmentRegister4fv( unsigned int reg, const float * __restrict v ) {}
void _RGLPlatformSetFragmentRegisterBlock( unsigned int reg, unsigned int count, const float * __restrict v ) {}

template<int SIZE> inline static void swapandsetfp( int ucodeSize, unsigned int loadProgramId, unsigned int loadProgramOffset, unsigned short *ec, const unsigned int   * __restrict v )
{
   cellGcmSetTransferLocationInline ( &_RGLState.fifo, CELL_GCM_LOCATION_LOCAL);
   unsigned short count = *( ec++ );
   for ( unsigned long offsetIndex = 0; offsetIndex < count; ++offsetIndex )
   {
      void *pointer=NULL;
      const int paddedSIZE = (SIZE + 1) & ~1;
      cellGcmSetInlineTransferPointerInline( &_RGLState.fifo, gmmIdToOffset( loadProgramId ) + loadProgramOffset + *(ec++), paddedSIZE, &pointer);
      float *fp = (float*)pointer;
      float *src = (float*)v;
      for (uint32_t j=0; j<SIZE;j++)
      {
         *fp = cellGcmSwap16Float32(*src);
	 fp++;src++;
      }
   }
}

template<int SIZE> static void setVectorTypefp( CgRuntimeParameter* __restrict ptr, const void* __restrict v )
{
   float * __restrict  f = ( float* )v;
   float * __restrict  data = ( float* )ptr->pushBufferPointer;

   for ( long i = 0; i < SIZE; ++i )
      data[i] = f[i];

   _CGprogram *program = ptr->program;

   CgParameterResource *parameterResource = _RGLGetParameterResource( ptr->program, ptr->parameterEntry );
   unsigned short resource = parameterResource->resource;
   unsigned short *ec = ( unsigned short * )( ptr->program->resources ) + resource + 1;
   if ( RGL_LIKELY( *ec ) )
   {
      swapandsetfp<SIZE>( program->header.instructionCount*16, program->loadProgramId, program->loadProgramOffset, ec, ( unsigned int * )data );
   }
}

template<int SIZE> static void setVectorTypeSharedfpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int ) {}

template<int SIZE> static void setVectorTypeSharedfpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index ) {}

template<int SIZE> static void setVectorTypeSharedvpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int )
{
    const float * __restrict f = ( const float * __restrict )v;
    const CgParameterResource *parameterResource = _RGLGetParameterResource( ptr->program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;
    float * __restrict dst = ( float * __restrict )ptr->pushBufferPointer;
    for ( long i = 0; i < SIZE; ++ i )
        dst[i] = f[i];
    _RGLPlatformSetVertexRegister4fv( resource, dst );
}

template<int SIZE> static void setVectorTypeSharedvpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    const float * __restrict f = ( const float * __restrict )v;
    const CgParameterResource *parameterResource = _RGLGetParameterResource( ptr->program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource + index;
    float * __restrict dst = ( float * __restrict )ptr->pushBufferPointer;
    for ( long i = 0; i < SIZE; ++ i )
        dst[i] = f[i];
    _RGLPlatformSetVertexRegister4fv( resource, dst );
}

template <int SIZE> static void setVectorTypevpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int )
{
    PSGLcontext * LContext = _CurrentContext;
    const float * __restrict f = ( const float* )v;
    float * __restrict dst = ( float* )ptr->pushBufferPointer;
    for ( long i = 0; i < SIZE; ++ i )
        dst[i] = f[i];
    LContext->needValidate |= PSGL_VALIDATE_VERTEX_CONSTANTS;
}
template <int SIZE> static void setVectorTypevpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    PSGLcontext * LContext = _CurrentContext;
    const float * __restrict f = ( const float* )v;
    float *  __restrict dst = ( float* )( *(( unsigned int ** )ptr->pushBufferPointer + index ) );
    for ( long i = 0; i < SIZE; ++ i )
        dst[i] = f[i];
    LContext->needValidate |= PSGL_VALIDATE_VERTEX_CONSTANTS;
}
template<int SIZE> static void setVectorTypefpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int )
{
    float * __restrict  f = ( float* )v;
    float * __restrict  data = ( float* )ptr->pushBufferPointer;
    for ( long i = 0; i < SIZE; ++i )
        data[i] = f[i];
    _CGprogram *program = ptr->program;

    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;
    unsigned short *ec = ( unsigned short * )( ptr->program->resources ) + resource + 1;
    if ( RGL_LIKELY( *ec ) )
    {
        swapandsetfp<SIZE>( program->header.instructionCount*16, program->loadProgramId, program->loadProgramOffset, ec, ( unsigned int * )data );
    }
}
template<int SIZE> static void setVectorTypefpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    float * __restrict  f = ( float* )v;
    float * __restrict  data = ( float* )ptr->pushBufferPointer;
    for ( long i = 0; i < SIZE; ++i )
        data[i] = f[i];
    _CGprogram *program = ptr->program;

    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;
    unsigned short *ec = ( unsigned short * )( program->resources ) + resource + 1;
    int arrayIndex = index;
    while ( arrayIndex )
    {
        ec += (( *ec ) + 2 );
        arrayIndex--;
    }
    if ( RGL_LIKELY( *ec ) )
    {
        swapandsetfp<SIZE>( program->header.instructionCount*16, program->loadProgramId, program->loadProgramOffset, ec, ( unsigned int * )data );
    }
}

template <int ROWS, int COLS, int ORDER> static void setMatrixvpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    PSGLcontext * LContext = _CurrentContext;
    float *  __restrict f = ( float* )v;
    float *  __restrict dst = ( float* )ptr->pushBufferPointer;
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
            dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
    }
    LContext->needValidate |= PSGL_VALIDATE_VERTEX_CONSTANTS;
}

template <int ROWS, int COLS, int ORDER> static void setMatrixSharedvpIndex( CgRuntimeParameter*  __restrict ptr, const void*  __restrict v, const int /*index*/ )
{
    float * __restrict f = ( float* )v;
    float * __restrict dst = ( float* )ptr->pushBufferPointer;

    const CgParameterResource *parameterResource = _RGLGetParameterResource( ptr->program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;

    float tmp[ROWS*4];
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
        {
            tmp[row*4 + col] = dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
        }
        for ( long col = COLS; col < 4; ++col ) tmp[row*4 + col] = dst[row*4+col];
    }

    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, resource, ROWS, (const float*)tmp);
}

template <int ROWS, int COLS, int ORDER> static void setMatrixSharedvpIndexArray( CgRuntimeParameter*  __restrict ptr, const void*  __restrict v, const int index )
{
    float * __restrict f = ( float* )v;
    float * __restrict dst = ( float* )ptr->pushBufferPointer;

    const CgParameterResource *parameterResource = _RGLGetParameterResource( ptr->program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource + index * ROWS;

    float tmp[ROWS*4];
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
        {
            tmp[row*4 + col] = dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
        }
        for ( long col = COLS; col < 4; ++col ) tmp[row*4 + col] = dst[row*4+col];
    }
    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, resource, ROWS, (const float*)tmp);
}

template <int ROWS, int COLS, int ORDER> static void setMatrixSharedfpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int /*index*/ )
{
}

template <int ROWS, int COLS, int ORDER> static void setMatrixSharedfpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
}

template <int ROWS, int COLS, int ORDER> static void setMatrixvpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    PSGLcontext * LContext = _CurrentContext;
    float *  __restrict f = ( float* )v;
    float *  __restrict dst = ( float* )( *(( unsigned int ** )ptr->pushBufferPointer + index ) );
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
            dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
    }
    LContext->needValidate |= PSGL_VALIDATE_VERTEX_CONSTANTS;
}
template <int ROWS, int COLS, int ORDER> static void setMatrixfpIndex( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int /*index*/ )
{
    float *  __restrict f = ( float* )v;
    float *  __restrict dst = ( float* )ptr->pushBufferPointer;
    _CGprogram *program = (( CgRuntimeParameter* )ptr )->program;
    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;
    unsigned short *ec = ( unsigned short * )program->resources + resource + 1;
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
            dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
        int count = *ec;
        if ( RGL_LIKELY( count ) )
        {
            swapandsetfp<COLS>( program->header.instructionCount*16, program->loadProgramId, program->loadProgramOffset, ec, ( unsigned int * )dst + row * 4 );
        }
        ec += count + 2;
    }
}
template <int ROWS, int COLS, int ORDER> static void setMatrixfpIndexArray( CgRuntimeParameter* __restrict ptr, const void* __restrict v, const int index )
{
    float *  __restrict f = ( float* )v;
    float *  __restrict dst = ( float* )ptr->pushBufferPointer;
    _CGprogram *program = ptr->program;
    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, ptr->parameterEntry );
    unsigned short resource = parameterResource->resource;
    unsigned short *ec = ( unsigned short * )program->resources + resource + 1;
    int arrayIndex = index * ROWS;
    while ( arrayIndex ) 
    {
        unsigned short count = ( *ec );
        ec += ( count + 2 ); 
        arrayIndex--;
    }
    for ( long row = 0; row < ROWS; ++row )
    {
        for ( long col = 0; col < COLS; ++col )
            dst[row * 4 + col] = ( ORDER == ROW_MAJOR ) ? f[row * COLS + col] : f[col * ROWS + row];
        int count = *ec;
        if ( RGL_LIKELY( count ) )
        {
            swapandsetfp<COLS>( program->header.instructionCount*16, program->loadProgramId, program->loadProgramOffset, ec, ( unsigned int * )dst + row * 4 );
        }
        ec += count + 2;
    }
}

static _cgSetArrayIndexFunction setVectorTypeIndex[2][2][2][4] =
    {
        {
            {
                {&setVectorTypevpIndex<1>, &setVectorTypevpIndex<2>, &setVectorTypevpIndex<3>, &setVectorTypevpIndex<4>, },
                {&setVectorTypefpIndex<1>, &setVectorTypefpIndex<2>, &setVectorTypefpIndex<3>, &setVectorTypefpIndex<4>, }
            },
            {
                {&setVectorTypeSharedvpIndex<1>, &setVectorTypeSharedvpIndex<2>, &setVectorTypeSharedvpIndex<3>, &setVectorTypeSharedvpIndex<4>, }, 
                {&setVectorTypeSharedfpIndex<1>, &setVectorTypeSharedfpIndex<2>, &setVectorTypeSharedfpIndex<3>, &setVectorTypeSharedfpIndex<4>, } 
            },
        },
        {
            {
                {&setVectorTypevpIndexArray<1>, &setVectorTypevpIndexArray<2>, &setVectorTypevpIndexArray<3>, &setVectorTypevpIndexArray<4>, },
                {&setVectorTypefpIndexArray<1>, &setVectorTypefpIndexArray<2>, &setVectorTypefpIndexArray<3>, &setVectorTypefpIndexArray<4>, }
            },
            {
                {&setVectorTypeSharedvpIndexArray<1>, &setVectorTypeSharedvpIndexArray<2>, &setVectorTypeSharedvpIndexArray<3>, &setVectorTypeSharedvpIndexArray<4>, }, //should be the shared
                {&setVectorTypeSharedfpIndexArray<1>, &setVectorTypeSharedfpIndexArray<2>, &setVectorTypeSharedfpIndexArray<3>, &setVectorTypeSharedfpIndexArray<4>, } //should be the shared
            },
        },
    };

static _cgSetArrayIndexFunction setMatrixTypeIndex[2][2][2][4][4][2] =
    {
        {
            {
                {
                    {{ &setMatrixvpIndex<1, 1, 0>, &setMatrixvpIndex<1, 1, 1>}, { &setMatrixvpIndex<1, 2, 0>, &setMatrixvpIndex<1, 2, 1>}, { &setMatrixvpIndex<1, 3, 0>, &setMatrixvpIndex<1, 3, 1>}, { &setMatrixvpIndex<1, 4, 0>, &setMatrixvpIndex<1, 4, 1>}},
                    {{ &setMatrixvpIndex<2, 1, 0>, &setMatrixvpIndex<2, 1, 1>}, { &setMatrixvpIndex<2, 2, 0>, &setMatrixvpIndex<2, 2, 1>}, { &setMatrixvpIndex<2, 3, 0>, &setMatrixvpIndex<2, 3, 1>}, { &setMatrixvpIndex<2, 4, 0>, &setMatrixvpIndex<2, 4, 1>}},
                    {{ &setMatrixvpIndex<3, 1, 0>, &setMatrixvpIndex<3, 1, 1>}, { &setMatrixvpIndex<3, 2, 0>, &setMatrixvpIndex<3, 2, 1>}, { &setMatrixvpIndex<3, 3, 0>, &setMatrixvpIndex<3, 3, 1>}, { &setMatrixvpIndex<3, 4, 0>, &setMatrixvpIndex<3, 4, 1>}},
                    {{ &setMatrixvpIndex<4, 1, 0>, &setMatrixvpIndex<4, 1, 1>}, { &setMatrixvpIndex<4, 2, 0>, &setMatrixvpIndex<4, 2, 1>}, { &setMatrixvpIndex<4, 3, 0>, &setMatrixvpIndex<4, 3, 1>}, { &setMatrixvpIndex<4, 4, 0>, &setMatrixvpIndex<4, 4, 1>}},
                },
                {
                    {{ &setMatrixfpIndex<1, 1, 0>, &setMatrixfpIndex<1, 1, 1>}, { &setMatrixfpIndex<1, 2, 0>, &setMatrixfpIndex<1, 2, 1>}, { &setMatrixfpIndex<1, 3, 0>, &setMatrixfpIndex<1, 3, 1>}, { &setMatrixfpIndex<1, 4, 0>, &setMatrixfpIndex<1, 4, 1>}},
                    {{ &setMatrixfpIndex<2, 1, 0>, &setMatrixfpIndex<2, 1, 1>}, { &setMatrixfpIndex<2, 2, 0>, &setMatrixfpIndex<2, 2, 1>}, { &setMatrixfpIndex<2, 3, 0>, &setMatrixfpIndex<2, 3, 1>}, { &setMatrixfpIndex<2, 4, 0>, &setMatrixfpIndex<2, 4, 1>}},
                    {{ &setMatrixfpIndex<3, 1, 0>, &setMatrixfpIndex<3, 1, 1>}, { &setMatrixfpIndex<3, 2, 0>, &setMatrixfpIndex<3, 2, 1>}, { &setMatrixfpIndex<3, 3, 0>, &setMatrixfpIndex<3, 3, 1>}, { &setMatrixfpIndex<3, 4, 0>, &setMatrixfpIndex<3, 4, 1>}},
                    {{ &setMatrixfpIndex<4, 1, 0>, &setMatrixfpIndex<4, 1, 1>}, { &setMatrixfpIndex<4, 2, 0>, &setMatrixfpIndex<4, 2, 1>}, { &setMatrixfpIndex<4, 3, 0>, &setMatrixfpIndex<4, 3, 1>}, { &setMatrixfpIndex<4, 4, 0>, &setMatrixfpIndex<4, 4, 1>}},
                },
            },
            { //should be shared
                {
                    {{ &setMatrixSharedvpIndex<1, 1, 0>, &setMatrixSharedvpIndex<1, 1, 1>}, { &setMatrixSharedvpIndex<1, 2, 0>, &setMatrixSharedvpIndex<1, 2, 1>}, { &setMatrixSharedvpIndex<1, 3, 0>, &setMatrixSharedvpIndex<1, 3, 1>}, { &setMatrixSharedvpIndex<1, 4, 0>, &setMatrixSharedvpIndex<1, 4, 1>}},
                    {{ &setMatrixSharedvpIndex<2, 1, 0>, &setMatrixSharedvpIndex<2, 1, 1>}, { &setMatrixSharedvpIndex<2, 2, 0>, &setMatrixSharedvpIndex<2, 2, 1>}, { &setMatrixSharedvpIndex<2, 3, 0>, &setMatrixSharedvpIndex<2, 3, 1>}, { &setMatrixSharedvpIndex<2, 4, 0>, &setMatrixSharedvpIndex<2, 4, 1>}},
                    {{ &setMatrixSharedvpIndex<3, 1, 0>, &setMatrixSharedvpIndex<3, 1, 1>}, { &setMatrixSharedvpIndex<3, 2, 0>, &setMatrixSharedvpIndex<3, 2, 1>}, { &setMatrixSharedvpIndex<3, 3, 0>, &setMatrixSharedvpIndex<3, 3, 1>}, { &setMatrixSharedvpIndex<3, 4, 0>, &setMatrixSharedvpIndex<3, 4, 1>}},
                    {{ &setMatrixSharedvpIndex<4, 1, 0>, &setMatrixSharedvpIndex<4, 1, 1>}, { &setMatrixSharedvpIndex<4, 2, 0>, &setMatrixSharedvpIndex<4, 2, 1>}, { &setMatrixSharedvpIndex<4, 3, 0>, &setMatrixSharedvpIndex<4, 3, 1>}, { &setMatrixSharedvpIndex<4, 4, 0>, &setMatrixSharedvpIndex<4, 4, 1>}},
                },
                {
                    {{ &setMatrixSharedfpIndex<1, 1, 0>, &setMatrixSharedfpIndex<1, 1, 1>}, { &setMatrixSharedfpIndex<1, 2, 0>, &setMatrixSharedfpIndex<1, 2, 1>}, { &setMatrixSharedfpIndex<1, 3, 0>, &setMatrixSharedfpIndex<1, 3, 1>}, { &setMatrixSharedfpIndex<1, 4, 0>, &setMatrixSharedfpIndex<1, 4, 1>}},
                    {{ &setMatrixSharedfpIndex<2, 1, 0>, &setMatrixSharedfpIndex<2, 1, 1>}, { &setMatrixSharedfpIndex<2, 2, 0>, &setMatrixSharedfpIndex<2, 2, 1>}, { &setMatrixSharedfpIndex<2, 3, 0>, &setMatrixSharedfpIndex<2, 3, 1>}, { &setMatrixSharedfpIndex<2, 4, 0>, &setMatrixSharedfpIndex<2, 4, 1>}},
                    {{ &setMatrixSharedfpIndex<3, 1, 0>, &setMatrixSharedfpIndex<3, 1, 1>}, { &setMatrixSharedfpIndex<3, 2, 0>, &setMatrixSharedfpIndex<3, 2, 1>}, { &setMatrixSharedfpIndex<3, 3, 0>, &setMatrixSharedfpIndex<3, 3, 1>}, { &setMatrixSharedfpIndex<3, 4, 0>, &setMatrixSharedfpIndex<3, 4, 1>}},
                    {{ &setMatrixSharedfpIndex<4, 1, 0>, &setMatrixSharedfpIndex<4, 1, 1>}, { &setMatrixSharedfpIndex<4, 2, 0>, &setMatrixSharedfpIndex<4, 2, 1>}, { &setMatrixSharedfpIndex<4, 3, 0>, &setMatrixSharedfpIndex<4, 3, 1>}, { &setMatrixSharedfpIndex<4, 4, 0>, &setMatrixSharedfpIndex<4, 4, 1>}},
                },
            },
        },
        {
            {
                {
                    {{ &setMatrixvpIndexArray<1, 1, 0>, &setMatrixvpIndexArray<1, 1, 1>}, { &setMatrixvpIndexArray<1, 2, 0>, &setMatrixvpIndexArray<1, 2, 1>}, { &setMatrixvpIndexArray<1, 3, 0>, &setMatrixvpIndexArray<1, 3, 1>}, { &setMatrixvpIndexArray<1, 4, 0>, &setMatrixvpIndexArray<1, 4, 1>}},
                    {{ &setMatrixvpIndexArray<2, 1, 0>, &setMatrixvpIndexArray<2, 1, 1>}, { &setMatrixvpIndexArray<2, 2, 0>, &setMatrixvpIndexArray<2, 2, 1>}, { &setMatrixvpIndexArray<2, 3, 0>, &setMatrixvpIndexArray<2, 3, 1>}, { &setMatrixvpIndexArray<2, 4, 0>, &setMatrixvpIndexArray<2, 4, 1>}},
                    {{ &setMatrixvpIndexArray<3, 1, 0>, &setMatrixvpIndexArray<3, 1, 1>}, { &setMatrixvpIndexArray<3, 2, 0>, &setMatrixvpIndexArray<3, 2, 1>}, { &setMatrixvpIndexArray<3, 3, 0>, &setMatrixvpIndexArray<3, 3, 1>}, { &setMatrixvpIndexArray<3, 4, 0>, &setMatrixvpIndexArray<3, 4, 1>}},
                    {{ &setMatrixvpIndexArray<4, 1, 0>, &setMatrixvpIndexArray<4, 1, 1>}, { &setMatrixvpIndexArray<4, 2, 0>, &setMatrixvpIndexArray<4, 2, 1>}, { &setMatrixvpIndexArray<4, 3, 0>, &setMatrixvpIndexArray<4, 3, 1>}, { &setMatrixvpIndexArray<4, 4, 0>, &setMatrixvpIndexArray<4, 4, 1>}},
                },
                {
                    {{ &setMatrixfpIndexArray<1, 1, 0>, &setMatrixfpIndexArray<1, 1, 1>}, { &setMatrixfpIndexArray<1, 2, 0>, &setMatrixfpIndexArray<1, 2, 1>}, { &setMatrixfpIndexArray<1, 3, 0>, &setMatrixfpIndexArray<1, 3, 1>}, { &setMatrixfpIndexArray<1, 4, 0>, &setMatrixfpIndexArray<1, 4, 1>}},
                    {{ &setMatrixfpIndexArray<2, 1, 0>, &setMatrixfpIndexArray<2, 1, 1>}, { &setMatrixfpIndexArray<2, 2, 0>, &setMatrixfpIndexArray<2, 2, 1>}, { &setMatrixfpIndexArray<2, 3, 0>, &setMatrixfpIndexArray<2, 3, 1>}, { &setMatrixfpIndexArray<2, 4, 0>, &setMatrixfpIndexArray<2, 4, 1>}},
                    {{ &setMatrixfpIndexArray<3, 1, 0>, &setMatrixfpIndexArray<3, 1, 1>}, { &setMatrixfpIndexArray<3, 2, 0>, &setMatrixfpIndexArray<3, 2, 1>}, { &setMatrixfpIndexArray<3, 3, 0>, &setMatrixfpIndexArray<3, 3, 1>}, { &setMatrixfpIndexArray<3, 4, 0>, &setMatrixfpIndexArray<3, 4, 1>}},
                    {{ &setMatrixfpIndexArray<4, 1, 0>, &setMatrixfpIndexArray<4, 1, 1>}, { &setMatrixfpIndexArray<4, 2, 0>, &setMatrixfpIndexArray<4, 2, 1>}, { &setMatrixfpIndexArray<4, 3, 0>, &setMatrixfpIndexArray<4, 3, 1>}, { &setMatrixfpIndexArray<4, 4, 0>, &setMatrixfpIndexArray<4, 4, 1>}},
                },
            },
            { //should be shared
                {
                    {{ &setMatrixSharedvpIndexArray<1, 1, 0>, &setMatrixSharedvpIndexArray<1, 1, 1>}, { &setMatrixSharedvpIndexArray<1, 2, 0>, &setMatrixSharedvpIndexArray<1, 2, 1>}, { &setMatrixSharedvpIndexArray<1, 3, 0>, &setMatrixSharedvpIndexArray<1, 3, 1>}, { &setMatrixSharedvpIndexArray<1, 4, 0>, &setMatrixSharedvpIndexArray<1, 4, 1>}},
                    {{ &setMatrixSharedvpIndexArray<2, 1, 0>, &setMatrixSharedvpIndexArray<2, 1, 1>}, { &setMatrixSharedvpIndexArray<2, 2, 0>, &setMatrixSharedvpIndexArray<2, 2, 1>}, { &setMatrixSharedvpIndexArray<2, 3, 0>, &setMatrixSharedvpIndexArray<2, 3, 1>}, { &setMatrixSharedvpIndexArray<2, 4, 0>, &setMatrixSharedvpIndexArray<2, 4, 1>}},
                    {{ &setMatrixSharedvpIndexArray<3, 1, 0>, &setMatrixSharedvpIndexArray<3, 1, 1>}, { &setMatrixSharedvpIndexArray<3, 2, 0>, &setMatrixSharedvpIndexArray<3, 2, 1>}, { &setMatrixSharedvpIndexArray<3, 3, 0>, &setMatrixSharedvpIndexArray<3, 3, 1>}, { &setMatrixSharedvpIndexArray<3, 4, 0>, &setMatrixSharedvpIndexArray<3, 4, 1>}},
                    {{ &setMatrixSharedvpIndexArray<4, 1, 0>, &setMatrixSharedvpIndexArray<4, 1, 1>}, { &setMatrixSharedvpIndexArray<4, 2, 0>, &setMatrixSharedvpIndexArray<4, 2, 1>}, { &setMatrixSharedvpIndexArray<4, 3, 0>, &setMatrixSharedvpIndexArray<4, 3, 1>}, { &setMatrixSharedvpIndexArray<4, 4, 0>, &setMatrixSharedvpIndexArray<4, 4, 1>}},
                },
                {
                    {{ &setMatrixSharedfpIndexArray<1, 1, 0>, &setMatrixSharedfpIndexArray<1, 1, 1>}, { &setMatrixSharedfpIndexArray<1, 2, 0>, &setMatrixSharedfpIndexArray<1, 2, 1>}, { &setMatrixSharedfpIndexArray<1, 3, 0>, &setMatrixSharedfpIndexArray<1, 3, 1>}, { &setMatrixSharedfpIndexArray<1, 4, 0>, &setMatrixSharedfpIndexArray<1, 4, 1>}},
                    {{ &setMatrixSharedfpIndexArray<2, 1, 0>, &setMatrixSharedfpIndexArray<2, 1, 1>}, { &setMatrixSharedfpIndexArray<2, 2, 0>, &setMatrixSharedfpIndexArray<2, 2, 1>}, { &setMatrixSharedfpIndexArray<2, 3, 0>, &setMatrixSharedfpIndexArray<2, 3, 1>}, { &setMatrixSharedfpIndexArray<2, 4, 0>, &setMatrixSharedfpIndexArray<2, 4, 1>}},
                    {{ &setMatrixSharedfpIndexArray<3, 1, 0>, &setMatrixSharedfpIndexArray<3, 1, 1>}, { &setMatrixSharedfpIndexArray<3, 2, 0>, &setMatrixSharedfpIndexArray<3, 2, 1>}, { &setMatrixSharedfpIndexArray<3, 3, 0>, &setMatrixSharedfpIndexArray<3, 3, 1>}, { &setMatrixSharedfpIndexArray<3, 4, 0>, &setMatrixSharedfpIndexArray<3, 4, 1>}},
                    {{ &setMatrixSharedfpIndexArray<4, 1, 0>, &setMatrixSharedfpIndexArray<4, 1, 1>}, { &setMatrixSharedfpIndexArray<4, 2, 0>, &setMatrixSharedfpIndexArray<4, 2, 1>}, { &setMatrixSharedfpIndexArray<4, 3, 0>, &setMatrixSharedfpIndexArray<4, 3, 1>}, { &setMatrixSharedfpIndexArray<4, 4, 0>, &setMatrixSharedfpIndexArray<4, 4, 1>}},
                },
            },
        }
    };

_cgSetArrayIndexFunction getVectorTypeIndexSetterFunction( unsigned short a, unsigned short b, unsigned short c, unsigned short d )
{
    return setVectorTypeIndex[a][b][c][d];
}

_cgSetArrayIndexFunction getMatrixTypeIndexSetterFunction( unsigned short a, unsigned short b, unsigned short c, unsigned short d, unsigned short e, unsigned short f )
{
    return setMatrixTypeIndex[a][b][c][d][e][f];
}

static void _RGLBindTextureInternal( jsTextureImageUnit *unit, GLuint name)
{
   PSGLcontext*	LContext = _CurrentContext;
   jsTexture *texture = NULL;
   if ( name )
   {
      _RGLTexNameSpaceCreateNameLazy( &LContext->textureNameSpace, name );
      texture = ( jsTexture * )LContext->textureNameSpace.data[name];
      texture->target = GL_TEXTURE_2D;
   }

   unit->bound2D = name;

   unit->currentTexture = _RGLGetCurrentTexture( unit, GL_TEXTURE_2D );
   LContext->needValidate |= PSGL_VALIDATE_TEXTURES_USED;
}

static void setSamplerfp( CgRuntimeParameter*ptr, const void*v, int )
{
   _CGprogram *program = (( CgRuntimeParameter* )ptr )->program;
   const CgParameterResource *parameterResource = _RGLGetParameterResource( program, (( CgRuntimeParameter* )ptr )->parameterEntry );

   if ( v )
      *( GLuint* )ptr->pushBufferPointer = *( GLuint* )v;
   else
   {
      jsTextureImageUnit *unit = _CurrentContext->TextureImageUnits + ( parameterResource->resource - CG_TEXUNIT0 );
      _RGLBindTextureInternal( unit, *( GLuint* )ptr->pushBufferPointer);
   }
}

static void setSamplervp( CgRuntimeParameter*ptr, const void*v, int )
{
    _CGprogram *program = (( CgRuntimeParameter* )ptr )->program;
    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, (( CgRuntimeParameter* )ptr )->parameterEntry );

    if ( v )
    {
        *( GLuint* )ptr->pushBufferPointer = *( GLuint* )v;
    }
    else
    {
	    GLuint unit = parameterResource->resource - CG_TEXUNIT0;
	    GLuint name = *( GLuint* )ptr->pushBufferPointer;
	    PSGLcontext *LContext = _CurrentContext;
	    jsTexture *texture = NULL;

	    if ( name && (name < LContext->textureNameSpace.capacity) )
		    texture = ( jsTexture * )LContext->textureNameSpace.data[name];

	    LContext->VertexTextureImages[unit] = texture;
	    LContext->needValidate |= PSGL_VALIDATE_VERTEX_TEXTURES_USED;
    }
}

static void _RGLCreatePushBuffer( _CGprogram *program )
{
    int bufferSize = 0;
    int programPushBufferPointersSize = 0;
    int extraStorageInWords = 0;
    int offsetCount = 0;
    int samplerCount = 0;
    int profileIndex = ( program->header.profile == CG_PROFILE_SCE_FP_TYPEB ||
                         program->header.profile == CG_PROFILE_SCE_FP_RSX ) ? FRAGMENT_PROFILE_INDEX : VERTEX_PROFILE_INDEX;

    bool hasSharedParams = false;
    int arrayCount = 1;
    for ( int i = 0;i < program->rtParametersCount;i++ )
    {
        const CgParameterEntry *parameterEntry = program->parametersEntries + i;

        if (( parameterEntry->flags & CGP_STRUCTURE ) || ( parameterEntry->flags & CGP_UNROLLED ) )
        {
            arrayCount = 1;
            continue;
        }

        if (( parameterEntry->flags & CGPF_REFERENCED ) )
        {
            if ( parameterEntry->flags & CGP_ARRAY )
            {
                const CgParameterArray *parameterArray = _RGLGetParameterArray( program, parameterEntry );
                arrayCount = _RGLGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
                continue;
            }
            if (( parameterEntry->flags & CGPV_MASK ) == CGPV_UNIFORM )
            {
                const CgParameterResource *parameterResource = _RGLGetParameterResource( program, parameterEntry );
                if ( parameterResource->type >= CG_SAMPLER1D &&  parameterResource->type <= CG_SAMPLERCUBE )
                {
                    offsetCount += arrayCount;
                    samplerCount += arrayCount;
                }
                else if ( profileIndex == VERTEX_PROFILE_INDEX )
                {
                    if ( parameterResource->type == CGP_SCF_BOOL )
                    {
                    }
                    else if ( !( parameterEntry->flags & CGPF_SHARED ) )
                    {
                        int registerStride = isMatrix(( CGtype )parameterResource->type ) ? _RGLGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
                        if ( parameterEntry->flags & CGP_CONTIGUOUS )
                            bufferSize += 3 + 4 * arrayCount * registerStride;
                        else
                        {
                            programPushBufferPointersSize += arrayCount;
                            int resourceIndex = parameterResource->resource;
                            int referencedSize = 3 + 4 * registerStride;
                            int notReferencedSize = 4 * registerStride;
                            for ( int j = 0;j < arrayCount;j++, resourceIndex += registerStride )
                            {
                                if ( program->resources[resourceIndex] != 0xffff )
                                    bufferSize += referencedSize;
                                else
                                    extraStorageInWords += notReferencedSize;
                            }
                        }
                    }
                    else
                    {
                        hasSharedParams = true;
                        if ( !( parameterEntry->flags & CGP_CONTIGUOUS ) )
                        {
                            programPushBufferPointersSize += arrayCount;
                        }
                    }
                }
                else
                {
                    int registerStride = isMatrix(( CGtype )parameterResource->type ) ? _RGLGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
                    if ( !( parameterEntry->flags & CGPF_SHARED ) )
                    {
                        extraStorageInWords += 4 * arrayCount * registerStride;
                    }
                    else
                    {
                        hasSharedParams = true;
                        unsigned short *resource = program->resources + parameterResource->resource;
                        for ( int j = 0;j < arrayCount*registerStride;j++ )
                        {
                            resource++;
                            unsigned short count = *resource++;
                            bufferSize += 24 * count;
                            resource += count;
                        }
                    }
                }
            }
        }
        arrayCount = 1;
    }

    if (( profileIndex == FRAGMENT_PROFILE_INDEX ) && ( hasSharedParams ) )
        bufferSize += 8 + 3 + 2;

    bufferSize = _RGLPad( bufferSize, 4 );

    unsigned int storageSizeInWords = bufferSize + extraStorageInWords;
    if ( storageSizeInWords )
        program->memoryBlock = ( unsigned int* )memalign( 16, storageSizeInWords * 4 );
    else
        program->memoryBlock = NULL;

    program->samplerCount = samplerCount;
    if ( samplerCount )
    {
        program->samplerValuesLocation = ( GLuint* )malloc( samplerCount * sizeof( GLuint ) );
        program->samplerIndices = ( GLuint* )malloc( samplerCount * sizeof( GLuint ) );
        program->samplerUnits = ( GLuint* )malloc( samplerCount * sizeof( GLuint ) );
    }
    else
    {
        program->samplerValuesLocation = NULL;
        program->samplerIndices = NULL;
        program->samplerUnits = NULL;
    }

    GLuint *samplerValuesLocation = program->samplerValuesLocation;
    GLuint *samplerIndices = program->samplerIndices;
    GLuint *samplerUnits = program->samplerUnits;

    if ( programPushBufferPointersSize )
        program->constantPushBufferPointers = ( unsigned int** )malloc( programPushBufferPointersSize * 4 );
    else
        program->constantPushBufferPointers = NULL;

    uint32_t *RGLCurrent = ( uint32_t * )program->memoryBlock;
    program->constantPushBuffer = ( bufferSize > 0 ) ? ( unsigned int * )RGLCurrent : NULL;
    unsigned int **programPushBuffer = program->constantPushBufferPointers;
    program->constantPushBufferWordSize = bufferSize;
    GLuint *currentStorage = ( GLuint * )( RGLCurrent + bufferSize );

    arrayCount = 1;
    const CgParameterEntry *containerEntry = NULL;
    for ( int i = 0;i < program->rtParametersCount;i++ )
    {
        CgRuntimeParameter *rtParameter = program->runtimeParameters + i;
        const CgParameterEntry *parameterEntry = program->parametersEntries + i;
        if ( containerEntry == NULL )
            containerEntry = parameterEntry;

        rtParameter->samplerSetter = _cgRaiseInvalidParamIndex;
        rtParameter->setterIndex = _cgRaiseInvalidParamIndex;
        rtParameter->setterrIndex = _cgRaiseNotMatrixParamIndex;
        rtParameter->settercIndex = _cgRaiseNotMatrixParamIndex;

        CGparameter id = ( CGparameter )_RGLCreateName( &_CurrentContext->cgParameterNameSpace, ( void* )rtParameter );
        if ( !id )
            break;

        rtParameter->id = id;
        rtParameter->parameterEntry = parameterEntry;
        rtParameter->program = program;

        if (( parameterEntry->flags & CGP_STRUCTURE ) || ( parameterEntry->flags & CGP_UNROLLED ) )
        {
            arrayCount = 1;
            containerEntry = NULL;
            continue;
        }

        if ( parameterEntry->flags & CGPF_REFERENCED )
        {
            if ( parameterEntry->flags & CGP_ARRAY )
            {
                const CgParameterArray *parameterArray = _RGLGetParameterArray( program, parameterEntry );
                arrayCount = _RGLGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
                continue;
            }
            if (( parameterEntry->flags & CGPV_MASK ) == CGPV_UNIFORM )
            {
                rtParameter->glType = GL_NONE;
                const CgParameterResource *parameterResource = _RGLGetParameterResource( program, parameterEntry );
                if ( parameterResource->type >= CG_SAMPLER1D && parameterResource->type <= CG_SAMPLERCUBE )
                {
                    rtParameter->pushBufferPointer = samplerValuesLocation;

                    *samplerValuesLocation = 0;
                    samplerValuesLocation++;

                    *samplerIndices = i;
                    samplerIndices++;
                    *samplerUnits = parameterResource->resource - CG_TEXUNIT0;
                    samplerUnits++;

                    if ( profileIndex == VERTEX_PROFILE_INDEX )
                    {
                        rtParameter->setterIndex = _cgIgnoreSetParamIndex;
                        rtParameter->samplerSetter = setSamplervp;
                    }
                    else
                    {
                        rtParameter->samplerSetter = setSamplerfp;
                    }
                    rtParameter->glType = _RGLCgGetSamplerGLTypeFromCgType(( CGtype )( parameterResource->type ) );
                }
                else
                {
                    if ( profileIndex == VERTEX_PROFILE_INDEX )
                    {
                        if ( parameterResource->type == CGP_SCF_BOOL )
                        {
                        }
                        else if ( !( parameterEntry->flags & CGPF_SHARED ) )
                        {
                            int registerStride = isMatrix(( CGtype )parameterResource->type ) ? _RGLGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
                            int registerCount = arrayCount * registerStride;
                            if ( parameterEntry->flags & CGP_CONTIGUOUS )
                            {
                                memset( RGLCurrent, 0, 4*( 4*registerCount + 3 ) );
                                GCM_FUNC_BUFFERED( cellGcmSetVertexProgramParameterBlock, RGLCurrent, parameterResource->resource, registerCount, ( float* )RGLCurrent );
                                rtParameter->pushBufferPointer = RGLCurrent - 4 * registerCount;
                            }
                            else
                            {
                                rtParameter->pushBufferPointer = programPushBuffer;
                                int resourceIndex = parameterResource->resource;
                                for ( int j = 0;j < arrayCount;j++, resourceIndex += registerStride )
                                {
                                    if ( program->resources[resourceIndex] != 0xffff )
                                    {
                                        memset( RGLCurrent, 0, 4*( 4*registerStride + 3 ) );
                                        GCM_FUNC_BUFFERED( cellGcmSetVertexProgramParameterBlock, RGLCurrent, program->resources[resourceIndex], registerStride, ( float* )RGLCurrent );
                                        *( programPushBuffer++ ) = ( unsigned int* )( RGLCurrent - 4 * registerStride );
                                    }
                                    else
                                    {
                                        *( programPushBuffer++ ) = ( unsigned int* )currentStorage;
                                        currentStorage += 4 * registerStride;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        if ( parameterEntry->flags & CGPF_SHARED )
                        {
                            rtParameter->pushBufferPointer = NULL;
                        }
                        else
                        {
                            int registerStride = isMatrix(( CGtype )parameterResource->type ) ? _RGLGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
                            int registerCount = arrayCount * registerStride;
                            rtParameter->pushBufferPointer = currentStorage;
                            currentStorage += 4 * registerCount;
                        }
                    }

                    switch ( parameterResource->type )
                    {
                        case CG_FLOAT:
                        case CG_FLOAT1: case CG_FLOAT2: case CG_FLOAT3: case CG_FLOAT4:
                        {
                            unsigned int floatCount = _RGLCountFloatsInCgType(( CGtype )parameterResource->type );
                            rtParameter->setterIndex = setVectorTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][floatCount - 1];
                        }
                        break;
                        case CG_FLOAT1x1: case CG_FLOAT1x2: case CG_FLOAT1x3: case CG_FLOAT1x4:
                        case CG_FLOAT2x1: case CG_FLOAT2x2: case CG_FLOAT2x3: case CG_FLOAT2x4:
                        case CG_FLOAT3x1: case CG_FLOAT3x2: case CG_FLOAT3x3: case CG_FLOAT3x4:
                        case CG_FLOAT4x1: case CG_FLOAT4x2: case CG_FLOAT4x3: case CG_FLOAT4x4:
                            rtParameter->setterrIndex = setMatrixTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][_RGLGetTypeRowCount(( CGtype )parameterResource->type ) - 1][_RGLGetTypeColCount(( CGtype )parameterResource->type ) - 1][ROW_MAJOR];
                            rtParameter->settercIndex = setMatrixTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][_RGLGetTypeRowCount(( CGtype )parameterResource->type ) - 1][_RGLGetTypeColCount(( CGtype )parameterResource->type ) - 1][COL_MAJOR];
                            break;
                        case CG_SAMPLER1D: case CG_SAMPLER2D: case CG_SAMPLER3D: case CG_SAMPLERRECT: case CG_SAMPLERCUBE:
                            break;
                        case CGP_SCF_BOOL:
                            break;
                        case CG_HALF:
                        case CG_HALF1: case CG_HALF2: case CG_HALF3: case CG_HALF4:
                        case CG_INT:
                        case CG_INT1: case CG_INT2: case CG_INT3: case CG_INT4:
                        case CG_BOOL:
                        case CG_BOOL1: case CG_BOOL2: case CG_BOOL3: case CG_BOOL4:
                        case CG_FIXED:
                        case CG_FIXED1: case CG_FIXED2: case CG_FIXED3: case CG_FIXED4:
                        {
                            unsigned int floatCount = _RGLCountFloatsInCgType(( CGtype )parameterResource->type );
                            rtParameter->setterIndex = setVectorTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][floatCount - 1];
                        }
                        break;
                        case CG_HALF1x1: case CG_HALF1x2: case CG_HALF1x3: case CG_HALF1x4:
                        case CG_HALF2x1: case CG_HALF2x2: case CG_HALF2x3: case CG_HALF2x4:
                        case CG_HALF3x1: case CG_HALF3x2: case CG_HALF3x3: case CG_HALF3x4:
                        case CG_HALF4x1: case CG_HALF4x2: case CG_HALF4x3: case CG_HALF4x4:
                        case CG_INT1x1: case CG_INT1x2: case CG_INT1x3: case CG_INT1x4:
                        case CG_INT2x1: case CG_INT2x2: case CG_INT2x3: case CG_INT2x4:
                        case CG_INT3x1: case CG_INT3x2: case CG_INT3x3: case CG_INT3x4:
                        case CG_INT4x1: case CG_INT4x2: case CG_INT4x3: case CG_INT4x4:
                        case CG_BOOL1x1: case CG_BOOL1x2: case CG_BOOL1x3: case CG_BOOL1x4:
                        case CG_BOOL2x1: case CG_BOOL2x2: case CG_BOOL2x3: case CG_BOOL2x4:
                        case CG_BOOL3x1: case CG_BOOL3x2: case CG_BOOL3x3: case CG_BOOL3x4:
                        case CG_BOOL4x1: case CG_BOOL4x2: case CG_BOOL4x3: case CG_BOOL4x4:
                        case CG_FIXED1x1: case CG_FIXED1x2: case CG_FIXED1x3: case CG_FIXED1x4:
                        case CG_FIXED2x1: case CG_FIXED2x2: case CG_FIXED2x3: case CG_FIXED2x4:
                        case CG_FIXED3x1: case CG_FIXED3x2: case CG_FIXED3x3: case CG_FIXED3x4:
                        case CG_FIXED4x1: case CG_FIXED4x2: case CG_FIXED4x3: case CG_FIXED4x4:
                            rtParameter->setterrIndex = setMatrixTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][_RGLGetTypeRowCount(( CGtype )parameterResource->type ) - 1][_RGLGetTypeColCount(( CGtype )parameterResource->type ) - 1][ROW_MAJOR];
                            rtParameter->settercIndex = setMatrixTypeIndex[( containerEntry->flags&CGP_ARRAY ) ? 1 : 0][( containerEntry->flags&CGPF_SHARED ) ? 1 : 0][profileIndex][_RGLGetTypeRowCount(( CGtype )parameterResource->type ) - 1][_RGLGetTypeColCount(( CGtype )parameterResource->type ) - 1][COL_MAJOR];
                            break;
			case CG_STRING:
			    break;
                        default:
                            break;
                    }
                }
            }
        }
        else
        {
            if (( parameterEntry->flags & CGPV_MASK ) == CGPV_UNIFORM )
            {
                if ( parameterEntry->flags & CGP_ARRAY )
                    continue;

                const CgParameterResource *parameterResource = _RGLGetParameterResource( program, parameterEntry );
                switch ( parameterResource->type )
                {
                    case CG_FLOAT:
                    case CG_FLOAT1: case CG_FLOAT2: case CG_FLOAT3: case CG_FLOAT4:
                        rtParameter->setterIndex = _cgIgnoreSetParamIndex;
                        break;
                    case CG_FLOAT1x1: case CG_FLOAT1x2: case CG_FLOAT1x3: case CG_FLOAT1x4:
                    case CG_FLOAT2x1: case CG_FLOAT2x2: case CG_FLOAT2x3: case CG_FLOAT2x4:
                    case CG_FLOAT3x1: case CG_FLOAT3x2: case CG_FLOAT3x3: case CG_FLOAT3x4:
                    case CG_FLOAT4x1: case CG_FLOAT4x2: case CG_FLOAT4x3: case CG_FLOAT4x4:
                        rtParameter->setterrIndex = _cgIgnoreSetParamIndex;
                        rtParameter->settercIndex = _cgIgnoreSetParamIndex;
                        break;
                    case CG_SAMPLER1D: case CG_SAMPLER2D: case CG_SAMPLER3D: case CG_SAMPLERRECT: case CG_SAMPLERCUBE:
                        rtParameter->samplerSetter = _cgIgnoreSetParamIndex;
                        break;
                    case CGP_SCF_BOOL:
                        break;
		    case CG_HALF:
                    case CG_HALF1: case CG_HALF2: case CG_HALF3: case CG_HALF4:
		    case CG_INT:
                    case CG_INT1: case CG_INT2: case CG_INT3: case CG_INT4:
		    case CG_BOOL:
                    case CG_BOOL1: case CG_BOOL2: case CG_BOOL3: case CG_BOOL4:
		    case CG_FIXED:
                    case CG_FIXED1: case CG_FIXED2: case CG_FIXED3: case CG_FIXED4:
                        rtParameter->setterIndex = _cgIgnoreSetParamIndex;
                        break;
                    case CG_HALF1x1: case CG_HALF1x2: case CG_HALF1x3: case CG_HALF1x4:
                    case CG_HALF2x1: case CG_HALF2x2: case CG_HALF2x3: case CG_HALF2x4:
                    case CG_HALF3x1: case CG_HALF3x2: case CG_HALF3x3: case CG_HALF3x4:
                    case CG_HALF4x1: case CG_HALF4x2: case CG_HALF4x3: case CG_HALF4x4:
                    case CG_INT1x1: case CG_INT1x2: case CG_INT1x3: case CG_INT1x4:
                    case CG_INT2x1: case CG_INT2x2: case CG_INT2x3: case CG_INT2x4:
                    case CG_INT3x1: case CG_INT3x2: case CG_INT3x3: case CG_INT3x4:
                    case CG_INT4x1: case CG_INT4x2: case CG_INT4x3: case CG_INT4x4:
                    case CG_BOOL1x1: case CG_BOOL1x2: case CG_BOOL1x3: case CG_BOOL1x4:
                    case CG_BOOL2x1: case CG_BOOL2x2: case CG_BOOL2x3: case CG_BOOL2x4:
                    case CG_BOOL3x1: case CG_BOOL3x2: case CG_BOOL3x3: case CG_BOOL3x4:
                    case CG_BOOL4x1: case CG_BOOL4x2: case CG_BOOL4x3: case CG_BOOL4x4:
                    case CG_FIXED1x1: case CG_FIXED1x2: case CG_FIXED1x3: case CG_FIXED1x4:
                    case CG_FIXED2x1: case CG_FIXED2x2: case CG_FIXED2x3: case CG_FIXED2x4:
                    case CG_FIXED3x1: case CG_FIXED3x2: case CG_FIXED3x3: case CG_FIXED3x4:
                    case CG_FIXED4x1: case CG_FIXED4x2: case CG_FIXED4x3: case CG_FIXED4x4:
                        rtParameter->setterrIndex = _cgIgnoreSetParamIndex;
                        rtParameter->settercIndex = _cgIgnoreSetParamIndex;
                        break;
		    case CG_STRING:
			break;
                    default:
                        break;
                }
            }
        }
        arrayCount = 1;
        containerEntry = NULL;
    }

    if ( bufferSize > 0 )
    {
      int nopCount = ( program->constantPushBuffer + bufferSize ) - ( unsigned int * )RGLCurrent;
      GCM_FUNC_BUFFERED( cellGcmSetNopCommand, RGLCurrent, nopCount );
    }
}

static int _RGLGenerateProgram( _CGprogram *program, int profileIndex, const CgProgramHeader *programHeader, const void *ucode, const CgParameterTableHeader *parameterHeader,
                           const CgParameterEntry *parameterEntries, const char *stringTable, const float *defaultValues )
{
    CGprofile profile = ( CGprofile )programHeader->profile;

    int need_swapping = 0;

    if ( profile == ( CGprofile )7005 )
        profile = CG_PROFILE_SCE_VP_RSX;
    if ( profile == ( CGprofile )7006 )
        profile = CG_PROFILE_SCE_FP_RSX;

    if (( profile != CG_PROFILE_SCE_FP_TYPEB ) && ( profile != CG_PROFILE_SCE_VP_TYPEB ) &&
            ( profile != CG_PROFILE_SCE_FP_RSX ) && ( profile != CG_PROFILE_SCE_VP_RSX ) )
    {
        need_swapping = 1;
    }

    int invalidProfile = 0;
    switch ( ENDIAN_32( profile, need_swapping ) )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
            if ( profileIndex != VERTEX_PROFILE_INDEX ) invalidProfile = 1;
            break;
        case CG_PROFILE_SCE_FP_TYPEB:
            if ( profileIndex != FRAGMENT_PROFILE_INDEX ) invalidProfile = 1;
            break;
        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;

    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 == NULL )
        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->defaultValues = NULL;

    memset( program->runtimeParameters, 0, parameterHeader->entryCount*sizeof( CgRuntimeParameter ) );

    program->stringTable = stringTable;
    program->defaultValues = defaultValues;

    _RGLCreatePushBuffer( program );

    int count = program->defaultValuesIndexCount;
    if ( profileIndex == FRAGMENT_PROFILE_INDEX)
    {
	    for ( int i = 0; i < count;i++ )
	    {
		    const void * __restrict pItemDefaultValues = program->defaultValues + program->defaultValuesIndices[i].defaultValueIndex;
		    const unsigned int * itemDefaultValues = ( const unsigned int * )pItemDefaultValues;
		    int index = ( int )program->defaultValuesIndices[i].entryIndex;

		    CgRuntimeParameter *rtParameter = program->runtimeParameters + index;
		    float *hostMemoryCopy = ( float * )rtParameter->pushBufferPointer;

		    if ( hostMemoryCopy )
		    {
			    const CgParameterEntry *parameterEntry = rtParameter->parameterEntry;
			    int arrayCount = 1;
			    if ( parameterEntry->flags & CGP_ARRAY )
			    {
				    const CgParameterArray *parameterArray = _RGLGetParameterArray( program, parameterEntry );
				    arrayCount = _RGLGetSizeofSubArray( parameterArray->dimensions, parameterArray->dimensionCount );
				    i++;
				    parameterEntry++;
			    }
			    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, parameterEntry );
			    unsigned short *resource = program->resources + parameterResource->resource + 1;
			    int registerStride = isMatrix(( CGtype )parameterResource->type ) ? _RGLGetTypeRowCount(( CGtype )parameterResource->type ) : 1;
			    int registerCount = arrayCount * registerStride;
			    int j;
			    for ( j = 0;j < registerCount;j++ )
			    {
				    unsigned short embeddedConstCount = *( resource++ );
				    int k;
				    for ( k = 0;k < embeddedConstCount;k++ )
				    {
					    unsigned short ucodePatchOffset = *( resource )++;
					    unsigned int *dst = ( unsigned int* )(( char* )program->ucode + ucodePatchOffset );
					    dst[0] = SWAP_IF_BIG_ENDIAN( itemDefaultValues[0] );
					    dst[1] = SWAP_IF_BIG_ENDIAN( itemDefaultValues[1] );
					    dst[2] = SWAP_IF_BIG_ENDIAN( itemDefaultValues[2] );
					    dst[3] = SWAP_IF_BIG_ENDIAN( itemDefaultValues[3] );
				    }
				    memcpy(( void* )hostMemoryCopy, ( void* )itemDefaultValues, sizeof( float )*4 );
				    hostMemoryCopy += 4;
				    itemDefaultValues += 4;
				    resource++;
			    }
		    }
	    }
    }
    else
    {
	    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 )
		    {
			    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;
				    }
			    }
		    }
	    }
    }

    program->loadProgramId = GMM_ERROR;
    program->loadProgramOffset = 0;
    if ( profileIndex == FRAGMENT_PROFILE_INDEX )
    {
        int loaded = _RGLLoadFPShader( program );
        if ( ! loaded )
        {
            _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
            return 0;
        }
    }

    program->programGroup = NULL;
    program->programIndexInGroup = -1;

    return 1;
}

void _RGLPlatformVertexProgramErase( void* platformProgram )
{
    _CGprogram* program = ( _CGprogram* )platformProgram;
    if ( program->runtimeParameters )
        free( program->runtimeParameters );

    if ( program->memoryBlock )
        free( program->memoryBlock );

    if ( program->samplerIndices )
    {
        free( program->samplerValuesLocation );
        free( program->samplerIndices );
        free( program->samplerUnits );
    }

    if ( program->constantPushBufferPointers )
        free( program->constantPushBufferPointers );
}

void _RGLPlatformProgramErase( void* platformProgram )
{
    _CGprogram* program = ( _CGprogram* )platformProgram;

    if ( program->loadProgramId != GMM_ERROR )
        _RGLUnloadFPShader( program );

    if ( program->runtimeParameters )
    {
        int i;
        int count = ( int )program->rtParametersCount;
        for ( i = 0;i < count;i++ )
        {
            _RGLEraseName( &_CurrentContext->cgParameterNameSpace, ( jsName )program->runtimeParameters[i].id );
        }
        free( program->runtimeParameters );
    }

    if ( program->memoryBlock )
        free( program->memoryBlock );

    if ( program->samplerIndices )
    {
        free( program->samplerValuesLocation );
        free( program->samplerIndices );
        free( program->samplerUnits );
    }

    if ( program->constantPushBufferPointers )
        free( program->constantPushBufferPointers );
}


CGbool _RGLPlatformSupportsFragmentProgram( CGprofile p )
{
    if ( p == CG_PROFILE_SCE_FP_TYPEB )
        return CG_TRUE;
    if ( CG_PROFILE_SCE_FP_RSX == p )
        return CG_TRUE;
    return CG_FALSE;
}

CGprofile _RGLPlatformGetLatestProfile( 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;
}

int _RGLPlatformCopyProgram( _CGprogram* source, _CGprogram* destination )
{
    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 ) );

    int profileIndex;

    switch ( source->header.profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
            profileIndex = VERTEX_PROFILE_INDEX;
            break;

        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_FP_RSX:
            profileIndex = FRAGMENT_PROFILE_INDEX;
            break;
        default:
            return 0;
    }
    return _RGLGenerateProgram( destination, profileIndex, &source->header, source->ucode, &parameterHeader, source->parametersEntries, source->stringTable, source->defaultValues );
}

static char *_RGLPlatformBufferObjectMap( jsBufferObject* bufferObject, GLenum access )
{
    RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

    if ( jsBuffer->mapCount++ == 0 )
    {
        if ( access == GL_WRITE_ONLY )
        {
            _RGLAllocateBuffer( bufferObject );
            if ( jsBuffer->pool == _RGL_SURFACE_POOL_NONE )
            {
                _RGLSetError( GL_OUT_OF_MEMORY );
                return NULL;
            }
	}
	else
	{
		cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
		_RGLFifoFinish( &_RGLState.fifo );
	}

        jsBuffer->mapAccess = access;

        if ( jsBuffer->mapAccess != GL_READ_ONLY )
	{
		RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;
		++driver->flushBufferCount;
	}

	GmmBaseBlock    *pBaseBlock = (GmmBaseBlock *)jsBuffer->bufferId;

	if (!pBaseBlock->isTile)
	{
		GmmBlock *pBlock = (GmmBlock *)jsBuffer->bufferId;

		pBlock->isPinned = 1;
	}
    }

    return gmmIdToAddress( jsBuffer->bufferId );
}

static jsFramebuffer* _RGLCreateFramebuffer( void )
{
   jsFramebuffer* framebuffer = new jsPlatformFramebuffer();
   return framebuffer;
}

static void _RGLDestroyFramebuffer( jsFramebuffer* framebuffer )
{
   delete framebuffer;
}

static void _RGLPlatformDestroyTexture( jsTexture* texture )
{
    if ( !texture->referenceBuffer )
    {
        RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
        if ( gcmTexture->pbo != NULL )
        {
            _RGLFreeBufferObject( gcmTexture->pbo );
            gcmTexture->pbo = NULL;
            gcmTexture->pool = _RGL_SURFACE_POOL_NONE;
            gcmTexture->gpuAddressId = GMM_ERROR;
            gcmTexture->gpuAddressIdOffset = 0;
            gcmTexture->gpuSize = 0;
        }

        _RGLPlatformFreeGcmTexture( texture );
    }
    _RGLTextureTouchFBOs( texture );
}

// Get size of texture in GPU layout
static inline GLuint _RGLPlatformTextureGetGPUSize( const jsTexture* texture )
{
    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
    return _RGLPad( gcmTexture->gpuLayout.baseHeight * gcmTexture->gpuLayout.pitch, 1);
}

#include <cell/gcm/gcm_method_data.h>

static void _RGLPlatformValidateTextureStage( int unit, jsTexture* texture )
{
    if ( RGL_UNLIKELY( texture->revalidate ) )
    {
        _RGLPlatformValidateTextureResources( texture );
    }

    GLboolean isCompleteCache = texture->isComplete;

    if ( RGL_LIKELY( isCompleteCache ) )
    {
        RGLTexture *platformTexture = ( RGLTexture * )texture->platformTexture;
	const GLuint imageOffset = gmmIdToOffset(platformTexture->gpuAddressId) + platformTexture->gpuAddressIdOffset;
	platformTexture->gcmTexture.offset = imageOffset; 

	cellGcmSetTexture( &_RGLState.fifo, unit, &platformTexture->gcmTexture);
	CellGcmContextData *gcm_context = (CellGcmContextData*)&_RGLState.fifo;
	cellGcmReserveMethodSizeInline(gcm_context, 11);
	uint32_t *current = gcm_context->current;
	current[0] = CELL_GCM_METHOD_HEADER_TEXTURE_OFFSET(unit, 8);
	current[1] = CELL_GCM_METHOD_DATA_TEXTURE_OFFSET(platformTexture->gcmTexture.offset);
	current[2] = CELL_GCM_METHOD_DATA_TEXTURE_FORMAT(platformTexture->gcmTexture.location,
			CELL_GCM_FALSE, 
			CELL_GCM_TEXTURE_DIMENSION_2,
			platformTexture->gcmTexture.format,
			1);
	current[3] = CELL_GCM_METHOD_DATA_TEXTURE_ADDRESS(
			CELL_GCM_TEXTURE_BORDER, /* wrapS */
			CELL_GCM_TEXTURE_BORDER, /* wrapT */
			CELL_GCM_TEXTURE_BORDER, /* wrapR */
			CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL, /* unsignedRemap */
			CELL_GCM_TEXTURE_ZFUNC_NEVER,
			platformTexture->gcmMethods.address.gamma,
			0);
	current[4] = CELL_GCM_METHOD_DATA_TEXTURE_CONTROL0(CELL_GCM_TRUE,
			0, /* minLOD */
			256000, /* maxLOD */
			CELL_GCM_TEXTURE_MAX_ANISO_1);
	current[5] = platformTexture->gcmTexture.remap;
	current[6] = CELL_GCM_METHOD_DATA_TEXTURE_FILTER(
			(platformTexture->gcmMethods.filter.bias & 0x1fff),
			platformTexture->gcmMethods.filter.min,
			platformTexture->gcmMethods.filter.mag,
			CELL_GCM_TEXTURE_CONVOLUTION_QUINCUNX); /* filter */
	current[7] = CELL_GCM_METHOD_DATA_TEXTURE_IMAGE_RECT(
			platformTexture->gcmTexture.height,
			platformTexture->gcmTexture.width);
	current[8] = CELL_GCM_METHOD_DATA_TEXTURE_BORDER_COLOR(0);
	current[9] = CELL_GCM_METHOD_HEADER_TEXTURE_CONTROL3(unit,1);
	current[10] = CELL_GCM_METHOD_DATA_TEXTURE_CONTROL3(
			platformTexture->gcmTexture.pitch,
			1); /* depth */
	gcm_context->current = &current[11];
    }
    else
    {
        //printf("RGL WARN: Texture bound to unit %d is incomplete.\n", unit);
	GLuint remap = CELL_GCM_REMAP_MODE(
			CELL_GCM_TEXTURE_REMAP_ORDER_XYXY,
			CELL_GCM_TEXTURE_REMAP_FROM_A,
			CELL_GCM_TEXTURE_REMAP_FROM_R,
			CELL_GCM_TEXTURE_REMAP_FROM_G,
			CELL_GCM_TEXTURE_REMAP_FROM_B,
			CELL_GCM_TEXTURE_REMAP_ONE,
			CELL_GCM_TEXTURE_REMAP_ZERO,
			CELL_GCM_TEXTURE_REMAP_ZERO,
			CELL_GCM_TEXTURE_REMAP_ZERO );

	cellGcmSetTextureControlInline( &_RGLState.fifo, unit, CELL_GCM_FALSE, 0, 0, 0);
	cellGcmSetTextureRemapInline( &_RGLState.fifo, unit, remap);
    }


}

static GLenum _RGLPlatformChooseInternalFormat( GLenum internalFormat )
{
    switch ( internalFormat )
    {
        case GL_ALPHA:
        case GL_ALPHA4:
        case GL_ALPHA8:
            return RGL_ALPHA8;
        case GL_RGB10:
        case GL_RGB10_A2:
        case GL_RGB12:
        case GL_RGB16:
            return RGL_FLOAT_RGBX32;
        case GL_RGBA12:
        case GL_RGBA16:
            return RGL_FLOAT_RGBA32;
        case 3:
        case GL_R3_G3_B2:
        case GL_RGB4:
        case GL_RGB:
        case GL_RGB8:
        case RGL_RGBX8:
            return RGL_RGBX8;
        case 4:
        case GL_RGBA2:
        case GL_RGBA4:
        case GL_RGBA8:
        case GL_RGBA:
            return RGL_RGBA8;
        case GL_RGB5_A1:
            return RGL_RGB5_A1_SCE;
        case GL_RGB5:
            return RGL_RGB565_SCE;
        case GL_BGRA:
        case RGL_BGRA8:
            return RGL_BGRA8;
        case GL_ARGB_SCE:
            return RGL_ARGB8;
        default:
            return GL_INVALID_ENUM;
    }
    return GL_INVALID_ENUM;
}

static void _RGLPlatformExpandInternalFormat( GLenum internalFormat, GLenum *format, GLenum *type )
{
   switch ( internalFormat )
   {
      case RGL_ALPHA8:
         *format = GL_ALPHA;
	 *type = GL_UNSIGNED_BYTE;
	 break;
      case RGL_ARGB8:
	 *format = GL_BGRA;
	 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
	 break;
      case RGL_RGB5_A1_SCE:
	 *format = GL_RGBA;
	 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
	 break;
      case RGL_RGB565_SCE:
	 *format = GL_RGB;
	 *type = GL_UNSIGNED_SHORT_5_6_5_REV;
	 break;
      default:
	 return;
   }
}

static GLenum _RGLPlatformChooseInternalStorage( jsImage* image, GLenum internalFormat )
{
   image->storageSize = 0;

   GLenum platformInternalFormat = _RGLPlatformChooseInternalFormat( internalFormat );

   if ( platformInternalFormat == GL_INVALID_ENUM )
      return GL_INVALID_ENUM;

   image->internalFormat = platformInternalFormat;
   _RGLPlatformExpandInternalFormat( platformInternalFormat, &image->format, &image->type );

   image->storageSize = _RGLGetStorageSize(
   image->format, image->type,
   image->width, image->height, 1 );

   return GL_NO_ERROR;
}

static inline GLuint _RGLGetBufferObjectOrigin( GLuint buffer )
{
    jsBufferObject *bufferObject = _RGLGetBufferObject( _CurrentContext, buffer );
    RGLBufferObject *gcmBuffer = ( RGLBufferObject * ) & bufferObject->platformBufferObject;
    return gcmBuffer->bufferId;
}

static void _RGLSetImage( jsImage *image, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei alignment, GLenum format, GLenum type, const GLvoid *pixels )
{
    image->width = width;
    image->height = height;
    image->alignment = alignment;

    image->xblk = 0;
    image->yblk = 0;

    image->xstride = 0;
    image->ystride = 0;

    image->format = 0;
    image->type = 0;
    image->internalFormat = 0;
    const GLenum status = _RGLPlatformChooseInternalStorage( image, internalFormat );
    (( void )status );

    image->data = NULL;
    image->mallocData = NULL;
    image->mallocStorageSize = 0;

    image->isSet = GL_TRUE;

    if ( image->xstride == 0 )
	    image->xstride = _RGLGetPixelSize( image->format, image->type );
    if ( image->ystride == 0 )
	    image->ystride = image->width * image->xstride;

    if ( pixels )
    {
        _RGLImageAllocCPUStorage( image );
        if ( !image->data )
            return;

        jsRaster raster;
        raster.format = format;
        raster.type = type;
        raster.width = width;
        raster.height = height;
        raster.data = ( void * )pixels;

        raster.xstride = _RGLGetPixelSize( raster.format, raster.type );
        raster.ystride = ( raster.width * raster.xstride + alignment - 1 ) / alignment * alignment;

        _RGLRasterToImage( &raster, image);
        image->dataState = _RGL_IMAGE_DATASTATE_HOST;
    }
    else
        image->dataState = _RGL_IMAGE_DATASTATE_UNSET;
}

static GLboolean _RGLPlatformTexturePBOImage(
    jsTexture* texture,
    jsImage* image,
    GLint internalFormat,
    GLsizei width, GLsizei height,
    GLenum format, GLenum type,
    const GLvoid *offset )
{
    PSGLcontext*	LContext = _CurrentContext;

    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;

    image->dataState = _RGL_IMAGE_DATASTATE_UNSET;
    if ( gcmTexture->pbo != NULL )
        _RGLPlatformDropTexture( texture );

    _RGLSetImage( image, internalFormat, width, height, 1 /* depth */, LContext->unpackAlignment, format, type, NULL );

    if ( LContext->PixelUnpackBuffer == 0 )
        return GL_FALSE;

    const GLuint pboPitch = _RGLPad(
                                _RGLGetStorageSize( format, type, width, 1, 1 ),
                                LContext->unpackAlignment );
    if (( pboPitch&3 ) != 0 )
    {
        RARCH_WARN("PBO image pitch not a multiple of 4, using slow path.\n" );
        return GL_FALSE;
    }

    GLuint gpuId = _RGLGetBufferObjectOrigin( LContext->PixelUnpackBuffer );
    GLuint gpuIdOffset = (( GLubyte* )offset - ( GLubyte* )NULL );

    if ( gmmIdToOffset(gpuId)+gpuIdOffset & 63 )
    {
        RARCH_WARN("PBO offset not 64-byte aligned, using slow path.\n");
        return GL_FALSE;
    }

    GLboolean formatOK = GL_FALSE;
    switch ( internalFormat )
    {
        case 4:
        case GL_RGBA:
        case GL_RGBA8:
            if ( format == GL_RGBA && type == GL_UNSIGNED_INT_8_8_8_8 )
                formatOK = GL_TRUE;
            break;
        case GL_ALPHA:
        case GL_ALPHA8:
            if ( format == GL_ALPHA && type == GL_UNSIGNED_BYTE )
                formatOK = GL_TRUE;
            break;
        case GL_ARGB_SCE:
            if ( format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8_REV )
                formatOK = GL_TRUE;
            break;
        default:
            formatOK = GL_FALSE;
    }

    if ( !formatOK )
    {
        RARCH_WARN("PBO format/type requires conversion to texture internal format, using slow path.\n");
        return GL_FALSE;
    }

    if ( !_RGLTextureIsValid( texture ) )
    {
        RARCH_WARN("PBO transfering to incomplete texture, using slow path.\n");
        return GL_FALSE;
    }

    RGLTextureLayout newLayout;
    _RGLPlatformChooseGPUFormatAndLayout( texture, GL_TRUE, pboPitch, &newLayout );

    jsBufferObject* bufferObject = _RGLGetBufferObject( LContext, LContext->PixelUnpackBuffer );
    if ( newLayout.pitch != 0 && !bufferObject->mapped )
    {
        gcmTexture->gpuLayout = newLayout;
        if ( gcmTexture->gpuAddressId != GMM_ERROR && gcmTexture->pbo == NULL )
        {
            _RGLPlatformFreeGcmTexture( texture );
        }
        gcmTexture->pbo = bufferObject;
        gcmTexture->gpuAddressId = gpuId; 
        gcmTexture->gpuAddressIdOffset = gpuIdOffset;
        gcmTexture->pool = _RGL_SURFACE_POOL_LINEAR;
        gcmTexture->gpuSize = _RGLPlatformTextureGetGPUSize( texture );
        ++bufferObject->refCount;
    }
    else
    {
        const GLuint bytesPerPixel = newLayout.pixelBits / 8;
        RGLSurface src =
	{
                source:		_RGL_SURFACE_SOURCE_PBO,
		width:		image->width,
		height:		image->height,
		bpp:		bytesPerPixel,
		pitch:		pboPitch,
		format:		newLayout.internalFormat,
		pool:		_RGL_SURFACE_POOL_LINEAR,
		ppuData:    NULL,
		dataId:     gpuId,
		dataIdOffset: gpuIdOffset, 
	};

        texture->revalidate |= _RGL_TEXTURE_REVALIDATE_LAYOUT;
        _RGLPlatformValidateTextureResources( texture );

        RGLSurface dst =
	{
                source:		_RGL_SURFACE_SOURCE_TEXTURE,
		width:		image->width,
		height:		image->height,
		bpp:		bytesPerPixel,
		pitch:		gcmTexture->gpuLayout.pitch,
		format:		gcmTexture->gpuLayout.internalFormat,
		pool:		gcmTexture->pool,
		ppuData:    NULL,
		dataId:     gcmTexture->gpuAddressId,
		dataIdOffset: gcmTexture->gpuAddressIdOffset,
	};

	_RGLTransferDataVidToVid( dst.dataId, dst.dataIdOffset, dst.pitch ? dst.pitch : (dst.bpp * dst.width), 0, 0, src.dataId, src.dataIdOffset, src.pitch ? src.pitch : (src.bpp * src.width), 0, 0, width, height, src.bpp );
    }

    _RGLImageFreeCPUStorage( image );
    image->dataState = _RGL_IMAGE_DATASTATE_GPU;

    texture->revalidate &= ~( _RGL_TEXTURE_REVALIDATE_LAYOUT | _RGL_TEXTURE_REVALIDATE_IMAGES );
    texture->revalidate |= _RGL_TEXTURE_REVALIDATE_PARAMETERS;
    _RGLTextureTouchFBOs( texture );

    return GL_TRUE;
}

static GLboolean _RGLPlatformTextureReference( jsTexture *texture, GLuint pitch, jsBufferObject *bufferObject, GLintptr offset )
{
    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;

    RGLTextureLayout newLayout;
    _RGLPlatformChooseGPUFormatAndLayout( texture, GL_TRUE, pitch, &newLayout );

    texture->isRenderTarget = GL_TRUE;

    if ( gcmTexture->gpuAddressId != GMM_ERROR )
        _RGLPlatformDestroyTexture( texture );

    RGLBufferObject *gcmBuffer = ( RGLBufferObject * ) & bufferObject->platformBufferObject;

    gcmTexture->gpuLayout = newLayout;
    gcmTexture->pool = gcmBuffer->pool;
    gcmTexture->gpuAddressId = gcmBuffer->bufferId;
    gcmTexture->gpuAddressIdOffset = offset;
    gcmTexture->gpuSize = _RGLPad( newLayout.baseHeight * newLayout.pitch, 1);

    texture->revalidate &= ~( _RGL_TEXTURE_REVALIDATE_LAYOUT | _RGL_TEXTURE_REVALIDATE_IMAGES );
    texture->revalidate |= _RGL_TEXTURE_REVALIDATE_PARAMETERS;
    _RGLTextureTouchFBOs( texture );
    return GL_TRUE;
}

static inline void _RGLSetColorDepthBuffers( RGLRenderTarget *rt, RGLRenderTargetEx const * const args )
{
    CellGcmSurface *   grt = &rt->gcmRenderTarget;

    rt->colorBufferCount = args->colorBufferCount;

    GLuint oldHeight;
    GLuint oldyInverted;

    oldyInverted = rt->yInverted;
    oldHeight = rt->gcmRenderTarget.height;

    GLuint i;

    for ( i = 0; i < args->colorBufferCount; i++ )
    {
        if ( args->colorPitch[i] == 0 )
        {
            grt->colorOffset[i] = 0;
            grt->colorPitch[i] = 0x200;
            grt->colorLocation[i] = CELL_GCM_LOCATION_LOCAL;
        }
        else
        {
            if ( args->colorId[i] != GMM_ERROR )
            {
                if ( gmmIdIsMain(args->colorId[i]) )
                    grt->colorLocation[i] = CELL_GCM_LOCATION_MAIN;
                else
                    grt->colorLocation[i] = CELL_GCM_LOCATION_LOCAL;
                grt->colorOffset[i] = gmmIdToOffset(args->colorId[i]) + args->colorIdOffset[i];
                grt->colorPitch[i] = args->colorPitch[i];
            }
        }
    }

    for ( ; i < RGL_SETRENDERTARGET_MAXCOUNT; i++ )
    {
        grt->colorOffset[i] = grt->colorOffset[0];
        grt->colorPitch[i]   = grt->colorPitch[0];
        grt->colorLocation[i] = grt->colorLocation[0];
    }

    rt->yInverted = args->yInverted;
    grt->x        = args->xOffset; 
    grt->y        = args->yOffset; 
    grt->width    = args->width;
    grt->height   = args->height;

    if (( grt->height != oldHeight ) | ( rt->yInverted != oldyInverted ) )
    {
	    RGLViewportState *v = &_RGLState.state.viewport;
	    _RGLFifoGlViewport( v->x, v->y, v->w, v->h );
    }
}

static inline void _RGLSetColorDepthFormats( RGLRenderTarget *rt, RGLRenderTargetEx const * const args )
{
	CellGcmSurface *   grt = &rt->gcmRenderTarget;

	grt->colorFormat = CELL_GCM_SURFACE_A8R8G8B8;
	grt->depthFormat = CELL_GCM_SURFACE_Z24S8;
	grt->depthLocation = CELL_GCM_LOCATION_LOCAL;
	grt->depthOffset = 0;
	grt->depthPitch = 64;
}

static inline void _RGLSetTarget( RGLRenderTarget *rt, RGLRenderTargetEx const * const args )
{
    CellGcmSurface *   grt = &rt->gcmRenderTarget;

    switch ( rt->colorBufferCount )
    {
        case 0:
            grt->colorTarget = CELL_GCM_SURFACE_TARGET_NONE;
            break;
        case 1:
            grt->colorTarget = CELL_GCM_SURFACE_TARGET_1;
            break;
        case 2:
            grt->colorTarget = CELL_GCM_SURFACE_TARGET_MRT1;
            break;
        case 3:
            grt->colorTarget = CELL_GCM_SURFACE_TARGET_MRT2;
            break;
        case 4:
            grt->colorTarget = CELL_GCM_SURFACE_TARGET_MRT3;
            break;
        default:
            break;
    }
}

void _RGLFifoGlSetRenderTarget( RGLRenderTargetEx const * const args )
{
    RGLRenderTarget *rt = &_RGLState.renderTarget;
    CellGcmSurface *   grt = &_RGLState.renderTarget.gcmRenderTarget;

    _RGLSetColorDepthBuffers( rt, args );
    _RGLSetColorDepthFormats( rt, args );

    grt->antialias = CELL_GCM_SURFACE_CENTER_1;

    cellGcmSetAntiAliasingControlInline( &_RGLState.fifo, CELL_GCM_FALSE, CELL_GCM_FALSE, CELL_GCM_FALSE, 0xFFFF);

    grt->type = CELL_GCM_SURFACE_PITCH;
    _RGLSetTarget( rt, args );

    cellGcmSetSurfaceInline( &_RGLState.fifo, grt);
    cellGcmSetDepthTestEnableInline( &_RGLState.fifo, CELL_GCM_FALSE);
}

void _RGLSetError( GLenum error ) {}

GLAPI GLenum APIENTRY glGetError()
{
    if ( !_CurrentContext )
	return GL_INVALID_OPERATION;
    else
    {
        GLenum error = _CurrentContext->error;

        _CurrentContext->error = GL_NO_ERROR;
        return error;
    }
}

static uint32_t * _RGLFifoWaitForFreeSpace( RGLFifo *fifo, GLuint spaceInWords )
{
   if ( fifo->current + spaceInWords + 1024 > fifo->end )
      _RGLOutOfSpaceCallback( fifo, spaceInWords );

   return _RGLState.fifo.current;
}

static inline void _RGLPushProgramPushBuffer( _CGprogram * cgprog )
{
	_RGLFifoWaitForFreeSpace( &_RGLState.fifo,  cgprog->constantPushBufferWordSize + 4 + 32); 
	uint32_t padding_in_word = ( ( 0x10-(((uint32_t)_RGLState.fifo.current)&0xf))&0xf )>>2;
	uint32_t padded_size = ( ((cgprog->constantPushBufferWordSize)<<2) + 0xf )&~0xf;
	cellGcmSetNopCommandUnsafeInline( &_RGLState.fifo, padding_in_word);
	memcpy16(_RGLState.fifo.current, cgprog->constantPushBuffer, padded_size);
	_RGLState.fifo.current+=cgprog->constantPushBufferWordSize;
}

static GLuint _RGLValidateStates( void )
{
    PSGLcontext* LContext = _CurrentContext;
    RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;

    LContext->needValidate &= PSGL_VALIDATE_ALL;
    GLuint  dirty = LContext->needValidate;
    GLuint  needValidate = LContext->needValidate;

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_FRAMEBUFFER ) )
    {
        _RGLValidateFramebuffer();

        needValidate = LContext->needValidate;
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_TEXTURES_USED ) )
    {
        long unitInUseCount = LContext->BoundFragmentProgram->samplerCount;
        const GLuint* unitsInUse = LContext->BoundFragmentProgram->samplerUnits;

	for ( long i = 0; i < unitInUseCount; ++i )
	{
		long unit = unitsInUse[i];
		jsTexture* texture = LContext->TextureImageUnits[unit].currentTexture;

		_RGLPlatformValidateTextureStage( unit, texture );
	}
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_VERTEX_PROGRAM ) )
    {
	    const void *header = LContext->BoundVertexProgram;
	    const _CGprogram *vs = ( const _CGprogram* ) header;

	    __dcbt(vs->ucode);
	    __dcbt(((uint8_t*)vs->ucode)+128);
	    __dcbt(((uint8_t*)vs->ucode)+256);
	    __dcbt(((uint8_t*)vs->ucode)+384);

	    CellCgbVertexProgramConfiguration conf;
	    conf.instructionSlot = vs->header.vertexProgram.instructionSlot;
	    conf.instructionCount = vs->header.instructionCount;
	    conf.registerCount = vs->header.vertexProgram.registerCount;
	    conf.attributeInputMask = vs->header.attributeInputMask;

	    _RGLFifoWaitForFreeSpace( &_RGLState.fifo, 7 + 5 * conf.instructionCount );

	    cellGcmSetVertexProgramLoadInline( &_RGLState.fifo, &conf, vs->ucode);
	    cellGcmSetUserClipPlaneControlInline( &_RGLState.fifo, 0, 0, 0, 0, 0, 0 );

	    RGLInterpolantState *s = &_RGLState.state.interpolant;
	    s->vertexProgramAttribMask = vs->header.vertexProgram.attributeOutputMask;

	    cellGcmSetVertexAttribOutputMaskInline( &_RGLState.fifo,  s->vertexProgramAttribMask & s->fragmentProgramAttribMask);

	    _CGprogram *program = ( _CGprogram* )vs;
	    int count = program->defaultValuesIndexCount;
	    for ( int i = 0; i < count; i++ )
	    {
		    const CgParameterEntry *parameterEntry = program->parametersEntries + program->defaultValuesIndices[i].entryIndex;
		    if (( parameterEntry->flags & CGPF_REFERENCED ) && ( parameterEntry->flags & CGPV_MASK ) == CGPV_CONSTANT )
		    {
			    const float *itemDefaultValues = program->defaultValues + program->defaultValuesIndices[i].defaultValueIndex;
			    const GLfloat *value = itemDefaultValues;
			    const CgParameterResource *parameterResource = _RGLGetParameterResource( program, parameterEntry );
			    if ( parameterResource->resource != ( unsigned short ) - 1 )
			    {
				    switch ( parameterResource->type )
				    {
					    case CG_FLOAT:
					    case CG_FLOAT1:
					    case CG_FLOAT2:
					    case CG_FLOAT3:
					    case CG_FLOAT4:
						    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 1, value );
						    break;
					    case CG_FLOAT4x4:
						    {
							    GLfloat v2[16];
							    v2[0] = value[0];v2[1] = value[4];v2[2] = value[8];v2[3] = value[12];
							    v2[4] = value[1];v2[5] = value[5];v2[6] = value[9];v2[7] = value[13];
							    v2[8] = value[2];v2[9] = value[6];v2[10] = value[10];v2[11] = value[14];
							    v2[12] = value[3];v2[13] = value[7];v2[14] = value[11];v2[15] = value[15];
							    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 4, v2 );
						    }
						    break;
					    case CG_FLOAT3x3:
						    {
							    GLfloat v2[12];
							    v2[0] = value[0];v2[1] = value[3];v2[2] = value[6];v2[3] = 0;
							    v2[4] = value[1];v2[5] = value[4];v2[6] = value[7];v2[7] = 0;
							    v2[8] = value[2];v2[9] = value[5];v2[10] = value[8];v2[11] = 0;
							    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 3, v2 );
						    }
						    break;
					    case CG_HALF:
					    case CG_HALF1:
					    case CG_HALF2:
					    case CG_HALF3:
					    case CG_HALF4:
					    case CG_INT:
					    case CG_INT1:
					    case CG_INT2:
					    case CG_INT3:
					    case CG_INT4:
					    case CG_BOOL:
					    case CG_BOOL1:
					    case CG_BOOL2:
					    case CG_BOOL3:
					    case CG_BOOL4:
					    case CG_FIXED:
					    case CG_FIXED1:
					    case CG_FIXED2:
					    case CG_FIXED3:
					    case CG_FIXED4:
						    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 1, value );
						    break;
					    case CG_HALF4x4:
					    case CG_INT4x4:
					    case CG_BOOL4x4:
					    case CG_FIXED4x4:
						    {
							    GLfloat v2[16];
							    v2[0] = value[0];v2[1] = value[4];v2[2] = value[8];v2[3] = value[12];
							    v2[4] = value[1];v2[5] = value[5];v2[6] = value[9];v2[7] = value[13];
							    v2[8] = value[2];v2[9] = value[6];v2[10] = value[10];v2[11] = value[14];
							    v2[12] = value[3];v2[13] = value[7];v2[14] = value[11];v2[15] = value[15];
							    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 4, v2 );
						    }
						    break;
					    case CG_HALF3x3:
					    case CG_INT3x3:
					    case CG_BOOL3x3:
					    case CG_FIXED3x3:
						    {
							    GLfloat v2[12];
							    v2[0] = value[0];v2[1] = value[3];v2[2] = value[6];v2[3] = 0;
							    v2[4] = value[1];v2[5] = value[4];v2[6] = value[7];v2[7] = 0;
							    v2[8] = value[2];v2[9] = value[5];v2[10] = value[8];v2[11] = 0;
							    cellGcmSetVertexProgramParameterBlockInline( &_RGLState.fifo, parameterResource->resource, 3, v2 );
						    }
						    break;
					    default:
						    break;
				    }
			    }
		    }
	    }

	    if(!(LContext->needValidate & PSGL_VALIDATE_VERTEX_CONSTANTS) && LContext->BoundVertexProgram->parentContext)
	    {
		    cellGcmSetTransformBranchBitsInline( &_RGLState.fifo, LContext->BoundVertexProgram->controlFlowBools | LContext->BoundVertexProgram->parentContext->controlFlowBoolsShared );

		    _RGLPushProgramPushBuffer( LContext->BoundVertexProgram );
	    }
    }

    if ( RGL_LIKELY( needValidate & PSGL_VALIDATE_VERTEX_CONSTANTS ) && LContext->BoundVertexProgram->parentContext)
    {
	    cellGcmSetTransformBranchBitsInline( &_RGLState.fifo, LContext->BoundVertexProgram->controlFlowBools | LContext->BoundVertexProgram->parentContext->controlFlowBoolsShared );

	    _RGLPushProgramPushBuffer( LContext->BoundVertexProgram );
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_VERTEX_TEXTURES_USED ) )
    {
	    for ( int unit = 0; unit < _RGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; ++unit )
	    {
		    jsTexture *texture = LContext->VertexTextureImages[unit];
		    if ( texture )
			    if ( RGL_UNLIKELY( texture->revalidate ) )
				    _RGLPlatformValidateTextureResources( texture );

		    cellGcmSetVertexTextureAddressInline( &_RGLState.fifo, unit, CELL_GCM_TEXTURE_WRAP, CELL_GCM_TEXTURE_WRAP);
		    cellGcmSetVertexTextureControlInline( &_RGLState.fifo, unit, GL_FALSE, 0, 256);
		    cellGcmSetVertexTextureFilterInline( &_RGLState.fifo, unit, 0);
	    }
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_FRAGMENT_PROGRAM ) )
    {
	    _CGprogram *program = LContext->BoundFragmentProgram;

	    const GLvoid *header = program;
	    const _CGprogram *ps = ( const _CGprogram * )header;

	    CellCgbFragmentProgramConfiguration conf;

	    conf.offset = gmmIdToOffset(ps->loadProgramId) + ps->loadProgramOffset;

	    RGLInterpolantState *s = &_RGLState.state.interpolant;
	    s->fragmentProgramAttribMask |= ps->header.attributeInputMask | CELL_GCM_ATTRIB_OUTPUT_MASK_POINTSIZE;

	    conf.attributeInputMask = s->vertexProgramAttribMask & s->fragmentProgramAttribMask;
	    conf.texCoordsInputMask = ps->header.fragmentProgram.texcoordInputMask;
	    conf.texCoords2D = ps->header.fragmentProgram.texcoord2d;
	    conf.texCoordsCentroid = ps->header.fragmentProgram.texcoordCentroid;

	    int fragmentControl = ( 1 << 15 ) | ( 1 << 10 );
	    fragmentControl |= ps->header.fragmentProgram.flags & CGF_DEPTHREPLACE ? 0xE : 0x0;
	    fragmentControl |= ps->header.fragmentProgram.flags & CGF_OUTPUTFROMH0 ? 0x00 : 0x40;
	    fragmentControl |= ps->header.fragmentProgram.flags & CGF_PIXELKILL ? 0x80 : 0x00;

	    conf.fragmentControl  = fragmentControl;
	    conf.registerCount = ps->header.fragmentProgram.registerCount < 2 ? 2 : ps->header.fragmentProgram.registerCount;

	    uint32_t controlTxp = _CurrentContext->AllowTXPDemotion; 
	    conf.fragmentControl &= ~CELL_GCM_MASK_SET_SHADER_CONTROL_CONTROL_TXP; 
	    conf.fragmentControl |= controlTxp << CELL_GCM_SHIFT_SET_SHADER_CONTROL_CONTROL_TXP; 

	    cellGcmSetFragmentProgramLoadInline( &_RGLState.fifo, &conf);
	    cellGcmSetZMinMaxControlInline( &_RGLState.fifo, ( ps->header.fragmentProgram.flags & CGF_DEPTHREPLACE ) ? CELL_GCM_FALSE : CELL_GCM_TRUE, CELL_GCM_FALSE, CELL_GCM_FALSE );


	    driver->fpLoadProgramId = program->loadProgramId;
	    driver->fpLoadProgramOffset = program->loadProgramOffset;
    }
	
    if ( RGL_LIKELY(( needValidate & ~( PSGL_VALIDATE_TEXTURES_USED |
                                       PSGL_VALIDATE_VERTEX_PROGRAM |
                                       PSGL_VALIDATE_VERTEX_CONSTANTS |
                                       PSGL_VALIDATE_VERTEX_TEXTURES_USED |
                                       PSGL_VALIDATE_FRAGMENT_PROGRAM ) ) == 0 ) )
    {
        LContext->needValidate = 0;
        return dirty;
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_BLENDING ) )
    {
	if ((LContext->Blending || LContext->BlendingMrt[0] || LContext->BlendingMrt[1] || LContext->BlendingMrt[2]))
	{
		GLuint hwColor;
		cellGcmSetBlendEnableInline( &_RGLState.fifo, LContext->Blending);
		cellGcmSetBlendEnableMrtInline( &_RGLState.fifo, LContext->BlendingMrt[0], LContext->BlendingMrt[1], LContext->BlendingMrt[2] );

		RGL_CALC_COLOR_LE_ARGB8( &hwColor, RGL_CLAMPF_01(LContext->BlendColor.R), RGL_CLAMPF_01(LContext->BlendColor.G), RGL_CLAMPF_01(LContext->BlendColor.B), RGL_CLAMPF_01(LContext->BlendColor.A) );
		cellGcmSetBlendColorInline( &_RGLState.fifo, hwColor, hwColor);
		cellGcmSetBlendEquationInline( &_RGLState.fifo, (RGLEnum)LContext->BlendEquationRGB, (RGLEnum)LContext->BlendEquationAlpha );
		cellGcmSetBlendFuncInline( &_RGLState.fifo, (RGLEnum)LContext->BlendFactorSrcRGB, (RGLEnum)LContext->BlendFactorDestRGB, (RGLEnum)LContext->BlendFactorSrcAlpha, (RGLEnum)LContext->BlendFactorDestAlpha);
	}
	else 
	{
		cellGcmSetBlendEnableInline( &_RGLState.fifo, CELL_GCM_FALSE);
		cellGcmSetBlendEnableMrtInline( &_RGLState.fifo, CELL_GCM_FALSE, CELL_GCM_FALSE, CELL_GCM_FALSE );
	}
    }

    if ( RGL_UNLIKELY( needValidate & PSGL_VALIDATE_SHADER_SRGB_REMAP ) )
    {
	cellGcmSetFragmentProgramGammaEnableInline( &_RGLState.fifo, LContext->ShaderSRGBRemap ? CELL_GCM_TRUE : CELL_GCM_FALSE);

	LContext->needValidate &= ~PSGL_VALIDATE_SHADER_SRGB_REMAP;
    }

    LContext->needValidate = 0;
    return dirty;
}

PSGLcontext *psglGetCurrentContext()
{
   return _CurrentContext;
}

const GLfloat _RGLIdentityMatrixf[ELEMENTS_IN_MATRIX] =
{
   1.f, 0.f, 0.f, 0.f,
   0.f, 1.f, 0.f, 0.f,
   0.f, 0.f, 1.f, 0.f,
   0.f, 0.f, 0.f, 1.f
};

static void _RGLMatrixStackReset( jsMatrixStack* LMatrixStack )
{
   LMatrixStack->MatrixStackPtr = 0;
   memcpy( LMatrixStack->MatrixStackf, _RGLIdentityMatrixf, sizeof(GLfloat)*ELEMENTS_IN_MATRIX );
   LMatrixStack->dirty = GL_TRUE;
}

static void _RGLResetContext( PSGLcontext *LContext )
{
    _RGLMatrixStackReset( &( LContext->ModelViewMatrixStack ) );
    _RGLMatrixStackReset( &( LContext->ProjectionMatrixStack ) );
    _RGLTexNameSpaceResetNames( &LContext->textureNameSpace );
    _RGLTexNameSpaceResetNames( &LContext->bufferObjectNameSpace );
    _RGLTexNameSpaceResetNames( &LContext->framebufferNameSpace );
    _RGLTexNameSpaceResetNames( &LContext->attribSetNameSpace );

    LContext->InverseModelViewValid = GL_FALSE;
    LContext->ScalingFactor = 1.f;

    LContext->ViewPort.X = 0;
    LContext->ViewPort.Y = 0;
    LContext->ViewPort.XSize = 0;
    LContext->ViewPort.YSize = 0;

    LContext->ClearColor.R = 0.f;
    LContext->ClearColor.G = 0.f;
    LContext->ClearColor.B = 0.f;
    LContext->ClearColor.A = 0.f;
    LContext->AccumClearColor.R = 0.f;
    LContext->AccumClearColor.G = 0.f;
    LContext->AccumClearColor.B = 0.f;
    LContext->AccumClearColor.A = 0.f;

    LContext->ShaderSRGBRemap = GL_FALSE;

    LContext->Blending = GL_FALSE;
    LContext->BlendingMrt[0] = GL_FALSE;
    LContext->BlendingMrt[1] = GL_FALSE;
    LContext->BlendingMrt[2] = GL_FALSE;
    LContext->BlendColor.R = 0.0f;
    LContext->BlendColor.G = 0.0f;
    LContext->BlendColor.B = 0.0f;
    LContext->BlendColor.A = 0.0f;
    LContext->BlendEquationRGB = GL_FUNC_ADD;
    LContext->BlendEquationAlpha = GL_FUNC_ADD;
    LContext->BlendFactorSrcRGB = GL_ONE;
    LContext->BlendFactorDestRGB = GL_ZERO;
    LContext->BlendFactorSrcAlpha = GL_ONE;
    LContext->BlendFactorDestAlpha = GL_ZERO;

    LContext->Dithering = GL_TRUE;

    for ( int i = 0;i < _RGL_MAX_TEXTURE_COORDS;++i )
    {
        jsTextureCoordsUnit *tu = LContext->TextureCoordsUnits + i;
        tu->revalidate = 0;
        _RGLMatrixStackReset( &( tu->TextureMatrixStack ) );
    }
    for ( int i = 0;i < _RGL_MAX_TEXTURE_IMAGE_UNITS;++i )
    {
        jsTextureImageUnit *tu = LContext->TextureImageUnits + i;
        tu->bound2D = 0;

        tu->fragmentTarget = 0;

        tu->envMode = GL_MODULATE;
        tu->envColor.R = 0.f;
        tu->envColor.G = 0.f;
        tu->envColor.B = 0.f;
        tu->envColor.A = 0.f;

        tu->currentTexture = NULL;
    }
    for ( int i = 0;i < _RGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS;++i )
        LContext->VertexTextureImages[i] = NULL;

    LContext->ActiveTexture = 0;
    LContext->CurrentImageUnit = LContext->TextureImageUnits;
    LContext->CurrentCoordsUnit = LContext->TextureCoordsUnits;

    LContext->packAlignment = 4;
    LContext->unpackAlignment = 4;

    LContext->CS_ActiveTexture = 0;

    _RGLResetAttributeState( &LContext->defaultAttribs0 );
    LContext->attribs = &LContext->defaultAttribs0;
    LContext->attribSetName = 0;
    LContext->attribSetDirty = GL_FALSE;

    LContext->framebuffer = 0;

    LContext->VertexProgram = GL_FALSE;
    LContext->BoundVertexProgram = 0;

    LContext->FragmentProgram = GL_FALSE;
    LContext->BoundFragmentProgram = 0;

    LContext->ArrayBuffer = 0;
    LContext->PixelUnpackBuffer = 0;
    LContext->TextureBuffer = 0;

    LContext->VSync = GL_FALSE;

    LContext->AllowTXPDemotion = GL_FALSE; 
}

static void _RGLMatrixStackInit( jsMatrixStack* LMatrixStack, GLuint depth )
{
   LMatrixStack->MatrixStackf = (GLfloat *)malloc( sizeof(GLfloat)*ELEMENTS_IN_MATRIX * depth );

   if (!LMatrixStack->MatrixStackf)
      return;

   _RGLMatrixStackReset( LMatrixStack );
}

static jsTexture *_RGLAllocateTexture (void)
{
    GLuint size = sizeof( jsTexture ) + sizeof( RGLTexture);
    jsTexture *texture = ( jsTexture * )malloc( size );
    memset( texture, 0, size );
    texture->target = 0;
    texture->minFilter = GL_NEAREST_MIPMAP_LINEAR;
    texture->magFilter = GL_LINEAR;
    texture->gammaRemap = 0;
    texture->usage = 0;
    texture->isRenderTarget = GL_FALSE;
    texture->image = NULL;
    texture->isComplete = GL_FALSE;
    texture->imageCount = 0;
    texture->revalidate = 0;
    texture->referenceBuffer = NULL;
    new( &texture->framebuffers ) RGL::Vector<jsFramebuffer *>();
    RGLTexture *gcmTexture = ( RGLTexture * )texture->platformTexture;
    memset( gcmTexture, 0, sizeof( RGLTexture ) );
    gcmTexture->gpuAddressId = GMM_ERROR;
    return texture;
}

static void _RGLFreeTexture( jsTexture *texture )
{
    _RGLTextureTouchFBOs( texture );
    texture->framebuffers.~Vector<jsFramebuffer *>();
    if ( texture->image )
    {
	    for ( GLuint i = 0;i < texture->imageCount;++i )
	    {
		    jsImage *image = texture->image + i;
		    _RGLImageFreeCPUStorage( image );
	    }
	    if(texture->image != NULL)
		    free( texture->image );
    }
    if ( texture->referenceBuffer )
    {
        texture->referenceBuffer->textureReferences.removeElement( texture );
    }
    _RGLPlatformDestroyTexture( texture );
    if(texture != NULL)
	    free( texture );
}

PSGLcontext* psglCreateContext (void)
{
    PSGLcontext* LContext = ( PSGLcontext* )malloc( sizeof( PSGLcontext ) );
    if ( !LContext ) return NULL;

    memset( LContext, 0, sizeof( PSGLcontext ) );

    LContext->error = GL_NO_ERROR;
    LContext->MatrixMode = GL_MODELVIEW;

    _RGLMatrixStackInit( &( LContext->ModelViewMatrixStack ), _RGL_MAX_MODELVIEW_STACK_DEPTH );
    if ( !LContext->ModelViewMatrixStack.MatrixStackf )
    {
        psglDestroyContext( LContext );
        return NULL;
    }
    _RGLMatrixStackInit( &( LContext->ProjectionMatrixStack ), _RGL_MAX_PROJECTION_STACK_DEPTH );
    if ( !LContext->ProjectionMatrixStack.MatrixStackf )
    {
        psglDestroyContext( LContext );
        return NULL;
    }

    for ( int i = 0; i < _RGL_MAX_TEXTURE_COORDS; i++ )
    {
        _RGLMatrixStackInit( &( LContext->TextureCoordsUnits[i].TextureMatrixStack ), _RGL_MAX_TEXTURE_STACK_DEPTH );
        if ( !LContext->TextureCoordsUnits[i].TextureMatrixStack.MatrixStackf )
        {
            psglDestroyContext( LContext );
            return NULL;
        }
    }

    _RGLTexNameSpaceInit( &LContext->textureNameSpace, ( jsTexNameSpaceCreateFunction )_RGLAllocateTexture, ( jsTexNameSpaceDestroyFunction )_RGLFreeTexture );

    for ( int i = 0;i < _RGL_MAX_TEXTURE_IMAGE_UNITS;++i )
    {
        jsTextureImageUnit *tu = LContext->TextureImageUnits + i;

        tu->default2D = _RGLAllocateTexture();
        if ( !tu->default2D )
        {
            psglDestroyContext( LContext );
            return NULL;
        }
        tu->default2D->target = GL_TEXTURE_2D;
    }

    _RGLTexNameSpaceInit( &LContext->bufferObjectNameSpace, ( jsTexNameSpaceCreateFunction )_RGLCreateBufferObject, ( jsTexNameSpaceDestroyFunction )_RGLFreeBufferObject );
    _RGLTexNameSpaceInit( &LContext->framebufferNameSpace, ( jsTexNameSpaceCreateFunction )_RGLCreateFramebuffer, ( jsTexNameSpaceDestroyFunction )_RGLDestroyFramebuffer );
    _RGLTexNameSpaceInit( &LContext->attribSetNameSpace, ( jsTexNameSpaceCreateFunction )_RGLCreateAttribSet, ( jsTexNameSpaceDestroyFunction )_RGLDestroyAttribSet );

    LContext->needValidate = 0;
    LContext->everAttached = 0;

    LContext->RGLcgLastError = CG_NO_ERROR;
    LContext->RGLcgErrorCallbackFunction = NULL;
    LContext->RGLcgContextHead = ( CGcontext )NULL;
	
    _RGLInitNameSpace( &LContext->cgProgramNameSpace );
    _RGLInitNameSpace( &LContext->cgParameterNameSpace );
    _RGLInitNameSpace( &LContext->cgContextNameSpace );

    _RGLResetContext( LContext );

    if ( _RGLContextCreateHook )
       _RGLContextCreateHook( LContext );

    return( LContext );
}

void  psglResetCurrentContext (void)
{
    PSGLcontext *context = _CurrentContext;
    _RGLResetContext( context );
    context->needValidate |= PSGL_VALIDATE_ALL;
}

static void _RGLMatrixStackClear( jsMatrixStack* LMatrixStack )
{
    if ( LMatrixStack->MatrixStackf ) free( LMatrixStack->MatrixStackf );
    LMatrixStack->MatrixStackf = NULL;
    LMatrixStack->MatrixStackPtr = 0;
    LMatrixStack->dirty = GL_FALSE;
}

void  psglDestroyContext( PSGLcontext* LContext )
{
    if ( _CurrentContext == LContext )
    {
	cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
        _RGLFifoFinish( &_RGLState.fifo );
    }

    while ( LContext->RGLcgContextHead != ( CGcontext )NULL )
    {
        PSGLcontext* current = _CurrentContext;
        _CurrentContext = LContext;
        cgDestroyContext( LContext->RGLcgContextHead );
        _CurrentContext = current;
    }
    _RGLFreeNameSpace( &LContext->cgProgramNameSpace );
    _RGLFreeNameSpace( &LContext->cgParameterNameSpace );
    _RGLFreeNameSpace( &LContext->cgContextNameSpace );

    if ( _RGLContextDestroyHook ) _RGLContextDestroyHook( LContext );

    _RGLMatrixStackClear( &( LContext->ModelViewMatrixStack ) );
    _RGLMatrixStackClear( &( LContext->ProjectionMatrixStack ) );

    for ( int i = 0; i < _RGL_MAX_TEXTURE_COORDS; i++ )
        _RGLMatrixStackClear( &( LContext->TextureCoordsUnits[i].TextureMatrixStack ) );

    for ( int i = 0; i < _RGL_MAX_TEXTURE_IMAGE_UNITS; ++i )
    {
        jsTextureImageUnit* tu = LContext->TextureImageUnits + i;
        if ( tu->default2D ) _RGLFreeTexture( tu->default2D );
    }

    _RGLTexNameSpaceFree( &LContext->textureNameSpace );
    _RGLTexNameSpaceFree( &LContext->bufferObjectNameSpace );
    _RGLTexNameSpaceFree( &LContext->framebufferNameSpace );
    _RGLTexNameSpaceFree( &LContext->attribSetNameSpace );

    if ( _CurrentContext == LContext )
        psglMakeCurrent( NULL, NULL );

    if(LContext != NULL)
       free( LContext );
}

void _RGLAttachContext( PSGLdevice *device, PSGLcontext* context )
{
   if ( !context->everAttached )
   {
      context->ViewPort.XSize = device->deviceParameters.width;
      context->ViewPort.YSize = device->deviceParameters.height;
      context->everAttached = GL_TRUE;
      _RGLFifoGlViewport(context->ViewPort.X, context->ViewPort.Y, 
      context->ViewPort.XSize, context->ViewPort.YSize, 0.0f, 1.0f);
   }
   context->needValidate = PSGL_VALIDATE_ALL;

   for ( int unit = 0;unit < _RGL_MAX_TEXTURE_UNITS;unit++ )
      context->TextureCoordsUnits[unit].TextureMatrixStack.dirty = GL_TRUE;

   context->ModelViewMatrixStack.dirty = GL_TRUE;
   context->ProjectionMatrixStack.dirty = GL_TRUE;
   context->attribs->DirtyMask = ( 1 << _RGL_MAX_VERTEX_ATTRIBS ) - 1;
}

GLAPI void APIENTRY glGetFloatv( GLenum pname, GLfloat* params )
{
    PSGLcontext* LContext = _CurrentContext;
    jsMatrixStack* LMatrixStack = NULL;
    GLfloat *LMatrix = NULL;

    switch (pname)
    {
        case GL_MODELVIEW_MATRIX:
            jsContextGetMatrixf( LContext, GL_MODELVIEW, LMatrixStack, LMatrix );
            break;

        case GL_PROJECTION_MATRIX:
            jsContextGetMatrixf( LContext, GL_PROJECTION, LMatrixStack, LMatrix );
            break;

        case GL_TEXTURE_MATRIX:
            jsContextGetMatrixf( LContext, GL_TEXTURE, LMatrixStack, LMatrix );
            break;
        case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
	    return;
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }
    memcpy( params, LMatrixStack->MatrixStackf + LMatrixStack->MatrixStackPtr * ELEMENTS_IN_MATRIX, sizeof(GLfloat)*ELEMENTS_IN_MATRIX );
}

GLAPI void APIENTRY glEnable( GLenum cap )
{
    PSGLcontext* LContext = _CurrentContext;

    switch (cap)
    {
        case GL_SHADER_SRGB_REMAP_SCE:
            LContext->ShaderSRGBRemap = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_SHADER_SRGB_REMAP;
            break;
        case GL_BLEND:
            LContext->Blending = GL_TRUE;
            LContext->BlendingMrt[0] = GL_TRUE;
            LContext->BlendingMrt[1] = GL_TRUE;
            LContext->BlendingMrt[2] = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_BLEND_MRT0_SCE:
            LContext->Blending = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_BLEND_MRT1_SCE:
            LContext->BlendingMrt[0] = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_BLEND_MRT2_SCE:
            LContext->BlendingMrt[1] = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_BLEND_MRT3_SCE:
            LContext->BlendingMrt[2] = GL_TRUE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_POINT_SMOOTH:
            break;
        case GL_DITHER:
            LContext->Dithering = GL_TRUE;
            break;
        case GL_POINT_SPRITE_OES:
        case GL_VERTEX_PROGRAM_POINT_SIZE_ARB:
            break;
        case GL_VSYNC_SCE:
            LContext->VSync = GL_TRUE;
            break;
	case GL_FRAGMENT_PROGRAM_CONTROL_CONTROLTXP_SCE:
	    LContext->AllowTXPDemotion = GL_TRUE;
	    LContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM;
	    break; 
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }
}

GLAPI void APIENTRY glDisable( GLenum cap )
{
    PSGLcontext* LContext = _CurrentContext;

    switch ( cap )
    {
        case GL_SHADER_SRGB_REMAP_SCE:
            LContext->ShaderSRGBRemap = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_SHADER_SRGB_REMAP;
            break;
        case GL_BLEND:
            LContext->Blending = GL_FALSE;
            LContext->BlendingMrt[0] = GL_FALSE;
            LContext->BlendingMrt[1] = GL_FALSE;
            LContext->BlendingMrt[2] = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;

        case GL_BLEND_MRT0_SCE:
            LContext->Blending = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;

        case GL_BLEND_MRT1_SCE:
            LContext->BlendingMrt[0] = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;

        case GL_BLEND_MRT2_SCE:
            LContext->BlendingMrt[1] = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;

        case GL_BLEND_MRT3_SCE:
            LContext->BlendingMrt[2] = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_BLENDING;
            break;
        case GL_POINT_SMOOTH:
        case GL_LINE_SMOOTH:
            break;
        case GL_DITHER:
            LContext->Dithering = GL_FALSE;
            break;
        case GL_POINT_SPRITE_OES:
        case GL_VERTEX_PROGRAM_POINT_SIZE_ARB:
            break;
        case GL_VSYNC_SCE:
            LContext->VSync = GL_FALSE;
            break;
	case GL_FRAGMENT_PROGRAM_CONTROL_CONTROLTXP_SCE:
	    LContext->AllowTXPDemotion = GL_FALSE;
	    LContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM;
	    break; 

        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }
}

GLAPI void APIENTRY glEnableClientState( GLenum array )
{
    PSGLcontext* LContext = _CurrentContext;

    switch(array)
    {
        case GL_VERTEX_ARRAY:
            _RGLEnableVertexAttribArrayNV( _RGL_ATTRIB_POSITION_INDEX );
            break;
        case GL_COLOR_ARRAY:
            _RGLEnableVertexAttribArrayNV( _RGL_ATTRIB_PRIMARY_COLOR_INDEX );
            break;
        case GL_NORMAL_ARRAY:
            _RGLEnableVertexAttribArrayNV( _RGL_ATTRIB_NORMAL_INDEX );
            break;
        case GL_TEXTURE_COORD_ARRAY:
            _RGLEnableVertexAttribArrayNV( _RGL_ATTRIB_TEX_COORD0_INDEX + LContext->CS_ActiveTexture );
            break;
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }
}

GLAPI void APIENTRY glDisableClientState( GLenum array )
{
    PSGLcontext* LContext = _CurrentContext;

    switch ( array )
    {
        case GL_VERTEX_ARRAY:
            _RGLDisableVertexAttribArrayNV( _RGL_ATTRIB_POSITION_INDEX );
            break;
        case GL_COLOR_ARRAY:
            _RGLDisableVertexAttribArrayNV( _RGL_ATTRIB_PRIMARY_COLOR_INDEX );
            break;
        case GL_NORMAL_ARRAY:
            _RGLDisableVertexAttribArrayNV( _RGL_ATTRIB_NORMAL_INDEX );
            break;
        case GL_TEXTURE_COORD_ARRAY:
            _RGLDisableVertexAttribArrayNV( _RGL_ATTRIB_TEX_COORD0_INDEX + LContext->CS_ActiveTexture );
            break;
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }
}

GLAPI void APIENTRY glFlush()
{
    PSGLcontext *LContext = _CurrentContext;
    RGLFifo *fifo = &_RGLState.fifo;

    if ( RGL_UNLIKELY( LContext->needValidate ) )
	_RGLValidateStates();

    cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);

    _RGLFifoFlush( fifo );
}

GLAPI void APIENTRY glFinish (void)
{
    glFlush();
    cellGcmSetInvalidateVertexCacheInline( &_RGLState.fifo);
    _RGLFifoFinish( &_RGLState.fifo );
}

GLAPI const GLubyte* APIENTRY glGetString( GLenum name )
{
    switch ( name )
    {
        case GL_VENDOR:
            return(( GLubyte* )_RGLVendorString );
        case GL_RENDERER:
            return(( GLubyte* )_RGLRendererString );
        case GL_VERSION:
            return(( GLubyte* )_RGLVersionNumber );
        case GL_EXTENSIONS:
            return(( GLubyte* )_RGLExtensionsString );
        default:
        {
            _RGLSetError( GL_INVALID_ENUM );
            return(( GLubyte* )NULL );
        }
    }
}


void psglInit( PSGLinitOptions* options )
{
   if ( !_RGLInitCompleted )
   {
      int ret = cellSysmoduleLoadModule( CELL_SYSMODULE_GCM_SYS );
      ret = cellSysmoduleLoadModule( CELL_SYSMODULE_RESC );

      _RGLDeviceInit( options );
      _CurrentContext = NULL;
      _CurrentDevice = NULL;
   }

   _RGLInitCompleted = 1;
}

void psglExit (void)
{
   PSGLcontext* LContext = _CurrentContext;
   if ( LContext )
   {
      glFlush();
      cellGcmSetInvalidateVertexCacheInline ( &_RGLState.fifo);
      _RGLFifoFinish( &_RGLState.fifo );

      psglMakeCurrent( NULL, NULL );
      _RGLDeviceExit();

      _CurrentContext = NULL; 

      _RGLInitCompleted = 0;
   }
}

#undef __STRICT_ANSI__

GLAPI void APIENTRY glLoadIdentity(void)
{
   PSGLcontext* LContext = _CurrentContext;
   jsMatrixStack* LMatrixStack = NULL;

   jsContextGetMatrixStack(LContext, LContext->MatrixMode, LMatrixStack);

   memcpy( LMatrixStack->MatrixStackf + LMatrixStack->MatrixStackPtr * ELEMENTS_IN_MATRIX, _RGLIdentityMatrixf, sizeof(GLfloat)*ELEMENTS_IN_MATRIX );

   LMatrixStack->dirty = GL_TRUE;
}

GLAPI void APIENTRY glMatrixMode( GLenum mode )
{
   PSGLcontext* LContext = _CurrentContext;
   LContext->MatrixMode = mode;
}

GLAPI void APIENTRY glOrthof( GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar )
{
    PSGLcontext* LContext = _CurrentContext;
    jsMatrixStack* LMatrixStack = NULL;
    GLfloat *LMatrix = NULL;

    jsContextGetMatrixStack(LContext, LContext->MatrixMode, LMatrixStack);

    if (LMatrixStack)
       LMatrix = LMatrixStack->MatrixStackf + LMatrixStack->MatrixStackPtr * ELEMENTS_IN_MATRIX;

    GLfloat L00, L01, L02, L03, L10, L11, L12, L13, L20, L21, L22, L23, L30, L31, L32, L33;


    GLfloat m00 = 2.f / ( right - left );
    GLfloat m03 = -( right + left ) / ( right - left );
    GLfloat m11 = 2.f / ( top - bottom );
    GLfloat m13 = -( top + bottom ) / ( top - bottom );
    GLfloat m22 = -2.f / ( zFar - zNear );
    GLfloat m23 = -( zFar + zNear ) / ( zFar - zNear );

    L00 = LMatrix[0];
    L01 = LMatrix[M01];
    L02 = LMatrix[M02];
    L03 = LMatrix[M03];
    L10 = LMatrix[M10];
    L11 = LMatrix[M11];
    L12 = LMatrix[M12];
    L13 = LMatrix[M13];
    L20 = LMatrix[M20];
    L21 = LMatrix[M21];
    L22 = LMatrix[M22];
    L23 = LMatrix[M23];
    L30 = LMatrix[M30];
    L31 = LMatrix[M31];
    L32 = LMatrix[M32];
    L33 = LMatrix[M33];

    LMatrix[0] = L00 * m00;
    LMatrix[M01] = L01 * m11;
    LMatrix[M02] = L02 * m22;
    LMatrix[M03] = L00 * m03 + L01 * m13 + L02 * m23 + L03;

    LMatrix[M10] = L10 * m00;
    LMatrix[M11] = L11 * m11;
    LMatrix[M12] = L12 * m22;
    LMatrix[M13] = L10 * m03 + L11 * m13 + L12 * m23 + L13;

    LMatrix[M20] = L20 * m00;
    LMatrix[M21] = L21 * m11;
    LMatrix[M22] = L22 * m22;
    LMatrix[M23] = L20 * m03 + L21 * m13 + L22 * m23 + L23;

    LMatrix[M30] = L30 * m00;
    LMatrix[M31] = L31 * m11;
    LMatrix[M32] = L32 * m22;
    LMatrix[M33] = L30 * m03 + L31 * m13 + L32 * m23 + L33;

    LMatrixStack->dirty = GL_TRUE;
    if ( LContext->MatrixMode == GL_MODELVIEW )
        LContext->InverseModelViewValid = GL_FALSE;
}


int _psglCheckDrawElementsVBOBounds = 0;

GLAPI void APIENTRY glVertexPointer( GLint size, GLenum type, GLsizei stride, const GLvoid* pointer )
{
    _RGLVertexAttribPointerNV( _RGL_ATTRIB_POSITION_INDEX, size, type, GL_FALSE, stride, pointer );
}

GLAPI void APIENTRY glTexCoordPointer( GLint size, GLenum type, GLsizei stride, const GLvoid* pointer )
{
    PSGLcontext*	LContext = _CurrentContext;

    _RGLVertexAttribPointerNV(
        _RGL_ATTRIB_TEX_COORD0_INDEX + LContext->CS_ActiveTexture,
        size,
        type,
        GL_FALSE,
        stride,
        pointer );
}

GLAPI void APIENTRY glColorPointer( GLint size, GLenum type, GLsizei stride, const GLvoid* pointer )
{
    _RGLVertexAttribPointerNV( _RGL_ATTRIB_PRIMARY_COLOR_INDEX, size, type, GL_TRUE, stride, pointer );
}

static GLboolean _RGLPlatformNeedsConversion( const jsAttributeState* as, GLuint index )
{
    const jsAttribute* attrib = as->attrib + index;

    switch ( attrib->clientType )
    {
        case GL_SHORT:
        case GL_HALF_FLOAT_ARB:
        case GL_FLOAT:
        case GL_FIXED_11_11_10_SCE:
            return GL_FALSE;
        case GL_UNSIGNED_BYTE:
            if ( attrib->normalized ||
                 attrib->clientSize == 4 ) 
                 return GL_FALSE; 
            break;
        default:
            break;
    }
    RARCH_WARN("Attribute %d needs conversion. Slow path ahead.\n", index);
    return GL_TRUE;
}

static int _RGLGetTypeSize( GLenum type )
{
    switch ( type )
    {

#define DECLARE_PACKED_TYPE(REALTYPE,TYPE,N,S1,S2,S3,S4,REV) \
		case TYPE: \
			return sizeof(type_##REALTYPE);
            DECLARE_PACKED_TYPES
#undef DECLARE_PACKED_TYPE

#define DECLARE_UNPACKED_TYPE(TYPE) \
		case TYPE: \
			return sizeof(type_##TYPE);
            DECLARE_UNPACKED_TYPES
#undef DECLARE_UNPACKED_TYPE

        default:
            return 0;
    }
}

void _RGLVertexAttribPointerNV(
    GLuint index,
    GLint fsize,
    GLenum type,
    GLboolean normalized,
    GLsizei stride,
    const GLvoid* pointer )
{
    PSGLcontext* LContext = _CurrentContext;

    GLsizei defaultStride = 0;
    switch ( type )
    {
        case GL_FLOAT:
        case GL_HALF_FLOAT_ARB:
        case GL_BYTE:
        case GL_UNSIGNED_BYTE:
        case GL_SHORT:
        case GL_FIXED:
            defaultStride = fsize * _RGLGetTypeSize( type );
            break;
        case GL_FIXED_11_11_10_SCE:
            defaultStride = 4;
            break;
        default:
            _RGLSetError( GL_INVALID_ENUM );
            return;
    }

    jsAttributeState* as = LContext->attribs;
    jsAttribute* attrib = as->attrib + index;
    attrib->clientSize = fsize;
    attrib->clientType = type;
    attrib->clientStride = stride ? stride : defaultStride;
    attrib->clientData = ( void* )pointer;
    GLuint oldArrayBuffer = attrib->arrayBuffer;
    attrib->arrayBuffer = LContext->ArrayBuffer;
    attrib->normalized = normalized;
    RGLBIT_ASSIGN( as->HasVBOMask, index, attrib->arrayBuffer != 0 );
    GLboolean needConvert = _RGLPlatformNeedsConversion( as, index );
    RGLBIT_ASSIGN( as->NeedsConversionMask, index, needConvert );

    RGLBIT_TRUE( as->DirtyMask, index );

    if ( LContext->attribSetName )
    {
        jsAttribSet* attribSet = _RGLGetAttribSet( LContext->attribSetName );

        if ( oldArrayBuffer )
        {
            int refcount = 0;
            for ( unsigned int i = 0; i < _RGL_MAX_VERTEX_ATTRIBS; ++i )
            {
                if ( attribSet->attribs.attrib[i].arrayBuffer == oldArrayBuffer ) ++refcount;
            }
            if ( refcount == 1 ) _RGLGetBufferObject( LContext, oldArrayBuffer )->attribSets.removeElement( attribSet );
        }

        if ( attrib->arrayBuffer )
        {
            _RGLGetBufferObject( LContext, attrib->arrayBuffer )->attribSets.appendUnique( attribSet );
        }

        _RGLAttribSetMarkDirty();
    }
}

void _RGLEnableVertexAttribArrayNV( GLuint index )
{
    PSGLcontext *LContext = _CurrentContext;

    RGLBIT_TRUE( LContext->attribs->EnabledMask, index );
    RGLBIT_TRUE( LContext->attribs->DirtyMask, index );

    if ( LContext->attribSetName )
	    _RGLAttribSetMarkDirty();
}

void _RGLDisableVertexAttribArrayNV( GLuint index )
{
    PSGLcontext *LContext = _CurrentContext;

    RGLBIT_FALSE( LContext->attribs->EnabledMask, index );
    RGLBIT_TRUE( LContext->attribs->DirtyMask, index );

    if ( LContext->attribSetName )
	    _RGLAttribSetMarkDirty();
}

static GLuint _RGLValidateAttributesSlow( jsDrawParams *dparams, GLboolean *isMain )
{
	PSGLcontext*	LContext = _CurrentContext;
	RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;
	jsAttributeState* as = LContext->attribs;

	void* xferBuffer = NULL;
	GLuint xferId = GMM_ERROR;
	GLuint VBOId = GMM_ERROR;
	GLuint gpuOffset;
	if ( RGL_UNLIKELY( dparams->xferTotalSize ) )
	{
		xferId = gmmAlloc(0, dparams->xferTotalSize);
		xferBuffer = gmmIdToAddress(xferId);
	}

	unsigned int needsUpdateMask = ( as->DirtyMask | ( as->EnabledMask & ~as->HasVBOMask ) );

	LContext->attribSetDirty = GL_FALSE;

	if ( needsUpdateMask )
	{
		for ( GLuint i = 0; i < _RGL_MAX_VERTEX_ATTRIBS; ++i )
		{
			if ( ! RGLBIT_GET( needsUpdateMask, i ) )
				continue;

			jsAttribute* attrib = as->attrib + i;
			if ( RGLBIT_GET( as->EnabledMask, i ) )
			{
				const GLsizei stride = attrib->clientStride;
				const GLuint freq = attrib->frequency;

				if ( RGL_UNLIKELY( dparams->attribXferSize[i] ) )
				{
					GLuint maxElements = dparams->firstVertex + dparams->vertexCount;

					GLuint offset;
					if ( RGLBIT_GET( as->ModuloMask, i ) )
						offset = ( maxElements > freq ) ? 0 : dparams->firstVertex * stride;
					else
						offset = ( dparams->firstVertex / freq ) * stride;

					char * b = ( char * )xferBuffer + dparams->attribXferOffset[i];
					memcpy( b + offset,
							( char * )attrib->clientData + offset,
							dparams->attribXferSize[i] - offset );

					*isMain = gmmIdIsMain(xferId);
					gpuOffset = gmmIdToOffset(xferId) + (b - ( char * )xferBuffer);

				}
				else
				{
					VBOId = _RGLGetBufferObjectOrigin( attrib->arrayBuffer );
					*isMain = gmmIdIsMain(VBOId);
					gpuOffset = gmmIdToOffset(VBOId)
						+ (( const GLubyte* )attrib->clientData - ( const GLubyte* )NULL );
				}

				_RGLFifoGlVertexAttribPointer( i, attrib->clientSize,
						( RGLEnum )attrib->clientType, attrib->normalized,
						stride, freq, *isMain, gpuOffset );
			}
			else
			{
				_RGLFifoGlVertexAttribPointer( i, 0, RGL_FLOAT, 0, 0, 0, 0, 0 );
				cellGcmSetVertexData4fInline( &_RGLState.fifo, i,attrib->value);
			}
		}
		cellGcmSetFrequencyDividerOperationInline( &_RGLState.fifo, as->ModuloMask);
		driver->invalidateVertexCache = GL_TRUE;
	}
	as->DirtyMask = 0;

	if ( xferId != GMM_ERROR )
		gmmFree( xferId );

	return 0;
}

GLAPI void APIENTRY glDrawArrays( GLenum mode, GLint first, GLsizei count )
{
   static uint8_t s_dparams_buff[ (sizeof(jsDrawParams) + 0x7f) & ~0x7f ] __attribute__((aligned(128)));

   PSGLcontext*	LContext = _CurrentContext;
   jsAttributeState* as = LContext->attribs;
   RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;
   const GLuint clientSideMask = as->EnabledMask & ~as->HasVBOMask;

   if ( RGL_UNLIKELY( ! RGLBIT_GET( LContext->attribs->EnabledMask, _RGL_ATTRIB_POSITION_INDEX ) ) ) return;

   uint32_t _tmp_clear_loop = ((sizeof(jsDrawParams) + 0x7f) & ~0x7f) >> 7;

   do{
      --_tmp_clear_loop;
      __dcbz(s_dparams_buff+(_tmp_clear_loop << 7));
   }while(_tmp_clear_loop);

   jsDrawParams *dparams = (jsDrawParams *)s_dparams_buff;
   dparams->mode = mode;
   dparams->firstVertex = first;
   dparams->vertexCount = count;
   GLuint maxElements = dparams->firstVertex + dparams->vertexCount;

   if ( LContext->needValidate )
	   _RGLValidateStates();

   if ( RGL_UNLIKELY( clientSideMask ) )
   {
      for ( int i = 0; i < _RGL_MAX_VERTEX_ATTRIBS; ++i )
      {
         dparams->attribXferOffset[i] = 0;
	 dparams->attribXferSize[i] = 0;

	 if(clientSideMask & (1 << i))
	 {
            jsAttribute* attrib = as->attrib + i;
	    const GLuint freq = attrib->frequency;
	    GLuint count;

	    if (RGLBIT_GET(as->ModuloMask, i))
               count = maxElements > freq ? freq : maxElements;
            else
               count = ( maxElements + freq - 1 ) / freq;

	    const GLuint numBytes = attrib->clientStride * count;
	    dparams->attribXferOffset[i] = dparams->xferTotalSize;
	    dparams->attribXferSize[i] = numBytes;

	    const GLuint numBytesPadded = _RGLPad( numBytes, 128 );
	    dparams->xferTotalSize += numBytesPadded;
	    dparams->attribXferTotalSize += numBytesPadded;
	 }
      }
   }

   if ( driver->flushBufferCount != 0 )
	   driver->invalidateVertexCache = GL_TRUE;

   GLboolean isMain = 0;

   if ( LContext->attribSetDirty && LContext->attribSetName )
	   jsAttribSet* attribSet = _RGLGetAttribSet( LContext->attribSetName );

   uint32_t totalXfer = 0;

   for (GLuint i = 0; i < _RGL_MAX_VERTEX_ATTRIBS; ++i)
      totalXfer += dparams->attribXferSize[i];

   GLuint gpuOffset = _RGLValidateAttributesSlow( dparams, &isMain );
   (void)gpuOffset;

   if(driver->invalidateVertexCache)
   {
      driver->invalidateVertexCache = GL_FALSE;
      cellGcmSetInvalidateVertexCacheInline ( &_RGLState.fifo);
   }

   cellGcmSetUpdateFragmentProgramParameterInline( &_RGLState.fifo, gmmIdToOffset( driver->fpLoadProgramId) +driver->fpLoadProgramOffset );

   cellGcmSetDrawArraysInline( &_RGLState.fifo, CELL_GCM_PRIMITIVE_QUADS, dparams->firstVertex, dparams->vertexCount);
}

GLAPI void APIENTRY glGenTextures( GLsizei n, GLuint *textures )
{
   PSGLcontext*	LContext = _CurrentContext;
   _RGLTexNameSpaceGenNames( &LContext->textureNameSpace, n, textures );
}

static void _RGLTextureUnbind( PSGLcontext* context, GLuint name )
{
    int unit;
    for ( unit = 0;unit < _RGL_MAX_TEXTURE_IMAGE_UNITS;++unit )
    {
        jsTextureImageUnit *tu = context->TextureImageUnits + unit;
        GLboolean dirty = GL_FALSE;
        if ( tu->bound2D == name )
        {
            tu->bound2D = 0;
            dirty = GL_TRUE;
        }
        if ( dirty )
        {
	    tu->currentTexture = _RGLGetCurrentTexture( tu, GL_TEXTURE_2D );
            context->needValidate |= PSGL_VALIDATE_TEXTURES_USED;
        }
    }
    if ( _RGLTexNameSpaceIsName( &context->textureNameSpace, name ) )
    {
        jsTexture*texture = ( jsTexture * )context->textureNameSpace.data[name];
        for ( unit = 0;unit < _RGL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; ++unit )
        {
            if ( context->VertexTextureImages[unit] == texture )
            {
                context->VertexTextureImages[unit] = NULL;
                context->needValidate |= PSGL_VALIDATE_VERTEX_TEXTURES_USED;
            }
        }
    }
}

GLAPI void APIENTRY glDeleteTextures( GLsizei n, const GLuint *textures )
{
   PSGLcontext*	LContext = _CurrentContext;
   for ( int i = 0;i < n;++i )
      if ( textures[i] )
         _RGLTextureUnbind( LContext, textures[i] );

   _RGLTexNameSpaceDeleteNames( &LContext->textureNameSpace, n, textures );
} 
GLAPI void APIENTRY glTexParameteri( GLenum target, GLenum pname, GLint param )
{
   PSGLcontext*	LContext = _CurrentContext;
   jsTexture *texture = _RGLGetCurrentTexture( LContext->CurrentImageUnit, target );

   switch ( pname )
   {
	   case GL_TEXTURE_MIN_FILTER:
		   texture->minFilter = param;
		   if ( texture->referenceBuffer == 0 )
		   {
			   texture->revalidate |= _RGL_TEXTURE_REVALIDATE_LAYOUT; }
		   break;
	   case GL_TEXTURE_MAG_FILTER:
		   texture->magFilter = param;
		   break;
	   case GL_TEXTURE_MAX_LEVEL:
	   case GL_TEXTURE_WRAP_S:
	   case GL_TEXTURE_WRAP_T:
	   case GL_TEXTURE_WRAP_R:
	   case GL_TEXTURE_FROM_VERTEX_PROGRAM_SCE:
		   break;
	   case GL_TEXTURE_ALLOCATION_HINT_SCE:
		   texture->usage = param;
		   texture->revalidate |= _RGL_TEXTURE_REVALIDATE_LAYOUT;
		   break;
	   case GL_TEXTURE_MIN_LOD:
	   case GL_TEXTURE_MAX_LOD:
	   case GL_TEXTURE_LOD_BIAS:
	   case GL_TEXTURE_MAX_ANISOTROPY_EXT:
	   case GL_DEPTH_TEXTURE_MODE_ARB:
	   case GL_TEXTURE_COMPARE_MODE_ARB:
	   case GL_TEXTURE_COMPARE_FUNC_ARB:
		   break;
	   case GL_TEXTURE_GAMMA_REMAP_R_SCE:
	   case GL_TEXTURE_GAMMA_REMAP_G_SCE:
	   case GL_TEXTURE_GAMMA_REMAP_B_SCE:
	   case GL_TEXTURE_GAMMA_REMAP_A_SCE:
		   {
			   GLuint bit = 1 << ( pname - GL_TEXTURE_GAMMA_REMAP_R_SCE );
			   if ( param ) texture->gammaRemap |= bit;
			   else texture->gammaRemap &= ~bit;
		   }
		   break;
	   default:
		   _RGLSetError( GL_INVALID_ENUM );
		   return;
   }

   texture->revalidate |= _RGL_TEXTURE_REVALIDATE_PARAMETERS;
   LContext->needValidate |= PSGL_VALIDATE_TEXTURES_USED | PSGL_VALIDATE_VERTEX_TEXTURES_USED;
}

GLAPI void APIENTRY glBindTexture( GLenum target, GLuint name )
{
   PSGLcontext*	LContext = _CurrentContext;
   jsTextureImageUnit *unit = LContext->CurrentImageUnit;

   _RGLBindTextureInternal( unit, name);
}

static void _RGLReallocateImages( jsTexture *texture, GLsizei dimension )
{
   GLuint oldCount = texture->imageCount;

   if ( dimension <= 0 )
      dimension = 1;

   GLuint n = 1 + _RGLLog2( dimension );
   n = MAX( n, oldCount );

   jsImage *images = ( jsImage * )realloc( texture->image, n * sizeof( jsImage ) );

   memset( images + oldCount, 0, ( n - oldCount )*sizeof( jsImage ) );

   texture->image = images;
   texture->imageCount = n;
}

static int _RGLGetImage( GLenum target, GLint level, jsTexture **texture, jsImage **image, GLsizei reallocateSize )
{
   PSGLcontext*	LContext = _CurrentContext;
   jsTextureImageUnit *unit = LContext->CurrentImageUnit;

   jsTexture *tex = _RGLGetCurrentTexture( unit, GL_TEXTURE_2D );

   if ( level >= ( int )tex->imageCount )
      _RGLReallocateImages( tex, reallocateSize );

   *image = tex->image;
   *texture = tex;
   return 0;
}


GLAPI void APIENTRY glTexImage2D( GLenum target, GLint level, GLint internalFormat,
GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels )
{
    PSGLcontext* LContext = _CurrentContext;
    jsTexture *texture;
    jsImage *image;

     _RGLGetImage( GL_TEXTURE_2D, 0, &texture, &image, MAX( width, height ) );

    image->dataState = _RGL_IMAGE_DATASTATE_UNSET;

    GLboolean directPBO = GL_FALSE;
    if ( LContext->PixelUnpackBuffer != 0 )
    {
        directPBO = _RGLPlatformTexturePBOImage(
                        texture,
                        image,
                        internalFormat,
                        width, height,
                        format, type,
                        pixels );
    }

    if ( !directPBO )
    {
        jsBufferObject* bufferObject = NULL;
        if ( LContext->PixelUnpackBuffer != 0 )
        {
            bufferObject = _RGLGetBufferObject( LContext, LContext->PixelUnpackBuffer );
            pixels = _RGLPlatformBufferObjectMap( bufferObject, GL_READ_ONLY ) +
                     (( const GLubyte* )pixels - ( const GLubyte* )NULL );
        }

        _RGLSetImage(image, internalFormat, width, height, 1, LContext->unpackAlignment,
        format, type, pixels);

        if ( LContext->PixelUnpackBuffer != 0 )
	{
           RGLBufferObject *jsBuffer = ( RGLBufferObject * )bufferObject->platformBufferObject;

	   if ( --jsBuffer->mapCount == 0 )
	   {
              if ( jsBuffer->mapAccess != GL_READ_ONLY )
	      {
                 RGLDriver *driver= (RGLDriver *)_CurrentDevice->rasterDriver;
		 --driver->flushBufferCount;

		 driver->invalidateVertexCache = GL_TRUE;
	      }

	      jsBuffer->mapAccess = GL_NONE;

	      GmmBaseBlock    *pBaseBlock = (GmmBaseBlock *)jsBuffer->bufferId;

	      if (!pBaseBlock->isTile)
	      {
                 GmmBlock *pBlock = (GmmBlock *)jsBuffer->bufferId;
		 pBlock->isPinned = 0;
	      }
	   }
	}

        texture->revalidate |= _RGL_TEXTURE_REVALIDATE_IMAGES;
    }

    _RGLTextureTouchFBOs( texture );
    LContext->needValidate |= PSGL_VALIDATE_TEXTURES_USED | PSGL_VALIDATE_VERTEX_TEXTURES_USED;
}

GLAPI void APIENTRY glActiveTexture( GLenum texture )
{
   PSGLcontext* LContext = _CurrentContext;

   int unit = texture - GL_TEXTURE0;
   LContext->ActiveTexture = unit;
   LContext->CurrentImageUnit = unit < _RGL_MAX_TEXTURE_IMAGE_UNITS ? LContext->TextureImageUnits + unit : NULL;
   LContext->CurrentCoordsUnit = unit < _RGL_MAX_TEXTURE_COORDS ? LContext->TextureCoordsUnits + unit : NULL;
}

GLAPI void APIENTRY glClientActiveTexture( GLenum texture )
{
   PSGLcontext*	LContext = _CurrentContext;
   LContext->CS_ActiveTexture = texture - GL_TEXTURE0;
}

GLAPI void APIENTRY glPixelStorei( GLenum pname, GLint param )
{
   PSGLcontext* LContext = _CurrentContext;

   switch ( pname )
   {
      case GL_PACK_ALIGNMENT:
         LContext->packAlignment = param;
	 break;
      case GL_UNPACK_ALIGNMENT:
	 LContext->unpackAlignment = param;
	 break;
      default:
	 _RGLSetError( GL_INVALID_ENUM );
	 return;
   }
}


GLAPI void APIENTRY glTextureReferenceSCE( GLenum target, GLuint levels, GLuint baseWidth, GLuint baseHeight, GLuint baseDepth, GLenum internalFormat, GLuint pitch, GLintptr offset )
{
   PSGLcontext*	LContext = _CurrentContext;

   jsTexture *texture = _RGLGetCurrentTexture( LContext->CurrentImageUnit, GL_TEXTURE_2D);
   jsBufferObject *bufferObject = _RGLGetBufferObject( LContext, LContext->TextureBuffer );
   _RGLReallocateImages( texture, MAX( baseWidth, MAX( baseHeight, baseDepth ) ) );

   GLuint width = baseWidth;
   GLuint height = baseHeight;
   _RGLSetImage(texture->image, GL_RGB5_A1, width, height, 0, LContext->unpackAlignment,
   0, 0, NULL );
   width = MAX( 1U, width / 2 );
   height = MAX( 1U, height / 2 );
   texture->usage = GL_TEXTURE_LINEAR_GPU_SCE;

   GLboolean r = _RGLPlatformTextureReference( texture, pitch, bufferObject, offset );

   if(!r)
      return;

   bufferObject->textureReferences.pushBack( texture );
   texture->referenceBuffer = bufferObject;
   texture->offset = offset;
   _RGLTextureTouchFBOs( texture );
   LContext->needValidate |= PSGL_VALIDATE_TEXTURES_USED | PSGL_VALIDATE_VERTEX_TEXTURES_USED ;
}

GLAPI void APIENTRY glViewport( GLint x, GLint y, GLsizei width, GLsizei height )
{
   PSGLcontext*	LContext = _CurrentContext;

   LContext->ViewPort.X = x;
   LContext->ViewPort.Y = y;
   LContext->ViewPort.XSize = width;
   LContext->ViewPort.YSize = height;

   _RGLFifoGlViewport(LContext->ViewPort.X, LContext->ViewPort.Y, 
   LContext->ViewPort.XSize, LContext->ViewPort.YSize, 0.0f, 1.0f);
}

jsTexture *_RGLGetCurrentTexture( const jsTextureImageUnit *unit, GLenum target )
{
    PSGLcontext*	LContext = _CurrentContext;
    GLuint name = unit->bound2D;
    jsTexture *defaultTexture = unit->default2D;

    if ( name )
        return ( jsTexture * )LContext->textureNameSpace.data[name];
    else
    	return defaultTexture;
}

CgprogramHookFunction _cgProgramCreateHook = NULL;
CgprogramHookFunction _cgProgramDestroyHook = NULL;
CgprogramCopyHookFunction _cgProgramCopyHook = NULL;

cgRTCgcCompileHookFunction _cgRTCgcCompileProgramHook = NULL;
cgRTCgcFreeHookFunction _cgRTCgcFreeCompiledProgramHook;

CgcontextHookFunction _cgContextCreateHook = NULL;
CgcontextHookFunction _cgContextDestroyHook = NULL;

CgparameterHookFunction _cgParameterCreateHook = NULL;
CgparameterHookFunction _cgParameterDestroyHook = NULL;

typedef struct RGLcgProfileMapType
{
   CGprofile id;
   char* string;
   int is_vertex_program;
} RGLcgProfileMapType;

static void _RGLCgProgramPushFront( _CGcontext* ctx, _CGprogram* prog )
{
   prog->next = ctx->programList;
   ctx->programList = prog;
   prog->parentContext = ctx;
   ctx->programCount++;
}

static _CGprogram* _RGLCgProgramFindPrev( _CGcontext* ctx, _CGprogram* prog )
{
   _CGprogram* ptr = ctx->programList;

   while ( NULL != ptr && prog != ptr->next )
      ptr = ptr->next;

   return ptr;
}

void _RGLCgProgramErase( _CGprogram* prog )
{
   if ( _cgProgramDestroyHook ) _cgProgramDestroyHook( prog );

   switch ( prog->header.profile )
   {
      case CG_PROFILE_SCE_VP_TYPEB:
      case CG_PROFILE_SCE_VP_RSX:
      case CG_PROFILE_SCE_FP_TYPEB:
      case CG_PROFILE_SCE_FP_RSX:
         _RGLPlatformProgramErase( prog );
	 break;
      default:
         break;
   }

   if ( prog->id )
      _RGLEraseName( &_CurrentContext->cgProgramNameSpace, ( jsName )prog->id );

   if ( prog->runtimeElf )
      free( prog->runtimeElf );

   memset( prog, 0, sizeof( _CGprogram ) );
}

bool _RGLCgCreateProgramChecks( CGcontext ctx, CGprofile profile, CGenum program_type )
{
    if ( !CG_IS_CONTEXT( ctx ) )
    {
        _RGLCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
        return false;
    }

    switch ( profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
        case CG_PROFILE_SCE_FP_RSX:
            break;
        default:
            _RGLCgRaiseError( CG_UNKNOWN_PROFILE_ERROR );
            return false;
    }

    switch ( program_type )
    {
        case CG_BINARY:
        case CG_SOURCE:
            break;
        default:
            _RGLCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
            return false;
    }

    return true;
}

typedef struct
{
   const char* elfFile;
   size_t elfFileSize;

   const char *symtab;
   size_t symbolSize;
   size_t symbolCount;
   const char *symbolstrtab;

   const char* shadertab;
   size_t shadertabSize;
   const char* strtab;
   size_t strtabSize;
   const char* consttab;
   size_t consttabSize;
} CGELFBinary;

typedef struct
{
   const char *texttab;
   size_t texttabSize;
   const char *paramtab;
   size_t paramtabSize;
   int index;
} CGELFProgram;

static bool cgOpenElf( const void *ptr, size_t size, CGELFBinary *elfBinary )
{
    while(1)
    {
        size_t shadertabSize;
        size_t consttabSize;
        size_t strtabSize;
        size_t symbolSize;
        size_t symbolCount;
        const char *symbolstrtab;

        const char *symtab = findSymbolSectionInPlace(( const char * )ptr, size, &symbolSize, &symbolCount, &symbolstrtab );
        if ( !symtab )
            break;

        const char *shadertab = findSectionInPlace(( const char* )ptr, size, ".shadertab", &shadertabSize );

        if ( !shadertab )
            break;

        const char *strtab = findSectionInPlace(( const char* )ptr, size, ".strtab", &strtabSize );

        if ( !strtab )
            break;

        const char *consttab = findSectionInPlace(( const char* )ptr, size, ".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;
}

static bool cgGetElfProgramByIndex( CGELFBinary *elfBinary, int index, CGELFProgram *elfProgram )
{
    while ( true )
    {
        char sectionName[64];
        snprintf( sectionName, sizeof(sectionName), ".text%04i", index );
        size_t texttabSize;
        const char *texttab = findSectionInPlace( elfBinary->elfFile, elfBinary->elfFileSize, sectionName, &texttabSize );
        if ( !texttab )
            break;
        snprintf( sectionName, sizeof(sectionName), ".paramtab%04i", index );
        size_t paramtabSize;
        const char *paramtab = findSectionInPlace( elfBinary->elfFile, elfBinary->elfFileSize, 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 == NULL || 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;
}

static 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 ( prog == NULL )
    {
        _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;

    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_TYPEB:
            //case CG_PROFILE_SCE_VP_TYPEC:
        case CG_PROFILE_SCE_VP_RSX:
            // TODO ************** need to include the entry symbol too
	    success =  _RGLGenerateProgram( prog, VERTEX_PROFILE_INDEX, programHeader,
			    ucode, parameterHeader, NULL, stringTable, defaultValues );
            break;
        case CG_PROFILE_SCE_FP_TYPEB:
            //case CG_PROFILE_SCE_FP_TYPEC:
        case CG_PROFILE_SCE_FP_RSX:
            success = _RGLGenerateProgram( prog, FRAGMENT_PROFILE_INDEX, programHeader, ucode, parameterHeader, NULL, 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, ( jsName )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;
}

static CGprogram _RGLCgUpdateProgramAtIndex( CGprogramGroup group, int index, int refcount );

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.

    if ( profile == ( CGprofile )7005 )
        profile = CG_PROFILE_SCE_VP_RSX;
    if ( profile == ( CGprofile )7006 )
        profile = CG_PROFILE_SCE_FP_RSX;

    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, cgGetProfileString(profile), entry, args, &compiled_program );
            if ( !compiled_program )
            {
                _RGLCgRaiseError( CG_COMPILER_ERROR );
                return NULL;
            }
            binaryBuffer = compiled_program;
        }
        else
        {
            RARCH_ERR("The CG runtime compiler hasn't been setup. cgRTCgcInit() should be called prior to this function.\n" );
            _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))
    {
        //convert NV file to the runtime format

        if ( program_type == CG_BINARY )
        {
            RARCH_WARN("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.\n");
        }

        //convert from NV format to the runtime format
        int compiled_program_size = 0;
        STL_NAMESPACE vector<char> stringTableArray;
        STL_NAMESPACE vector<float> defaultValuesArray;
        CgBinaryProgram* nvProgram = ( CgBinaryProgram* )binaryBuffer;
        char *runtimeElfShader = NULL;

        //check the endianness
        int totalSize;

        if (( nvProgram->profile != CG_PROFILE_SCE_FP_TYPEB ) && ( nvProgram->profile != CG_PROFILE_SCE_VP_TYPEB ) &&
                ( 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 )
        {
            RARCH_ERR("Invalid CG binary program.\n");
            _RGLCgRaiseError( CG_COMPILER_ERROR );
            if ( compiled_program )
                _cgRTCgcFreeCompiledProgramHook( compiled_program );
            return NULL;
        }

        if ( compiled_program )
            _cgRTCgcFreeCompiledProgramHook( compiled_program );

        size_t stringTableSize = stringTableArray.size() * sizeof( stringTable[0] );
        size_t defaultTableSize = defaultValuesArray.size() * sizeof( defaultValues[0] );
        int paddedSize = _RGLPad( compiled_program_size, 4 );

        char *runtimeElf = (char*)memalign( 16, paddedSize + stringTableSize + defaultTableSize );

        if ( !runtimeElf )
        {
            _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
            return NULL;
        }
        bConvertedToElf = true;
        memcpy( runtimeElf, runtimeElfShader, compiled_program_size );

        convertNvToElfFreeBinaryShader( runtimeElfShader );

        float* pDefaultValues = ( float* )(( char* )runtimeElf + paddedSize );
        defaultValues = pDefaultValues;

        if ( defaultTableSize )
            memcpy( pDefaultValues, &defaultValuesArray[0], defaultTableSize );

        char *pStringTable = ( char* )runtimeElf + paddedSize + defaultTableSize;
        stringTable = pStringTable;

        if ( stringTableSize )
            memcpy( pStringTable, &stringTableArray[0], stringTableSize );

        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
    {
        CGELFBinary elfBinary;
        CGELFProgram elfProgram;
        if ((( intptr_t )binaryBuffer ) & 15 )
        {
            RARCH_ERR("CG Binary not aligned on 16 bytes, needed for ucode section.\n");
            _RGLCgRaiseError( CG_PROGRAM_LOAD_ERROR );
            return NULL;
        }
        bool res = cgOpenElf( binaryBuffer, 0, &elfBinary );
        if ( !res )
        {
            RARCH_ERR("Not a valid ELF.\n");
            _RGLCgRaiseError( CG_PROGRAM_LOAD_ERROR );
            return NULL;
        }
        if ( !cgGetElfProgramByName( &elfBinary, entry, &elfProgram ) )
        {
            RARCH_ERR("Couldn't find the shader entry in the CG binary.\n");
            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 ( 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 )
{
    if ( profile == ( CGprofile )7005 )
        profile = CG_PROFILE_SCE_VP_RSX;
    if ( profile == ( CGprofile )7006 )
        profile = CG_PROFILE_SCE_FP_RSX;

    if ( program_type == CG_ROW_MAJOR )
        program_type = CG_BINARY;

    if ( !_RGLCgCreateProgramChecks( ctx, profile, program_type ) )
        return NULL;

    FILE* fp = NULL;
    if ( RGL_LIKELY( program_type == CG_BINARY ) )
    {
        CGprogram ret = NULL;

        _CGcontext *context = _cgGetContextPtr( ctx );
        CGprogramGroup group = NULL;

        group = context->groupList;
        while ( group )
        {
            const char *groupName = _RGLCgGetProgramGroupName( group );
            if ( groupName && !strcmp( groupName, program_file ) )
            {
                int index;
                if ( entry == NULL )
                    index = 0;
                else
                    index = _RGLCgGetProgramIndex( group, entry );
                if ( index >= 0 )
                {
                    ret = _RGLCgUpdateProgramAtIndex( group, index, 1 );
                    break;
                }
                else
                {
                    return ( CGprogram )NULL;
                }
            }
            group = group->next;
        }

        if ( ret )
            return ret;
        else
        {
            fp = fopen( program_file, "rb" );

            if ( fp == NULL )
            {
                _RGLCgRaiseError( CG_FILE_READ_ERROR );
                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;
            if ( filetag == ElfTag )
            {
                fclose( fp );

                group = _RGLCgCreateProgramGroupFromFile( ctx, program_file );
                if ( group )
                {
                    _CGprogramGroup *_group = ( _CGprogramGroup * )group;
                    _group->userCreated = false;
                    if ( entry == NULL )
                    {
                        if ( group->programCount == 1 )
                            ret = _RGLCgUpdateProgramAtIndex( group, 0, 1 );
                    }
                    else
                    {
                        int index = _RGLCgGetProgramIndex( group, entry );
                        if ( index == -1 )
                        {
                            RARCH_ERR("Couldn't find the shader entry in the CG binary.\n");
                        }
                        else
                            ret = _RGLCgUpdateProgramAtIndex( group, index, 1 );
                    }
                }
                return ret;
            }
        }
    }

    if ( !fp )
    {
        fp = fopen( program_file, "rb" );
        if ( fp == NULL )
        {
            _RGLCgRaiseError( CG_FILE_READ_ERROR );
            return ( CGprogram )NULL;
        }
    }

    size_t file_size = 0;
    fseek( fp, 0, SEEK_END );
    file_size = ftell( fp );
    rewind( fp );

    char* ptr = ( char* )malloc( file_size + 1 );
    if ( ptr == NULL )
    {
        _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
        fclose( fp );
        return ( CGprogram )NULL;
    }

    fread( ptr, file_size, 1, fp );
    fclose( fp );

    if ( program_type == CG_SOURCE )
       ptr[file_size] = '\0';

    CGprogram ret = cgCreateProgram( ctx, program_type, ptr, profile, entry, args );

    free( ptr );

    return ret;
}

CG_API CGprogram cgCopyProgram( CGprogram program )
{
    if ( !CG_IS_PROGRAM( program ) )
    {
        _RGLCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
        return NULL;
    }
    _CGprogram* prog = _cgGetProgPtr( program );
    if ( prog == NULL )
    {
        _RGLCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
        return ( CGprogram )NULL;
    }
    
    _CGprogram* newprog;
    size_t paddedProgramSize = 0;
    size_t ucodeSize = 0;

    if (prog->header.profile == CG_PROFILE_SCE_FP_TYPEB || 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 ( newprog == NULL )
    {
        _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
        return ( CGprogram )NULL;
    }
    memset( newprog, 0, sizeof( _CGprogram ) );

    newprog->header.profile = prog->header.profile;
    newprog->parentContext = prog->parentContext;

    newprog->id = ( CGprogram )_RGLCreateName( &_CurrentContext->cgProgramNameSpace, newprog );

    int success = 0;
    switch ( prog->header.profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_FP_RSX:
            success = _RGLPlatformCopyProgram( prog, newprog );
            break;
        default:
            _RGLCgRaiseError( CG_UNKNOWN_PROFILE_ERROR );
            success = 0;
            break;
    }

    if ( success == 0 )
    {
        free( newprog );
        _RGLEraseName( &_CurrentContext->cgProgramNameSpace, ( jsName )newprog->id );
        return ( CGprogram )NULL;
    }

    if (prog->header.profile == CG_PROFILE_SCE_FP_TYPEB || prog->header.profile == CG_PROFILE_SCE_FP_RSX)
	{
	    newprog->ucode = (char*)newprog + paddedProgramSize;
	    memcpy((char*)newprog->ucode, (char*)prog->ucode, ucodeSize);
	}

    if ( prog->programGroup )
    {
        newprog->programGroup = prog->programGroup;
        newprog->programIndexInGroup = -1;
        _RGLCgUpdateProgramAtIndex( newprog->programGroup, -1, 1 );
    }

    _RGLCgProgramPushFront( newprog->parentContext, newprog );

    if ( _cgProgramCopyHook ) _cgProgramCopyHook( newprog, prog );

    return newprog->id;
}


CG_API void cgDestroyProgram( CGprogram program )
{
    if ( !CG_IS_PROGRAM( program ) )
    {
        _RGLCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
        return;
    }
    _CGprogram* ptr = _cgGetProgPtr( program );
    if ( ptr == NULL )
    {
        _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 )
            {
                _RGLCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
                return;
            }
            else
            {
                bool isGroupMember = ( ptr->programIndexInGroup != -1 );
                _RGLCgUpdateProgramAtIndex( ptr->programGroup, ptr->programIndexInGroup, -1 );
                if ( isGroupMember )
                    return;
            }
        }
    }

    _CGcontext* ctx = ptr->parentContext;

    if ( ptr == ctx->programList )
    {
        _CGprogram* p = ctx->programList;
        ctx->programList = p->next;
	_RGLCgProgramErase( p );
	if(p != NULL)
		free( p );
    }
    else
    {
        _CGprogram* p = _RGLCgProgramFindPrev( ctx, ptr );

	_CGprogram* next = p->next;
	if ( next )
	{
		p->next = next->next;
		_RGLCgProgramErase( next );
		if(next != NULL)
			free( next );
	}
    }
    return;
}

static CGprogram _RGLCgUpdateProgramAtIndex( CGprogramGroup group, int index, int refcount )
{
    if ( index < ( int )group->programCount )
    {
        if ( index >= 0 )
        {
            if ( refcount == 1 && group->programs[index].refCount == 1 )
            {
                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 void _RGLCgAddGroup( CGcontext ctx, CGprogramGroup group )
{
    _CGcontext *context = _cgGetContextPtr( ctx );
    if ( !context->groupList )
        context->groupList = group;
    else
    {
        _CGprogramGroup *current = context->groupList;
        while ( current->next )
            current = current->next;
        current->next = group;
    }
}

static void _RGLCgRemoveGroup( CGcontext ctx, CGprogramGroup group )
{
    _CGcontext *context = _cgGetContextPtr( 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;
    }
}

CGprogramGroup _RGLCgCreateProgramGroupFromFile( CGcontext ctx, const char *group_file )
{
    FILE* fp = fopen( group_file, "rb" );

    if ( fp == NULL )
    {
        _RGLCgRaiseError( CG_FILE_READ_ERROR );
        return ( CGprogramGroup )NULL;
    }

    size_t file_size = 0;
    fseek( fp, 0, SEEK_END );
    file_size = ftell( fp );
    rewind( fp );

    char* ptr = ( char* )malloc( file_size + 1 );
    if ( ptr == NULL )
    {
        _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
        return ( CGprogramGroup )NULL;
    }

    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;

        int programCount = elfBinary.shadertabSize / sizeof( CgProgramHeader );
        int i;

        size_t nvProgramNamesOffset = _RGLPad( sizeof( _CGprogramGroup ), sizeof( _CGnamedProgram ) );
        size_t nvDefaultValuesTableOffset = _RGLPad( nvProgramNamesOffset + programCount * sizeof( _CGnamedProgram ), 16 );

        size_t nvStringTableOffset = nvDefaultValuesTableOffset + elfConstTableSize;
        size_t structureSize = nvStringTableOffset + elfStringTableSize;

        group = ( CGprogramGroup )malloc( structureSize );
        if ( !group )
            break;

        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 )
                break;
            strlcpy( group->name, name, sizeof(group->name));
        }
        else
            group->name = NULL;

        if ( elfConstTableSize )
            memcpy(( char* )group + nvDefaultValuesTableOffset, elfBinary.consttab, elfConstTableSize );
        if ( elfStringTableSize )
            memcpy(( char* )group + nvStringTableOffset, elfBinary.strtab, elfStringTableSize );

        _RGLCgAddGroup( ctx, group );

        for ( i = 0;i < ( int )group->programCount;i++ )
        {
            CgProgramHeader *cgShader = ( CgProgramHeader* )elfBinary.shadertab + i;

            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;

            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++ )
    {
        _CGprogram *cgProgram = _cgGetProgPtr( group->programs[i].program );
        cgProgram->programGroup = NULL;
        cgDestroyProgram( _group->programs[i].program );
    }
    if(_group->filedata != NULL)
	    free( _group->filedata );
    if ( _group->name )
        free( _group->name );

    _RGLCgRemoveGroup( group->ctx, group );
    if(_group != NULL)
	    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;
}

CGprogram _RGLCgGetProgramAtIndex( CGprogramGroup group, unsigned int index )
{
    return _RGLCgUpdateProgramAtIndex( group, index, 0 );
}

int _RGLCgGetProgramCount( CGprogramGroup group )
{
    return group->programCount;
}

static const RGLcgProfileMapType RGLcgProfileMap[] =
{
   {( CGprofile )6144, "CG_PROFILE_START", 1 },
   {( CGprofile )6145, "unknown", 1 },
#define CG_PROFILE_MACRO(name, compiler_id, compiler_id_caps, compiler_opt,int_id,vertex_profile) \
   {CG_PROFILE_ ## compiler_id_caps, compiler_opt, vertex_profile},
#include <Cg/cg_profiles.h>
   {( CGprofile )0, "", 0 }
};

CG_API const char* cgGetProfileString( CGprofile profile )
{
    const size_t arraysize = sizeof( RGLcgProfileMap ) / sizeof( RGLcgProfileMapType );
    unsigned int i = 0;
    while ( i < arraysize )
    {
        if ( profile == RGLcgProfileMap[i].id )
        {
            return RGLcgProfileMap[i].string;
        }
        ++i;
    }
    return "";
}

CG_API CGerror cgGetError(void)
{
   CGerror err = _CurrentContext->RGLcgLastError;
   _CurrentContext->RGLcgLastError = CG_NO_ERROR;
   return err;
}


CG_API const char* cgGetErrorString( CGerror error )
{
   static char strbuf[256];
   snprintf(strbuf, sizeof(strbuf), "%d", error);
   return strbuf;
}

void _RGLCgDestroyContextParam( CgRuntimeParameter* ptr )
{
   if ( _cgParameterDestroyHook ) _cgParameterDestroyHook( ptr );
      _RGLEraseName( &_CurrentContext->cgParameterNameSpace, ( jsName )( 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 == NULL )
        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;

    bool bWasUnrolled = false;
    const char *prevStructureStart = structureStart;

    while (( !done ) && ( *structureStart ) && ( containerCount != -1 ) )
    {
        structureEnd = strpbrk( structureStart, ".[" );
        if ( structureEnd == NULL )
        {
            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;
        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 )
            {
                int skipCount = 1;
                while ( skipCount && currentEntry < lastEntry )
                {
                    if ( currentEntry->flags & CGP_STRUCTURE )
                    {
                        const CgParameterStructure *parameterStructure = _RGLGetParameterStructure( program, currentEntry );
                        skipCount += parameterStructure->memberCount;
                    }
                    else if ( currentEntry->flags & CGP_ARRAY )
                    {
                        if ( currentEntry->flags & CGP_UNROLLED )
                        {
                            const CgParameterArray *parameterArray =  _RGLGetParameterArray( program, currentEntry );
                            skipCount += _RGLGetSizeofSubArray(( short* )parameterArray->dimensions, parameterArray->dimensionCount );
                        }
                        else
                            skipCount++;
                    }
                    currentEntry++;
                    skipCount--;
                }
            }
            if ( containerCount != -2 )
                containerCount--;
        }

        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' )
                    {
                        itemIndex = ( int )( currentEntry - program->parametersEntries );
                        break;
                    }

                    currentEntry++;
                    if ( currentEntry->flags &CGP_STRUCTURE )
                    {
                        bWasUnrolled = true;
                        containerCount = _RGLGetSizeofSubArray(( short* )parameterArray->dimensions, parameterArray->dimensionCount );
                        break;
                    }
                    else
                    {
                        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 )
                            {
				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 )
                        {
                            ( *arrayIndex ) = arrayCellIndex;
                            itemIndex = ( int )( currentEntry - program->parametersEntries );
                        }
                    }
                }
                break;
                case CGP_STRUCTURE:
                    if ( done )
                    {
                        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;
    }

    if ( itemIndex != -1 )
        return ( _CGparameter* )( program->runtimeParameters + itemIndex );
    else
        return NULL;
}

CG_API CGparameter cgGetNamedParameter( CGprogram prog, const char* name )
{
    if ( !CG_IS_PROGRAM( prog ) )
    {
        _RGLCgRaiseError( CG_INVALID_PROGRAM_HANDLE_ERROR );
        return ( CGparameter )NULL;
    }

    _CGprogram* progPtr = _cgGetProgPtr( prog );
    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;
}

static CGbool _RGLPlatformSupportsVertexProgram( CGprofile p )
{
    if ( p == CG_PROFILE_SCE_VP_TYPEB )
        return CG_TRUE;
    if ( p == CG_PROFILE_SCE_VP_TYPEC )
        return CG_TRUE;
    if ( p == CG_PROFILE_SCE_VP_RSX )
        return CG_TRUE;
    return CG_FALSE;
}

CGGL_API CGbool cgGLIsProfileSupported( CGprofile profile )
{
    if ( profile == ( CGprofile )7005 )
        profile = CG_PROFILE_SCE_VP_RSX;
    if ( profile == ( CGprofile )7006 )
        profile = CG_PROFILE_SCE_FP_RSX;

    switch ( profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
            return ( CGbool ) _RGLPlatformSupportsVertexProgram( profile );
        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_FP_RSX:
            return ( CGbool ) _RGLPlatformSupportsFragmentProgram( profile );
        default:
            return CG_FALSE;
    }
}

CGGL_API void cgGLEnableProfile( CGprofile profile )
{
   if ( profile == ( CGprofile )7005 )
      profile = CG_PROFILE_SCE_VP_RSX;
   if ( profile == ( CGprofile )7006 )
      profile = CG_PROFILE_SCE_FP_RSX;

   PSGLcontext* LContext = _CurrentContext;
   struct _CGprogram* current = LContext->BoundFragmentProgram;
   switch ( profile )
   {
      case CG_PROFILE_SCE_VP_TYPEB:
      case CG_PROFILE_SCE_VP_RSX:
         LContext->VertexProgram = GL_TRUE;
	 LContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM | PSGL_VALIDATE_VERTEX_TEXTURES_USED;
	 break;
      case CG_PROFILE_SCE_FP_TYPEB:
      case CG_PROFILE_SCE_FP_RSX:
         LContext->FragmentProgram = GL_TRUE;
	 if ( current )
	 {
            for (GLuint i = 0; i < current->samplerCount; ++i)
	    {
               int unit = current->samplerUnits[i];
	       _CurrentContext->TextureImageUnits[unit].currentTexture = _RGLGetCurrentTexture( &_CurrentContext->TextureImageUnits[unit], GL_TEXTURE_2D );
	    }
	 }
	 LContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;
	 break;
      default:
         _RGLCgRaiseError( CG_INVALID_PROFILE_ERROR );
	 break;
   }
}

CGGL_API void cgGLDisableProfile( CGprofile profile )
{
    if ( profile == ( CGprofile )7005 )
        profile = CG_PROFILE_SCE_VP_RSX;
    if ( profile == ( CGprofile )7006 )
        profile = CG_PROFILE_SCE_FP_RSX;

    PSGLcontext* LContext = _CurrentContext;
    switch ( profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
            LContext->VertexProgram = GL_FALSE;
            LContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM ;
            break;
        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_FP_RSX:
            LContext->FragmentProgram = GL_FALSE;
	    for ( GLuint unit = 0; unit < _RGL_MAX_TEXTURE_UNITS; ++unit )
		    LContext->TextureImageUnits[unit].currentTexture = _RGLGetCurrentTexture( &LContext->TextureImageUnits[unit], GL_TEXTURE_2D );

	    LContext->needValidate |= PSGL_VALIDATE_FFX_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;
            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 _RGLPlatformGetLatestProfile( profile_type );
        default:
            _RGLCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
            return CG_PROFILE_UNKNOWN;
    }
}

CGGL_API void cgGLSetOptimalOptions( CGprofile profile )
{
}

CGGL_API void cgGLLoadProgram( CGprogram program )
{
}

CGGL_API void cgGLBindProgram( CGprogram program )
{
    _CGprogram* ptr = _cgGetProgPtr( program );

    switch ( ptr->header.profile )
    {
	    case CG_PROFILE_SCE_VP_TYPEB:
	    case 7005:
	    case CG_PROFILE_SCE_VP_RSX:
		    _CurrentContext->BoundVertexProgram = ptr;
		    _CurrentContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM | PSGL_VALIDATE_VERTEX_TEXTURES_USED;
		    break;

	    case CG_PROFILE_SCE_FP_TYPEB:
	    case 7006:
	    case CG_PROFILE_SCE_FP_RSX:
		    _CurrentContext->BoundFragmentProgram = ptr;
		    _CurrentContext->needValidate |= PSGL_VALIDATE_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;

		    for ( GLuint index = 0; index < ptr->samplerCount; ++index )
		    {
			    CgRuntimeParameter *rtParameter = ptr->runtimeParameters + ptr->samplerIndices[index];
			    CgParameterResource *parameter = ( CgParameterResource * )( ptr->parameterResources + rtParameter->parameterEntry->typeIndex );
			    unsigned int unit = parameter->resource - CG_TEXUNIT0;

			    _CurrentContext->TextureImageUnits[unit].fragmentTarget = rtParameter->glType;
			    _CurrentContext->TextureImageUnits[unit].currentTexture = _RGLGetCurrentTexture( &_CurrentContext->TextureImageUnits[unit], GL_TEXTURE_2D );
		    }
		    break;

	    default:
		    _RGLCgRaiseError( CG_INVALID_PROFILE_ERROR );
		    return;
    }

}

CGGL_API void cgGLUnbindProgram( CGprofile profile )
{
    switch ( profile )
    {
        case CG_PROFILE_SCE_VP_TYPEB:
        case CG_PROFILE_SCE_VP_RSX:
        case 7005:
		_CurrentContext->BoundVertexProgram = NULL;
		_CurrentContext->needValidate |= PSGL_VALIDATE_VERTEX_PROGRAM;
            break;
        case CG_PROFILE_SCE_FP_TYPEB:
        case CG_PROFILE_SCE_FP_RSX:
        case 7006:
	    _CurrentContext->BoundFragmentProgram = NULL;
	    for ( GLuint unit = 0; unit < _RGL_MAX_TEXTURE_UNITS; ++unit )
		    _CurrentContext->TextureImageUnits[unit].currentTexture = _RGLGetCurrentTexture( &_CurrentContext->TextureImageUnits[unit], GL_TEXTURE_2D );

	    _CurrentContext->needValidate |= PSGL_VALIDATE_FFX_FRAGMENT_PROGRAM | PSGL_VALIDATE_TEXTURES_USED;
            break;
        default:
            _RGLCgRaiseError( CG_INVALID_PROFILE_ERROR );
            return;
    }

}

CGGL_API void cgGLSetParameter1f( CGparameter param, float x )
{
   CgRuntimeParameter *ptr = _RGLCgGLTestParameter( param );

   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 );

   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,
		   fsize,
		   type,
		   ( _ptr->parameterEntry->flags & CGP_NORMALIZE ) ? 1 : 0,
		   stride,
		   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 );
}

CGGL_API void cgGLSetStateMatrixParameter( CGparameter param,
        CGGLenum matrix,
        CGGLenum transform )
{
    float m[4][4];
    switch ( matrix )
    {
        case CG_GL_MODELVIEW_MATRIX:
            glGetFloatv( GL_MODELVIEW_MATRIX, ( float* )m );
            break;
        case CG_GL_PROJECTION_MATRIX:
            glGetFloatv( GL_PROJECTION_MATRIX, ( float* )m );
            break;
        case CG_GL_TEXTURE_MATRIX:
            glGetFloatv( GL_TEXTURE_MATRIX, ( float* )m );
            break;
        case CG_GL_MODELVIEW_PROJECTION_MATRIX:
        {
            float mv[4][4], p[4][4];
            glGetFloatv( GL_MODELVIEW_MATRIX, ( float* )mv );
            glGetFloatv( GL_PROJECTION_MATRIX, ( float* )p );

#define M(I,J) \
			m[I][J] = mv[I][0]*p[0][J] + mv[I][1]*p[1][J] + mv[I][2]*p[2][J] + mv[I][3]*p[3][J]
            M( 0, 0 ); M( 0, 1 ); M( 0, 2 ); M( 0, 3 );
            M( 1, 0 ); M( 1, 1 ); M( 1, 2 ); M( 1, 3 );
            M( 2, 0 ); M( 2, 1 ); M( 2, 2 ); M( 2, 3 );
            M( 3, 0 ); M( 3, 1 ); M( 3, 2 ); M( 3, 3 );
#undef M
        }
        break;
        default:
            _RGLCgRaiseError( CG_INVALID_ENUMERANT_ERROR );
            return;
    }

    CgRuntimeParameter* ptr = _RGLCgGLTestParameter( param );

    CGtype parameterType = _RGLGetParameterCGtype( ptr->program, ptr->parameterEntry );
    if ( RGL_LIKELY( parameterType == CG_FLOAT4x4 ) )
    {
        ptr->settercIndex( ptr, m, CG_GETINDEX( param ) );
    }
    else
    {
        float packedmatrix[16];
        unsigned int rows = _RGLGetTypeRowCount( parameterType );
        unsigned int cols = _RGLGetTypeColCount( parameterType );
        for ( GLuint row = 0; row < rows; ++row )
            for ( GLuint j = 0; j < cols; ++j )
                packedmatrix[row*cols + j] = m[j][row];
        ptr->setterrIndex( ptr, packedmatrix, CG_GETINDEX( param ) );
    }
}

CGGL_API void cgGLSetTextureParameter( CGparameter param, GLuint texobj )
{
    CgRuntimeParameter* ptr = _cgGLTestTextureParameter( param );

    ptr->samplerSetter( ptr, &texobj, 0 );
}

CGGL_API void cgGLEnableTextureParameter( CGparameter param )
{
    CgRuntimeParameter* ptr = _cgGLTestTextureParameter( param );
    ptr->samplerSetter( ptr, NULL, 0 );
}

static void _RGLCgContextZero( _CGcontext* p )
{
   memset( p, 0, sizeof( *p ) );
   p->compileType = CG_UNKNOWN;
}

static void _RGLCgContextPushFront(_CGcontext* ctx)
{
   if(_CurrentContext->RGLcgContextHead)
   {
      _CGcontext* head = _cgGetContextPtr( _CurrentContext->RGLcgContextHead );
      ctx->next = head;
   }
   _CurrentContext->RGLcgContextHead = ctx->id;
}

static void destroy_context(_CGcontext*ctx)
{
   if (_cgContextDestroyHook)
      _cgContextDestroyHook(ctx);

   _RGLEraseName( &_CurrentContext->cgContextNameSpace, ( jsName )ctx->id );
   _RGLCgContextZero( ctx );
   free( ctx );
}

CG_API CGcontext cgCreateContext(void)
{
    _CGcontext* ptr = NULL;

    ptr = ( _CGcontext* )malloc( sizeof( _CGcontext ) );
    if ( ptr == NULL )
    {
        _RGLCgRaiseError( CG_MEMORY_ALLOC_ERROR );
        return ( CGcontext )NULL;
    }

    _RGLCgContextZero( ptr );

    CGcontext result = ( CGcontext )_RGLCreateName( &_CurrentContext->cgContextNameSpace, ptr );
    if ( !result ) { free( ptr ); return NULL; }

    ptr->id = result;
    ptr->defaultProgram.parentContext = ptr;

    _RGLCgContextPushFront( ptr );

    if ( _cgContextCreateHook ) _cgContextCreateHook( ptr );

    return result;
}

CG_API void cgDestroyContext(CGcontext c)
{
    if(!CG_IS_CONTEXT(c))
    {
       _RGLCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
       return;
    }

    _CGcontext* ctx = _cgGetContextPtr( c );

    _RGLCgProgramErase( &ctx->defaultProgram );

    while ( ctx->programList )
    {
        _CGprogram * p = ctx->programList;
        ctx->programList = p->next;
	_RGLCgProgramErase( p );
	if(p != NULL)
		free( p );
    }

    _CGcontext * const head = _cgGetContextPtr( _CurrentContext->RGLcgContextHead );
    if ( head != ctx )
    {
        _CGcontext* ptr = head;
        while ( ptr->next != ctx ) ptr = ptr->next;
        ptr->next = ctx->next;
        destroy_context( ctx );
    }
    else
    {

        _CGcontext* second = head->next;
        destroy_context( head );

        if ( second )
        {
            _CurrentContext->RGLcgContextHead = second->id;
        }
        else
        {
            _CurrentContext->RGLcgContextHead = 0;
        }
    }
}

CG_API const char* cgGetLastListing( CGcontext c )
{
    if ( !CG_IS_CONTEXT( c ) )
    {
        _RGLCgRaiseError( CG_INVALID_CONTEXT_HANDLE_ERROR );
        return NULL;
    }

    return NULL;
}

void _RGLCgRaiseError( CGerror error )
{
    _CurrentContext->RGLcgLastError = error;

    RARCH_ERR("Cg error: %d.\n", 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:
            size = 1;
            break;
        case CG_FLOAT2:
        case CG_FLOAT2x1:
        case CG_FLOAT1x2:
            size = 2;
            break;
        case CG_FLOAT3:
        case CG_FLOAT3x1:
        case CG_FLOAT1x3:
            size = 3;
            break;
        case CG_FLOAT4:
        case CG_FLOAT4x1:
        case CG_FLOAT1x4:
        case CG_FLOAT2x2:
            size = 4;
            break;
        case CG_FLOAT2x3:
        case CG_FLOAT3x2:
            size = 6;
            break;
        case CG_FLOAT2x4:
        case CG_FLOAT4x2:
            size = 8;
            break;
        case CG_FLOAT3x3:
            size = 9;
            break;
        case CG_FLOAT3x4:
        case CG_FLOAT4x3:
            size = 12;
            break;
        case CG_FLOAT4x4:
            size = 16;
            break;
        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:
            size = 1;
            break;
        case CG_HALF2:
        case CG_HALF2x1:
        case CG_HALF1x2:
            size = 2;
            break;
        case CG_HALF3:
        case CG_HALF3x1:
        case CG_HALF1x3:
            size = 3;
            break;
        case CG_HALF4:
        case CG_HALF4x1:
        case CG_HALF1x4:
        case CG_HALF2x2:
            size = 4;
            break;
        case CG_HALF2x3:
        case CG_HALF3x2:
            size = 6;
            break;
        case CG_HALF2x4:
        case CG_HALF4x2:
            size = 8;
            break;
        case CG_HALF3x3:
            size = 9;
            break;
        case CG_HALF3x4:
        case CG_HALF4x3:
            size = 12;
            break;
        case CG_HALF4x4:
            size = 16;
            break;
        case CG_INT:
        case CG_INT1:
        case CG_INT1x1:
            size = 1;
            break;
        case CG_INT2:
        case CG_INT2x1:
        case CG_INT1x2:
            size = 2;
            break;
        case CG_INT3:
        case CG_INT3x1:
        case CG_INT1x3:
            size = 3;
            break;
        case CG_INT4:
        case CG_INT4x1:
        case CG_INT1x4:
        case CG_INT2x2:
            size = 4;
            break;
        case CG_INT2x3:
        case CG_INT3x2:
            size = 6;
            break;
        case CG_INT2x4:
        case CG_INT4x2:
            size = 8;
            break;
        case CG_INT3x3:
            size = 9;
            break;
        case CG_INT3x4:
        case CG_INT4x3:
            size = 12;
            break;
        case CG_INT4x4:
            size = 16;
            break;
        case CG_BOOL1:
        case CG_BOOL1x1:
            size = 1;
            break;
        case CG_BOOL2:
        case CG_BOOL2x1:
        case CG_BOOL1x2:
            size = 2;
            break;
        case CG_BOOL3:
        case CG_BOOL3x1:
        case CG_BOOL1x3:
            size = 3;
            break;
        case CG_BOOL4:
        case CG_BOOL4x1:
        case CG_BOOL1x4:
        case CG_BOOL2x2:
            size = 4;
            break;
        case CG_BOOL2x3:
        case CG_BOOL3x2:
            size = 6;
            break;
        case CG_BOOL2x4:
        case CG_BOOL4x2:
            size = 8;
            break;
        case CG_BOOL3x3:
            size = 9;
            break;
        case CG_BOOL3x4:
        case CG_BOOL4x3:
            size = 12;
            break;
        case CG_BOOL4x4:
            size = 16;
            break;
        case CG_FIXED:
        case CG_FIXED1:
        case CG_FIXED1x1:
            size = 1;
            break;
        case CG_FIXED2:
        case CG_FIXED2x1:
        case CG_FIXED1x2:
            size = 2;
            break;
        case CG_FIXED3:
        case CG_FIXED3x1:
        case CG_FIXED1x3:
            size = 3;
            break;
        case CG_FIXED4:
        case CG_FIXED4x1:
        case CG_FIXED1x4:
        case CG_FIXED2x2:
            size = 4;
            break;
        case CG_FIXED2x3:
        case CG_FIXED3x2:
            size = 6;
            break;
        case CG_FIXED2x4:
        case CG_FIXED4x2:
            size = 8;
            break;
        case CG_FIXED3x3:
            size = 9;
            break;
        case CG_FIXED3x4:
        case CG_FIXED4x3:
            size = 12;
            break;
        case CG_FIXED4x4:
            size = 16;
            break;
        default:
            size = 0;
            break;
    }
    return size;
}

void _cgRaiseInvalidParam( CgRuntimeParameter*p, const void*v )
{
   _RGLCgRaiseError( CG_INVALID_PARAMETER_ERROR );
}
void _cgRaiseInvalidParamIndex( CgRuntimeParameter*p, const void*v, const int index )
{
   _RGLCgRaiseError( CG_INVALID_PARAMETER_ERROR );
}

void _cgRaiseNotMatrixParam( CgRuntimeParameter*p, const void*v )
{
   _RGLCgRaiseError( CG_NOT_MATRIX_PARAM_ERROR );
}

void _cgRaiseNotMatrixParamIndex( CgRuntimeParameter*p, const void*v, const int index )
{
   _RGLCgRaiseError( CG_NOT_MATRIX_PARAM_ERROR );
}

void _cgIgnoreSetParam( CgRuntimeParameter*p, const void*v ) {}
void _cgIgnoreSetParamIndex( CgRuntimeParameter*p, const void*v, const int index ) {}

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];
}
