d07c171f |
1 | /* |
2 | Copyright (C) 2003 Rice1964 |
3 | |
4 | This program is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU General Public License |
6 | as published by the Free Software Foundation; either version 2 |
7 | of the License, or (at your option) any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | */ |
18 | |
19 | #include "OGLExtensions.h" |
20 | #include "OGLDebug.h" |
21 | #include "OGLFragmentShaders.h" |
22 | #include "OGLRender.h" |
23 | #include "OGLGraphicsContext.h" |
24 | |
25 | COGLFragmentShaderCombiner::COGLFragmentShaderCombiner(CRender *pRender) |
26 | : COGLColorCombiner(pRender) |
27 | { |
28 | m_bShaderIsSupported = false; |
29 | } |
30 | COGLFragmentShaderCombiner::~COGLFragmentShaderCombiner() |
31 | { |
32 | } |
33 | |
34 | bool COGLFragmentShaderCombiner::Initialize(void) |
35 | { |
36 | if( !COGLColorCombiner::Initialize() ) |
37 | return false; |
38 | |
39 | COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext); |
40 | if( pcontext->IsExtensionSupported("GL_ARB_fragment_shader") ) |
41 | { |
42 | m_bShaderIsSupported = true; |
43 | } |
44 | |
45 | return true; |
46 | } |
47 | |
48 | void COGLFragmentShaderCombiner::InitCombinerCycle12(void) |
49 | { |
50 | } |
51 | void COGLFragmentShaderCombiner::DisableCombiner(void) |
52 | { |
53 | COGLColorCombiner::DisableCombiner(); |
54 | } |
55 | |
56 | void COGLFragmentShaderCombiner::InitCombinerCycleCopy(void) |
57 | { |
58 | COGLColorCombiner::InitCombinerCycleCopy(); |
59 | } |
60 | |
61 | void COGLFragmentShaderCombiner::InitCombinerCycleFill(void) |
62 | { |
63 | COGLColorCombiner::InitCombinerCycleFill(); |
64 | } |
65 | void COGLFragmentShaderCombiner::InitCombinerBlenderForSimpleTextureDraw(uint32 tile) |
66 | { |
67 | COGLColorCombiner::InitCombinerBlenderForSimpleTextureDraw(tile); |
68 | } |
69 | |
70 | #ifdef DEBUGGER |
71 | void COGLFragmentShaderCombiner::DisplaySimpleMuxString(void) |
72 | { |
73 | COGLColorCombiner::DisplaySimpleMuxString(); |
74 | } |
75 | #endif |
76 | |
77 | |
78 | |
79 | COGL_FragmentProgramCombiner::COGL_FragmentProgramCombiner(CRender *pRender) |
80 | : COGLColorCombiner4(pRender) |
81 | { |
82 | delete m_pDecodedMux; |
83 | m_pDecodedMux = new DecodedMuxForPixelShader; |
84 | m_bFragmentProgramIsSupported = false; |
85 | } |
86 | COGL_FragmentProgramCombiner::~COGL_FragmentProgramCombiner() |
87 | { |
88 | int size = m_vCompiledShaders.size(); |
89 | for (int i=0; i<size; i++) |
90 | { |
91 | GLuint ID = m_vCompiledShaders[i].programID; |
92 | pglDeleteProgramsARB(1, &ID); |
93 | OPENGL_CHECK_ERRORS; |
94 | m_vCompiledShaders[i].programID = 0; |
95 | } |
96 | |
97 | m_vCompiledShaders.clear(); |
98 | } |
99 | |
100 | bool COGL_FragmentProgramCombiner::Initialize(void) |
101 | { |
102 | if( !COGLColorCombiner4::Initialize() ) |
103 | return false; |
104 | |
105 | COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext); |
106 | if( pcontext->IsExtensionSupported("GL_ARB_fragment_program") ) |
107 | { |
108 | m_bFragmentProgramIsSupported = true; |
109 | } |
110 | |
111 | return true; |
112 | } |
113 | |
114 | |
115 | |
116 | void COGL_FragmentProgramCombiner::DisableCombiner(void) |
117 | { |
118 | glDisable(GL_FRAGMENT_PROGRAM_ARB); |
119 | OPENGL_CHECK_ERRORS; |
120 | COGLColorCombiner4::DisableCombiner(); |
121 | } |
122 | |
123 | void COGL_FragmentProgramCombiner::InitCombinerCycleCopy(void) |
124 | { |
125 | glDisable(GL_FRAGMENT_PROGRAM_ARB); |
126 | OPENGL_CHECK_ERRORS; |
127 | COGLColorCombiner4::InitCombinerCycleCopy(); |
128 | } |
129 | |
130 | void COGL_FragmentProgramCombiner::InitCombinerCycleFill(void) |
131 | { |
132 | glDisable(GL_FRAGMENT_PROGRAM_ARB); |
133 | OPENGL_CHECK_ERRORS; |
134 | COGLColorCombiner4::InitCombinerCycleFill(); |
135 | } |
136 | |
137 | const char *muxToFP_Maps[][2] = { |
138 | //color -- alpha |
139 | {"0", "0"}, //MUX_0 = 0, |
140 | {"1", "1"}, //MUX_1, |
141 | {"comb", "comb.a"}, //MUX_COMBINED, |
142 | {"t0", "t0.a"}, //MUX_TEXEL0, |
143 | {"t1", "t1.a"}, //MUX_TEXEL1, |
144 | {"program.env[2]", "program.env[2].a"}, //MUX_PRIM, |
145 | {"fragment.color", "fragment.color.a"}, //MUX_SHADE, |
146 | {"program.env[1]", "program.env[1].a"}, //MUX_ENV, |
147 | {"comb.a", "comb.a"}, //MUX_COMBALPHA, |
148 | {"t0.a", "t0.a"}, //MUX_T0_ALPHA, |
149 | {"t1.a", "t1.a"}, //MUX_T1_ALPHA, |
150 | {"primcolor.a", "primcolor.a"}, //MUX_PRIM_ALPHA, |
151 | {"fragment.color.a", "fragment.color.a"}, //MUX_SHADE_ALPHA, |
152 | {"envcolor.a", "envcolor.a"}, //MUX_ENV_ALPHA, |
153 | {"program.env[3]", "program.env[3]"}, //MUX_LODFRAC, |
154 | {"program.env[4]", "program.env[4]"}, //MUX_PRIMLODFRAC, |
155 | {"1", "1"}, //MUX_K5, |
156 | {"1", "1"}, //MUX_UNK, // Should not be used |
157 | }; |
158 | |
159 | |
160 | const char *oglFPTest = |
161 | "!!ARBfp1.0\n" |
162 | "#Declarations\n" |
163 | "TEMP t0;\n" |
164 | "TEMP t1;\n" |
165 | "TEMP comb;\n" |
166 | "TEMP comb2;\n" |
167 | "\n" |
168 | "ATTRIB coord0 = fragment.texcoord[0];\n" |
169 | "ATTRIB coord1 = fragment.texcoord[1];\n" |
170 | "ATTRIB shade = fragment.color;\n" |
171 | "ATTRIB fogfactor = fragment.fogcoord;\n" |
172 | "\n" |
173 | "OUTPUT out = result.color;\n" |
174 | "\n" |
175 | "#Instructions\n" |
176 | "TEX t0, coord0, texture[0], 2D;\n" |
177 | "TEX t1, coord1, texture[1], 2D;\n" |
178 | "\n" |
179 | "MAD_SAT out, t0, program.env[1],program.env[0];\n" |
180 | //"SUB comb.rgb, t0, 0;\n" |
181 | //"MAD_SAT out.rgb, comb, program.env[1], 0;\n" |
182 | //"SUB comb.a, t0, 0;\n" |
183 | //"MAD_SAT out.a, comb, program.env[1], 0;\n" |
184 | "END\n"; |
185 | |
186 | char oglNewFP[4092]; |
187 | |
188 | char* MuxToOC(uint8 val) |
189 | { |
190 | // For color channel |
191 | if( val&MUX_ALPHAREPLICATE ) |
192 | return (char*)muxToFP_Maps[val&0x1F][1]; |
193 | else |
194 | return (char*)muxToFP_Maps[val&0x1F][0]; |
195 | } |
196 | |
197 | char* MuxToOA(uint8 val) |
198 | { |
199 | // For alpha channel |
200 | return (char*)muxToFP_Maps[val&0x1F][0]; |
201 | } |
202 | |
203 | static void CheckFpVars(uint8 MuxVar, bool &bNeedT0, bool &bNeedT1) |
204 | { |
205 | MuxVar &= 0x1f; |
206 | if (MuxVar == MUX_TEXEL0 || MuxVar == MUX_T0_ALPHA) |
207 | bNeedT0 = true; |
208 | if (MuxVar == MUX_TEXEL1 || MuxVar == MUX_T1_ALPHA) |
209 | bNeedT1 = true; |
210 | } |
211 | |
212 | void COGL_FragmentProgramCombiner::GenerateProgramStr() |
213 | { |
214 | DecodedMuxForPixelShader &mux = *(DecodedMuxForPixelShader*)m_pDecodedMux; |
215 | |
216 | mux.splitType[0] = mux.splitType[1] = mux.splitType[2] = mux.splitType[3] = CM_FMT_TYPE_NOT_CHECKED; |
217 | m_pDecodedMux->Reformat(false); |
218 | |
219 | char tempstr[500], newFPBody[4092]; |
220 | bool bNeedT0 = false, bNeedT1 = false, bNeedComb2 = false; |
221 | newFPBody[0] = 0; |
222 | |
223 | for( int cycle=0; cycle<2; cycle++ ) |
224 | { |
225 | for( int channel=0; channel<2; channel++) |
226 | { |
227 | char* (*func)(uint8) = channel==0?MuxToOC:MuxToOA; |
228 | char *dst = channel==0?(char*)"rgb":(char*)"a"; |
229 | N64CombinerType &m = mux.m_n64Combiners[cycle*2+channel]; |
230 | switch( mux.splitType[cycle*2+channel] ) |
231 | { |
232 | case CM_FMT_TYPE_NOT_USED: |
233 | tempstr[0] = 0; |
234 | break; |
235 | case CM_FMT_TYPE_D: |
236 | sprintf(tempstr, "MOV comb.%s, %s;\n", dst, func(m.d)); |
237 | CheckFpVars(m.d, bNeedT0, bNeedT1); |
238 | break; |
239 | case CM_FMT_TYPE_A_MOD_C: |
240 | sprintf(tempstr, "MUL comb.%s, %s, %s;\n", dst, func(m.a), func(m.c)); |
241 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
242 | CheckFpVars(m.c, bNeedT0, bNeedT1); |
243 | break; |
244 | case CM_FMT_TYPE_A_ADD_D: |
245 | sprintf(tempstr, "ADD_SAT comb.%s, %s, %s;\n", dst, func(m.a), func(m.d)); |
246 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
247 | CheckFpVars(m.d, bNeedT0, bNeedT1); |
248 | break; |
249 | case CM_FMT_TYPE_A_SUB_B: |
250 | sprintf(tempstr, "SUB comb.%s, %s, %s;\n", dst, func(m.a), func(m.b)); |
251 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
252 | CheckFpVars(m.b, bNeedT0, bNeedT1); |
253 | break; |
254 | case CM_FMT_TYPE_A_MOD_C_ADD_D: |
255 | sprintf(tempstr, "MAD_SAT comb.%s, %s, %s, %s;\n", dst, func(m.a), func(m.c), func(m.d)); |
256 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
257 | CheckFpVars(m.c, bNeedT0, bNeedT1); |
258 | CheckFpVars(m.d, bNeedT0, bNeedT1); |
259 | break; |
260 | case CM_FMT_TYPE_A_LERP_B_C: |
261 | sprintf(tempstr, "LRP_SAT comb.%s, %s, %s, %s;\n", dst, func(m.c), func(m.a), func(m.b)); |
262 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
263 | CheckFpVars(m.b, bNeedT0, bNeedT1); |
264 | CheckFpVars(m.c, bNeedT0, bNeedT1); |
265 | //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)); |
266 | break; |
267 | default: |
268 | 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)); |
269 | CheckFpVars(m.a, bNeedT0, bNeedT1); |
270 | CheckFpVars(m.b, bNeedT0, bNeedT1); |
271 | CheckFpVars(m.c, bNeedT0, bNeedT1); |
272 | CheckFpVars(m.d, bNeedT0, bNeedT1); |
273 | bNeedComb2 = true; |
274 | break; |
275 | } |
276 | strcat(newFPBody, tempstr); |
277 | } |
278 | } |
279 | |
280 | strcpy(oglNewFP, "!!ARBfp1.0\n"); |
281 | strcat(oglNewFP, "#Declarations\n"); |
282 | if (gRDP.bFogEnableInBlender && gRSP.bFogEnabled) |
283 | strcat(oglNewFP, "OPTION ARB_fog_linear;\n"); |
284 | if (bNeedT0) |
285 | strcat(oglNewFP, "TEMP t0;\n"); |
286 | if (bNeedT1) |
287 | strcat(oglNewFP, "TEMP t1;\n"); |
288 | strcat(oglNewFP, "TEMP comb;\n"); |
289 | if (bNeedComb2) |
290 | strcat(oglNewFP, "TEMP comb2;\n"); |
291 | strcat(oglNewFP, "#Instructions\n"); |
292 | if (bNeedT0) |
293 | strcat(oglNewFP, "TEX t0, fragment.texcoord[0], texture[0], 2D;\n"); |
294 | if (bNeedT1) |
295 | strcat(oglNewFP, "TEX t1, fragment.texcoord[1], texture[1], 2D;\n"); |
296 | strcat(oglNewFP, "# N64 cycle 1, result is in comb\n"); |
297 | |
298 | strcat(oglNewFP, newFPBody); |
299 | |
300 | strcat(oglNewFP, "MOV result.color, comb;\n"); |
301 | strcat(oglNewFP, "END\n\n"); |
302 | } |
303 | |
304 | int COGL_FragmentProgramCombiner::ParseDecodedMux() |
305 | { |
306 | if( !m_bFragmentProgramIsSupported ) |
307 | return COGLColorCombiner4::ParseDecodedMux(); |
308 | |
309 | OGLShaderCombinerSaveType res; |
310 | |
311 | pglGenProgramsARB( 1, &res.programID); |
312 | OPENGL_CHECK_ERRORS; |
313 | pglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, res.programID); |
314 | OPENGL_CHECK_ERRORS; |
315 | GenerateProgramStr(); |
316 | |
317 | pglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(oglNewFP), oglNewFP); |
318 | OPENGL_CHECK_ERRORS; |
319 | //pglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(oglFPTest), oglFPTest); |
320 | |
321 | if (glGetError() != 0) |
322 | { |
323 | GLint position; |
324 | #ifdef DEBUGGER |
325 | char *str = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB); |
326 | #endif |
327 | glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &position); |
328 | if( position >= 0 ) |
329 | { |
330 | #ifdef DEBUGGER |
331 | if( m_lastIndex >= 0 ) COGLColorCombiner4::DisplaySimpleMuxString(); |
332 | DebugMessage(M64MSG_ERROR, "%s - %s", str, oglNewFP+position); |
333 | #endif |
334 | glDisable(GL_FRAGMENT_PROGRAM_ARB); |
335 | return COGLColorCombiner4::ParseDecodedMux(); |
336 | } |
337 | } |
338 | |
339 | glEnable(GL_FRAGMENT_PROGRAM_ARB); |
340 | OPENGL_CHECK_ERRORS; |
341 | res.dwMux0 = m_pDecodedMux->m_dwMux0; |
342 | res.dwMux1 = m_pDecodedMux->m_dwMux1; |
343 | res.fogIsUsed = gRDP.bFogEnableInBlender && gRSP.bFogEnabled; |
344 | |
345 | m_vCompiledShaders.push_back(res); |
346 | m_lastIndex = m_vCompiledShaders.size()-1; |
347 | |
348 | return m_lastIndex; |
349 | } |
350 | |
351 | void COGL_FragmentProgramCombiner::GenerateCombinerSetting(int index) |
352 | { |
353 | GLuint ID = m_vCompiledShaders[index].programID; |
354 | pglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, ID ); |
355 | OPENGL_CHECK_ERRORS; |
356 | glEnable(GL_FRAGMENT_PROGRAM_ARB); |
357 | OPENGL_CHECK_ERRORS; |
358 | } |
359 | |
360 | void COGL_FragmentProgramCombiner::GenerateCombinerSettingConstants(int index) |
361 | { |
362 | float *pf; |
363 | pf = GetEnvColorfv(); |
364 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 1, pf); |
365 | OPENGL_CHECK_ERRORS; |
366 | pf = GetPrimitiveColorfv(); |
367 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 2, pf); |
368 | OPENGL_CHECK_ERRORS; |
369 | |
370 | float frac = gRDP.LODFrac / 255.0f; |
371 | float tempf[4] = {frac,frac,frac,frac}; |
372 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 3, tempf); |
373 | OPENGL_CHECK_ERRORS; |
374 | |
375 | float frac2 = gRDP.primLODFrac / 255.0f; |
376 | float tempf2[4] = {frac2,frac2,frac2,frac2}; |
377 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 4, tempf2); |
378 | OPENGL_CHECK_ERRORS; |
379 | |
380 | float tempf3[4] = {0,0,0,0}; |
381 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 0, tempf3); |
382 | OPENGL_CHECK_ERRORS; |
383 | pglProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, 6, tempf3); |
384 | OPENGL_CHECK_ERRORS; |
385 | } |
386 | |
387 | int COGL_FragmentProgramCombiner::FindCompiledMux() |
388 | { |
389 | #ifdef DEBUGGER |
390 | if( debuggerDropCombiners ) |
391 | { |
392 | m_vCompiledShaders.clear(); |
393 | //m_dwLastMux0 = m_dwLastMux1 = 0; |
394 | debuggerDropCombiners = false; |
395 | } |
396 | #endif |
397 | for( uint32 i=0; i<m_vCompiledShaders.size(); i++ ) |
398 | { |
399 | if( m_vCompiledShaders[i].dwMux0 == m_pDecodedMux->m_dwMux0 |
400 | && m_vCompiledShaders[i].dwMux1 == m_pDecodedMux->m_dwMux1 |
401 | && m_vCompiledShaders[i].fogIsUsed == (gRDP.bFogEnableInBlender && gRSP.bFogEnabled) ) |
402 | return (int)i; |
403 | } |
404 | |
405 | return -1; |
406 | } |
407 | |
408 | ////////////////////////////////////////////////////////////////////////// |
409 | void COGL_FragmentProgramCombiner::InitCombinerCycle12(void) |
410 | { |
411 | if( !m_bFragmentProgramIsSupported ) |
412 | { |
413 | COGLColorCombiner4::InitCombinerCycle12(); |
414 | return; |
415 | } |
416 | |
417 | #ifdef DEBUGGER |
418 | if( debuggerDropCombiners ) |
419 | { |
420 | UpdateCombiner(m_pDecodedMux->m_dwMux0,m_pDecodedMux->m_dwMux1); |
421 | m_vCompiledShaders.clear(); |
422 | m_dwLastMux0 = m_dwLastMux1 = 0; |
423 | debuggerDropCombiners = false; |
424 | } |
425 | #endif |
426 | |
427 | m_pOGLRender->EnableMultiTexture(); |
428 | |
429 | bool combinerIsChanged = false; |
430 | |
431 | if( m_pDecodedMux->m_dwMux0 != m_dwLastMux0 || m_pDecodedMux->m_dwMux1 != m_dwLastMux1 || m_lastIndex < 0 ) |
432 | { |
433 | combinerIsChanged = true; |
434 | m_lastIndex = FindCompiledMux(); |
435 | if( m_lastIndex < 0 ) // Can not found |
436 | { |
437 | m_lastIndex = ParseDecodedMux(); |
438 | } |
439 | |
440 | m_dwLastMux0 = m_pDecodedMux->m_dwMux0; |
441 | m_dwLastMux1 = m_pDecodedMux->m_dwMux1; |
442 | } |
443 | |
444 | |
445 | GenerateCombinerSettingConstants(m_lastIndex); |
446 | if( m_bCycleChanged || combinerIsChanged || gRDP.texturesAreReloaded || gRDP.colorsAreReloaded ) |
447 | { |
448 | if( m_bCycleChanged || combinerIsChanged ) |
449 | { |
450 | GenerateCombinerSettingConstants(m_lastIndex); |
451 | GenerateCombinerSetting(m_lastIndex); |
452 | } |
453 | else if( gRDP.colorsAreReloaded ) |
454 | { |
455 | GenerateCombinerSettingConstants(m_lastIndex); |
456 | } |
457 | |
458 | m_pOGLRender->SetAllTexelRepeatFlag(); |
459 | |
460 | gRDP.colorsAreReloaded = false; |
461 | gRDP.texturesAreReloaded = false; |
462 | } |
463 | else |
464 | { |
465 | m_pOGLRender->SetAllTexelRepeatFlag(); |
466 | } |
467 | } |
468 | |
469 | #ifdef DEBUGGER |
470 | void COGL_FragmentProgramCombiner::DisplaySimpleMuxString(void) |
471 | { |
472 | COGLColorCombiner::DisplaySimpleMuxString(); |
473 | DecodedMuxForPixelShader &mux = *(DecodedMuxForPixelShader*)m_pDecodedMux; |
474 | mux.Reformat(false); |
475 | GenerateProgramStr(); |
476 | //sprintf(oglNewFP, oglFP, |
477 | // MuxToOC(mux.aRGB0), MuxToOC(mux.bRGB0), MuxToOC(mux.cRGB0), MuxToOC(mux.dRGB0), |
478 | // MuxToOA(mux.aA0), MuxToOA(mux.bA0), MuxToOA(mux.cA0), MuxToOA(mux.dA0), |
479 | // MuxToOC(mux.aRGB1), MuxToOC(mux.bRGB1), MuxToOC(mux.cRGB1), MuxToOC(mux.dRGB1), |
480 | // MuxToOA(mux.aA1), MuxToOA(mux.bA1), MuxToOA(mux.cA1), MuxToOA(mux.dA1) |
481 | // ); |
482 | |
483 | TRACE0("OGL Fragment Program:"); |
484 | TRACE0(oglNewFP); |
485 | } |
486 | #endif |
487 | |