--- /dev/null
+/*
+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 "OGLExtensions.h"
+#include "OGLDebug.h"
+#include "OGLFragmentShaders.h"
+#include "OGLRender.h"
+#include "OGLGraphicsContext.h"
+
+COGLFragmentShaderCombiner::COGLFragmentShaderCombiner(CRender *pRender)
+: COGLColorCombiner(pRender)
+{
+ m_bShaderIsSupported = false;
+}
+COGLFragmentShaderCombiner::~COGLFragmentShaderCombiner()
+{
+}
+
+bool COGLFragmentShaderCombiner::Initialize(void)
+{
+ if( !COGLColorCombiner::Initialize() )
+ return false;
+
+ COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext);
+ if( pcontext->IsExtensionSupported("GL_ARB_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 = false;
+}
+COGL_FragmentProgramCombiner::~COGL_FragmentProgramCombiner()
+{
+ int size = m_vCompiledShaders.size();
+ for (int i=0; i<size; i++)
+ {
+ GLuint ID = m_vCompiledShaders[i].programID;
+ pglDeleteProgramsARB(1, &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_ARB_fragment_program") )
+ {
+ m_bFragmentProgramIsSupported = true;
+ }
+
+ return true;
+}
+
+
+
+void COGL_FragmentProgramCombiner::DisableCombiner(void)
+{
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ OPENGL_CHECK_ERRORS;
+ COGLColorCombiner4::DisableCombiner();
+}
+
+void COGL_FragmentProgramCombiner::InitCombinerCycleCopy(void)
+{
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ OPENGL_CHECK_ERRORS;
+ COGLColorCombiner4::InitCombinerCycleCopy();
+}
+
+void COGL_FragmentProgramCombiner::InitCombinerCycleFill(void)
+{
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ OPENGL_CHECK_ERRORS;
+ COGLColorCombiner4::InitCombinerCycleFill();
+}
+
+const char *muxToFP_Maps[][2] = {
+//color -- alpha
+{"0", "0"}, //MUX_0 = 0,
+{"1", "1"}, //MUX_1,
+{"comb", "comb.a"}, //MUX_COMBINED,
+{"t0", "t0.a"}, //MUX_TEXEL0,
+{"t1", "t1.a"}, //MUX_TEXEL1,
+{"program.env[2]", "program.env[2].a"}, //MUX_PRIM,
+{"fragment.color", "fragment.color.a"}, //MUX_SHADE,
+{"program.env[1]", "program.env[1].a"}, //MUX_ENV,
+{"comb.a", "comb.a"}, //MUX_COMBALPHA,
+{"t0.a", "t0.a"}, //MUX_T0_ALPHA,
+{"t1.a", "t1.a"}, //MUX_T1_ALPHA,
+{"primcolor.a", "primcolor.a"}, //MUX_PRIM_ALPHA,
+{"fragment.color.a", "fragment.color.a"}, //MUX_SHADE_ALPHA,
+{"envcolor.a", "envcolor.a"}, //MUX_ENV_ALPHA,
+{"program.env[3]", "program.env[3]"}, //MUX_LODFRAC,
+{"program.env[4]", "program.env[4]"}, //MUX_PRIMLODFRAC,
+{"1", "1"}, //MUX_K5,
+{"1", "1"}, //MUX_UNK, // Should not be used
+};
+
+
+const char *oglFPTest =
+"!!ARBfp1.0\n"
+"#Declarations\n"
+"TEMP t0;\n"
+"TEMP t1;\n"
+"TEMP comb;\n"
+"TEMP comb2;\n"
+"\n"
+"ATTRIB coord0 = fragment.texcoord[0];\n"
+"ATTRIB coord1 = fragment.texcoord[1];\n"
+"ATTRIB shade = fragment.color;\n"
+"ATTRIB fogfactor = fragment.fogcoord;\n"
+"\n"
+"OUTPUT out = result.color;\n"
+"\n"
+"#Instructions\n"
+"TEX t0, coord0, texture[0], 2D;\n"
+"TEX t1, coord1, texture[1], 2D;\n"
+"\n"
+"MAD_SAT out, t0, program.env[1],program.env[0];\n"
+//"SUB comb.rgb, t0, 0;\n"
+//"MAD_SAT out.rgb, comb, program.env[1], 0;\n"
+//"SUB comb.a, t0, 0;\n"
+//"MAD_SAT out.a, comb, program.env[1], 0;\n"
+"END\n";
+
+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][0];
+}
+
+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, "MOV comb.%s, %s;\n", dst, func(m.d));
+ CheckFpVars(m.d, bNeedT0, bNeedT1);
+ break;
+ case CM_FMT_TYPE_A_MOD_C:
+ sprintf(tempstr, "MUL 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, "ADD_SAT comb.%s, %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, "SUB 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, "MAD_SAT comb.%s, %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:
+ sprintf(tempstr, "LRP_SAT comb.%s, %s, %s, %s;\n", dst, func(m.c), func(m.a), 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, "SUB comb2.%s, %s, %s;\nMAD_SAT comb.%s, comb2, %s, %s;\n", dst, func(m.a), func(m.b), 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);
+ }
+ }
+
+ strcpy(oglNewFP, "!!ARBfp1.0\n");
+ strcat(oglNewFP, "#Declarations\n");
+ if (gRDP.bFogEnableInBlender && gRSP.bFogEnabled)
+ strcat(oglNewFP, "OPTION ARB_fog_linear;\n");
+ if (bNeedT0)
+ strcat(oglNewFP, "TEMP t0;\n");
+ if (bNeedT1)
+ strcat(oglNewFP, "TEMP t1;\n");
+ strcat(oglNewFP, "TEMP comb;\n");
+ if (bNeedComb2)
+ strcat(oglNewFP, "TEMP comb2;\n");
+ strcat(oglNewFP, "#Instructions\n");
+ if (bNeedT0)
+ strcat(oglNewFP, "TEX t0, fragment.texcoord[0], texture[0], 2D;\n");
+ if (bNeedT1)
+ strcat(oglNewFP, "TEX t1, fragment.texcoord[1], texture[1], 2D;\n");
+ strcat(oglNewFP, "# N64 cycle 1, result is in comb\n");
+
+ strcat(oglNewFP, newFPBody);
+
+ strcat(oglNewFP, "MOV result.color, comb;\n");
+ strcat(oglNewFP, "END\n\n");
+}
+
+int COGL_FragmentProgramCombiner::ParseDecodedMux()
+{
+ if( !m_bFragmentProgramIsSupported )
+ return COGLColorCombiner4::ParseDecodedMux();
+
+ OGLShaderCombinerSaveType res;
+
+ pglGenProgramsARB( 1, &res.programID);
+ OPENGL_CHECK_ERRORS;
+ pglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, res.programID);
+ OPENGL_CHECK_ERRORS;
+ GenerateProgramStr();
+
+ pglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(oglNewFP), oglNewFP);
+ OPENGL_CHECK_ERRORS;
+ //pglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(oglFPTest), oglFPTest);
+
+ if (glGetError() != 0)
+ {
+ GLint position;
+#ifdef DEBUGGER
+ char *str = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
+#endif
+ glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &position);
+ if( position >= 0 )
+ {
+#ifdef DEBUGGER
+ if( m_lastIndex >= 0 ) COGLColorCombiner4::DisplaySimpleMuxString();
+ DebugMessage(M64MSG_ERROR, "%s - %s", str, oglNewFP+position);
+#endif
+ glDisable(GL_FRAGMENT_PROGRAM_ARB);
+ return COGLColorCombiner4::ParseDecodedMux();
+ }
+ }
+
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ 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()-1;
+
+ return m_lastIndex;
+}
+
+void COGL_FragmentProgramCombiner::GenerateCombinerSetting(int index)
+{
+ GLuint ID = m_vCompiledShaders[index].programID;
+ pglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, ID );
+ OPENGL_CHECK_ERRORS;
+ glEnable(GL_FRAGMENT_PROGRAM_ARB);
+ OPENGL_CHECK_ERRORS;
+}
+
+void COGL_FragmentProgramCombiner::GenerateCombinerSettingConstants(int index)
+{
+ float *pf;
+ pf = GetEnvColorfv();
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 1, pf);
+ OPENGL_CHECK_ERRORS;
+ pf = GetPrimitiveColorfv();
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 2, pf);
+ OPENGL_CHECK_ERRORS;
+
+ float frac = gRDP.LODFrac / 255.0f;
+ float tempf[4] = {frac,frac,frac,frac};
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 3, tempf);
+ OPENGL_CHECK_ERRORS;
+
+ float frac2 = gRDP.primLODFrac / 255.0f;
+ float tempf2[4] = {frac2,frac2,frac2,frac2};
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 4, tempf2);
+ OPENGL_CHECK_ERRORS;
+
+ float tempf3[4] = {0,0,0,0};
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 0, tempf3);
+ OPENGL_CHECK_ERRORS;
+ pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 6, tempf3);
+ 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) )
+ 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
+