1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-07 03:13:49 +00:00
GLideN64/src/ShaderUtils.cpp
2016-11-21 15:47:53 +07:00

455 lines
12 KiB
C++

#include <assert.h>
#include <stdio.h>
#include "ShaderUtils.h"
#include "Config.h"
#include "Log.h"
#ifdef VC
#include "RaspberryPi/ShaderUtils_VC.h"
#endif
static const GLsizei nShaderLogSize = 1024;
bool checkShaderCompileStatus(GLuint obj)
{
GLint status;
glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLchar shader_log[nShaderLogSize];
GLsizei nLogSize = nShaderLogSize;
glGetShaderInfoLog(obj, nShaderLogSize, &nLogSize, shader_log);
shader_log[nLogSize] = 0;
LOG(LOG_ERROR, "shader_compile error: %s\n", shader_log);
return false;
}
return true;
}
bool checkProgramLinkStatus(GLuint obj)
{
GLint status;
glGetProgramiv(obj, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLsizei nLogSize = nShaderLogSize;
GLchar shader_log[nShaderLogSize];
glGetProgramInfoLog(obj, nShaderLogSize, &nLogSize, shader_log);
LOG(LOG_ERROR, "shader_link error: %s\n", shader_log);
return false;
}
return true;
}
void logErrorShader(GLenum _shaderType, const std::string & _strShader)
{
LOG(LOG_ERROR, "Error in %s shader", _shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment");
const int max = 800;
int pos = 0;
while(pos < _strShader.length() ) {
if (_strShader.length() - pos < max) {
LOG(LOG_ERROR, "%s", _strShader.substr(pos).data());
} else {
LOG(LOG_ERROR, "%s", _strShader.substr(pos, max).data());
}
pos += max;
}
}
GLuint createShaderProgram(const char * _strVertex, const char * _strFragment)
{
GLuint vertex_shader_object = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader_object, 1, &_strVertex, nullptr);
glCompileShader(vertex_shader_object);
assert(checkShaderCompileStatus(vertex_shader_object));
if (!checkShaderCompileStatus(vertex_shader_object))
logErrorShader(GL_VERTEX_SHADER, _strVertex);
GLuint fragment_shader_object = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader_object, 1, &_strFragment, nullptr);
glCompileShader(fragment_shader_object);
assert(checkShaderCompileStatus(fragment_shader_object));
if (!checkShaderCompileStatus(fragment_shader_object))
logErrorShader(GL_VERTEX_SHADER, _strFragment);
GLuint program = glCreateProgram();
glBindAttribLocation(program, SC_POSITION, "aPosition");
glBindAttribLocation(program, SC_TEXCOORD0, "aTexCoord0");
glBindAttribLocation(program, SC_TEXCOORD1, "aTexCoord1");
glAttachShader(program, vertex_shader_object);
glAttachShader(program, fragment_shader_object);
glLinkProgram(program);
glDeleteShader(vertex_shader_object);
glDeleteShader(fragment_shader_object);
assert(checkProgramLinkStatus(program));
return program;
}
#ifndef VC
static
const char* fragment_shader_blender1 =
" if (uForceBlendCycle1 != 0) { \n"
" muxPM[0] = clampedColor; \n"
" muxA[0] = clampedColor.a; \n"
" muxB[0] = 1.0 - muxA[uBlendMux1[1]]; \n"
" lowp vec4 blend1 = (muxPM[uBlendMux1[0]] * muxA[uBlendMux1[1]]) + (muxPM[uBlendMux1[2]] * muxB[uBlendMux1[3]]); \n"
" clampedColor.rgb = clamp(blend1.rgb, 0.0, 1.0); \n"
" } \n"
;
static
const char* fragment_shader_blender2 =
" if (uForceBlendCycle2 != 0) { \n"
" muxPM[0] = clampedColor; \n"
" muxA[0] = clampedColor.a; \n"
" muxB[0] = 1.0 - muxA[uBlendMux2[1]]; \n"
" lowp vec4 blend2 = muxPM[uBlendMux2[0]] * muxA[uBlendMux2[1]] + muxPM[uBlendMux2[2]] * muxB[uBlendMux2[3]]; \n"
" clampedColor.rgb = clamp(blend2.rgb, 0.0, 1.0); \n"
" } \n"
;
#endif
/*
// N64 color wrap and clamp on floats
// See https://github.com/gonetz/GLideN64/issues/661 for reference
if (c < -1.0) return c + 2.0;
if (c < -0.5) return 1;
if (c < 0.0) return 0;
if (c > 2.0) return c - 2.0;
if (c > 1.5) return 0;
if (c > 1.0) return 1;
return c;
*/
const char* fragment_shader_clamp =
" lowp vec4 wrappedColor = cmbRes + 2.0 * step(cmbRes, vec4(-0.51)) - 2.0*step(vec4(1.51), cmbRes); \n"
" lowp vec4 clampedColor = clamp(wrappedColor, 0.0, 1.0); \n"
;
/*
N64 sign-extension for C component of combiner
if (c > 1.0)
return c - 2.0;
return c;
*/
const char* fragment_shader_sign_extend_color_c =
" color1 = color1 - 2.0*(vec3(1.0) - step(color1, vec3(1.0))); \n"
;
const char* fragment_shader_sign_extend_alpha_c =
" alpha1 = alpha1 - 2.0*(1.0 - step(alpha1, 1.0)); \n"
;
/*
N64 sign-extension for ABD components of combiner
if (c > 1.5)
return c - 2.0;
if (c < -0.5)
return c + 2.0;
return c;
*/
const char* fragment_shader_sign_extend_color_abd =
" color1 = color1 + 2.0*step(color1, vec3(-0.51)) - 2.0*step(vec3(1.51), color1); \n"
;
const char* fragment_shader_sign_extend_alpha_abd =
" alpha1 = alpha1 + 2.0*step(alpha1, -0.51) - 2.0*step(1.51, alpha1); \n"
;
static
const char *ColorInput[] = {
"combined_color.rgb",
"readtex0.rgb",
"readtex1.rgb",
"uPrimColor.rgb",
"vec_color.rgb",
"uEnvColor.rgb",
"uCenterColor.rgb",
"uScaleColor.rgb",
"combined_color.a",
"vec3(readtex0.a)",
"vec3(readtex1.a)",
"vec3(uPrimColor.a)",
"vec3(vec_color.a)",
"vec3(uEnvColor.a)",
"vec3(lod_frac)",
"vec3(uPrimLod)",
"vec3(0.5 + 0.5*snoise())",
"vec3(uK4)",
"vec3(uK5)",
"vec3(1.0)",
"vec3(0.0)"
};
static
const char *AlphaInput[] = {
"combined_color.a",
"readtex0.a",
"readtex1.a",
"uPrimColor.a",
"vec_color.a",
"uEnvColor.a",
"uCenterColor.a",
"uScaleColor.a",
"combined_color.a",
"readtex0.a",
"readtex1.a",
"uPrimColor.a",
"vec_color.a",
"uEnvColor.a",
"lod_frac",
"uPrimLod",
"0.5 + 0.5*snoise()",
"uK4",
"uK5",
"1.0",
"0.0"
};
inline
int correctFirstStageParam(int _param)
{
switch (_param) {
case TEXEL1:
return TEXEL0;
case TEXEL1_ALPHA:
return TEXEL0_ALPHA;
}
return _param;
}
static
void _correctFirstStageParams(CombinerStage & _stage)
{
for (int i = 0; i < _stage.numOps; ++i) {
_stage.op[i].param1 = correctFirstStageParam(_stage.op[i].param1);
_stage.op[i].param2 = correctFirstStageParam(_stage.op[i].param2);
_stage.op[i].param3 = correctFirstStageParam(_stage.op[i].param3);
}
}
inline
int correctSecondStageParam(int _param)
{
switch (_param) {
case TEXEL0:
return TEXEL1;
case TEXEL1:
return TEXEL0;
case TEXEL0_ALPHA:
return TEXEL1_ALPHA;
case TEXEL1_ALPHA:
return TEXEL0_ALPHA;
}
return _param;
}
static
bool needClampColor() {
return gDP.otherMode.cycleType <= G_CYC_2CYCLE;
}
static
bool combinedColorC(const gDPCombine & _combine) {
if (gDP.otherMode.cycleType != G_CYC_2CYCLE)
return false;
return _combine.mRGB1 == G_CCMUX_COMBINED;
}
static
bool combinedAlphaC(const gDPCombine & _combine) {
if (gDP.otherMode.cycleType != G_CYC_2CYCLE)
return false;
return _combine.mA1 == G_ACMUX_COMBINED;
}
static
bool combinedColorABD(const gDPCombine & _combine) {
if (gDP.otherMode.cycleType != G_CYC_2CYCLE)
return false;
if (_combine.aRGB1 == G_CCMUX_COMBINED)
return true;
if (_combine.saRGB1 == G_CCMUX_COMBINED || _combine.sbRGB1 == G_CCMUX_COMBINED)
return _combine.mRGB1 != G_CCMUX_0;
return false;
}
static
bool combinedAlphaABD(const gDPCombine & _combine) {
if (gDP.otherMode.cycleType != G_CYC_2CYCLE)
return false;
if (_combine.aA1 == G_ACMUX_COMBINED)
return true;
if (_combine.saA1 == G_ACMUX_COMBINED || _combine.sbA1 == G_ACMUX_COMBINED)
return _combine.mA1 != G_ACMUX_0;
return false;
}
static
void _correctSecondStageParams(CombinerStage & _stage) {
for (int i = 0; i < _stage.numOps; ++i) {
_stage.op[i].param1 = correctSecondStageParam(_stage.op[i].param1);
_stage.op[i].param2 = correctSecondStageParam(_stage.op[i].param2);
_stage.op[i].param3 = correctSecondStageParam(_stage.op[i].param3);
}
}
static
int _compileCombiner(const CombinerStage & _stage, const char** _Input, std::string & _strShader) {
char buf[128];
bool bBracketOpen = false;
int nRes = 0;
for (int i = 0; i < _stage.numOps; ++i) {
switch (_stage.op[i].op) {
case LOAD:
sprintf(buf, "(%s ", _Input[_stage.op[i].param1]);
_strShader += buf;
bBracketOpen = true;
nRes |= 1 << _stage.op[i].param1;
break;
case SUB:
if (bBracketOpen) {
sprintf(buf, "- %s)", _Input[_stage.op[i].param1]);
bBracketOpen = false;
}
else
sprintf(buf, "- %s", _Input[_stage.op[i].param1]);
_strShader += buf;
nRes |= 1 << _stage.op[i].param1;
break;
case ADD:
if (bBracketOpen) {
sprintf(buf, "+ %s)", _Input[_stage.op[i].param1]);
bBracketOpen = false;
}
else
sprintf(buf, "+ %s", _Input[_stage.op[i].param1]);
_strShader += buf;
nRes |= 1 << _stage.op[i].param1;
break;
case MUL:
if (bBracketOpen) {
sprintf(buf, ")*%s", _Input[_stage.op[i].param1]);
bBracketOpen = false;
}
else
sprintf(buf, "*%s", _Input[_stage.op[i].param1]);
_strShader += buf;
nRes |= 1 << _stage.op[i].param1;
break;
case INTER:
sprintf(buf, "mix(%s, %s, %s)", _Input[_stage.op[0].param2], _Input[_stage.op[0].param1], _Input[_stage.op[0].param3]);
_strShader += buf;
nRes |= 1 << _stage.op[i].param1;
nRes |= 1 << _stage.op[i].param2;
nRes |= 1 << _stage.op[i].param3;
break;
// default:
// assert(false);
}
}
if (bBracketOpen)
_strShader.append(")");
_strShader.append("; \n");
return nRes;
}
int compileCombiner(const gDPCombine & _combine, Combiner & _color, Combiner & _alpha, std::string & _strShader)
{
if (gDP.otherMode.cycleType != G_CYC_2CYCLE) {
_correctFirstStageParams(_alpha.stage[0]);
_correctFirstStageParams(_color.stage[0]);
}
_strShader.append(" alpha1 = ");
int nInputs = _compileCombiner(_alpha.stage[0], AlphaInput, _strShader);
// Simulate N64 color sign-extend.
if (combinedAlphaC(_combine))
_strShader.append(fragment_shader_sign_extend_alpha_c);
else if (combinedAlphaABD(_combine))
_strShader.append(fragment_shader_sign_extend_alpha_abd);
_strShader.append(
" if (uEnableAlphaTest != 0) { \n"
" lowp float alphaTestValue = (uAlphaCompareMode == 3) ? snoise() : uAlphaTestValue; \n"
" lowp float alphaValue; \n"
" if ((uAlphaCvgSel != 0) && (uCvgXAlpha == 0)) { \n"
" alphaValue = 0.125; \n"
" } else { \n"
" alphaValue = clamp(alpha1, 0.0, 1.0); \n"
" } \n"
" if (alphaValue < alphaTestValue) discard; \n"
" } \n"
);
_strShader.append(" color1 = ");
nInputs |= _compileCombiner(_color.stage[0], ColorInput, _strShader);
// Simulate N64 color sign-extend.
if (combinedColorC(_combine))
_strShader.append(fragment_shader_sign_extend_color_c);
else if (combinedColorABD(_combine))
_strShader.append(fragment_shader_sign_extend_color_abd);
_strShader.append(" combined_color = vec4(color1, alpha1); \n");
if (_alpha.numStages == 2) {
_strShader.append(" alpha2 = ");
_correctSecondStageParams(_alpha.stage[1]);
nInputs |= _compileCombiner(_alpha.stage[1], AlphaInput, _strShader);
}
else
_strShader.append(" alpha2 = alpha1; \n");
_strShader.append(" if (uCvgXAlpha != 0 && alpha2 < 0.125) discard; \n");
if (_color.numStages == 2) {
_strShader.append(" color2 = ");
_correctSecondStageParams(_color.stage[1]);
nInputs |= _compileCombiner(_color.stage[1], ColorInput, _strShader);
}
else
_strShader.append(" color2 = color1; \n");
_strShader.append(" lowp vec4 cmbRes = vec4(color2, alpha2);\n");
// Simulate N64 color clamp.
if (needClampColor())
_strShader.append(fragment_shader_clamp);
else
_strShader.append(" lowp vec4 clampedColor = clamp(cmbRes, 0.0, 1.0);\n");
#ifndef GLES2
if (config.generalEmulation.enableNoise != 0) {
_strShader.append(
" if (uColorDitherMode == 2) colorNoiseDither(snoise(), clampedColor.rgb); \n"
" if (uAlphaDitherMode == 2) alphaNoiseDither(snoise(), clampedColor.a); \n"
);
}
#endif
if (config.generalEmulation.enableLegacyBlending == 0) {
if (gDP.otherMode.cycleType <= G_CYC_2CYCLE)
_strShader.append(fragment_shader_blender1);
if (gDP.otherMode.cycleType == G_CYC_2CYCLE)
_strShader.append(fragment_shader_blender2);
_strShader.append(
" fragColor = clampedColor; \n"
);
} else {
_strShader.append(
" fragColor = clampedColor; \n"
" if (uFogUsage == 1) \n"
" fragColor.rgb = mix(fragColor.rgb, uFogColor.rgb, vShadeColor.a); \n"
);
}
return nInputs;
}