Rice Video Plugin for GLES1.1
[mupen64plus-pandora.git] / source / rice_gles / src / OGLES2FragmentShaders.cpp
diff --git a/source/rice_gles/src/OGLES2FragmentShaders.cpp b/source/rice_gles/src/OGLES2FragmentShaders.cpp
new file mode 100644 (file)
index 0000000..4c5f06e
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+Copyright (C) 2003 Rice1964
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include "OGLDebug.h"
+#include "OGLES2FragmentShaders.h"
+#include "OGLRender.h"
+#include "OGLGraphicsContext.h"
+#include "OGLTexture.h"
+
+#define ALPHA_TEST "    if(gl_FragColor.a < AlphaRef) discard;                        \n"
+//#define ALPHA_TEST
+
+
+GLuint vertexProgram = 9999;
+const char *vertexShader =
+"#version " GLSL_VERSION "\n"
+"attribute mediump vec4 aPosition;                          \n"\
+"attribute lowp vec4    aColor;                             \n"\
+"attribute lowp vec2    aTexCoord0;                         \n"\
+"attribute lowp vec2    aTexCoord1;                         \n"\
+"attribute lowp vec2    aAtlasTransform;                    \n"\
+"                                                           \n"\
+"uniform lowp vec2 FogMinMax;                               \n"\
+"                                                           \n"\
+"varying lowp float vFactor;                                \n"\
+"varying lowp vec4  vShadeColor;                            \n"\
+"varying mediump vec2 vTexCoord0;                           \n"\
+"varying lowp vec2    vTexCoord1;                           \n"\
+"varying lowp float   vFog;                                 \n"\
+"                                                           \n"\
+"void main()                                                \n"\
+"{                                                          \n"\
+"gl_Position = aPosition; //gl_Position.z = max(0.0,gl_Position.z);                                  \n"\
+"vShadeColor = aColor;                                      \n"\
+"vTexCoord0 = aTexCoord0;                                   \n"\
+"vTexCoord1 = aTexCoord1;                                   \n"\
+"vFog = clamp((FogMinMax[1] - (gl_Position.z/aPosition.w))/(FogMinMax[1]-FogMinMax[0]),0.0,1.0);                                   \n"\
+"                                                           \n"\
+"}                                                          \n"\
+"                                                           \n";
+
+const char *fragmentHeader =
+"#define saturate(x) clamp( x, 0.0, 1.0 )                   \n"\
+"precision lowp float;                                      \n"\
+"#ifdef NEED_TEX0                                           \n"\
+"uniform sampler2D uTex0;                                   \n"\
+"#endif                                                     \n"\
+"                                                           \n"\
+"#ifdef NEED_TEX1                                           \n"\
+"uniform sampler2D uTex1;                                   \n"\
+"#endif                                                     \n"\
+"                                                           \n"\
+"uniform vec4 EnvColor;                                     \n"\
+"uniform vec4 PrimColor;                                    \n"\
+"uniform vec4 EnvFrac;                                      \n"\
+"uniform vec4 PrimFrac;                                     \n"\
+"uniform float AlphaRef;                                    \n"\
+"uniform vec4 FogColor;                                     \n"\
+"                                                           \n"\
+"varying lowp float vFactor;                                \n"\
+"varying lowp vec4  vShadeColor;                            \n"\
+"varying mediump vec2  vTexCoord0;                          \n"\
+"varying lowp vec2  vTexCoord1;                             \n"\
+"varying lowp float vFog;                                   \n"\
+"                                                           \n"\
+"void main()                                                \n"\
+"{                                                          \n"\
+"vec4 comb,comb2;                                           \n"\
+"                                                           \n"\
+"#ifdef NEED_TEX0                                              \n"\
+"vec4 t0 = texture2D(uTex0,vTexCoord0);                     \n"\
+"#endif                                                     \n"\
+"                                                           \n"\
+"#ifdef NEED_TEX1                                           \n"\
+"vec4 t1 = texture2D(uTex1,vTexCoord1);                     \n"\
+"#endif                                                     \n";
+
+const char *fragmentFooter =
+"                                                           \n"\
+"#ifdef FOG                                                 \n"\
+"gl_FragColor.rgb = mix(FogColor.rgb,comb.rgb,vFog * step(0.5,1.0-FogColor.a));        \n"\
+"gl_FragColor.a = comb.a;                                   \n"\
+"#else                                                      \n"\
+"gl_FragColor = comb;                                       \n"\
+"#endif                                                     \n"\
+"                                                           \n"\
+"#ifdef ALPHA_TEST                                          \n"\
+ALPHA_TEST
+"#endif                                                     \n"\
+"                                                           \n"\
+"                                                           \n"\
+"                                                           \n"\
+"                                                           \n"\
+"}                                                          \n";
+
+//Fragment shader for InitCycleCopy
+const char *fragmentCopy =
+"#version " GLSL_VERSION "\n"\
+"precision lowp float;                                      \n"\
+"uniform sampler2D uTex0;                                   \n"\
+"uniform float AlphaRef;                                    \n"\
+"varying lowp vec2 vTexCoord0;                              \n"\
+"void main()                                                \n"\
+"{                                                          \n"\
+"   gl_FragColor = texture2D(uTex0,vTexCoord0).bgra;        \n"\
+ALPHA_TEST
+"}";
+
+GLuint copyProgram,copyAlphaLocation;
+
+//Fragment shader for InitCycleFill
+const char *fragmentFill =
+"#version " GLSL_VERSION "\n"\
+"precision lowp float;                                      \n"
+"uniform vec4 uColor;                                       \n"
+"void main()                                                \n"
+"{                                                          \n"
+"   gl_FragColor = uColor;                                  \n"
+"}";
+
+GLuint fillProgram,fillColorLocation;
+
+COGLFragmentShaderCombiner::COGLFragmentShaderCombiner(CRender *pRender)
+: COGLColorCombiner(pRender)
+{
+    m_bShaderIsSupported = true;
+}
+COGLFragmentShaderCombiner::~COGLFragmentShaderCombiner()
+{
+}
+
+bool COGLFragmentShaderCombiner::Initialize(void)
+{
+    if( !COGLColorCombiner::Initialize() )
+        return false;
+
+    COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext);
+//    if( pcontext->IsExtensionSupported("GL_fragment_shader") )
+//    {
+        m_bShaderIsSupported = true;
+//    }
+
+    return true;
+}
+
+void COGLFragmentShaderCombiner::InitCombinerCycle12(void)
+{
+}
+void COGLFragmentShaderCombiner::DisableCombiner(void)
+{
+    COGLColorCombiner::DisableCombiner();
+}
+
+void COGLFragmentShaderCombiner::InitCombinerCycleCopy(void)
+{
+    COGLColorCombiner::InitCombinerCycleCopy();
+}
+
+void COGLFragmentShaderCombiner::InitCombinerCycleFill(void)
+{
+    COGLColorCombiner::InitCombinerCycleFill();
+}
+void COGLFragmentShaderCombiner::InitCombinerBlenderForSimpleTextureDraw(uint32 tile)
+{
+    COGLColorCombiner::InitCombinerBlenderForSimpleTextureDraw(tile);
+}
+
+#ifdef DEBUGGER
+void COGLFragmentShaderCombiner::DisplaySimpleMuxString(void)
+{
+    COGLColorCombiner::DisplaySimpleMuxString();
+}
+#endif
+
+
+
+COGL_FragmentProgramCombiner::COGL_FragmentProgramCombiner(CRender *pRender)
+: COGLColorCombiner4(pRender)
+{
+    delete m_pDecodedMux;
+    m_pDecodedMux = new DecodedMuxForPixelShader;
+    m_bFragmentProgramIsSupported = true;
+    m_AlphaRef = 0.0f;
+
+    //Create shaders for fill and copy
+    GLint success;
+    GLuint vs,fs;
+    copyProgram = glCreateProgram();
+    vs = glCreateShader(GL_VERTEX_SHADER);
+    glShaderSource(vs,1,&vertexShader,NULL);
+    glCompileShader(vs);
+
+    glGetShaderiv(vs,GL_COMPILE_STATUS,&success);
+    if(!success)
+    {
+        char log[1024];
+        glGetShaderInfoLog(vs,1024,NULL,log);
+        printf("%s\n",log);
+    }
+
+    fs = glCreateShader(GL_FRAGMENT_SHADER);
+    glShaderSource(fs,1,&fragmentCopy,NULL);
+    glCompileShader(fs);
+
+    glGetShaderiv(fs,GL_COMPILE_STATUS,&success);
+    if(!success)
+    {
+        char log[1024];
+        glGetShaderInfoLog(fs,1024,NULL,log);
+        printf("%s\n",log);
+    }
+
+    glAttachShader(copyProgram,vs);
+    glAttachShader(copyProgram,fs);
+
+    glBindAttribLocation(copyProgram,VS_TEXCOORD0,"aTexCoord0");
+    OPENGL_CHECK_ERRORS;
+    glBindAttribLocation(copyProgram,VS_POSITION,"aPosition");
+    OPENGL_CHECK_ERRORS;
+
+    glLinkProgram(copyProgram);
+    copyAlphaLocation = glGetUniformLocation(copyProgram,"AlphaRef");
+    glGetProgramiv(copyProgram,GL_LINK_STATUS,&success);
+    if(!success)
+    {
+        char log[1024];
+        glGetProgramInfoLog(copyProgram,1024,NULL,log);
+        printf("%s\n",log);
+    }
+
+    glDeleteShader(fs);
+
+    //Fill shader
+    fs = glCreateShader(GL_FRAGMENT_SHADER);
+    glShaderSource(fs,1,&fragmentFill,NULL);
+    glCompileShader(fs);
+
+    glGetShaderiv(fs,GL_COMPILE_STATUS,&success);
+    if(!success)
+    {
+        char log[1024];
+        glGetShaderInfoLog(fs,1024,NULL,log);
+        printf("%s\n",log);
+    }
+
+    fillProgram = glCreateProgram();
+    glAttachShader(fillProgram,vs);
+    glAttachShader(fillProgram,fs);
+
+    glBindAttribLocation(fillProgram,VS_POSITION,"aPosition");
+    OPENGL_CHECK_ERRORS;
+
+    glLinkProgram(fillProgram);
+
+
+    fillColorLocation = glGetUniformLocation(fillProgram,"uColor");
+
+    glDeleteShader(fs);
+    glDeleteShader(vs);
+}
+COGL_FragmentProgramCombiner::~COGL_FragmentProgramCombiner()
+{
+    int size = m_vCompiledShaders.size();
+    for (int i=0; i<size; i++)
+    {
+        GLuint ID = m_vCompiledShaders[i].programID;
+        glDeleteProgram(ID);
+
+        OPENGL_CHECK_ERRORS;
+        m_vCompiledShaders[i].programID = 0;
+    }
+
+    m_vCompiledShaders.clear();
+}
+
+bool COGL_FragmentProgramCombiner::Initialize(void)
+{
+    if( !COGLColorCombiner4::Initialize() )
+        return false;
+
+    COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext);
+//    if( pcontext->IsExtensionSupported("GL_fragment_program") )
+//    {
+        m_bFragmentProgramIsSupported = true;
+//    }
+
+    return true;
+}
+
+
+
+void COGL_FragmentProgramCombiner::DisableCombiner(void)
+{
+    //glDisable(GL_FRAGMENT_PROGRAM);
+    //OPENGL_CHECK_ERRORS;
+    COGLColorCombiner4::DisableCombiner();
+}
+
+void COGL_FragmentProgramCombiner::InitCombinerCycleCopy(void)
+{
+    m_pOGLRender->DisableMultiTexture();
+    m_pOGLRender->EnableTexUnit(0,TRUE);
+    glUseProgram(copyProgram);
+    glUniform1f(copyAlphaLocation,m_AlphaRef);
+    OPENGL_CHECK_ERRORS;
+    glEnableVertexAttribArray(VS_POSITION);
+    OPENGL_CHECK_ERRORS;
+    glEnableVertexAttribArray(VS_TEXCOORD0);
+    OPENGL_CHECK_ERRORS;
+    glDisableVertexAttribArray(VS_COLOR);
+    OPENGL_CHECK_ERRORS;
+    glDisableVertexAttribArray(VS_TEXCOORD1);
+    OPENGL_CHECK_ERRORS;
+    COGLTexture* pTexture = g_textures[gRSP.curTile].m_pCOGLTexture;
+    if( pTexture )
+    {
+        m_pOGLRender->BindTexture(pTexture->m_dwTextureName, 0);
+        m_pOGLRender->SetTexelRepeatFlags(gRSP.curTile);
+    }
+}
+
+void COGL_FragmentProgramCombiner::InitCombinerCycleFill(void)
+{
+    glUseProgram(fillProgram);
+    glUniform4f(fillColorLocation,((gRDP.fillColor>>16)&0xFF)/255.0f,((gRDP.fillColor>>8)&0xFF)/255.0f,((gRDP.fillColor)&0xFF)/255.0f,((gRDP.fillColor>>24)&0xFF)/255.0f);
+    OPENGL_CHECK_ERRORS;
+}
+
+#ifdef BGR_SHADER
+const char *muxToFP_Maps[][2] = {
+//color -- alpha
+{"vec3(0.0)", "0.0"},                      //MUX_0 = 0,
+{"vec3(1.0)", "1.0"},                      //MUX_1,
+{"comb.rgb", "comb.a"},                    //MUX_COMBINED,
+{"t0.rgb", "t0.a"},                        //MUX_TEXEL0,
+{"t1.rgb", "t1.a"},                        //MUX_TEXEL1,
+{"PrimColor.rgb", "PrimColor.a"},          //MUX_PRIM,
+{"vShadeColor.rgb", "vShadeColor.a"},      //MUX_SHADE,
+{"EnvColor.rgb", "EnvColor.a"},            //MUX_ENV,
+{"comb.rgb", "comb.a"},                    //MUX_COMBALPHA,
+{"t0.rgb", "t0.a"},                        //MUX_T0_ALPHA,
+{"t1.rgb", "t1.a"},                        //MUX_T1_ALPHA,
+{"PrimColor.rgb", "PrimColor.a"},          //MUX_PRIM_ALPHA,
+{"vShadeColor.rgb", "vShadeColor.a"},      //MUX_SHADE_ALPHA,
+{"EnvColor.rgb", "EnvColor.a"},            //MUX_ENV_ALPHA,
+{"EnvFrac.a", "EnvFrac.a"},                //MUX_LODFRAC,
+{"PrimFrac.a", "PrimFrac.a"},              //MUX_PRIMLODFRAC,
+{"vec3(1.0)", "1.0"},                      //MUX_K5,
+{"vec3(1.0)", "1.0"},                      //MUX_UNK,  // Should not be used
+};
+#else
+const char *muxToFP_Maps[][2] = {
+//color -- alpha
+{"vec3(0.0)", "0.0"}, //MUX_0 = 0,
+{"vec3(1.0)", "1.0"}, //MUX_1,
+{"comb.rgb", "comb.a"}, //MUX_COMBINED,
+{"t0.bgr", "t0.a"}, //MUX_TEXEL0,
+{"t1.bgr", "t1.a"}, //MUX_TEXEL1,
+{"PrimColor.rgb", "PrimColor.a"}, //MUX_PRIM,
+{"vShadeColor.rgb", "vShadeColor.a"}, //MUX_SHADE,
+{"EnvColor.rgb", "EnvColor.a"}, //MUX_ENV,
+{"comb.rgb", "comb.a"}, //MUX_COMBALPHA,
+{"t0.bgr", "t0.a"}, //MUX_T0_ALPHA,
+{"t1.bgr", "t1.a"}, //MUX_T1_ALPHA,
+{"PrimColor.rgb", "PrimColor.a"}, //MUX_PRIM_ALPHA,
+{"vShadeColor.rgb", "vShadeColor.a"}, //MUX_SHADE_ALPHA,
+{"EnvColor.rgb", "EnvColor.a"}, //MUX_ENV_ALPHA,
+{"EnvFrac.a", "EnvFrac.a"}, //MUX_LODFRAC,
+{"PrimFrac.a", "PrimFrac.a"}, //MUX_PRIMLODFRAC,
+{"vec3(1.0)", "1.0"}, //MUX_K5,
+{"vec3(1.0)", "1.0"}, //MUX_UNK,  // Should not be used
+};
+#endif
+
+
+char oglNewFP[4092];
+
+char* MuxToOC(uint8 val)
+{
+// For color channel
+if( val&MUX_ALPHAREPLICATE )
+    return (char*)muxToFP_Maps[val&0x1F][1];
+else
+    return (char*)muxToFP_Maps[val&0x1F][0];
+}
+
+char* MuxToOA(uint8 val)
+{
+// For alpha channel
+return (char*)muxToFP_Maps[val&0x1F][1];
+}
+
+static void CheckFpVars(uint8 MuxVar, bool &bNeedT0, bool &bNeedT1)
+{
+    MuxVar &= 0x1f;
+    if (MuxVar == MUX_TEXEL0 || MuxVar == MUX_T0_ALPHA)
+        bNeedT0 = true;
+    if (MuxVar == MUX_TEXEL1 || MuxVar == MUX_T1_ALPHA)
+        bNeedT1 = true;
+}
+
+void COGL_FragmentProgramCombiner::GenerateProgramStr()
+{
+    DecodedMuxForPixelShader &mux = *(DecodedMuxForPixelShader*)m_pDecodedMux;
+
+    mux.splitType[0] = mux.splitType[1] = mux.splitType[2] = mux.splitType[3] = CM_FMT_TYPE_NOT_CHECKED;
+    m_pDecodedMux->Reformat(false);
+
+    char tempstr[500], newFPBody[4092];
+    bool bNeedT0 = false, bNeedT1 = false, bNeedComb2 = false;
+    newFPBody[0] = 0;
+
+    for( int cycle=0; cycle<2; cycle++ )
+    {
+        for( int channel=0; channel<2; channel++)
+        {
+            char* (*func)(uint8) = channel==0?MuxToOC:MuxToOA;
+            char *dst = channel==0?(char*)"rgb":(char*)"a";
+            N64CombinerType &m = mux.m_n64Combiners[cycle*2+channel];
+            switch( mux.splitType[cycle*2+channel] )
+            {
+            case CM_FMT_TYPE_NOT_USED:
+                tempstr[0] = 0;
+                break;
+            case CM_FMT_TYPE_D:
+                sprintf(tempstr, "comb.%s = %s;\n", dst, func(m.d));
+                CheckFpVars(m.d, bNeedT0, bNeedT1);
+                break;
+            case CM_FMT_TYPE_A_MOD_C:
+                sprintf(tempstr, "comb.%s = %s * %s;\n", dst, func(m.a), func(m.c));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.c, bNeedT0, bNeedT1);
+                break;
+            case CM_FMT_TYPE_A_ADD_D:
+                sprintf(tempstr, "comb.%s = saturate(%s + %s);\n", dst, func(m.a), func(m.d));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.d, bNeedT0, bNeedT1);
+                break;
+            case CM_FMT_TYPE_A_SUB_B:
+                sprintf(tempstr, "comb.%s = %s - %s;\n", dst, func(m.a), func(m.b));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.b, bNeedT0, bNeedT1);
+                break;
+            case CM_FMT_TYPE_A_MOD_C_ADD_D:
+                sprintf(tempstr, "comb.%s = saturate(%s * %s + %s);\n", dst, func(m.a), func(m.c),func(m.d));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.c, bNeedT0, bNeedT1);
+                CheckFpVars(m.d, bNeedT0, bNeedT1);
+                break;
+            case CM_FMT_TYPE_A_LERP_B_C:
+                //ARB ASM LERP and mix have different parameter ordering.
+                //sprintf(tempstr, "comb.%s = saturate(mix(%s, %s, %s));\n", dst,func(m.a),func(m.b), func(m.c));
+                sprintf(tempstr, "comb.%s = (%s - %s) * %s + %s;\n", dst,func(m.a),func(m.b), func(m.c),func(m.b));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.b, bNeedT0, bNeedT1);
+                CheckFpVars(m.c, bNeedT0, bNeedT1);
+                //sprintf(tempstr, "SUB comb.%s, %s, %s;\nMAD_SAT comb.%s, comb, %s, %s;\n", dst, func(m.a), func(m.b), dst, func(m.c), func(m.b));
+                break;
+            default:
+                sprintf(tempstr, "comb2.%s = %s - %s;\ncomb.%s = saturate(comb2.%s * %s + %s);\n", dst, func(m.a), func(m.b),  dst,dst, func(m.c),func(m.d));
+                CheckFpVars(m.a, bNeedT0, bNeedT1);
+                CheckFpVars(m.b, bNeedT0, bNeedT1);
+                CheckFpVars(m.c, bNeedT0, bNeedT1);
+                CheckFpVars(m.d, bNeedT0, bNeedT1);
+                bNeedComb2 = true;
+                break;
+            }
+            strcat(newFPBody, tempstr);
+        }
+    }
+
+    oglNewFP[0] = 0;
+    if (bNeedT0)
+        strcat(oglNewFP, "#define NEED_TEX0\n");
+    if (bNeedT1)
+        strcat(oglNewFP, "#define NEED_TEX1\n");
+    if(gRDP.bFogEnableInBlender && gRSP.bFogEnabled && options.fogMethod > 0)
+        strcat(oglNewFP,"#define FOG");
+    strcat(oglNewFP, fragmentHeader);
+    strcat(oglNewFP, newFPBody);
+    strcat(oglNewFP, fragmentFooter);
+
+}
+
+int COGL_FragmentProgramCombiner::ParseDecodedMux()
+{
+    if( !m_bFragmentProgramIsSupported )
+        return COGLColorCombiner4::ParseDecodedMux();
+
+    OGLShaderCombinerSaveType res;
+    GLint success;
+
+    if(vertexProgram == 9999)
+    {
+        vertexProgram = res.vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
+        glShaderSource(res.vertexShaderID, 1, &vertexShader,NULL);
+        OPENGL_CHECK_ERRORS;
+        glCompileShader(res.vertexShaderID);
+        OPENGL_CHECK_ERRORS;
+    }
+    else
+    {
+        res.vertexShaderID = vertexProgram;
+    }
+
+
+    //Create 2 shaders, with and without alphatest
+    GenerateProgramStr();
+
+    for(int alphaTest = 0;alphaTest < 2;alphaTest++)
+    {
+        res.fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
+
+        char* tmpShader = (char*)malloc(sizeof(char) * 4096);
+        strcpy(tmpShader,"#version " GLSL_VERSION "\n");
+
+        if(alphaTest == 1)
+        {
+            strcat(tmpShader,"#define ALPHA_TEST\n");
+        }
+
+        res.alphaTest = alphaTest == 1;
+        strcat(tmpShader,oglNewFP);
+
+        glShaderSource(res.fragmentShaderID, 1,(const char**) &tmpShader,NULL);
+        free(tmpShader);
+
+
+        OPENGL_CHECK_ERRORS;
+        glCompileShader(res.fragmentShaderID);
+
+        glGetShaderiv(res.fragmentShaderID, GL_COMPILE_STATUS, &success);
+        if (!success)
+        {
+            char Log[1024];
+            GLint nLength;
+            glGetShaderInfoLog(res.fragmentShaderID, 1024, &nLength, Log);
+            printf("Error compiling shader!\n %s",oglNewFP);
+            printf("%s", Log);
+        }
+
+        res.programID = glCreateProgram();
+        glAttachShader(res.programID,res.vertexShaderID);
+        glAttachShader(res.programID,res.fragmentShaderID);
+
+        //Bind Attributes
+        glBindAttribLocation(res.programID,VS_COLOR,"aColor");
+        OPENGL_CHECK_ERRORS;
+        glBindAttribLocation(res.programID,VS_TEXCOORD0,"aTexCoord0");
+        OPENGL_CHECK_ERRORS;
+        glBindAttribLocation(res.programID,VS_TEXCOORD1,"aTexCoord1");
+        OPENGL_CHECK_ERRORS;
+        glBindAttribLocation(res.programID,VS_POSITION,"aPosition");
+        OPENGL_CHECK_ERRORS;
+
+        glLinkProgram(res.programID);
+        OPENGL_CHECK_ERRORS;
+
+        glGetProgramiv(res.programID, GL_LINK_STATUS, &success);
+        if (!success)
+        {
+            char Log[1024];
+            GLint nLength;
+            glGetShaderInfoLog(res.fragmentShaderID, 1024, &nLength, Log);
+            printf("Error linking program!\n");
+            printf("%s\n",Log);
+        }
+
+        glUseProgram(res.programID);
+        OPENGL_CHECK_ERRORS;
+
+        //Bind texture samplers
+        GLint tex0 = glGetUniformLocation(res.programID,"uTex0");
+        GLint tex1 = glGetUniformLocation(res.programID,"uTex1");
+
+        if(tex0 != -1)
+            glUniform1i(tex0,0);
+        if(tex1 != -1)
+            glUniform1i(tex1,1);
+
+        //Bind Uniforms
+        res.PrimColorLocation = glGetUniformLocation(res.programID,"PrimColor");
+        OPENGL_CHECK_ERRORS;
+        res.EnvColorLocation = glGetUniformLocation(res.programID,"EnvColor");
+        OPENGL_CHECK_ERRORS;
+        res.PrimFracLocation = glGetUniformLocation(res.programID,"PrimFrac");
+        OPENGL_CHECK_ERRORS;
+        res.EnvFracLocation = glGetUniformLocation(res.programID,"EnvFrac");
+        OPENGL_CHECK_ERRORS;
+        res.AlphaRefLocation = glGetUniformLocation(res.programID,"AlphaRef");
+        OPENGL_CHECK_ERRORS;
+        res.FogColorLocation = glGetUniformLocation(res.programID,"FogColor");
+        OPENGL_CHECK_ERRORS;
+        res.FogMinMaxLocation = glGetUniformLocation(res.programID,"FogMinMax");
+        OPENGL_CHECK_ERRORS;
+
+        res.dwMux0 = m_pDecodedMux->m_dwMux0;
+        res.dwMux1 = m_pDecodedMux->m_dwMux1;
+        res.fogIsUsed = gRDP.bFogEnableInBlender && gRSP.bFogEnabled;
+
+        m_vCompiledShaders.push_back(res);
+    }
+    m_lastIndex = m_vCompiledShaders.size()-2;
+
+    return m_lastIndex;
+}
+
+void COGL_FragmentProgramCombiner::GenerateCombinerSetting(int index)
+{
+    GLuint ID = m_vCompiledShaders[index].programID;
+
+    glUseProgram(ID);
+    glEnableVertexAttribArray(VS_POSITION);
+    OPENGL_CHECK_ERRORS;
+    glVertexAttribPointer(VS_POSITION,4,GL_FLOAT,GL_FALSE,sizeof(float)*5,&(g_vtxProjected5[0][0]));
+    OPENGL_CHECK_ERRORS;
+
+    glEnableVertexAttribArray(VS_TEXCOORD0);
+    OPENGL_CHECK_ERRORS;
+    glVertexAttribPointer(VS_TEXCOORD0,2,GL_FLOAT,GL_FALSE, sizeof( TLITVERTEX ), &(g_vtxBuffer[0].tcord[0].u));
+    OPENGL_CHECK_ERRORS;
+
+    glEnableVertexAttribArray(VS_TEXCOORD1);
+    OPENGL_CHECK_ERRORS;
+    glVertexAttribPointer(VS_TEXCOORD1,2,GL_FLOAT,GL_FALSE, sizeof( TLITVERTEX ), &(g_vtxBuffer[0].tcord[1].u));
+    OPENGL_CHECK_ERRORS;
+
+    glEnableVertexAttribArray(VS_COLOR);
+    OPENGL_CHECK_ERRORS;
+    glVertexAttribPointer(VS_COLOR, 4, GL_UNSIGNED_BYTE,GL_TRUE, sizeof(uint8)*4, &(g_oglVtxColors[0][0]) );
+    OPENGL_CHECK_ERRORS;
+}
+
+void COGL_FragmentProgramCombiner::GenerateCombinerSettingConstants(int index)
+{
+    OGLShaderCombinerSaveType prog = m_vCompiledShaders[index];
+
+    glUseProgram(prog.programID);
+    float *pf;
+    if(prog.EnvColorLocation != -1)
+    {
+        pf = GetEnvColorfv();
+        glUniform4fv(prog.EnvColorLocation,1, pf);
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if(prog.PrimColorLocation != -1)
+    {
+        pf = GetPrimitiveColorfv();
+        glUniform4fv(prog.PrimColorLocation,1, pf);
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if(prog.EnvFracLocation != -1)
+    {
+        float frac = gRDP.LODFrac / 255.0f;
+        float tempf[4] = {frac,frac,frac,frac};
+        glUniform4fv(prog.EnvFracLocation,1, tempf);
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if(prog.PrimFracLocation != -1)
+    {
+        float frac2 = gRDP.primLODFrac / 255.0f;
+        float tempf2[4] = {frac2,frac2,frac2,frac2};
+        glUniform4fv(prog.PrimFracLocation,1, tempf2);
+        OPENGL_CHECK_ERRORS;
+    }
+
+    //if(prog.FogColorLocation != -1 && prog.FogMinMaxLocation != -1)
+    //{
+    //    //Pass fog colour and distance, use 0 alpha if fog disabled
+    //    glUniform4f(prog.FogColorLocation, gRDP.fvFogColor[0],gRDP.fvFogColor[1],gRDP.fvFogColor[2],
+    //        gRSP.bFogEnabled ? gRDP.fvFogColor[0] : 0.0f);
+    //    glUniform2f(prog.FogMinMaxLocation,gRSPfFogMin,gRSPfFogMax);
+    //}
+
+    if(prog.AlphaRefLocation != -1)
+        glUniform1f(prog.AlphaRefLocation,m_AlphaRef);
+    OPENGL_CHECK_ERRORS;
+}
+
+void COGL_FragmentProgramCombiner::UpdateFog(bool bEnable)
+{
+    if(m_lastIndex < 0 || m_lastIndex >= m_vCompiledShaders.size())
+        return;
+    OGLShaderCombinerSaveType prog = m_vCompiledShaders[m_lastIndex];
+
+    //if(bEnable)
+    //    DebugMessage(M64MSG_INFO,"Fog Color %x Min %f Max %f",gRDP.fogColor,gRSPfFogMin,gRSPfFogMax);
+
+    if(prog.FogColorLocation != -1 && prog.FogMinMaxLocation != -1)
+    {
+        //Pass fog colour and distance, use 0 alpha if fog disabled
+        glUniform4f(prog.FogColorLocation, gRDP.fvFogColor[0],gRDP.fvFogColor[1],gRDP.fvFogColor[2],
+            bEnable ? gRDP.fvFogColor[0] : 0.0f);
+        //glUniform4f(prog.FogColorLocation, 1.0f,0.3f,0.3f,1.0f);
+        //OPENGL_CHECK_ERRORS;
+        glUniform2f(prog.FogMinMaxLocation,gRSPfFogMin,gRSPfFogMax);
+        OPENGL_CHECK_ERRORS;
+    }
+}
+
+int COGL_FragmentProgramCombiner::FindCompiledMux()
+{
+#ifdef DEBUGGER
+    if( debuggerDropCombiners )
+    {
+        m_vCompiledShaders.clear();
+        //m_dwLastMux0 = m_dwLastMux1 = 0;
+        debuggerDropCombiners = false;
+    }
+#endif
+    for( uint32 i=0; i<m_vCompiledShaders.size(); i++ )
+    {
+        if( m_vCompiledShaders[i].dwMux0 == m_pDecodedMux->m_dwMux0 
+            && m_vCompiledShaders[i].dwMux1 == m_pDecodedMux->m_dwMux1 
+            && m_vCompiledShaders[i].fogIsUsed == (gRDP.bFogEnableInBlender && gRSP.bFogEnabled)
+            && m_vCompiledShaders[i].alphaTest == m_AlphaRef > 0.0f)
+        {
+            return (int)i;
+        }
+    }
+
+    return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void COGL_FragmentProgramCombiner::InitCombinerCycle12(void)
+{
+    if( !m_bFragmentProgramIsSupported )    
+    {
+        COGLColorCombiner4::InitCombinerCycle12();
+        return;
+    }
+
+#ifdef DEBUGGER
+    if( debuggerDropCombiners )
+    {
+        UpdateCombiner(m_pDecodedMux->m_dwMux0,m_pDecodedMux->m_dwMux1);
+        m_vCompiledShaders.clear();
+        m_dwLastMux0 = m_dwLastMux1 = 0;
+        debuggerDropCombiners = false;
+    }
+#endif
+
+    m_pOGLRender->EnableMultiTexture();
+
+    bool combinerIsChanged = false;
+
+    if( m_pDecodedMux->m_dwMux0 != m_dwLastMux0 || m_pDecodedMux->m_dwMux1 != m_dwLastMux1 || m_lastIndex < 0 )
+    {
+        combinerIsChanged = true;
+        m_lastIndex = FindCompiledMux();
+        if( m_lastIndex < 0 )       // Can not found
+        {
+            m_lastIndex = ParseDecodedMux();
+        }
+
+        m_dwLastMux0 = m_pDecodedMux->m_dwMux0;
+        m_dwLastMux1 = m_pDecodedMux->m_dwMux1;
+    }
+
+
+    GenerateCombinerSettingConstants(m_lastIndex);
+    if( m_bCycleChanged || combinerIsChanged || gRDP.texturesAreReloaded || gRDP.colorsAreReloaded )
+    {
+        if( m_bCycleChanged || combinerIsChanged )
+        {
+            GenerateCombinerSettingConstants(m_lastIndex);
+            GenerateCombinerSetting(m_lastIndex);
+        }
+        else if( gRDP.colorsAreReloaded )
+        {
+            GenerateCombinerSettingConstants(m_lastIndex);
+        }
+
+        m_pOGLRender->SetAllTexelRepeatFlag();
+
+        gRDP.colorsAreReloaded = false;
+        gRDP.texturesAreReloaded = false;
+    }
+    else
+    {
+        m_pOGLRender->SetAllTexelRepeatFlag();
+    }
+}
+
+#ifdef DEBUGGER
+void COGL_FragmentProgramCombiner::DisplaySimpleMuxString(void)
+{
+    COGLColorCombiner::DisplaySimpleMuxString();
+    DecodedMuxForPixelShader &mux = *(DecodedMuxForPixelShader*)m_pDecodedMux;
+    mux.Reformat(false);
+    GenerateProgramStr();
+    //sprintf(oglNewFP, oglFP, 
+    //  MuxToOC(mux.aRGB0), MuxToOC(mux.bRGB0), MuxToOC(mux.cRGB0), MuxToOC(mux.dRGB0),
+    //  MuxToOA(mux.aA0), MuxToOA(mux.bA0), MuxToOA(mux.cA0), MuxToOA(mux.dA0),
+    //  MuxToOC(mux.aRGB1), MuxToOC(mux.bRGB1), MuxToOC(mux.cRGB1), MuxToOC(mux.dRGB1),
+    //  MuxToOA(mux.aA1), MuxToOA(mux.bA1), MuxToOA(mux.cA1), MuxToOA(mux.dA1)
+    //  );
+
+    TRACE0("OGL Fragment Program:");
+    TRACE0(oglNewFP);
+}
+#endif
+