diff --git a/projects/msvc/GLideN64.vcxproj b/projects/msvc/GLideN64.vcxproj
index 4c466f1b..56358ff6 100644
--- a/projects/msvc/GLideN64.vcxproj
+++ b/projects/msvc/GLideN64.vcxproj
@@ -309,8 +309,14 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)")
+
+
+
+
+
+
@@ -465,8 +471,14 @@ copy /Y "$(OutDir)$(TargetName).*" "$(Mupen64PluginsDir_x64)")
+
+
+
+
+
+
diff --git a/projects/msvc/GLideN64.vcxproj.filters b/projects/msvc/GLideN64.vcxproj.filters
index c514ca90..1687c729 100644
--- a/projects/msvc/GLideN64.vcxproj.filters
+++ b/projects/msvc/GLideN64.vcxproj.filters
@@ -221,6 +221,9 @@
Source Files\Graphics\OpenGL\GLSL
+
+ Source Files\Graphics\OpenGL\GLSL
+
Source Files\Graphics\OpenGL\GLSL
@@ -236,6 +239,9 @@
Source Files\Graphics\OpenGL\GLSL
+
+ Source Files\Graphics\OpenGL\GLSL
+
Source Files\Graphics\OpenGL\GLSL
@@ -425,6 +431,18 @@
Source Files\BufferCopy
+
+ Source Files\Graphics\OpenGL\GLSL
+
+
+ Source Files\Graphics\OpenGL\GLSL
+
+
+ Source Files\Graphics\OpenGL\GLSL
+
+
+ Source Files\Graphics\OpenGL\GLSL
+
@@ -787,6 +805,24 @@
Resource Files
+
+ Header Files\Graphics\OpenGL\GLSL
+
+
+ Header Files\Graphics\OpenGL\GLSL
+
+
+ Header Files\Graphics\OpenGL\GLSL
+
+
+ Header Files\Graphics\OpenGL\GLSL
+
+
+ Header Files\Graphics\OpenGL\GLSL
+
+
+ Header Files\Graphics\OpenGL\GLSL
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 63f0722e..e3e82dbb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -89,8 +89,14 @@ set(GLideN64_SOURCES
Graphics/OpenGLContext/opengl_Utils.cpp
Graphics/OpenGLContext/GLSL/glsl_CombinerInputs.cpp
Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.cpp
Graphics/OpenGLContext/GLSL/glsl_CombinerProgramImpl.cpp
Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp
+ Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.cpp
Graphics/OpenGLContext/GLSL/glsl_FXAA.cpp
Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp
Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp
diff --git a/src/Config.cpp b/src/Config.cpp
index bdc68473..772a60ec 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -51,6 +51,7 @@ void Config::resetToDefaults()
generalEmulation.enableShadersStorage = 1;
generalEmulation.enableLegacyBlending = 0;
generalEmulation.enableHybridFilter = 1;
+ generalEmulation.enableInaccurateTextureCoordinates = 0;
generalEmulation.hacks = 0;
#if defined(OS_ANDROID) || defined(OS_IOS)
generalEmulation.enableFragmentDepthWrite = 0;
diff --git a/src/Config.h b/src/Config.h
index e854b858..9f87076d 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -65,6 +65,7 @@ struct Config
u32 enableShadersStorage;
u32 enableLegacyBlending;
u32 enableHybridFilter;
+ u32 enableInaccurateTextureCoordinates;
u32 enableFragmentDepthWrite;
u32 hacks;
#if defined(OS_ANDROID) || defined(OS_IOS)
diff --git a/src/GLideNUI/ConfigDialog.cpp b/src/GLideNUI/ConfigDialog.cpp
index 5c2efa4c..b1c0e1ee 100644
--- a/src/GLideNUI/ConfigDialog.cpp
+++ b/src/GLideNUI/ConfigDialog.cpp
@@ -223,6 +223,7 @@ void ConfigDialog::_init(bool reInit, bool blockCustomSettings)
// Emulation settings
ui->emulateLodCheckBox->setChecked(config.generalEmulation.enableLOD != 0);
+ ui->enableInaccurateTextureCoordinatesCheckBox->setChecked(config.generalEmulation.enableInaccurateTextureCoordinates != 0);
ui->enableHWLightingCheckBox->setChecked(config.generalEmulation.enableHWLighting != 0);
ui->enableCoverageCheckBox->setChecked(config.generalEmulation.enableCoverage != 0);
ui->enableShadersStorageCheckBox->setChecked(config.generalEmulation.enableShadersStorage != 0);
@@ -562,6 +563,7 @@ void ConfigDialog::accept(bool justSave) {
// Emulation settings
config.generalEmulation.enableLOD = ui->emulateLodCheckBox->isChecked() ? 1 : 0;
+ config.generalEmulation.enableInaccurateTextureCoordinates = ui->enableInaccurateTextureCoordinatesCheckBox->isChecked() ? 1 : 0;
config.generalEmulation.enableHWLighting = ui->enableHWLightingCheckBox->isChecked() ? 1 : 0;
config.generalEmulation.enableCoverage = ui->enableCoverageCheckBox->isChecked() ? 1 : 0;
config.generalEmulation.enableShadersStorage = ui->enableShadersStorageCheckBox->isChecked() ? 1 : 0;
diff --git a/src/GLideNUI/Settings.cpp b/src/GLideNUI/Settings.cpp
index cee4ef13..5685f575 100644
--- a/src/GLideNUI/Settings.cpp
+++ b/src/GLideNUI/Settings.cpp
@@ -48,6 +48,7 @@ void _loadSettings(QSettings & settings)
config.generalEmulation.enableHiresNoiseDithering = settings.value("enableHiresNoiseDithering", config.generalEmulation.enableHiresNoiseDithering).toInt();
config.generalEmulation.rdramImageDitheringMode = settings.value("rdramImageDitheringMode", config.generalEmulation.rdramImageDitheringMode).toInt();
config.generalEmulation.enableLOD = settings.value("enableLOD", config.generalEmulation.enableLOD).toInt();
+ config.generalEmulation.enableInaccurateTextureCoordinates = settings.value("enableFastInaccurateShaders", config.generalEmulation.enableInaccurateTextureCoordinates).toInt();
config.generalEmulation.enableHWLighting = settings.value("enableHWLighting", config.generalEmulation.enableHWLighting).toInt();
config.generalEmulation.enableCoverage = settings.value("enableCoverage", config.generalEmulation.enableCoverage).toInt();
config.generalEmulation.enableShadersStorage = settings.value("enableShadersStorage", config.generalEmulation.enableShadersStorage).toInt();
@@ -193,6 +194,7 @@ void _writeSettingsToFile(const QString & filename)
settings.setValue("enableHiresNoiseDithering", config.generalEmulation.enableHiresNoiseDithering);
settings.setValue("rdramImageDitheringMode", config.generalEmulation.rdramImageDitheringMode);
settings.setValue("enableLOD", config.generalEmulation.enableLOD);
+ settings.setValue("enableFastInaccurateShaders", config.generalEmulation.enableInaccurateTextureCoordinates);
settings.setValue("enableHWLighting", config.generalEmulation.enableHWLighting);
settings.setValue("enableCoverage", config.generalEmulation.enableCoverage);
settings.setValue("enableShadersStorage", config.generalEmulation.enableShadersStorage);
@@ -359,8 +361,8 @@ void resetSettings(const QString & _strIniFolder)
static
u32 Adler32(u32 crc, const void *buffer, u32 count)
{
- register u32 s1 = crc & 0xFFFF;
- register u32 s2 = (crc >> 16) & 0xFFFF;
+ u32 s1 = crc & 0xFFFF;
+ u32 s2 = (crc >> 16) & 0xFFFF;
int k;
const u8 *Buffer = (const u8*)buffer;
@@ -465,6 +467,7 @@ void saveCustomRomSettings(const QString & _strIniFolder, const char * _strRomNa
WriteCustomSetting(generalEmulation, enableHiresNoiseDithering);
WriteCustomSetting(generalEmulation, rdramImageDitheringMode);
WriteCustomSetting(generalEmulation, enableLOD);
+ WriteCustomSetting(generalEmulation, enableInaccurateTextureCoordinates);
WriteCustomSetting(generalEmulation, enableHWLighting);
WriteCustomSetting(generalEmulation, enableCoverage);
WriteCustomSetting(generalEmulation, enableShadersStorage);
@@ -540,11 +543,11 @@ void saveCustomRomSettings(const QString & _strIniFolder, const char * _strRomNa
settings.beginGroup("hotkeys");
for (u32 idx = 0; idx < Config::HotKey::hkTotal; ++idx) {
- if (origConfig.hotkeys.keys[idx] != config.hotkeys.keys[idx] ||
+ if (origConfig.hotkeys.keys[idx] != config.hotkeys.keys[idx] ||
origConfig.hotkeys.keys[idx] != settings.value(Config::hotkeyIniName(idx), config.hotkeys.keys[idx]).toInt()) {
settings.setValue(Config::hotkeyIniName(idx), config.hotkeys.keys[idx]);
}
- if (origConfig.hotkeys.enabledKeys[idx] != config.hotkeys.enabledKeys[idx] ||
+ if (origConfig.hotkeys.enabledKeys[idx] != config.hotkeys.enabledKeys[idx] ||
origConfig.hotkeys.enabledKeys[idx] != settings.value(Config::enabledHotkeyIniName(idx), config.hotkeys.enabledKeys[idx]).toInt()) {
settings.setValue(Config::enabledHotkeyIniName(idx), config.hotkeys.enabledKeys[idx]);
}
diff --git a/src/GLideNUI/configDialog.ui b/src/GLideNUI/configDialog.ui
index b0b50476..4a8f4f1f 100644
--- a/src/GLideNUI/configDialog.ui
+++ b/src/GLideNUI/configDialog.ui
@@ -1098,6 +1098,16 @@
+ -
+
+
+ <html><head/><body><p><span style=" font-weight:600;">Enable inaccurate texture coordinates. </span></p><p>Enables inaccurate texture coordinate calculations. This can improve performance and texture pack compatibity at the cost of accuracy.</p><p>[Recommended: Unchecked, unless performance is hurt or for texture pack compatibility]</p></body></html>
+
+
+ Enable inaccurate texture coordinates
+
+
+
-
diff --git a/src/Graphics/CombinerProgram.cpp b/src/Graphics/CombinerProgram.cpp
index bc3bcd86..be59bc71 100644
--- a/src/Graphics/CombinerProgram.cpp
+++ b/src/Graphics/CombinerProgram.cpp
@@ -21,6 +21,7 @@ namespace graphics {
vecOptions.push_back(config.frameBufferEmulation.N64DepthCompare == Config::dcCompatible ? 1 : 0);
vecOptions.push_back(config.generalEmulation.enableLegacyBlending);
vecOptions.push_back(config.generalEmulation.enableFragmentDepthWrite);
+ vecOptions.push_back(config.generalEmulation.enableInaccurateTextureCoordinates);
u32 optionsSet = 0;
for (u32 i = 0; i < vecOptions.size(); ++i)
optionsSet |= vecOptions[i] << i;
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp
index 141f5451..f0552f9f 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp
@@ -3,44 +3,16 @@
#include
#include
#include "glsl_Utils.h"
-#include "glsl_ShaderPart.h"
#include "glsl_CombinerInputs.h"
#include "glsl_CombinerProgramImpl.h"
-#include "glsl_CombinerProgramBuilder.h"
-#include "glsl_CombinerProgramUniformFactory.h"
+#include "glsl_CombinerProgramBuilderAccurate.h"
+#include "glsl_CombinerProgramUniformFactoryAccurate.h"
#include "GraphicsDrawer.h"
-using namespace glsl;
+namespace glsl {
-class TextureConvert {
-public:
- void setMode(u32 _mode) {
- m_mode = _mode;
- }
-
- bool getBilerp1() const {
- return (m_mode & 1) != 0;
- }
-
- bool getBilerp0() const {
- return (m_mode & 2) != 0;
- }
-
- bool useYUVCoversion() const {
- return (m_mode & 3) != 3;
- }
-
- bool useTextureFiltering() const {
- return (m_mode & 3) != 0;
- }
-
-private:
- u32 m_mode;
-};
-
-
-u32 g_cycleType = G_CYC_1CYCLE;
-TextureConvert g_textureConvert;
+u32 CombinerProgramBuilder::s_cycleType = G_CYC_1CYCLE;
+TextureConvert CombinerProgramBuilder::s_textureConvert;
/*---------------_compileCombiner-------------*/
@@ -230,2546 +202,28 @@ CombinerInputs _compileCombiner(const CombinerStage & _stage, const char** _Inpu
return inputs;
}
-/*---------------ShaderParts-------------*/
-
-class VertexShaderHeader : public ShaderPart
-{
-public:
- VertexShaderHeader(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part = "#version 100 \n";
- m_part +=
- "#if (__VERSION__ > 120) \n"
- "# define IN in \n"
- "# define OUT out \n"
- "#else \n"
- "# define IN attribute \n"
- "# define OUT varying \n"
- "#ifndef GL_FRAGMENT_PRECISION_HIGH \n"
- "# define highp mediump \n"
- "#endif \n"
- "#endif // __VERSION \n"
- ;
- }
- else if (_glinfo.isGLESX) {
- std::stringstream ss;
- ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 es " << std::endl;
- ss << "# define IN in" << std::endl << "# define OUT out" << std::endl;
- if (_glinfo.noPerspective) {
- ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl
- << "noperspective OUT highp float vZCoord;" << std::endl
- << "uniform lowp int uClampMode;" << std::endl;
- }
- m_part = ss.str();
- }
- else {
- std::stringstream ss;
- ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 core " << std::endl;
- ss << "# define IN in" << std::endl << "# define OUT out" << std::endl;
- m_part = ss.str();
- }
- m_part += "uniform lowp vec2 uVertexOffset; \n";
- std::stringstream ss;
- ss << "const lowp float screenSizeDims = " << std::setprecision(1) << std::fixed << SCREEN_SIZE_DIM << ";" << std::endl;
- m_part += ss.str();
- }
-};
-
-class VertexShaderTexturedTriangle : public ShaderPart
-{
-public:
- VertexShaderTexturedTriangle(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "IN highp vec4 aPosition; \n"
- "IN lowp vec4 aColor; \n"
- "IN highp vec2 aTexCoord; \n"
- "IN lowp float aNumLights; \n"
- "IN highp vec4 aModify; \n"
- "IN highp vec2 aBaryCoords; \n"
- " \n"
- "uniform int uTexturePersp; \n"
- "uniform lowp int uTextureFilterMode; \n"
- " \n"
- "uniform lowp int uFogUsage; \n"
- "uniform mediump vec2 uFogScale; \n"
- "uniform mediump vec2 uScreenCoordsScale; \n"
- " \n"
- "uniform mediump vec2 uTexScale; \n"
- "uniform mediump vec2 uVTrans; \n"
- "uniform mediump vec2 uVScale; \n"
- "uniform mediump vec2 uAdjustTrans; \n"
- "uniform mediump vec2 uAdjustScale; \n"
- "uniform lowp ivec2 uCacheFrameBuffer; \n"
- "OUT highp vec2 vTexCoord0; \n"
- "OUT highp vec2 vTexCoord1; \n"
- "OUT mediump vec2 vLodTexCoord; \n"
- "OUT lowp float vNumLights; \n"
- "OUT lowp vec4 vShadeColor; \n"
- "OUT highp vec4 vBaryCoords; \n"
- ;
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
- else
- m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
- m_part +=
- "void main() \n"
- "{ \n"
- " gl_Position = aPosition; \n"
- " vShadeColor = aColor; \n"
- " vec2 texCoord = aTexCoord; \n"
- " texCoord *= uTexScale; \n"
- " if (uTexturePersp == 0 && aModify[2] == 0.0) texCoord *= 0.5;\n"
- " vTexCoord0 = texCoord; \n"
- " vTexCoord1 = texCoord; \n"
- " vLodTexCoord = texCoord; \n"
- " vNumLights = aNumLights; \n"
- " if ((aModify[0]) != 0.0) { \n"
- " gl_Position.xy *= gl_Position.w; \n"
- " } \n"
- " else { \n"
- " gl_Position.xy = gl_Position.xy * uVScale.xy + uVTrans.xy * gl_Position.ww; \n"
- " gl_Position.xy = floor(gl_Position.xy * vec2(4.0)) * vec2(0.25); \n"
- " gl_Position.xy = gl_Position.xy * uAdjustScale + gl_Position.ww * uAdjustTrans; \n"
- " } \n"
- " if ((aModify[1]) != 0.0) \n"
- " gl_Position.z *= gl_Position.w; \n"
- " if ((aModify[3]) != 0.0) \n"
- " vNumLights = 0.0; \n"
- " if (uFogUsage > 0) { \n"
- " lowp float fp; \n"
- " if (aPosition.z < -aPosition.w && aModify[1] == 0.0) \n"
- " fp = -uFogScale.s + uFogScale.t; \n"
- " else \n"
- " fp = aPosition.z/aPosition.w*uFogScale.s + uFogScale.t; \n"
- " fp = clamp(fp, 0.0, 1.0); \n"
- " if (uFogUsage == 1) \n"
- " vShadeColor.a = fp; \n"
- " else \n"
- " vShadeColor.rgb = vec3(fp); \n"
- " } \n"
- " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n"
- " vShadeColorNoperspective = vShadeColor; \n"
- ;
- }
-};
-
-class VertexShaderTriangle : public ShaderPart
-{
-public:
- VertexShaderTriangle(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "IN highp vec4 aPosition; \n"
- "IN lowp vec4 aColor; \n"
- "IN lowp float aNumLights; \n"
- "IN highp vec4 aModify; \n"
- "IN highp vec2 aBaryCoords; \n"
- " \n"
- "uniform lowp int uFogUsage; \n"
- "uniform mediump vec2 uFogScale; \n"
- "uniform mediump vec2 uScreenCoordsScale; \n"
- "uniform mediump vec2 uVTrans; \n"
- "uniform mediump vec2 uVScale; \n"
- "uniform mediump vec2 uAdjustTrans; \n"
- "uniform mediump vec2 uAdjustScale; \n"
- " \n"
- "OUT lowp float vNumLights; \n"
- "OUT lowp vec4 vShadeColor; \n"
- "OUT highp vec4 vBaryCoords; \n"
- ;
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
- else
- m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
- m_part +=
- " \n"
- "void main() \n"
- "{ \n"
- " gl_Position = aPosition; \n"
- " vShadeColor = aColor; \n"
- " vNumLights = aNumLights; \n"
- " if ((aModify[0]) != 0.0) { \n"
- " gl_Position.xy *= gl_Position.w; \n"
- " } \n"
- " else { \n"
- " gl_Position.xy = gl_Position.xy * uVScale.xy + uVTrans.xy * gl_Position.ww; \n"
- " gl_Position.xy = floor(gl_Position.xy * vec2(4.0)) * vec2(0.25); \n"
- " gl_Position.xy = gl_Position.xy * uAdjustScale + gl_Position.ww * uAdjustTrans; \n"
- " } \n"
- " if ((aModify[1]) != 0.0) \n"
- " gl_Position.z *= gl_Position.w; \n"
- " if ((aModify[3]) != 0.0) \n"
- " vNumLights = 0.0; \n"
- " if (uFogUsage > 0) { \n"
- " lowp float fp; \n"
- " if (aPosition.z < -aPosition.w && aModify[1] == 0.0) \n"
- " fp = -uFogScale.s + uFogScale.t; \n"
- " else \n"
- " fp = aPosition.z/aPosition.w*uFogScale.s + uFogScale.t; \n"
- " fp = clamp(fp, 0.0, 1.0); \n"
- " if (uFogUsage == 1) \n"
- " vShadeColor.a = fp; \n"
- " else \n"
- " vShadeColor.rgb = vec3(fp); \n"
- " } \n"
- " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n"
- " vShadeColorNoperspective = vShadeColor; \n"
- ;
- }
-};
-
-class VertexShaderTexturedRect : public ShaderPart
-{
-public:
- VertexShaderTexturedRect(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "IN highp vec4 aRectPosition; \n"
- "IN highp vec2 aTexCoord0; \n"
- "IN highp vec2 aTexCoord1; \n"
- "IN highp vec2 aBaryCoords; \n"
- " \n"
- "OUT highp vec2 vTexCoord0; \n"
- "OUT highp vec2 vTexCoord1; \n"
- "OUT lowp vec4 vShadeColor; \n"
- "OUT highp vec4 vBaryCoords;"
- ;
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
- else
- m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
- m_part +=
- "uniform lowp vec4 uRectColor; \n"
- "void main() \n"
- "{ \n"
- " gl_Position = aRectPosition; \n"
- " vShadeColor = uRectColor; \n"
- " vShadeColorNoperspective = uRectColor; \n"
- " vTexCoord0 = aTexCoord0; \n"
- " vTexCoord1 = aTexCoord1; \n"
- " vBaryCoords = vec4(aBaryCoords, vec2(1.0) - aBaryCoords); \n"
- ;
- }
-};
-
-class VertexShaderRect : public ShaderPart
-{
-public:
- VertexShaderRect(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "IN highp vec4 aRectPosition; \n"
- "IN highp vec2 aBaryCoords; \n"
- " \n"
- "OUT lowp vec4 vShadeColor; \n"
- "OUT highp vec4 vBaryCoords; \n"
- ;
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
- else
- m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
- m_part +=
- "uniform lowp vec4 uRectColor; \n"
- "void main() \n"
- "{ \n"
- " gl_Position = aRectPosition; \n"
- " vShadeColor = uRectColor; \n"
- " vShadeColorNoperspective = uRectColor; \n"
- " vBaryCoords = vec4(aBaryCoords, vec2(1.0) - aBaryCoords);\n"
- ;
- }
-};
-
-class VertexShaderEnd : public ShaderPart
-{
-public:
- VertexShaderEnd(const opengl::GLInfo & _glinfo)
- {
- if (!_glinfo.isGLESX) {
- m_part =
- " gl_ClipDistance[0] = gl_Position.w - gl_Position.z; \n"
- ;
- } else if (config.generalEmulation.enableFragmentDepthWrite != 0 && _glinfo.noPerspective) {
- m_part =
- " vZCoord = gl_Position.z / gl_Position.w; \n"
- " if (uClampMode > 0) \n"
- " gl_Position.z = 0.0; \n"
- ;
- } else if (config.generalEmulation.enableClipping != 0) {
- // Move the near plane towards the camera.
- // It helps to avoid issues with near-plane clipping in games, which do not use it.
- // Z must be scaled back in fragment shader.
- m_part = " gl_Position.z /= 8.0; \n";
- }
- m_part +=
- " gl_Position.xy += uVertexOffset * vec2(gl_Position.w); \n"
- " gl_Position.xy -= vec2(0.5*screenSizeDims) * gl_Position.ww; \n"
- " gl_Position.xy /= vec2(0.5*screenSizeDims); \n"
- "} \n"
- ;
- }
-};
-
-class FragmentShaderHeader : public ShaderPart
-{
-public:
- FragmentShaderHeader(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part = "#version 100 \n";
- if (config.generalEmulation.enableLOD) {
- m_part += "#extension GL_EXT_shader_texture_lod : enable \n";
- m_part += "#extension GL_OES_standard_derivatives : enable \n";
- }
- m_part +=
- "#if (__VERSION__ > 120) \n"
- "# define IN in \n"
- "# define OUT out \n"
- "# define texture2D texture \n"
- "#else \n"
- "# define IN varying \n"
- "# define OUT \n"
- "#ifndef GL_FRAGMENT_PRECISION_HIGH \n"
- "# define highp mediump \n"
- "#endif \n"
- "#endif // __VERSION __ \n"
- ;
- } else if (_glinfo.isGLESX) {
- std::stringstream ss;
- ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 es " << std::endl;
- if (_glinfo.noPerspective)
- ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl;
- if (_glinfo.dual_source_blending)
- ss << "#extension GL_EXT_blend_func_extended : enable" << std::endl;
- if (_glinfo.ext_fetch)
- ss << "#extension GL_EXT_shader_framebuffer_fetch : enable" << std::endl;
- if (_glinfo.ext_fetch_arm)
- ss << "#extension GL_ARM_shader_framebuffer_fetch : enable" << std::endl;
-
- if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast) {
- if (_glinfo.imageTextures && _glinfo.fragment_interlockNV) {
- ss << "#extension GL_NV_fragment_shader_interlock : enable" << std::endl
- << "layout(pixel_interlock_ordered) in;" << std::endl;
- }
- } else if (_glinfo.fetch_depth)
- ss << "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : enable" << std::endl;
-
- ss << "# define IN in" << std::endl
- << "# define OUT out" << std::endl
- << "# define texture2D texture" << std::endl;
- m_part = ss.str();
- } else {
- std::stringstream ss;
- ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 core " << std::endl;
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- if (_glinfo.n64DepthWithFbFetch)
- ss << "#extension GL_EXT_shader_framebuffer_fetch : enable" << std::endl;
- else if (_glinfo.imageTextures) {
- if (_glinfo.majorVersion * 10 + _glinfo.minorVersion < 42) {
- ss << "#extension GL_ARB_shader_image_load_store : enable" << std::endl
- << "#extension GL_ARB_shading_language_420pack : enable" << std::endl;
- }
- if (_glinfo.fragment_interlock)
- ss << "#extension GL_ARB_fragment_shader_interlock : enable" << std::endl
- << "layout(pixel_interlock_ordered) in;" << std::endl;
- else if (_glinfo.fragment_interlockNV)
- ss << "#extension GL_NV_fragment_shader_interlock : enable" << std::endl
- << "layout(pixel_interlock_ordered) in;" << std::endl;
- else if (_glinfo.fragment_ordering)
- ss << "#extension GL_INTEL_fragment_shader_ordering : enable" << std::endl;
- }
- }
-
- ss << "# define IN in" << std::endl
- << "# define OUT out" << std::endl
- << "# define texture2D texture" << std::endl;
- m_part = ss.str();
- }
- m_part +=
- // Return the vector of the standard basis of R^4 with a 1 at position and 0 otherwise.
- " #define STVEC(pos) (step(float(pos), vec4(0.5,1.5,2.5,3.5)) - step(float(pos), vec4(-0.5,0.5,1.5,2.5))) \n";
-
- std::stringstream ss;
- if (_glinfo.isGLES2)
- ss << "const mediump float mipmapTileWidth = " << std::setprecision(1) << std::fixed << f32(MIPMAP_TILE_WIDTH) << ";" << std::endl;
- else
- ss << "const mediump int mipmapTileWidth = " << MIPMAP_TILE_WIDTH << ";" << std::endl;
- m_part += ss.str();
- }
-};
-
-class ShaderBlender1 : public ShaderPart
-{
-public:
- ShaderBlender1(const opengl::GLInfo & _glinfo)
- {
-#if 1
- m_part =
- " srcColor1 = vec4(0.0); \n"
- " dstFactor1 = 0.0; \n"
- " muxPM[0] = clampedColor; \n"
- " muxA[0] = clampedColor.a; \n"
- " muxa = MUXA(uBlendMux1[1]); \n"
- " muxB[0] = 1.0 - muxa; \n"
- " muxb = MUXB(uBlendMux1[3]); \n"
- " muxp = MUXPM(uBlendMux1[0]); \n"
- " muxm = MUXPM(uBlendMux1[2]); \n"
- " muxaf = MUXF(uBlendMux1[0]); \n"
- " muxbf = MUXF(uBlendMux1[2]); \n"
- " if (uForceBlendCycle1 != 0) { \n"
- " srcColor1 = muxp * muxa + muxm * muxb; \n"
- " dstFactor1 = muxaf * muxa + muxbf * muxb; \n"
- " srcColor1 = clamp(srcColor1, 0.0, 1.0); \n"
- " } else { \n"
- " srcColor1 = muxp; \n"
- " dstFactor1 = muxaf; \n"
- " } \n"
- " fragColor = srcColor1; \n"
- " fragColor1 = vec4(dstFactor1); \n"
- ;
-#else
- // Keep old code for reference
- m_part =
- " muxPM[0] = clampedColor; \n"
- " if (uForceBlendCycle1 != 0) { \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"
- " } else clampedColor.rgb = muxPM[uBlendMux1[0]].rgb; \n"
- ;
-#endif
- }
-};
-
-class ShaderBlender2 : public ShaderPart
-{
-public:
- ShaderBlender2(const opengl::GLInfo & _glinfo)
- {
-#if 1
- m_part =
- " srcColor2 = vec4(0.0); \n"
- " dstFactor2 = 0.0; \n"
- " muxPM[0] = srcColor1; \n"
- " muxa = MUXA(uBlendMux2[1]); \n"
- " muxB[0] = 1.0 - muxa; \n"
- " muxb = MUXB(uBlendMux2[3]); \n"
- " muxp = MUXPM(uBlendMux2[0]); \n"
- " muxm = MUXPM(uBlendMux2[2]); \n"
- " muxF[0] = dstFactor1; \n"
- " muxaf = MUXF(uBlendMux2[0]); \n"
- " muxbf = MUXF(uBlendMux2[2]); \n"
- " if (uForceBlendCycle2 != 0) { \n"
- " srcColor2 = muxp * muxa + muxm * muxb; \n"
- " dstFactor2 = muxaf * muxa + muxbf * muxb; \n"
- " srcColor2 = clamp(srcColor2, 0.0, 1.0); \n"
- " } else { \n"
- " srcColor2 = muxp; \n"
- " dstFactor2 = muxaf; \n"
- " } \n"
- " fragColor = srcColor2; \n"
- " fragColor1 = vec4(dstFactor2); \n"
- ;
-
-#else
- // Keep old code for reference
- m_part =
- " muxPM[0] = clampedColor; \n"
- " muxPM[1] = vec4(0.0); \n"
- " if (uForceBlendCycle2 != 0) { \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"
- " } else clampedColor.rgb = muxPM[uBlendMux2[0]].rgb; \n"
- ;
-#endif
- }
-};
-
-class ShaderBlenderAlpha : public ShaderPart
-{
-public:
- ShaderBlenderAlpha(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.dual_source_blending || _glinfo.ext_fetch || _glinfo.ext_fetch_arm) {
- m_part +=
- "if (uBlendAlphaMode != 2) { \n"
- " lowp vec4 srcAlpha = vec4(cvg, cvg, 1.0, 0.0); \n"
- " lowp vec4 dstFactorAlpha = vec4(1.0, 1.0, 0.0, 1.0); \n"
- " if (uBlendAlphaMode == 0) \n"
- " dstFactorAlpha[0] = 0.0; \n"
- " fragColor1.a = dstFactorAlpha[uCvgDest]; \n"
- " fragColor.a = srcAlpha[uCvgDest] + lastFragColor.a * fragColor1.a;\n"
- "} else fragColor.a = clampedColor.a; \n";
- }
- else {
- m_part +=
- "fragColor.a = clampedColor.a;"
- ;
- }
- }
-};
-
-class ShaderLegacyBlender : public ShaderPart
-{
-public:
- ShaderLegacyBlender()
- {
- m_part =
- " if (uFogUsage == 1) \n"
- " fragColor.rgb = mix(fragColor.rgb, uFogColor.rgb, vShadeColor.a); \n"
- ;
- }
-};
-
-
-/*
-// 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;
-*/
-class ShaderClamp : public ShaderPart
-{
-public:
- ShaderClamp()
- {
- m_part =
- " lowp vec4 wrappedColor = WRAP(cmbRes, -0.51, 1.51); \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;
-*/
-class ShaderSignExtendColorC : public ShaderPart
-{
-public:
- ShaderSignExtendColorC()
- {
- m_part =
- " color1 = WRAP(color1, -1.01, 1.01); \n"
- ;
- }
-};
-
-class ShaderSignExtendAlphaC : public ShaderPart
-{
-public:
- ShaderSignExtendAlphaC()
- {
- m_part =
- " alpha1 = WRAP(alpha1, -1.01, 1.01); \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;
-*/
-class ShaderSignExtendColorABD : public ShaderPart
-{
-public:
- ShaderSignExtendColorABD()
- {
- m_part =
- " color1 = WRAP(color1, -0.51, 1.51); \n"
- ;
- }
-};
-
-class ShaderSignExtendAlphaABD : public ShaderPart
-{
-public:
- ShaderSignExtendAlphaABD()
- {
- m_part =
- " alpha1 = WRAP(alpha1, -0.51,1.51); \n"
- ;
- }
-};
-
-class ShaderAlphaTest : public ShaderPart
-{
-public:
- ShaderAlphaTest()
- {
- m_part =
- " 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"
- ;
- }
-};
-
-class ShaderDithering : public ShaderPart
-{
-public:
- ShaderDithering(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2)
- return;
-
- if (config.generalEmulation.enableDitheringPattern == 0) {
- m_part =
- " if (uColorDitherMode == 2) { \n"
- " colorDither(snoiseRGB(), clampedColor.rgb); \n"
- " } \n"
- " if (uAlphaDitherMode == 2) { \n"
- " alphaDither(snoiseA(), clampedColor.a); \n"
- " } \n"
- ;
- return;
- }
-
- m_part +=
- " lowp mat4 bayer = mat4( 0.0, 0.5, 0.125, 0.625, \n"
- " 0.5, 0.0, 0.625, 0.125, \n"
- " 0.375, 0.875, 0.25, 0.75, \n"
- " 0.875, 0.375, 0.75, 0.25); \n"
- " lowp mat4 mSquare = mat4( 0.0, 0.75, 0.125, 0.875, \n"
- " 0.5, 0.25, 0.625, 0.375, \n"
- " 0.375, 0.625, 0.25, 0.5, \n"
- " 0.875, 0.125, 0.75, 0.0); \n"
- // Try to keep dithering visible even at higher resolutions
- " lowp float divider = 1.0 + step(3.0, uScreenScale.x); \n"
- " mediump ivec2 position = ivec2(mod((gl_FragCoord.xy - 0.5) / divider,4.0));\n"
- " lowp vec4 posX = STVEC(position.x); \n"
- " lowp vec4 posY = STVEC(position.y); \n"
- " lowp float bayerThreshold = dot(bayer*posY, posX); \n"
- " lowp float mSquareThreshold = dot(mSquare*posY, posX); \n"
- " switch (uColorDitherMode) { \n"
- " case 0: \n"
- " colorDither(vec3(mSquareThreshold), clampedColor.rgb); \n"
- " break; \n"
- " case 1: \n"
- " colorDither(vec3(bayerThreshold), clampedColor.rgb); \n"
- " break; \n"
- " case 2: \n"
- " colorDither(snoiseRGB(), clampedColor.rgb); \n"
- " break; \n"
- " case 3: \n"
- " break; \n"
- " } \n"
- " switch (uAlphaDitherMode) { \n"
- " case 0: \n"
- " switch (uColorDitherMode) { \n"
- " case 0: \n"
- " case 2: \n"
- " alphaDither(mSquareThreshold, clampedColor.a); \n"
- " break; \n"
- " case 1: \n"
- " case 3: \n"
- " alphaDither(bayerThreshold, clampedColor.a); \n"
- " break; \n"
- " } \n"
- " break; \n"
- " case 1: \n"
- " switch (uColorDitherMode) { \n"
- " case 0: \n"
- " case 2: \n"
- " alphaDither(bayerThreshold, clampedColor.a); \n"
- " break; \n"
- " case 1: \n"
- " case 3: \n"
- " alphaDither(mSquareThreshold, clampedColor.a); \n"
- " break; \n"
- " } \n"
- " break; \n"
- " case 2: \n"
- " alphaDither(snoiseA(), clampedColor.a); \n"
- " break; \n"
- " case 3: \n"
- " break; \n"
- " } \n"
- ;
- }
-};
-
-class ShaderFragmentGlobalVariablesTex : public ShaderPart
-{
-public:
- ShaderFragmentGlobalVariablesTex(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "uniform sampler2D uTex0; \n"
- "uniform sampler2D uTex1; \n"
- "uniform lowp vec4 uFogColor; \n"
- "uniform lowp vec4 uCenterColor;\n"
- "uniform lowp vec4 uScaleColor; \n"
- "uniform lowp vec4 uBlendColor; \n"
- "uniform lowp vec4 uEnvColor; \n"
- "uniform lowp vec4 uPrimColor; \n"
- "uniform lowp float uPrimLod; \n"
- "uniform lowp float uK4; \n"
- "uniform lowp float uK5; \n"
- "uniform lowp int uAlphaCompareMode; \n"
- "uniform lowp ivec2 uFbMonochrome; \n"
- "uniform lowp ivec2 uFbFixedAlpha; \n"
- "uniform lowp int uEnableAlphaTest; \n"
- "uniform lowp int uCvgXAlpha; \n"
- "uniform lowp int uAlphaCvgSel; \n"
- "uniform lowp float uAlphaTestValue; \n"
- "uniform lowp int uDepthSource; \n"
- "uniform highp float uPrimDepth; \n"
- "uniform mediump vec2 uScreenScale; \n"
- "uniform highp vec2 uTexClamp[2]; \n"
- "uniform highp vec2 uTexWrap[2]; \n"
- "uniform lowp vec2 uTexWrapEn[2]; \n"
- "uniform lowp vec2 uTexMirrorEn[2]; \n"
- "uniform lowp vec2 uTexClampEn[2]; \n"
- "uniform highp vec2 uTexSize[2]; \n"
- "uniform highp vec2 uShiftScale[2]; \n"
- "uniform highp vec2 uTexOffset[2]; \n"
- "uniform highp vec2 uHDRatio[2]; \n"
- "uniform highp vec2 uTexCoordOffset[2]; \n"
- "uniform highp vec2 uBilinearOffset; \n"
- "uniform highp vec2 uCacheOffset[2]; \n"
- "uniform lowp int uUseTexCoordBounds; \n"
- "uniform highp vec4 uTexCoordBounds0; \n"
- "uniform highp vec4 uTexCoordBounds1; \n"
- "uniform lowp int uScreenSpaceTriangle; \n"
- "highp vec2 texCoord0; \n"
- "highp vec2 texCoord1; \n"
- "highp vec2 tcData0[5]; \n"
- "highp vec2 tcData1[5]; \n"
- "uniform lowp int uCvgDest; \n"
- "uniform lowp int uBlendAlphaMode; \n"
- "lowp float cvg; \n"
- ;
-
- if (config.generalEmulation.enableLegacyBlending != 0) {
- m_part +=
- "uniform lowp int uFogUsage; \n"
- ;
- } else {
- m_part +=
- "uniform lowp ivec4 uBlendMux1; \n"
- "uniform lowp int uForceBlendCycle1;\n"
- ;
- }
-
- if (!_glinfo.isGLES2) {
- m_part +=
- "uniform sampler2D uDepthTex; \n"
- "uniform lowp int uAlphaDitherMode; \n"
- "uniform lowp int uColorDitherMode; \n"
- "uniform lowp int uRenderTarget; \n"
- "uniform mediump vec2 uDepthScale; \n"
- ;
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part +=
- "uniform lowp int uEnableDepthCompare; \n"
- ;
- }
- } else {
- m_part +=
- "lowp int nCurrentTile; \n"
- ;
- }
-
- if (config.video.multisampling > 0) {
- m_part +=
- "uniform lowp ivec2 uMSTexEnabled; \n"
- "uniform lowp sampler2DMS uMSTex0; \n"
- "uniform lowp sampler2DMS uMSTex1; \n"
- ;
- }
-
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective IN lowp vec4 vShadeColorNoperspective; \n";
- else
- m_part += "IN lowp vec4 vShadeColorNoperspective; \n";
-
- m_part +=
- "IN lowp vec4 vShadeColor; \n"
- "IN highp vec2 vTexCoord0; \n"
- "IN highp vec2 vTexCoord1; \n"
- "IN mediump vec2 vLodTexCoord; \n"
- "IN lowp float vNumLights; \n"
- "IN highp vec4 vBaryCoords; \n"
- ;
-
- if (_glinfo.dual_source_blending) {
- m_part +=
- "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" // SECONDARY FRAGMENT SHADER OUTPUT
- "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
- "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
- ;
- } else if (_glinfo.ext_fetch) {
- m_part +=
- "layout(location = 0) inout lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR fragColor \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
- "#define LAST_FRAG_ALPHA fragColor.a \n" // CURRENT FRAMEBUFFER ALPHA
- ;
- } else if (_glinfo.ext_fetch_arm) {
- m_part +=
- "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR gl_LastFragColorARM \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
- "#define LAST_FRAG_ALPHA gl_LastFragColorARM.a \n" // CURRENT FRAMEBUFFER ALPHA
- ;
- } else {
- m_part +=
- "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
- "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
- ;
- }
-
- if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.n64DepthWithFbFetch) {
- m_part +=
- "layout(location = 1) inout highp vec4 depthZ; \n"
- "layout(location = 2) inout highp vec4 depthDeltaZ; \n"
- ;
- }
-
- if (_glinfo.isGLES2)
- m_part +=
- "uniform mediump vec2 uTextureSize[2]; \n"
- ;
- }
-};
-
-class ShaderFragmentGlobalVariablesNotex : public ShaderPart
-{
-public:
- ShaderFragmentGlobalVariablesNotex(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "uniform lowp vec4 uFogColor; \n"
- "uniform lowp vec4 uCenterColor;\n"
- "uniform lowp vec4 uScaleColor; \n"
- "uniform lowp vec4 uBlendColor; \n"
- "uniform lowp vec4 uEnvColor; \n"
- "uniform lowp vec4 uPrimColor; \n"
- "uniform lowp float uPrimLod; \n"
- "uniform lowp float uK4; \n"
- "uniform lowp float uK5; \n"
- "uniform lowp int uAlphaCompareMode; \n"
- "uniform lowp ivec2 uFbMonochrome; \n"
- "uniform lowp ivec2 uFbFixedAlpha; \n"
- "uniform lowp int uEnableAlphaTest; \n"
- "uniform lowp int uCvgXAlpha; \n"
- "uniform lowp int uAlphaCvgSel; \n"
- "uniform lowp float uAlphaTestValue; \n"
- "uniform lowp int uDepthSource; \n"
- "uniform highp float uPrimDepth; \n"
- "uniform mediump vec2 uScreenScale; \n"
- "uniform lowp int uScreenSpaceTriangle; \n"
- "uniform lowp int uCvgDest; \n"
- "uniform lowp int uBlendAlphaMode; \n"
- "lowp float cvg; \n"
- ;
-
- if (config.generalEmulation.enableLegacyBlending != 0) {
- m_part +=
- "uniform lowp int uFogUsage; \n"
- ;
- } else {
- m_part +=
- "uniform lowp ivec4 uBlendMux1; \n"
- "uniform lowp int uForceBlendCycle1;\n"
- ;
- }
-
- if (!_glinfo.isGLES2) {
- m_part +=
- "uniform sampler2D uDepthTex; \n"
- "uniform lowp int uAlphaDitherMode; \n"
- "uniform lowp int uColorDitherMode; \n"
- "uniform lowp int uRenderTarget; \n"
- "uniform mediump vec2 uDepthScale; \n"
- ;
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part +=
- "uniform lowp int uEnableDepthCompare; \n"
- ;
- }
- } else {
- m_part +=
- "lowp int nCurrentTile; \n"
- ;
- }
-
- if (!_glinfo.isGLESX || _glinfo.noPerspective)
- m_part += "noperspective IN lowp vec4 vShadeColorNoperspective; \n";
- else
- m_part += "IN lowp vec4 vShadeColorNoperspective; \n";
-
- m_part +=
- "IN lowp vec4 vShadeColor; \n"
- "IN lowp float vNumLights; \n"
- "IN highp vec4 vBaryCoords; \n"
- ;
-
- if (_glinfo.dual_source_blending) {
- m_part +=
- "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" // SECONDARY FRAGMENT SHADER OUTPUT
- "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
- "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
- ;
- } else if (_glinfo.ext_fetch) {
- m_part +=
- "layout(location = 0) inout lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR fragColor \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
- "#define LAST_FRAG_ALPHA fragColor.a \n" // CURRENT FRAMEBUFFER ALPHA
- ;
- } else if (_glinfo.ext_fetch_arm) {
- m_part +=
- "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR gl_LastFragColorARM \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
- "#define LAST_FRAG_ALPHA gl_LastFragColorARM.a \n" // CURRENT FRAMEBUFFER ALPHA
- ;
- } else {
- m_part +=
- "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
- "lowp vec4 fragColor1; \n" // DUMMY
- "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
- "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
- ;
- }
-
- if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.n64DepthWithFbFetch) {
- m_part +=
- "layout(location = 1) inout highp vec4 depthZ; \n"
- "layout(location = 2) inout highp vec4 depthDeltaZ; \n"
- ;
- }
- }
-};
-
-class ShaderFragmentHeaderNoise : public ShaderPart
-{
-public:
- ShaderFragmentHeaderNoise(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "lowp float snoise();\n"
- ;
- }
-};
-
-class ShaderFragmentHeaderWriteDepth : public ShaderPart
-{
-public:
- ShaderFragmentHeaderWriteDepth(const opengl::GLInfo & _glinfo)
- {
- if (!_glinfo.isGLES2) {
- m_part =
- "highp float writeDepth();\n";
- ;
- }
- if (_glinfo.isGLESX && _glinfo.noPerspective) {
- m_part =
- "noperspective IN highp float vZCoord; \n"
- "uniform lowp float uPolygonOffset; \n"
- "uniform lowp int uClampMode; \n"
- + m_part
- ;
- }
-
- }
-};
-
-class ShaderFragmentHeaderCalcLight : public ShaderPart
-{
-public:
- ShaderFragmentHeaderCalcLight(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "void calc_light(in lowp float fLights, in lowp vec3 input_color, out lowp vec3 output_color);\n";
- ;
- }
-};
-
-class ShaderFragmentHeaderMipMap : public ShaderPart
-{
-public:
- ShaderFragmentHeaderMipMap(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1);\n";
- ;
- }
-};
-
-class ShaderFragmentHeaderTextureEngine : public ShaderPart
-{
-public:
- ShaderFragmentHeaderTextureEngine(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, \n"
- " in highp vec2 vWrap, in highp vec2 vClamp, \n"
- " in lowp vec2 vClampEn, in lowp vec2 vMirrorEn ); \n"
- "void textureEngine0(in highp vec2 texCoord, out highp vec2 tcData[5]); \n"
- "void textureEngine1(in highp vec2 texCoord, out highp vec2 tcData[5]); \n"
- ;
- }
-};
-
-class ShaderFragmentHeaderReadMSTex : public ShaderPart
-{
-public:
- ShaderFragmentHeaderReadMSTex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
- {
- }
-
- void write(std::stringstream & shader) const override
- {
- if (!m_glinfo.isGLES2 &&
- config.video.multisampling > 0 &&
- (g_cycleType == G_CYC_COPY || g_textureConvert.useTextureFiltering()))
- {
- shader <<
- "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha);\n";
- }
- }
-
-private:
- const opengl::GLInfo& m_glinfo;
-};
-
-class ShaderFragmentHeaderDither : public ShaderPart
-{
-public:
- ShaderFragmentHeaderDither(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2)
- return;
-
- m_part =
- "void colorDither(in lowp vec3 _threshold, inout lowp vec3 _color);\n"
- "void alphaDither(in lowp float _threshold, inout lowp float _alpha);\n"
- "lowp vec3 snoiseRGB();\n"
- "lowp float snoiseA();\n"
- ;
- }
-};
-
-class ShaderFragmentHeaderDepthCompare : public ShaderPart
-{
-public:
- ShaderFragmentHeaderDepthCompare(const opengl::GLInfo & _glinfo)
- {
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part =
- "bool depth_compare(highp float curZ); \n"
- "bool depth_render(highp float Z, highp float curZ); \n"
- ;
- if (_glinfo.imageTextures & !_glinfo.n64DepthWithFbFetch) {
- m_part +=
- "layout(binding = 2, r32f) highp uniform restrict image2D uDepthImageZ; \n"
- "layout(binding = 3, r32f) highp uniform restrict image2D uDepthImageDeltaZ; \n"
- ;
- }
- }
- }
-};
-
-class ShaderFragmentHeaderReadTex : public ShaderPart
-{
-public:
- ShaderFragmentHeaderReadTex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
- {
- }
-
- void write(std::stringstream & shader) const override
- {
- std::string shaderPart;
- if (!m_glinfo.isGLES2) {
-
- if (g_textureConvert.useTextureFiltering()) {
- shaderPart += "uniform lowp int uTextureFilterMode; \n";
- shaderPart += "#define TEX_NEAREST(name, tex, tcData) \\\n"
- "{ \\\n"
- " name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- "} \n"
- ;
- switch (config.texture.bilinearMode + config.texture.enableHalosRemoval * 2) {
- case BILINEAR_3POINT:
- // 3 point texture filtering.
- // Original author: ArthurCarvalho
- // GLSL implementation: twinaphex, mupen64plus-libretro project.
- shaderPart +=
- "#define TEX_FILTER(name, tex, tcData) \\\n"
- " { \\\n"
- " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
- " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
- " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
- " name = c0 + bottomRightTri * (c1-c0); \\\n"
- " } \n"
- ;
- break;
- case BILINEAR_STANDARD:
- shaderPart +=
- "#define TEX_FILTER(name, tex, tcData) \\\n"
- "{ \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
- " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
- " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
- " name = c0 + tcData[4].t * (c1-c0); \\\n"
- "} \n"
- ;
- break;
- case BILINEAR_3POINT_WITH_COLOR_BLEEDING:
- // 3 point texture filtering.
- // Original author: ArthurCarvalho
- // GLSL implementation: twinaphex, mupen64plus-libretro project.
- shaderPart +=
- "#define TEX_FILTER(name, tex, tcData) \\\n"
- "{ \\\n"
- " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
- " if(uEnableAlphaTest == 1 ){ \\\n" // Calculate premultiplied color values
- " c00.rgb *= c00.a; \\\n"
- " c01.rgb *= c01.a; \\\n"
- " c10.rgb *= c10.a; \\\n"
- " c11.rgb *= c11.a; \\\n"
- " } \\\n"
- " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
- " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
- " name = c0 + bottomRightTri * (c1-c0); \\\n"
- " if(uEnableAlphaTest == 1 ) name.rgb /= name.a; \\\n" // Divide alpha to get actual color value
- "} \n"
- ;
- break;
- case BILINEAR_STANDARD_WITH_COLOR_BLEEDING_AND_PREMULTIPLIED_ALPHA:
- shaderPart +=
- "#define TEX_FILTER(name, tex, tcData) \\\n"
- "{ \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
- " if(uEnableAlphaTest == 1){ \\\n" // Calculate premultiplied color values
- " c00.rgb *= c00.a; \\\n"
- " c01.rgb *= c01.a; \\\n"
- " c10.rgb *= c10.a; \\\n"
- " c11.rgb *= c11.a; \\\n"
- " } \\\n"
- " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
- " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
- " name = c0 + tcData[4].t * (c1-c0); \\\n"
- " if(uEnableAlphaTest == 1) name.rgb /= name.a; \\\n"
- "} \n"
- ;
- break;
- }
- shaderPart +=
- "#define READ_TEX(name, tex, tcData, fbMonochrome, fbFixedAlpha) \\\n"
- " { \\\n"
- " if (fbMonochrome == 3) { \\\n"
- " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
- " name = texelFetch(tex, coord, 0); \\\n"
- " } else { \\\n"
- " if (uTextureFilterMode == 0) \\\n"
- " { \\\n"
- " TEX_NEAREST(name, tex, tcData); \\\n"
- " } \\\n"
- " else TEX_FILTER(name, tex, tcData); \\\n"
- " } \\\n"
- " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
- " else if (fbMonochrome == 2) \\\n"
- " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
- " else if (fbMonochrome == 3) { \\\n"
- " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
- " name.a = 0.0; \\\n"
- " } \\\n"
- " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
- " } \n"
- ;
- }
-
- if (g_textureConvert.useYUVCoversion()) {
- shaderPart +=
- "uniform lowp ivec2 uTextureFormat; \n"
- "uniform lowp int uTextureConvert; \n"
- "uniform mediump ivec4 uConvertParams; \n"
- "#define YUVCONVERT(name, format) \\\n"
- " mediump ivec4 icolor = ivec4(name*255.0); \\\n"
- " if (format == 1) \\\n"
- " icolor.rg -= 128; \\\n"
- " mediump ivec4 iconvert; \\\n"
- " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \\\n"
- " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \\\n"
- " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \\\n"
- " iconvert.a = icolor.b; \\\n"
- " name = vec4(iconvert)/255.0; \n"
- "#define YUVCONVERT_TEX0(name, tex, tcData, format) \\\n"
- " { \\\n"
- " name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " YUVCONVERT(name, format) \\\n"
- " } \n"
- "#define YUVCONVERT_TEX1(name, tex, tcData, format, prev) \\\n"
- " { \\\n"
- " if (uTextureConvert != 0) name = prev; \\\n"
- " else name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " YUVCONVERT(name, format) \\\n"
- " } \n"
- ;
- }
-
- } else {
- if (g_textureConvert.useTextureFiltering()) {
- shaderPart +=
- "uniform lowp int uTextureFilterMode; \n"
- "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
- ;
- }
- if (g_textureConvert.useYUVCoversion()) {
- shaderPart +=
- "uniform lowp ivec2 uTextureFormat; \n"
- "uniform lowp int uTextureConvert; \n"
- "uniform mediump ivec4 uConvertParams; \n"
- "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 tcData[5], in lowp int convert, in lowp int format, in lowp vec4 prev); \n"
- ;
- }
- }
-
- shader << shaderPart;
- }
-
-private:
- const opengl::GLInfo& m_glinfo;
-};
-
-class ShaderFragmentHeaderReadTexCopyMode : public ShaderPart
-{
-public:
- ShaderFragmentHeaderReadTexCopyMode (const opengl::GLInfo & _glinfo)
- {
- if (!_glinfo.isGLES2) {
- m_part =
- "#define READ_TEX(name, tex, tcData, fbMonochrome, fbFixedAlpha) \\\n"
- " { \\\n"
- " if (fbMonochrome == 3) { \\\n"
- " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
- " name = texelFetch(tex, coord, 0); \\\n"
- " } else { \\\n"
- " name = texelFetch(tex, ivec2(tcData[0]),0); \\\n"
- " } \\\n"
- " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
- " else if (fbMonochrome == 2) \\\n"
- " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
- " else if (fbMonochrome == 3) { \\\n"
- " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
- " name.a = 0.0; \\\n"
- " } \\\n"
- " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
- " } \n"
- ;
- } else {
- m_part =
- "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
- ;
- }
- }
-};
-
-class ShaderFragmentMain : public ShaderPart
-{
-public:
- ShaderFragmentMain(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "void main() \n"
- "{ \n"
- ;
- if (!_glinfo.isGLES2) {
- m_part +=
- " highp float fragDepth = writeDepth(); \n"
- ;
- }
- m_part +=
- " lowp vec4 vec_color; \n"
- " lowp float alpha1; \n"
- " lowp vec3 color1, input_color; \n"
- ;
- if (config.generalEmulation.enableClipping != 0)
- m_part +=
- " lowp vec4 shadeColor = vShadeColorNoperspective; \n"
- ;
- else
- m_part +=
- " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n"
- ;
-
- m_part += "#define WRAP(x, low, high) (mod((x)-(low), (high)-(low)) + (low)) \n"; // Return wrapped value of x in interval [low, high)
- // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible?
- // m_part += "#define WRAP(x, low, high) (x) + ((high)-(low)) * (1.0-step(low,x)) - ((high)-(low)) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code.
- }
-
-};
-
-class ShaderFragmentMain2Cycle : public ShaderPart
-{
-public:
- ShaderFragmentMain2Cycle(const opengl::GLInfo & _glinfo)
- {
- m_part =
- "void main() \n"
- "{ \n"
- ;
- if (!_glinfo.isGLES2) {
- m_part +=
- " highp float fragDepth = writeDepth(); \n"
- ;
- }
- m_part +=
- " lowp vec4 vec_color, combined_color; \n"
- " lowp float alpha1, alpha2; \n"
- " lowp vec3 color1, color2, input_color; \n"
- ;
- if (config.generalEmulation.enableClipping != 0)
- m_part +=
- " lowp vec4 shadeColor = vShadeColorNoperspective; \n"
- ;
- else
- m_part +=
- " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n"
- ;
-
- m_part += "#define WRAP(x, low, high) mod((x)-(low), (high)-(low)) + (low) \n"; // Return wrapped value of x in interval [low, high)
- // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible?
- // m_part += "#define WRAP(x, low, high) (x) + (2.0) * (1.0-step(low,x)) - (2.0) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code.
- }
-};
-
-class ShaderFragmentBlendMux : public ShaderPart
-{
-public:
- ShaderFragmentBlendMux(const opengl::GLInfo & _glinfo)
- {
- if (config.generalEmulation.enableLegacyBlending == 0) {
- m_part =
- " #define MUXA(pos) dot(muxA, STVEC(pos)) \n"
- " #define MUXB(pos) dot(muxB, STVEC(pos)) \n"
- " #define MUXPM(pos) muxPM*(STVEC(pos)) \n"
- " #define MUXF(pos) dot(muxF, STVEC(pos)) \n"
- " lowp vec4 lastFragColor = LAST_FRAG_COLOR; \n"
- " lowp float lastFragAlpha = LAST_FRAG_ALPHA; \n"
- " lowp mat4 muxPM = mat4(vec4(0.0), lastFragColor, uBlendColor, uFogColor); \n"
- " lowp vec4 muxA = vec4(0.0, uFogColor.a, shadeColor.a, 0.0); \n"
- " lowp vec4 muxB = vec4(0.0, lastFragAlpha, 1.0, 0.0); \n"
- " lowp vec4 muxF = vec4(0.0, 1.0, 0.0, 0.0); \n"
- " lowp vec4 muxp, muxm, srcColor1, srcColor2; \n"
- " lowp float muxa, muxb, dstFactor1, dstFactor2, muxaf, muxbf; \n"
- ;
- }
- }
-};
-
-class ShaderFragmentReadTexMipmap : public ShaderPart
-{
-public:
- ShaderFragmentReadTexMipmap(const opengl::GLInfo & _glinfo)
- {
- m_part =
- " lowp vec4 readtex0, readtex1; \n"
- " lowp float lod_frac = mipmap(readtex0, readtex1); \n"
- ;
- }
-};
-
-class ShaderFragmentReadTexCopyMode : public ShaderPart
-{
-public:
- ShaderFragmentReadTexCopyMode(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part =
- " nCurrentTile = 0; \n"
- " lowp vec4 readtex0 = readTex(uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n"
- ;
- } else {
- if (config.video.multisampling > 0) {
- m_part =
- " lowp vec4 readtex0; \n"
- " if (uMSTexEnabled[0] == 0) { \n"
- " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
- " } else readtex0 = readTexMS(uMSTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]);\n"
- ;
- } else {
- m_part =
- " lowp vec4 readtex0; \n"
- " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
- ;
- }
- }
- }
-};
-
-class ShaderFragmentReadTex0 : public ShaderPart
-{
-public:
- ShaderFragmentReadTex0(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
- {
- }
-
- void write(std::stringstream & shader) const override
- {
- std::string shaderPart;
-
- if (m_glinfo.isGLES2) {
-
- shaderPart = " nCurrentTile = 0; \n";
- if (g_textureConvert.getBilerp0()) {
- shaderPart += " lowp vec4 readtex0 = readTex(uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
- } else {
- shaderPart += " lowp vec4 tmpTex = vec4(0.0); \n"
- " lowp vec4 readtex0 = YUV_Convert(uTex0, tcData0, 0, uTextureFormat[0], tmpTex); \n";
- }
-
- } else {
-
- if (!g_textureConvert.getBilerp0()) {
- shaderPart = " lowp vec4 readtex0; \n"
- " YUVCONVERT_TEX0(readtex0, uTex0, tcData0, uTextureFormat[0]) \n";
- } else {
- if (config.video.multisampling > 0) {
- shaderPart =
- " lowp vec4 readtex0; \n"
- " if (uMSTexEnabled[0] == 0) { \n"
- " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
- " } else readtex0 = readTexMS(uMSTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
- } else {
- shaderPart = " lowp vec4 readtex0; \n"
- " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n";
- }
- }
-
- }
-
- shader << shaderPart;
- }
-
-private:
- const opengl::GLInfo& m_glinfo;
-};
-
-class ShaderFragmentReadTex1 : public ShaderPart
-{
-public:
- ShaderFragmentReadTex1(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
- {
- }
-
- void write(std::stringstream & shader) const override
- {
- std::string shaderPart;
-
- if (m_glinfo.isGLES2) {
-
- shaderPart = " nCurrentTile = 1; \n";
-
- if (g_textureConvert.getBilerp1()) {
- shaderPart += " lowp vec4 readtex1 = readTex(uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
- } else {
- shaderPart += " lowp vec4 readtex1 = YUV_Convert(uTex1, tcData1, uTextureConvert, uTextureFormat[1], readtex0); \n";
- }
-
- } else {
-
- if (!g_textureConvert.getBilerp1()) {
- shaderPart =
- " lowp vec4 readtex1; \n"
- " YUVCONVERT_TEX1(readtex1, uTex1, tcData1, uTextureFormat[1], readtex0) \n";
- } else {
- if (config.video.multisampling > 0) {
- shaderPart =
- " lowp vec4 readtex1; \n"
- " if (uMSTexEnabled[1] == 0) { \n"
- " READ_TEX(readtex1, uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]) \n"
- " } else readtex1 = readTexMS(uMSTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
- } else {
- shaderPart = " lowp vec4 readtex1; \n"
- " READ_TEX(readtex1, uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]) \n";
-
- }
- }
-
- }
-
- shader << shaderPart;
- }
-
-private:
- const opengl::GLInfo& m_glinfo;
-};
-
-class ShaderFragmentCallN64Depth : public ShaderPart
-{
-public:
- ShaderFragmentCallN64Depth(const opengl::GLInfo & _glinfo)
- {
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part = " bool should_discard = false; \n";
-
- if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
- if (_glinfo.fragment_interlock)
- m_part += " beginInvocationInterlockARB(); \n";
- else if (_glinfo.fragment_interlockNV)
- m_part += " beginInvocationInterlockNV(); \n";
- else if (_glinfo.fragment_ordering)
- m_part += " beginFragmentShaderOrderingINTEL(); \n";
- }
-
- m_part +=
- " if (uRenderTarget != 0) { if (!depth_render(fragColor.r, fragDepth)) should_discard = true; } \n"
- " else if (!depth_compare(fragDepth)) should_discard = true; \n"
- ;
-
- if (_glinfo.imageTextures & !_glinfo.n64DepthWithFbFetch) {
- if (_glinfo.fragment_interlock)
- m_part += " endInvocationInterlockARB(); \n";
- else if (_glinfo.fragment_interlockNV)
- m_part += " endInvocationInterlockNV(); \n";
- }
-
- m_part += " if (should_discard) discard; \n";
-
- }
- }
-};
-
-class ShaderFragmentRenderTarget : public ShaderPart
-{
-public:
- ShaderFragmentRenderTarget(const opengl::GLInfo & _glinfo)
- {
- if (config.generalEmulation.enableFragmentDepthWrite != 0) {
- m_part =
- " if (uRenderTarget != 0) { \n"
- " if (uRenderTarget > 1) { \n"
- " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
- " if (fragDepth >= texelFetch(uDepthTex, coord, 0).r) discard; \n"
- " } \n"
- " fragDepth = fragColor.r; \n"
- " } \n"
- " gl_FragDepth = fragDepth; \n"
- ;
- }
- }
-};
-
-class ShaderFragmentMainEnd : public ShaderPart
-{
-public:
- ShaderFragmentMainEnd(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part =
- " gl_FragColor = fragColor; \n"
- "} \n\n";
- } else {
- m_part =
- "} \n\n"
- ;
- }
- }
-};
-
-class ShaderNoise : public ShaderPart
-{
-public:
- ShaderNoise(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part =
- "uniform sampler2D uTexNoise; \n"
- "lowp float snoise() \n"
- "{ \n"
- " mediump vec2 texSize = vec2(640.0, 580.0); \n"
- " mediump vec2 coord = gl_FragCoord.xy/uScreenScale/texSize; \n"
- " return texture2D(uTexNoise, coord).r; \n"
- "} \n"
- ;
- } else {
- m_part =
- "uniform sampler2D uTexNoise; \n"
- "lowp float snoise() \n"
- "{ \n"
- " ivec2 coord = ivec2(gl_FragCoord.xy/uScreenScale); \n"
- " return texelFetch(uTexNoise, coord, 0).r; \n"
- "} \n"
- ;
- }
- }
-};
-
-class ShaderDither : public ShaderPart
-{
-public:
- ShaderDither(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2)
- return;
-
- if (config.generalEmulation.enableDitheringQuantization != 0) {
- m_part =
- "void quantizeRGB(inout lowp vec3 _color) \n"
- "{ \n"
- " _color.rgb = round(_color.rgb * 32.0)/32.0; \n"
- "} \n"
- "void quantizeA(inout lowp float _alpha) \n"
- "{ \n"
- " _alpha = round(_alpha * 32.0)/32.0; \n"
- "} \n"
- ;
- } else {
- m_part =
- "void quantizeRGB(inout lowp vec3 _color){}\n"
- "void quantizeA(inout lowp float _alpha){}\n"
- ;
- }
-
- m_part +=
- "void colorDither(in lowp vec3 _noise, inout lowp vec3 _color)\n"
- "{ \n"
- " mediump vec3 threshold = 7.0 / 255.0 * (_noise - 0.5); \n"
- " _color = clamp(_color + threshold,0.0,1.0); \n"
- " quantizeRGB(_color); \n"
- "} \n"
- "void alphaDither(in lowp float _noise, inout lowp float _alpha)\n"
- "{ \n"
- " mediump float threshold = 7.0 / 255.0 * (_noise - 0.5); \n"
- " _alpha = clamp(_alpha + threshold,0.0,1.0); \n"
- " quantizeA(_alpha); \n"
- "} \n"
- "lowp vec3 snoiseRGB() \n"
- "{ \n"
- " mediump vec2 texSize = vec2(640.0, 580.0); \n"
- ;
- if (config.generalEmulation.enableHiresNoiseDithering != 0)
- // multiplier for higher res noise effect
- m_part +=
- " lowp float mult = 1.0 + step(2.0, uScreenScale.x); \n";
- else
- m_part +=
- " lowp float mult = 1.0; \n";
- m_part +=
- " mediump vec2 coordR = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n"
- " mediump vec2 coordG = mult * ((gl_FragCoord.xy + vec2( 0.0, texSize.y / 2.0 ))/uScreenScale/texSize);\n"
- " mediump vec2 coordB = mult * ((gl_FragCoord.xy + vec2( texSize.x / 2.0, 0.0))/uScreenScale/texSize);\n"
- // Only red channel of noise texture contains noise.
- " lowp float r = texture(uTexNoise,coordR).r; \n"
- " lowp float g = texture(uTexNoise,coordG).r; \n"
- " lowp float b = texture(uTexNoise,coordB).r; \n"
- " \n"
- " return vec3(r,g,b); \n"
- "} \n"
- "lowp float snoiseA() \n"
- "{ \n"
- " mediump vec2 texSize = vec2(640.0, 580.0); \n"
- ;
- if (config.generalEmulation.enableHiresNoiseDithering != 0)
- // multiplier for higher res noise effect
- m_part +=
- " lowp float mult = 1.0 + step(2.0, uScreenScale.x); \n";
- else
- m_part +=
- " lowp float mult = 1.0; \n";
- m_part +=
- " \n"
- " mediump vec2 coord = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n"
- " \n"
- // Only red channel of noise texture contains noise.
- " return texture(uTexNoise,coord).r; \n"
- "} \n"
- ;
- }
-};
-
-class ShaderWriteDepth : public ShaderPart
-{
-public:
- ShaderWriteDepth(const opengl::GLInfo & _glinfo)
- {
- if (!_glinfo.isGLES2) {
- if (config.generalEmulation.enableFragmentDepthWrite == 0 &&
- config.frameBufferEmulation.N64DepthCompare == Config::dcDisable) {
- // Dummy write depth
- m_part =
- "highp float writeDepth() \n"
- "{ \n"
- " return 0.0; \n"
- "} \n"
- ;
- } else {
- if ((config.generalEmulation.hacks & hack_RE2) != 0) {
- m_part =
- "uniform lowp usampler2D uZlutImage;\n"
- "highp float writeDepth() \n"
- "{ \n"
- ;
- if (_glinfo.isGLESX) {
- if (_glinfo.noPerspective) {
- m_part +=
- " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n"
- " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
- " clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- ;
- } else if (config.generalEmulation.enableClipping != 0) {
- m_part +=
- " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
- " clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- ;
- } else {
- m_part +=
- " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
- " clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- ;
- }
- } else {
- m_part +=
- " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
- " clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- ;
- }
- m_part +=
- " highp int iZ = FragDepth > 0.999 ? 262143 : int(floor(FragDepth * 262143.0)); \n"
- " mediump int y0 = clamp(iZ/512, 0, 511); \n"
- " mediump int x0 = iZ - 512*y0; \n"
- " highp uint iN64z = texelFetch(uZlutImage,ivec2(x0,y0), 0).r; \n"
- " return clamp(float(iN64z)/65532.0, 0.0, 1.0); \n"
- "} \n"
- ;
- } else {
- if (_glinfo.isGLESX) {
- if (_glinfo.noPerspective) {
- m_part =
- "highp float writeDepth() \n"
- "{ \n"
- " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n"
- " if (uDepthSource != 0) return uPrimDepth; \n"
- " return clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- "} \n"
- ;
- } else if (config.generalEmulation.enableClipping != 0) {
- m_part =
- "highp float writeDepth() \n"
- "{ \n"
- " if (uDepthSource != 0) return uPrimDepth; \n"
- " return clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- "} \n"
- ;
- } else {
- m_part =
- "highp float writeDepth() \n"
- "{ \n"
- " if (uDepthSource != 0) return uPrimDepth; \n"
- " return clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- "} \n"
- ;
- }
- } else {
- m_part =
- "highp float writeDepth() \n"
- "{ \n"
- " if (uDepthSource != 0) return uPrimDepth; \n"
- " return clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
- "} \n"
- ;
- }
- }
- }
- }
- }
-};
-
-class ShaderMipmap : public ShaderPart
-{
-public:
- ShaderMipmap(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- static const std::string strReadTex0 =
- "lowp vec4 TextureMipMap0(in sampler2D tex, in highp vec2 tcData[5]) \n"
- "{ \n"
- " mediump vec2 texSize = uTextureSize[0]; \n"
- " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
- " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
- " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
- " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
- ;
- static const std::string strReadTex1 =
- "lowp vec4 TextureMipMap1(in sampler2D tex, in highp vec2 tcData[5], in lowp float lod) \n"
- "{ \n"
- // Fetch from texture atlas
- // First 8 texels contain info about tile size and offset, 1 texel per tile
- " mediump vec2 texSize = uTextureSize[1]; \n"
- " mediump vec4 texWdthAndOff0 = 255.0 * texture2D(tex, vec2(0.5, 0.5)/texSize); \n"
- " mediump vec4 texWdthAndOff = 255.0 * texture2D(tex, vec2(lod + 0.5, 0.5)/texSize); \n"
- " mediump vec2 lod_scale = texWdthAndOff.ba / texWdthAndOff0.ba; \n"
- " mediump float offset = texWdthAndOff.r + texWdthAndOff.g * 256.0; \n"
- " mediump float width = texWdthAndOff.b; \n"
- " mediump vec2 Coords00 = floor(tcData[0] * lod_scale); \n"
- " mediump float offset00 = offset + width * Coords00.t + Coords00.s; \n"
- " mediump float Y00 = floor(offset00 / mipmapTileWidth); \n"
- " lowp vec4 c00 = texture2D(tex, (vec2(offset00 - mipmapTileWidth * Y00, Y00) + 0.5)/texSize); \n"
- " mediump vec2 Coords01 = floor(tcData[1] * lod_scale); \n"
- " mediump float offset01 = offset + width * Coords01.t + Coords01.s; \n"
- " mediump float Y01 = floor(offset01 / mipmapTileWidth); \n"
- " lowp vec4 c01 = texture2D(tex, (vec2(offset01 - mipmapTileWidth * Y01, Y01) + 0.5)/texSize); \n"
- " mediump vec2 Coords10 = floor(tcData[2] * lod_scale); \n"
- " mediump float offset10 = offset + width * Coords10.t + Coords10.s; \n"
- " mediump float Y10 = floor(offset10 / mipmapTileWidth); \n"
- " lowp vec4 c10 = texture2D(tex, (vec2(offset10 - mipmapTileWidth * Y10, Y10) + 0.5)/texSize); \n"
- " mediump vec2 Coords11 = floor(tcData[3] * lod_scale); \n"
- " mediump float offset11 = offset + width * Coords11.t + Coords11.s; \n"
- " mediump float Y11 = floor(offset11 / mipmapTileWidth); \n"
- " lowp vec4 c11 = texture2D(tex, (vec2(offset11 - mipmapTileWidth * Y11, Y11) + 0.5)/texSize); \n"
- ;
- static const std::string strBillinear3PointEndGles2 =
- " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \n"
- " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \n"
- " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \n"
- " return c0 + bottomRightTri * (c1-c0); \n"
- " return c00; \n"
- "} \n"
- ;
- static const std::string strBillinearStandardEndGles2 =
- " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \n"
- " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \n"
- " return c0 + tcData[4].t * (c1-c0); \n"
- " return c00; \n"
- "} \n"
- ;
-
- if (config.texture.bilinearMode == BILINEAR_3POINT) {
- m_part = strReadTex0;
- m_part += strBillinear3PointEndGles2;
- m_part += strReadTex1;
- m_part += strBillinear3PointEndGles2;
- } else {
- m_part = strReadTex0;
- m_part += strBillinearStandardEndGles2;
- m_part += strReadTex1;
- m_part += strBillinearStandardEndGles2;
- }
-
- m_part +=
- "uniform lowp int uEnableLod; \n"
- "uniform mediump float uMinLod; \n"
- "uniform lowp int uMaxTile; \n"
- "uniform lowp int uTextureDetail; \n"
- " \n"
- "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
- ;
-
- if (config.generalEmulation.enableLOD == 0) {
- m_part +=
- " mediump float lod = 1.0; \n"
- ;
- } else {
- m_part +=
- " mediump vec2 dx = abs(dFdx(vLodTexCoord)) * uScreenScale; \n"
- " mediump vec2 dy = abs(dFdy(vLodTexCoord)) * uScreenScale; \n"
- " mediump float lod = max(max(dx.x, dx.y), max(dy.x, dy.y)); \n"
- ;
- }
- m_part +=
- "#define MIN(x, y) y < x ? y : x \n"
- "#define MAX(x, y) x < y ? y : x \n"
- " lowp int lod_max_tile = uTextureDetail != 2 ? 7 : 6; \n"
- " lowp int max_tile = MIN(lod_max_tile, uMaxTile); \n"
- " mediump float min_lod = uTextureDetail != 0 ? uMinLod : 1.0; \n"
- " mediump float max_lod = pow(2.0, float(max_tile)) - 1.0 / 32.0; \n"
- " mediump float lod_clamp = min(max(lod, min_lod), max_lod); \n"
- // Simulate clamp function, needed for GLES 2.0 and integer types
- " mediump int lod_clamp_int = int(log2(lod_clamp)); \n"
- " mediump int lod_clamp_max = MAX(lod_clamp_int, 0); \n"
- " lowp int lod_tile = MIN(lod_clamp_max, max_tile); \n"
- " lowp int tile0 = 0; \n"
- " lowp int tile1 = 1; \n"
- " if (uEnableLod != 0) { \n"
- " if (lod_clamp < 1.0 && uTextureDetail == 0) { \n"
- " tile0 = 0; \n"
- " tile1 = 0; \n"
- " } else if (lod_clamp >= 1.0 && uTextureDetail == 2) { \n"
- " tile0 = lod_tile + 1; \n"
- " tile1 = lod_tile + 2; \n"
- " } else { \n"
- " tile0 = lod_tile; \n"
- " tile1 = lod_tile + 1; \n"
- " } \n"
- " } \n"
- " mediump float lod_frac = lod_clamp / pow(2.0, float(lod_tile)); \n"
- " if (uTextureDetail == 1 || lod_clamp >= 1.0) { \n"
- " lod_frac = clamp(lod_frac - 1.0, -1.0, 1.0); \n"
- " } \n"
- " \n"
- " if (tile0 == 0) \n"
- " readtex0 = TextureMipMap0(uTex0, tcData0); \n"
- " else if (uEnableLod == 0 || uMaxTile == 1) \n"
- " readtex0 = TextureMipMap0(uTex1, tcData1); \n"
- " else readtex0 = TextureMipMap1(uTex1, tcData1, float(tile0 - 1)); \n"
- " if (tile1 == 0) \n"
- " readtex1 = TextureMipMap0(uTex0, tcData0); \n"
- " else if (uEnableLod == 0 || uMaxTile == 1) \n"
- " readtex1 = TextureMipMap0(uTex1, tcData1); \n"
- " else readtex1 = TextureMipMap1(uTex1, tcData1, float(tile1 - 1)); \n"
- " return lod_frac; \n"
- "} \n"
- ;
- }
- else {
- static const std::string strReadTex0 =
- "#define READ_TEX0_MIPMAP(name, tex, tcData) \\\n"
- "{ \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
- ;
- static const std::string strReadTex1 =
- "#define READ_TEX1_MIPMAP(name, tex, tcData, tile) \\\n"
- "{ \\\n"
- // Fetch from texture atlas
- // First 8 texels contain info about tile size and offset, 1 texel per tile
- " mediump vec4 texWdthAndOff0 = 255.0 * texelFetch(tex, ivec2(0, 0), 0); \\\n"
- " mediump vec4 texWdthAndOff = 255.0 * texelFetch(tex, ivec2(int(tile), 0), 0); \\\n"
- " mediump vec2 lod_scale = texWdthAndOff.ba / texWdthAndOff0.ba; \\\n"
- " mediump int offset = int(texWdthAndOff.r) + int(texWdthAndOff.g) * 256; \\\n"
- " mediump int width = int(texWdthAndOff.b); \\\n"
- " mediump ivec2 iCoords00 = ivec2(tcData[0] * lod_scale); \\\n"
- " mediump int offset00 = offset + width * iCoords00.t + iCoords00.s; \\\n"
- " mediump int Y00 = offset00/mipmapTileWidth; \\\n"
- " lowp vec4 c00 = texelFetch(tex, ivec2(offset00 - mipmapTileWidth * Y00, Y00), 0); \\\n"
- " mediump ivec2 iCoords01 = ivec2(tcData[1] * lod_scale); \\\n"
- " mediump int offset01 = offset + width * iCoords01.t + iCoords01.s; \\\n"
- " mediump int Y01 = offset01/mipmapTileWidth; \\\n"
- " lowp vec4 c01 = texelFetch(tex, ivec2(offset01 - mipmapTileWidth * Y01, Y01), 0); \\\n"
- " mediump ivec2 iCoords10 = ivec2(tcData[2] * lod_scale); \\\n"
- " mediump int offset10 = offset + width * iCoords10.t + iCoords10.s; \\\n"
- " mediump int Y10 = offset10/mipmapTileWidth; \\\n"
- " lowp vec4 c10 = texelFetch(tex, ivec2(offset10 - mipmapTileWidth * Y10, Y10), 0); \\\n"
- " mediump ivec2 iCoords11 = ivec2(tcData[3] * lod_scale); \\\n"
- " mediump int offset11 = offset + width * iCoords11.t + iCoords11.s; \\\n"
- " mediump int Y11 = offset11/mipmapTileWidth; \\\n"
- " lowp vec4 c11 = texelFetch(tex, ivec2(offset11 - mipmapTileWidth * Y11, Y11), 0); \\\n"
- ;
- static const std::string strBillinear3PointEnd =
- " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
- " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
- " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
- " name = c0 + bottomRightTri * (c1-c0); \\\n"
- "} \n"
- ;
- static const std::string strBillinearStandardEnd =
- " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
- " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
- " name = c0 + tcData[4].t * (c1-c0); \\\n"
- "} \n"
- ;
- if (config.texture.bilinearMode == BILINEAR_3POINT) {
- m_part = strReadTex0;
- m_part += strBillinear3PointEnd;
- m_part += strReadTex1;
- m_part += strBillinear3PointEnd;
- } else {
- m_part = strReadTex0;
- m_part += strBillinearStandardEnd;
- m_part += strReadTex1;
- m_part += strBillinearStandardEnd;
- }
- m_part +=
- "uniform lowp int uEnableLod; \n"
- "uniform mediump float uMinLod; \n"
- "uniform lowp int uMaxTile; \n"
- "uniform lowp int uTextureDetail; \n"
- " \n"
- "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
- ;
- if (config.generalEmulation.enableLOD == 0) {
- m_part +=
- " mediump float lod = 1.0; \n"
- ;
- } else {
- m_part +=
- " mediump vec2 dx = abs(dFdx(vLodTexCoord)) * uScreenScale; \n"
- " mediump vec2 dy = abs(dFdy(vLodTexCoord)) * uScreenScale; \n"
- " mediump float lod = max(max(dx.x, dx.y), max(dy.x, dy.y)); \n"
- ;
- }
- m_part +=
- " lowp int max_tile = min(uTextureDetail != 2 ? 7 : 6, uMaxTile); \n"
- " mediump float min_lod = uTextureDetail != 0 ? uMinLod : 1.0; \n"
- " mediump float max_lod = pow(2.0, float(max_tile)) - 1.0 / 32.0; \n"
- " mediump float lod_clamp = min(max(lod, min_lod), max_lod); \n"
- " lowp int lod_tile = clamp(int(log2(lod_clamp)), 0 , max_tile); \n"
- " lowp int tile0 = 0; \n"
- " lowp int tile1 = 1; \n"
- " if (uEnableLod != 0) { \n"
- " if (lod_clamp < 1.0 && uTextureDetail == 0) { \n"
- " tile0 = 0; \n"
- " tile1 = 0; \n"
- " } else if (lod_clamp >= 1.0 && uTextureDetail == 2) { \n"
- " tile0 = lod_tile + 1; \n"
- " tile1 = lod_tile + 2; \n"
- " } else { \n"
- " tile0 = lod_tile; \n"
- " tile1 = lod_tile + 1; \n"
- " } \n"
- " } \n"
- " mediump float lod_frac = lod_clamp / pow(2.0, float(lod_tile)); \n"
- " if (uTextureDetail == 1 || lod_clamp >= 1.0) { \n"
- " lod_frac = clamp(lod_frac - 1.0, -1.0, 1.0); \n"
- " } \n"
- " \n"
- " if(tile0 == 0) {READ_TEX0_MIPMAP(readtex0, uTex0, tcData0);} \n"
- " else if (uEnableLod == 0 || uMaxTile == 1) {READ_TEX0_MIPMAP(readtex0, uTex1, tcData1);}\n"
- " else {READ_TEX1_MIPMAP(readtex0, uTex1, tcData1, tile0 - 1);} \n"
- " if(tile1 == 0) {READ_TEX0_MIPMAP(readtex1, uTex0, tcData0);} \n"
- " else if (uEnableLod == 0 || uMaxTile == 1) {READ_TEX0_MIPMAP(readtex1, uTex1, tcData1);}\n"
- " else {READ_TEX1_MIPMAP(readtex1, uTex1, tcData1, tile1 - 1);} \n"
- " return lod_frac; \n"
- "} \n"
- ;
- }
- }
-};
-
-class ShaderCalcLight : public ShaderPart
-{
-public:
- ShaderCalcLight(const opengl::GLInfo & /*_glinfo*/)
- {
- m_part =
- "uniform mediump vec3 uLightDirection[8]; \n"
- "uniform lowp vec3 uLightColor[8]; \n"
- "void calc_light(in lowp float fLights, in lowp vec3 input_color, out lowp vec3 output_color) {\n"
- " output_color = input_color; \n"
- " lowp int nLights = int(floor(fLights + 0.5)); \n"
- " if (nLights == 0) \n"
- " return; \n"
- " output_color = uLightColor[nLights]; \n"
- " mediump float intensity; \n"
- " for (int i = 0; i < nLights; i++) { \n"
- " intensity = max(dot(input_color, uLightDirection[i]), 0.0);\n"
- " output_color += intensity*uLightColor[i]; \n"
- " }; \n"
- " output_color = clamp(output_color, 0.0, 1.0); \n"
- "} \n"
- ;
- }
-};
-
-class ShaderReadtex : public ShaderPart
-{
-public:
- ShaderReadtex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
- {
- }
-
- void write(std::stringstream & shader) const override
- {
- std::string shaderPart;
-
- if (m_glinfo.isGLES2) {
- shaderPart +=
- "lowp vec4 TextureNearest(in sampler2D tex, in highp vec2 tcData[5]) \n"
- "{ \n"
- " mediump vec2 texSize; \n"
- " if (nCurrentTile == 0) \n"
- " texSize = uTextureSize[0]; \n"
- " else \n"
- " texSize = uTextureSize[1]; \n"
- " return texture2D(tex, (tcData[0] + 0.5) / texSize); \n"
- " } \n"
- ;
- if (g_textureConvert.useYUVCoversion())
- shaderPart +=
- "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 tcData[5], in lowp int convert, in lowp int format, in lowp vec4 prev) \n"
- "{ \n"
- " lowp vec4 texColor; \n"
- " if (convert != 0) texColor = prev; \n"
- " else texColor = TextureNearest(tex, tcData); \n"
- " mediump ivec4 icolor = ivec4(texColor*255.0); \n"
- " if (format == 1) \n"
- " icolor.rg -= 128; \n"
- " mediump ivec4 iconvert; \n"
- " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \n"
- " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \n"
- " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \n"
- " iconvert.a = icolor.b; \n"
- " return vec4(iconvert)/255.0; \n"
- " } \n"
- ;
- if (g_textureConvert.useTextureFiltering()) {
- if (config.texture.bilinearMode == BILINEAR_3POINT) {
- shaderPart +=
- // 3 point texture filtering.
- // Original author: ArthurCarvalho
- // GLSL implementation: twinaphex, mupen64plus-libretro project.
- "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 tcData[5]) \n"
- "{ \n"
- " mediump vec2 texSize; \n"
- " if (nCurrentTile == 0) \n"
- " texSize = uTextureSize[0]; \n"
- " else \n"
- " texSize = uTextureSize[1]; \n"
- " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \n"
- " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
- " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
- " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
- " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
- " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \n"
- " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \n"
- " return c0 + bottomRightTri * (c1-c0); \n"
- " } \n"
- ;
- } else {
- shaderPart +=
- // bilinear filtering.
- "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 tcData[5]) \n"
- "{ \n"
- " mediump vec2 texSize; \n"
- " if (nCurrentTile == 0) \n"
- " texSize = uTextureSize[0]; \n"
- " else \n"
- " texSize = uTextureSize[1]; \n"
- " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
- " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
- " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
- " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
- " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \n"
- " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \n"
- " return c0 + tcData[4].t * (c1-c0); \n"
- " } \n"
- ;
- }
- shaderPart +=
- "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
- "{ \n"
- " lowp vec4 texColor; \n"
- " if (uTextureFilterMode == 0) texColor = TextureNearest(tex, tcData); \n"
- " else texColor = TextureFilter(tex, tcData); \n"
- " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
- " else if (fbMonochrome == 2) \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
- " return texColor; \n"
- "} \n"
- ;
- }
- } else {
- if (config.video.multisampling > 0 && g_textureConvert.useTextureFiltering()) {
- shaderPart =
- "uniform lowp int uMSAASamples; \n"
- "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
- "{ \n"
- " lowp vec4 texel = vec4(0.0); \n"
- " for (int i = 0; i < uMSAASamples; ++i) \n"
- " texel += texelFetch(mstex, ipos, i); \n"
- " return texel / float(uMSAASamples); \n"
- "} \n"
- " \n"
- "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
- "{ \n"
- " mediump ivec2 itexCoord; \n"
- " if (fbMonochrome == 3) { \n"
- " itexCoord = ivec2(gl_FragCoord.xy); \n"
- " } else { \n"
- " itexCoord = ivec2(tcData[0]); \n"
- " } \n"
- " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
- " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
- " else if (fbMonochrome == 2) \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " else if (fbMonochrome == 3) { \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " texColor.a = 0.0; \n"
- " } \n"
- " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
- " return texColor; \n"
- "} \n"
- ;
- }
- }
-
- shader << shaderPart;
- }
-
-private:
- const opengl::GLInfo& m_glinfo;
-};
-
-class ShaderReadtexCopyMode : public ShaderPart
-{
-public:
- ShaderReadtexCopyMode(const opengl::GLInfo & _glinfo)
- {
- if (_glinfo.isGLES2) {
- m_part =
- "lowp vec4 TextureNearest(in sampler2D tex, in highp vec2 tcData[5]) \n"
- "{ \n"
- " mediump vec2 texSize; \n"
- " if (nCurrentTile == 0) \n"
- " texSize = uTextureSize[0]; \n"
- " else \n"
- " texSize = uTextureSize[1]; \n"
- " return texture2D(tex, (tcData[0] + 0.5) / texSize); \n"
- " } \n"
- "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
- "{ \n"
- " lowp vec4 texColor = TextureNearest(tex, tcData); \n"
- " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
- " else if (fbMonochrome == 2) \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
- " return texColor; \n"
- "} \n"
- ;
- } else {
- if (config.video.multisampling > 0) {
- m_part =
- "uniform lowp int uMSAASamples; \n"
- "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
- "{ \n"
- " lowp vec4 texel = vec4(0.0); \n"
- " for (int i = 0; i < uMSAASamples; ++i) \n"
- " texel += texelFetch(mstex, ipos, i); \n"
- " return texel / float(uMSAASamples); \n"
- "} \n"
- " \n"
- "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
- "{ \n"
- " mediump ivec2 itexCoord; \n"
- " if (fbMonochrome == 3) { \n"
- " itexCoord = ivec2(gl_FragCoord.xy); \n"
- " } else { \n"
- " itexCoord = ivec2(tcData[0]); \n"
- " } \n"
- " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
- " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
- " else if (fbMonochrome == 2) \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " else if (fbMonochrome == 3) { \n"
- " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
- " texColor.a = 0.0; \n"
- " } \n"
- " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
- " return texColor; \n"
- "} \n"
- ;
- }
- }
- }
-};
-
-class ShaderN64DepthCompare : public ShaderPart
-{
-public:
- ShaderN64DepthCompare(const opengl::GLInfo & _glinfo)
- {
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part =
- "uniform lowp int uEnableDepth; \n"
- "uniform lowp int uDepthMode; \n"
- "uniform lowp int uEnableDepthUpdate; \n"
- "uniform mediump float uDeltaZ; \n"
- "bool depth_compare(highp float curZ) \n"
- "{ \n"
- " if (uEnableDepth == 0) return true; \n"
- ;
- if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
- m_part +=
- " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
- " highp vec4 depthZ = imageLoad(uDepthImageZ,coord); \n"
- " highp vec4 depthDeltaZ = imageLoad(uDepthImageDeltaZ,coord);\n"
- ;
- }
- m_part +=
- " highp float bufZ = depthZ.r; \n"
- " highp float dz, dzMin; \n"
- " if (uDepthSource == 1) { \n"
- " dzMin = dz = uDeltaZ; \n"
- " } else { \n"
- " dz = 4.0*fwidth(curZ); \n"
- " dzMin = min(dz, depthDeltaZ.r); \n"
- " } \n"
- " bool bInfront = curZ < bufZ; \n"
- " bool bFarther = (curZ + dzMin) >= bufZ; \n"
- " bool bNearer = (curZ - dzMin) <= bufZ; \n"
- " bool bMax = bufZ == 1.0; \n"
- " bool bRes = false; \n"
- " switch (uDepthMode) { \n"
- " case 1: \n"
- " bRes = bMax || bNearer; \n"
- " break; \n"
- " case 0: \n"
- " case 2: \n"
- " bRes = bMax || bInfront; \n"
- " break; \n"
- " case 3: \n"
- " bRes = bFarther && bNearer && !bMax; \n"
- " break; \n"
- " } \n"
- " bRes = bRes || (uEnableDepthCompare == 0); \n"
- " if (uEnableDepthUpdate != 0 && bRes) { \n"
- ;
- if (_glinfo.n64DepthWithFbFetch) {
- m_part +=
- " depthZ.r = curZ; \n"
- " depthDeltaZ.r = dz; \n"
- ;
- } else if (_glinfo.imageTextures) {
- m_part +=
- " highp vec4 depthOutZ = vec4(curZ, 1.0, 1.0, 1.0); \n"
- " highp vec4 depthOutDeltaZ = vec4(dz, 1.0, 1.0, 1.0); \n"
- " imageStore(uDepthImageZ, coord, depthOutZ); \n"
- " imageStore(uDepthImageDeltaZ, coord, depthOutDeltaZ); \n"
- ;
- }
-
- m_part +=
- " } \n"
- " return bRes; \n"
- "} \n"
- ;
- }
- }
-};
-
-class ShaderN64DepthRender : public ShaderPart
-{
-public:
- ShaderN64DepthRender(const opengl::GLInfo & _glinfo)
- {
- if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
- m_part =
- "bool depth_render(highp float Z, highp float curZ) \n"
- "{ \n"
- " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
- " if (uEnableDepthCompare != 0) { \n"
- ;
- if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
- m_part +=
- " highp vec4 depthZ = imageLoad(uDepthImageZ,coord); \n"
- ;
- }
- m_part +=
- " highp float bufZ = depthZ.r; \n"
- " if (curZ >= bufZ) return false; \n"
- " } \n"
- ;
- if (_glinfo.n64DepthWithFbFetch) {
- m_part +=
- " depthZ.r = Z; \n"
- " depthDeltaZ.r = 0.0; \n"
- ;
- } else if (_glinfo.imageTextures) {
- m_part +=
- " highp vec4 depthOutZ = vec4(Z, 1.0, 1.0, 1.0); \n"
- " highp vec4 depthOutDeltaZ = vec4(0.0, 1.0, 1.0, 1.0);\n"
- " imageStore(uDepthImageZ,coord, depthOutZ); \n"
- " imageStore(uDepthImageDeltaZ,coord, depthOutDeltaZ); \n"
- ;
- }
-
- m_part +=
- " return true; \n"
- "} \n"
- ;
- }
- }
-};
-
-
-class ShaderFragmentCorrectTexCoords : public ShaderPart {
-public:
- ShaderFragmentCorrectTexCoords() {
- m_part +=
- " highp vec2 mTexCoord0 = vTexCoord0 + vec2(0.0001); \n"
- " highp vec2 mTexCoord1 = vTexCoord1 + vec2(0.0001); \n"
- " mTexCoord0 += uTexCoordOffset[0]; \n"
- " mTexCoord1 += uTexCoordOffset[1]; \n"
- " if (uUseTexCoordBounds != 0) { \n"
- " mTexCoord0 = clamp(mTexCoord0, uTexCoordBounds0.xy, uTexCoordBounds0.zw); \n"
- " mTexCoord1 = clamp(mTexCoord1, uTexCoordBounds1.xy, uTexCoordBounds1.zw); \n"
- " } \n"
- ;
- }
-};
-
-
-class ShaderTextureEngine : public ShaderPart
-{
-public:
- ShaderTextureEngine(const opengl::GLInfo _glinfo) {
- m_part =
- "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, in highp vec2 vWrap, \n"
- " in highp vec2 vClamp, in lowp vec2 vWrapEn, in lowp vec2 vClampEn, in lowp vec2 vMirrorEn) \n"
- "{ \n"
- " highp vec2 texCoord = vTexCoord; \n"
- " highp vec2 clampedCoord = clamp(texCoord, vec2(0.0), vClamp); \n"
- " texCoord += vClampEn*(clampedCoord-texCoord); \n"
- " lowp vec2 needMirror = step(vWrap, mod(texCoord, 2.0*vWrap)); \n"
- " highp vec2 invertedCoord = mod(-texCoord-vec2(1.0), vWrap); \n"
- " texCoord += vMirrorEn*needMirror*(invertedCoord-texCoord); \n"
- " highp vec2 wrappedCoord = mod(texCoord,vWrap); \n"
- " texCoord += vWrapEn*(wrappedCoord-texCoord); \n"
- " return texCoord; \n"
- "} \n"
-
- "highp vec2 wrap2D(in highp vec2 tc, in highp vec2 size) \n"
- "{ \n"
- " highp float divs = floor(tc.s / size.s); \n"
- " highp float divt = floor((tc.t + divs) / size.t); \n"
- " return vec2(tc.s - divs * size.s, tc.t + divs - divt*size.t); \n"
- "} \n"
-
- "void textureEngine0(in highp vec2 texCoord, out highp vec2 tcData[5]) \n"
- "{ \n"
- " highp vec2 tileCoord = (WRAP(texCoord * uShiftScale[0] - uTexOffset[0], -1024.0, 1024.0));\n"
- " tileCoord = (tileCoord + uBilinearOffset) * uHDRatio[0] - uBilinearOffset; \n"
- " mediump vec2 intPart = floor(tileCoord); \n"
- " highp vec2 tc00 = clampWrapMirror(intPart, uTexWrap[0], uTexClamp[0], uTexWrapEn[0], uTexClampEn[0], uTexMirrorEn[0]); \n"
- " highp vec2 tc11 = clampWrapMirror(intPart + vec2(1.0,1.0), uTexWrap[0], uTexClamp[0], uTexWrapEn[0], uTexClampEn[0], uTexMirrorEn[0]); \n"
- " tcData[0] = wrap2D(tc00, uTexSize[0]) + uCacheOffset[0]; \n"
- " tcData[3] = wrap2D(tc11, uTexSize[0]) + uCacheOffset[0]; \n"
- " tcData[1] = vec2(tcData[0].s, tcData[3].t); \n"
- " tcData[2] = vec2(tcData[3].s, tcData[0].t); \n"
- " tcData[4] = tileCoord - intPart; \n"
- "} \n"
-
- "void textureEngine1(in highp vec2 texCoord, out highp vec2 tcData[5]) \n"
- "{ \n"
- " highp vec2 tileCoord = (WRAP(texCoord * uShiftScale[1] - uTexOffset[1], -1024.0, 1024.0)); \n"
- " tileCoord = (tileCoord + uBilinearOffset) * uHDRatio[1] - uBilinearOffset; \n"
- " mediump vec2 intPart = floor(tileCoord); \n"
- " highp vec2 tc00 = clampWrapMirror(intPart, uTexWrap[1], uTexClamp[1], uTexWrapEn[1], uTexClampEn[1], uTexMirrorEn[1]); \n"
- " highp vec2 tc11 = clampWrapMirror(intPart + vec2(1.0,1.0), uTexWrap[1], uTexClamp[1], uTexWrapEn[1], uTexClampEn[1], uTexMirrorEn[1]); \n"
- " tcData[0] = wrap2D(tc00, uTexSize[1]) + uCacheOffset[1]; \n"
- " tcData[3] = wrap2D(tc11, uTexSize[1]) + uCacheOffset[1]; \n"
- " tcData[1] = vec2(tcData[0].s, tcData[3].t); \n"
- " tcData[2] = vec2(tcData[3].s, tcData[0].t); \n"
- " tcData[4] = tileCoord - intPart; \n"
- "} \n"
-
- ;
- }
-};
-
-
-class ShaderFragmentTextureEngineTex0 : public ShaderPart {
-public:
- ShaderFragmentTextureEngineTex0(const opengl::GLInfo _glinfo)
- {
- m_part =
- "textureEngine0(mTexCoord0, tcData0); \n"
- ;
- }
-};
-
-class ShaderFragmentTextureEngineTex1 : public ShaderPart {
-public:
- ShaderFragmentTextureEngineTex1(const opengl::GLInfo _glinfo)
- {
- m_part =
- "textureEngine1(mTexCoord1, tcData1); \n"
- ;
- }
-};
-
-class ShaderCoverage : public ShaderPart {
-public:
- ShaderCoverage() {
- m_part =
- "const highp vec2 bias[8] = vec2[8] (vec2(-0.5,-0.5), vec2(0.0, -0.5), vec2(-0.25,-0.25), vec2(0.25, -0.25), \n"
- " vec2(-0.5, 0.0), vec2(0.0,0.0), vec2(-0.25,0.25), vec2(0.25,0.25)); \n"
- "highp vec4 dBCdx = dFdx(vBaryCoords); \n"
- "highp vec4 dBCdy = dFdy(vBaryCoords); \n"
- "cvg = 0.0; \n"
- "for (int i = 0; i<8; i++) { \n"
- " highp vec2 currentBias = bias[i]; \n"
- " highp vec4 baryCoordsBiased = vBaryCoords + dBCdx*currentBias.x + dBCdy * currentBias.y; \n"
- " lowp vec4 inside = step(0.0, baryCoordsBiased); \n"
- " cvg += 0.125 * inside[0] * inside[1] * inside[2] * inside[3]; \n"
- "} \n"
- ;
- }
-};
-
-
-/*---------------ShaderPartsEnd-------------*/
-
static
bool needClampColor() {
- return g_cycleType <= G_CYC_2CYCLE;
+ return CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE;
}
static
bool combinedColorC(const gDPCombine & _combine) {
- if (g_cycleType != G_CYC_2CYCLE)
+ if (CombinerProgramBuilder::s_cycleType != G_CYC_2CYCLE)
return false;
return _combine.mRGB1 == G_CCMUX_COMBINED;
}
static
bool combinedAlphaC(const gDPCombine & _combine) {
- if (g_cycleType != G_CYC_2CYCLE)
+ if (CombinerProgramBuilder::s_cycleType != G_CYC_2CYCLE)
return false;
return _combine.mA1 == G_ACMUX_COMBINED;
}
static
bool combinedColorABD(const gDPCombine & _combine) {
- if (g_cycleType != G_CYC_2CYCLE)
+ if (CombinerProgramBuilder::s_cycleType != G_CYC_2CYCLE)
return false;
if (_combine.aRGB1 == G_CCMUX_COMBINED)
return true;
@@ -2780,7 +234,7 @@ bool combinedColorABD(const gDPCombine & _combine) {
static
bool combinedAlphaABD(const gDPCombine & _combine) {
- if (g_cycleType != G_CYC_2CYCLE)
+ if (CombinerProgramBuilder::s_cycleType != G_CYC_2CYCLE)
return false;
if (_combine.aA1 == G_ACMUX_COMBINED)
return true;
@@ -2796,7 +250,7 @@ CombinerInputs CombinerProgramBuilder::compileCombiner(const CombinerKey & _key,
std::stringstream ssShader;
- if (g_cycleType != G_CYC_2CYCLE) {
+ if (CombinerProgramBuilder::s_cycleType != G_CYC_2CYCLE) {
_correctFirstStageParams(_alpha.stage[0]);
_correctFirstStageParams(_color.stage[0]);
} else {
@@ -2807,22 +261,22 @@ CombinerInputs CombinerProgramBuilder::compileCombiner(const CombinerKey & _key,
CombinerInputs inputs = _compileCombiner(_alpha.stage[0], AlphaInput, ssShader);
// Simulate N64 color sign-extend.
if (combinedAlphaC(combine))
- m_signExtendAlphaC->write(ssShader);
+ _writeSignExtendAlphaC(ssShader);
else if (combinedAlphaABD(combine))
- m_signExtendAlphaABD->write(ssShader);
+ _writeSignExtendAlphaABD(ssShader);
- if (g_cycleType < G_CYC_FILL)
- m_alphaTest->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType < G_CYC_FILL)
+ _writeAlphaTest(ssShader);
ssShader << " color1 = ";
inputs += _compileCombiner(_color.stage[0], ColorInput, ssShader);
// Simulate N64 color sign-extend.
if (combinedColorC(combine))
- m_signExtendColorC->write(ssShader);
+ _writeSignExtendColorC(ssShader);
else if (combinedColorABD(combine))
- m_signExtendColorABD->write(ssShader);
+ _writeSignExtendColorABD(ssShader);
- if (g_cycleType == G_CYC_2CYCLE) {
+ if (CombinerProgramBuilder::s_cycleType == G_CYC_2CYCLE) {
ssShader << " combined_color = vec4(color1, alpha1);" << std::endl;
if (_alpha.numStages == 2) {
@@ -2846,19 +300,19 @@ CombinerInputs CombinerProgramBuilder::compileCombiner(const CombinerKey & _key,
ssShader << " lowp vec4 cmbRes = vec4(color2, alpha2);" << std::endl;
}
else {
- if (g_cycleType < G_CYC_FILL)
+ if (CombinerProgramBuilder::s_cycleType < G_CYC_FILL)
ssShader << " if (uCvgXAlpha != 0 && alpha1 < 0.125) discard;" << std::endl;
ssShader << " lowp vec4 cmbRes = vec4(color1, alpha1);" << std::endl;
}
// Simulate N64 color clamp.
if (needClampColor())
- m_clamp->write(ssShader);
+ _writeClamp(ssShader);
else
ssShader << " lowp vec4 clampedColor = clamp(cmbRes, 0.0, 1.0);" << std::endl;
- if (g_cycleType <= G_CYC_2CYCLE) {
- m_callDither->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE) {
+ _writeCallDither(ssShader);
ssShader << "if (uCvgXAlpha != 0) cvg *= clampedColor.a;" << std::endl;
ssShader << "if (uAlphaCvgSel != 0) clampedColor.a = cvg; " << std::endl;
@@ -2866,18 +320,18 @@ CombinerInputs CombinerProgramBuilder::compileCombiner(const CombinerKey & _key,
if (config.generalEmulation.enableLegacyBlending == 0) {
- if (g_cycleType <= G_CYC_2CYCLE) {
- m_blender1->write(ssShader);
- if (g_cycleType == G_CYC_2CYCLE)
- m_blender2->write(ssShader);
- m_blenderAlpha->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE) {
+ _writeBlender1(ssShader);
+ if (CombinerProgramBuilder::s_cycleType == G_CYC_2CYCLE)
+ _writeBlender2(ssShader);
+ _writeBlenderAlpha(ssShader);
} else
ssShader << " fragColor = clampedColor;" << std::endl;
}
else {
ssShader << " fragColor = clampedColor;" << std::endl;
- m_legacyBlender->write(ssShader);
+ _writeLegacyBlender(ssShader);
}
@@ -2892,8 +346,8 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine
Combiner & _alpha,
const CombinerKey & _key)
{
- g_cycleType = _key.getCycleType();
- g_textureConvert.setMode(_key.getBilerp());
+ CombinerProgramBuilder::s_cycleType = _key.getCycleType();
+ CombinerProgramBuilder::s_textureConvert.setMode(_key.getBilerp());
std::string strCombiner;
CombinerInputs combinerInputs(compileCombiner(_key, _color, _alpha, strCombiner));
@@ -2911,83 +365,83 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine
std::stringstream ssShader;
/* Write headers */
- m_fragmentHeader->write(ssShader);
+ _writeFragmentHeader(ssShader);
if (bUseTextures) {
- m_fragmentGlobalVariablesTex->write(ssShader);
+ _writeFragmentGlobalVariablesTex(ssShader);
- if (g_cycleType == G_CYC_2CYCLE && config.generalEmulation.enableLegacyBlending == 0)
+ if (CombinerProgramBuilder::s_cycleType == G_CYC_2CYCLE && config.generalEmulation.enableLegacyBlending == 0)
ssShader << "uniform lowp ivec4 uBlendMux2;" << std::endl << "uniform lowp int uForceBlendCycle2;" << std::endl;
- if (g_cycleType <= G_CYC_2CYCLE)
- m_fragmentHeaderDither->write(ssShader);
- m_fragmentHeaderNoise->write(ssShader);
- m_fragmentHeaderWriteDepth->write(ssShader);
- m_fragmentHeaderDepthCompare->write(ssShader);
- m_fragmentHeaderReadMSTex->write(ssShader);
- m_fragmentHeaderTextureEngine->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE)
+ _writeFragmentHeaderDither(ssShader);
+ _writeFragmentHeaderNoise(ssShader);
+ _writeFragmentHeaderWriteDepth(ssShader);
+ _writeFragmentHeaderDepthCompare(ssShader);
+ _writeFragmentHeaderReadMSTex(ssShader);
+ _writeFragmentHeaderClampWrapMirrorEngine(ssShader);
if (bUseLod)
- m_fragmentHeaderMipMap->write(ssShader);
- else if (g_cycleType < G_CYC_COPY)
- m_fragmentHeaderReadTex->write(ssShader);
+ _writeFragmentHeaderMipMap(ssShader);
+ else if (CombinerProgramBuilder::s_cycleType < G_CYC_COPY)
+ _writeFragmentHeaderReadTex(ssShader);
else
- m_fragmentHeaderReadTexCopyMode->write(ssShader);
+ _writeFragmentHeaderReadTexCopyMode(ssShader);
} else {
- m_fragmentGlobalVariablesNotex->write(ssShader);
+ _writeFragmentGlobalVariablesNotex(ssShader);
- if (g_cycleType == G_CYC_2CYCLE && config.generalEmulation.enableLegacyBlending == 0)
+ if (CombinerProgramBuilder::s_cycleType == G_CYC_2CYCLE && config.generalEmulation.enableLegacyBlending == 0)
ssShader << "uniform lowp ivec4 uBlendMux2;" << std::endl << "uniform lowp int uForceBlendCycle2;" << std::endl;
- if (g_cycleType <= G_CYC_2CYCLE)
- m_fragmentHeaderDither->write(ssShader);
- m_fragmentHeaderNoise->write(ssShader);
- m_fragmentHeaderWriteDepth->write(ssShader);
- m_fragmentHeaderDepthCompare->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE)
+ _writeFragmentHeaderDither(ssShader);
+ _writeFragmentHeaderNoise(ssShader);
+ _writeFragmentHeaderWriteDepth(ssShader);
+ _writeFragmentHeaderDepthCompare(ssShader);
}
if (bUseHWLight)
- m_fragmentHeaderCalcLight->write(ssShader);
+ _writeFragmentHeaderCalcLight(ssShader);
/* Write body */
- if (g_cycleType == G_CYC_2CYCLE)
- m_fragmentMain2Cycle->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType == G_CYC_2CYCLE)
+ _writeFragmentMain2Cycle(ssShader);
else
- m_fragmentMain->write(ssShader);
+ _writeFragmentMain(ssShader);
- if (g_cycleType <= G_CYC_2CYCLE)
- m_fragmentBlendMux->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE)
+ _writeFragmentBlendMux(ssShader);
- if (g_cycleType <= G_CYC_2CYCLE && m_useCoverage)
- m_shaderCoverage->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE && m_useCoverage)
+ _writeShaderCoverage(ssShader);
else
ssShader << "cvg = 1.0; \n" << std::endl;
if (bUseTextures) {
- m_fragmentCorrectTexCoords->write(ssShader);
+ _writeFragmentCorrectTexCoords(ssShader);
if (combinerInputs.usesTile(0))
{
- m_fragmentTextureEngineTex0->write(ssShader);
+ _writeFragmentClampWrapMirrorEngineTex0(ssShader);
}
if (combinerInputs.usesTile(1))
{
- m_fragmentTextureEngineTex1->write(ssShader);
+ _writeFragmentClampWrapMirrorEngineTex1(ssShader);
}
if (bUseLod) {
- m_fragmentReadTexMipmap->write(ssShader);
+ _writeFragmentReadTexMipmap(ssShader);
} else {
- if (g_cycleType < G_CYC_COPY) {
+ if (CombinerProgramBuilder::s_cycleType < G_CYC_COPY) {
if (combinerInputs.usesTile(0))
- m_fragmentReadTex0->write(ssShader);
+ _writeFragmentReadTex0(ssShader);
else
ssShader << " lowp vec4 readtex0;" << std::endl;
if (combinerInputs.usesTile(1))
- m_fragmentReadTex1->write(ssShader);
+ _writeFragmentReadTex1(ssShader);
} else
- m_fragmentReadTexCopyMode->write(ssShader);
+ _writeFragmentReadTexCopyMode(ssShader);
}
}
@@ -3000,39 +454,39 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine
ssShader << strCombiner << std::endl;
if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
- m_fragmentCallN64Depth->write(ssShader);
+ _writeFragmentCallN64Depth(ssShader);
else
- m_fragmentRenderTarget->write(ssShader);
+ _writeFragmentRenderTarget(ssShader);
// End of Main() function
- m_shaderFragmentMainEnd->write(ssShader);
+ _writeShaderFragmentMainEnd(ssShader);
/* Write other functions */
if (bUseHWLight)
- m_shaderCalcLight->write(ssShader);
+ _writeShaderCalcLight(ssShader);
if (bUseTextures) {
- m_shaderTextureEngine->write(ssShader);
+ _writeShaderClampWrapMirrorEngine(ssShader);
if (bUseLod)
- m_shaderMipmap->write(ssShader);
+ _writeShaderMipmap(ssShader);
else {
- if (g_cycleType < G_CYC_COPY)
- m_shaderReadtex->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType < G_CYC_COPY)
+ _writeShaderReadtex(ssShader);
else
- m_shaderReadtexCopyMode->write(ssShader);
+ _writeShaderReadtexCopyMode(ssShader);
}
}
- m_shaderNoise->write(ssShader);
+ _writeShaderNoise(ssShader);
- if (g_cycleType <= G_CYC_2CYCLE)
- m_shaderDither->write(ssShader);
+ if (CombinerProgramBuilder::s_cycleType <= G_CYC_2CYCLE)
+ _writeShaderDither(ssShader);
- m_shaderWriteDepth->write(ssShader);
+ _writeShaderWriteDepth(ssShader);
- m_shaderN64DepthCompare->write(ssShader);
+ _writeShaderN64DepthCompare(ssShader);
- m_shaderN64DepthRender->write(ssShader);
+ _writeShaderN64DepthRender(ssShader);
const std::string strFragmentShader(ssShader.str());
@@ -3043,14 +497,14 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine
glShaderSource(fragmentShader, 1, &strShaderData, nullptr);
glCompileShader(fragmentShader);
if (!Utils::checkShaderCompileStatus(fragmentShader))
- Utils::logErrorShader(GL_FRAGMENT_SHADER, strFragmentShader);
+ Utils::logErrorShader(GL_FRAGMENT_SHADER, strFragmentShader);
GLuint program = glCreateProgram();
Utils::locateAttributes(program, bIsRect, bUseTextures);
if (bIsRect)
- glAttachShader(program, bUseTextures ? m_vertexShaderTexturedRect : m_vertexShaderRect);
+ glAttachShader(program, bUseTextures ? _getVertexShaderTexturedRect() : _getVertexShaderRect());
else
- glAttachShader(program, bUseTextures ? m_vertexShaderTexturedTriangle : m_vertexShaderTriangle);
+ glAttachShader(program, bUseTextures ? _getVertexShaderTexturedTriangle() : _getVertexShaderTriangle());
glAttachShader(program, fragmentShader);
if (CombinerInfo::get().isShaderCacheSupported()) {
if (IS_GL_FUNCTION_VALID(ProgramParameteri))
@@ -3066,114 +520,16 @@ graphics::CombinerProgram * CombinerProgramBuilder::buildCombinerProgram(Combine
return new CombinerProgramImpl(_key, program, m_useProgram, combinerInputs, std::move(uniforms));
}
-const ShaderPart * CombinerProgramBuilder::getVertexShaderHeader() const
-{
- return m_vertexHeader.get();
-}
-
-const ShaderPart * CombinerProgramBuilder::getFragmentShaderHeader() const
-{
- return m_fragmentHeader.get();
-}
-
-const ShaderPart * CombinerProgramBuilder::getFragmentShaderEnd() const
-{
- return m_shaderFragmentMainEnd.get();
-}
-
-static
-GLuint _createVertexShader(ShaderPart * _header, ShaderPart * _body, ShaderPart * _footer)
-{
- std::stringstream ssShader;
- _header->write(ssShader);
- _body->write(ssShader);
- _footer->write(ssShader);
- const std::string strShader(ssShader.str());
- const GLchar * strShaderData = strShader.data();
-
- GLuint shader_object = glCreateShader(GL_VERTEX_SHADER);
- glShaderSource(shader_object, 1, &strShaderData, nullptr);
- glCompileShader(shader_object);
- if (!Utils::checkShaderCompileStatus(shader_object))
- Utils::logErrorShader(GL_VERTEX_SHADER, strShaderData);
- return shader_object;
-}
-
-CombinerProgramBuilder::CombinerProgramBuilder(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram)
-: m_blender1(new ShaderBlender1(_glinfo))
-, m_blender2(new ShaderBlender2(_glinfo))
-, m_blenderAlpha (new ShaderBlenderAlpha(_glinfo))
-, m_legacyBlender(new ShaderLegacyBlender)
-, m_clamp(new ShaderClamp)
-, m_signExtendColorC(new ShaderSignExtendColorC)
-, m_signExtendAlphaC(new ShaderSignExtendAlphaC)
-, m_signExtendColorABD(new ShaderSignExtendColorABD)
-, m_signExtendAlphaABD(new ShaderSignExtendAlphaABD)
-, m_alphaTest(new ShaderAlphaTest)
-, m_callDither(new ShaderDithering(_glinfo))
-, m_vertexHeader(new VertexShaderHeader(_glinfo))
-, m_vertexEnd(new VertexShaderEnd(_glinfo))
-, m_vertexRect(new VertexShaderRect(_glinfo))
-, m_vertexTexturedRect(new VertexShaderTexturedRect(_glinfo))
-, m_vertexTriangle(new VertexShaderTriangle(_glinfo))
-, m_vertexTexturedTriangle(new VertexShaderTexturedTriangle(_glinfo))
-, m_fragmentHeader(new FragmentShaderHeader(_glinfo))
-, m_fragmentGlobalVariablesTex(new ShaderFragmentGlobalVariablesTex(_glinfo))
-, m_fragmentGlobalVariablesNotex(new ShaderFragmentGlobalVariablesNotex(_glinfo))
-, m_fragmentHeaderNoise(new ShaderFragmentHeaderNoise(_glinfo))
-, m_fragmentHeaderWriteDepth(new ShaderFragmentHeaderWriteDepth(_glinfo))
-, m_fragmentHeaderCalcLight(new ShaderFragmentHeaderCalcLight(_glinfo))
-, m_fragmentHeaderMipMap(new ShaderFragmentHeaderMipMap(_glinfo))
-, m_fragmentHeaderTextureEngine(new ShaderFragmentHeaderTextureEngine(_glinfo))
-, m_fragmentHeaderReadMSTex(new ShaderFragmentHeaderReadMSTex(_glinfo))
-, m_fragmentHeaderDither(new ShaderFragmentHeaderDither(_glinfo))
-, m_fragmentHeaderDepthCompare(new ShaderFragmentHeaderDepthCompare(_glinfo))
-, m_fragmentHeaderReadTex(new ShaderFragmentHeaderReadTex(_glinfo))
-, m_fragmentHeaderReadTexCopyMode(new ShaderFragmentHeaderReadTexCopyMode(_glinfo))
-, m_fragmentMain(new ShaderFragmentMain(_glinfo))
-, m_fragmentMain2Cycle(new ShaderFragmentMain2Cycle(_glinfo))
-, m_fragmentBlendMux(new ShaderFragmentBlendMux(_glinfo))
-, m_fragmentReadTex0(new ShaderFragmentReadTex0(_glinfo))
-, m_fragmentReadTex1(new ShaderFragmentReadTex1(_glinfo))
-, m_fragmentCorrectTexCoords(new ShaderFragmentCorrectTexCoords())
-, m_fragmentTextureEngineTex0(new ShaderFragmentTextureEngineTex0(_glinfo))
-, m_fragmentTextureEngineTex1(new ShaderFragmentTextureEngineTex1(_glinfo))
-, m_fragmentReadTexCopyMode(new ShaderFragmentReadTexCopyMode(_glinfo))
-, m_fragmentReadTexMipmap(new ShaderFragmentReadTexMipmap(_glinfo))
-, m_fragmentCallN64Depth(new ShaderFragmentCallN64Depth(_glinfo))
-, m_fragmentRenderTarget(new ShaderFragmentRenderTarget(_glinfo))
-, m_shaderFragmentMainEnd(new ShaderFragmentMainEnd(_glinfo))
-, m_shaderNoise(new ShaderNoise(_glinfo))
-, m_shaderDither(new ShaderDither(_glinfo))
-, m_shaderWriteDepth(new ShaderWriteDepth(_glinfo))
-, m_shaderMipmap(new ShaderMipmap(_glinfo))
-, m_shaderCalcLight(new ShaderCalcLight(_glinfo))
-, m_shaderReadtex(new ShaderReadtex(_glinfo))
-, m_shaderReadtexCopyMode(new ShaderReadtexCopyMode(_glinfo))
-, m_shaderN64DepthCompare(new ShaderN64DepthCompare(_glinfo))
-, m_shaderN64DepthRender(new ShaderN64DepthRender(_glinfo))
-, m_shaderTextureEngine(new ShaderTextureEngine(_glinfo))
-, m_shaderCoverage(new ShaderCoverage())
+CombinerProgramBuilder::CombinerProgramBuilder(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram,
+ std::unique_ptr _uniformFactory)
+: m_uniformFactory(std::move(_uniformFactory))
, m_useProgram(_useProgram)
-, m_combinerOptionsBits(graphics::CombinerProgram::getShaderCombinerOptionsBits())
, m_useCoverage(_glinfo.coverage && config.generalEmulation.enableCoverage != 0)
{
- m_vertexShaderRect = _createVertexShader(m_vertexHeader.get(), m_vertexRect.get(), m_vertexEnd.get());
- m_vertexShaderTriangle = _createVertexShader(m_vertexHeader.get(), m_vertexTriangle.get(), m_vertexEnd.get());
- m_vertexShaderTexturedRect = _createVertexShader(m_vertexHeader.get(), m_vertexTexturedRect.get(), m_vertexEnd.get());
- m_vertexShaderTexturedTriangle = _createVertexShader(m_vertexHeader.get(), m_vertexTexturedTriangle.get(), m_vertexEnd.get());
- m_uniformFactory.reset(new CombinerProgramUniformFactory(_glinfo));
}
CombinerProgramBuilder::~CombinerProgramBuilder()
{
- glDeleteShader(m_vertexShaderRect);
- glDeleteShader(m_vertexShaderTriangle);
- glDeleteShader(m_vertexShaderTexturedRect);
- glDeleteShader(m_vertexShaderTexturedTriangle);
}
-bool CombinerProgramBuilder::isObsolete() const
-{
- return m_combinerOptionsBits != graphics::CombinerProgram::getShaderCombinerOptionsBits();
}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h
index 92698035..6910baff 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h
@@ -2,6 +2,8 @@
#include
#include
#include
+#include "glsl_CombinerProgramUniformFactory.h"
+#include "glsl_ShaderPart.h"
namespace graphics {
class CombinerProgram;
@@ -12,98 +14,121 @@ namespace opengl {
}
namespace glsl {
-
- class ShaderPart;
class CombinerInputs;
- class CombinerProgramUniformFactory;
+}
- class CombinerProgramBuilder
- {
- public:
- CombinerProgramBuilder(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram);
- ~CombinerProgramBuilder();
+namespace glsl {
- graphics::CombinerProgram * buildCombinerProgram(Combiner & _color, Combiner & _alpha, const CombinerKey & _key);
+class TextureConvert {
+public:
+ void setMode(u32 _mode) {
+ m_mode = _mode;
+ }
- const ShaderPart * getVertexShaderHeader() const;
+ bool getBilerp1() const {
+ return (m_mode & 1) != 0;
+ }
- const ShaderPart * getFragmentShaderHeader() const;
+ bool getBilerp0() const {
+ return (m_mode & 2) != 0;
+ }
- const ShaderPart * getFragmentShaderEnd() const;
+ bool useYUVCoversion() const {
+ return (m_mode & 3) != 3;
+ }
- bool isObsolete() const;
+ bool useTextureFiltering() const {
+ return (m_mode & 3) != 0;
+ }
- private:
- CombinerInputs compileCombiner(const CombinerKey & _key, Combiner & _color, Combiner & _alpha, std::string & _strShader);
+private:
+ u32 m_mode;
+};
- typedef std::unique_ptr ShaderPartPtr;
- ShaderPartPtr m_blender1;
- ShaderPartPtr m_blender2;
- ShaderPartPtr m_blenderAlpha;
- ShaderPartPtr m_legacyBlender;
- ShaderPartPtr m_clamp;
- ShaderPartPtr m_signExtendColorC;
- ShaderPartPtr m_signExtendAlphaC;
- ShaderPartPtr m_signExtendColorABD;
- ShaderPartPtr m_signExtendAlphaABD;
- ShaderPartPtr m_alphaTest;
- ShaderPartPtr m_callDither;
+class CombinerProgramBuilder
+{
+public:
+ using ShaderPartPtr = std::unique_ptr;
- ShaderPartPtr m_vertexHeader;
- ShaderPartPtr m_vertexEnd;
- ShaderPartPtr m_vertexRect;
- ShaderPartPtr m_vertexTexturedRect;
- ShaderPartPtr m_vertexTriangle;
- ShaderPartPtr m_vertexTexturedTriangle;
+ CombinerProgramBuilder(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram,
+ std::unique_ptr _uniformFactory);
+ virtual ~CombinerProgramBuilder();
- ShaderPartPtr m_fragmentHeader;
- ShaderPartPtr m_fragmentGlobalVariablesTex;
- ShaderPartPtr m_fragmentGlobalVariablesNotex;
- ShaderPartPtr m_fragmentHeaderNoise;
- ShaderPartPtr m_fragmentHeaderWriteDepth;
- ShaderPartPtr m_fragmentHeaderCalcLight;
- ShaderPartPtr m_fragmentHeaderMipMap;
- ShaderPartPtr m_fragmentHeaderTextureEngine;
- ShaderPartPtr m_fragmentHeaderReadMSTex;
- ShaderPartPtr m_fragmentHeaderDither;
- ShaderPartPtr m_fragmentHeaderDepthCompare;
- ShaderPartPtr m_fragmentHeaderReadTex;
- ShaderPartPtr m_fragmentHeaderReadTexCopyMode;
- ShaderPartPtr m_fragmentMain;
- ShaderPartPtr m_fragmentMain2Cycle;
- ShaderPartPtr m_fragmentBlendMux;
- ShaderPartPtr m_fragmentReadTex0;
- ShaderPartPtr m_fragmentReadTex1;
- ShaderPartPtr m_fragmentCorrectTexCoords;
- ShaderPartPtr m_fragmentTextureEngineTex0;
- ShaderPartPtr m_fragmentTextureEngineTex1;
- ShaderPartPtr m_fragmentReadTexCopyMode;
- ShaderPartPtr m_fragmentReadTexMipmap;
- ShaderPartPtr m_fragmentCallN64Depth;
- ShaderPartPtr m_fragmentRenderTarget;
- ShaderPartPtr m_shaderFragmentMainEnd;
+ graphics::CombinerProgram * buildCombinerProgram(Combiner & _color, Combiner & _alpha, const CombinerKey & _key);
- ShaderPartPtr m_shaderNoise;
- ShaderPartPtr m_shaderDither;
- ShaderPartPtr m_shaderWriteDepth;
- ShaderPartPtr m_shaderMipmap;
- ShaderPartPtr m_shaderCalcLight;
- ShaderPartPtr m_shaderReadtex;
- ShaderPartPtr m_shaderReadtexCopyMode;
- ShaderPartPtr m_shaderN64DepthCompare;
- ShaderPartPtr m_shaderN64DepthRender;
- ShaderPartPtr m_shaderTextureEngine;
- ShaderPartPtr m_shaderCoverage;
+ virtual const ShaderPart * getVertexShaderHeader() const = 0;
- std::unique_ptr m_uniformFactory;
+ virtual const ShaderPart * getFragmentShaderHeader() const = 0;
- GLuint m_vertexShaderRect;
- GLuint m_vertexShaderTriangle;
- GLuint m_vertexShaderTexturedRect;
- GLuint m_vertexShaderTexturedTriangle;
- opengl::CachedUseProgram * m_useProgram;
- u32 m_combinerOptionsBits;
- bool m_useCoverage = false;
- };
+ virtual const ShaderPart * getFragmentShaderEnd() const = 0;
+
+ virtual bool isObsolete() const = 0;
+
+ static u32 s_cycleType;
+ static TextureConvert s_textureConvert;
+
+private:
+ CombinerInputs compileCombiner(const CombinerKey & _key, Combiner & _color, Combiner & _alpha, std::string & _strShader);
+
+ virtual void _writeSignExtendAlphaC(std::stringstream& ssShader) const = 0;
+ virtual void _writeSignExtendAlphaABD(std::stringstream& ssShader) const = 0;
+ virtual void _writeAlphaTest(std::stringstream& ssShader) const = 0;
+ virtual void _writeSignExtendColorC(std::stringstream& ssShader) const = 0;
+ virtual void _writeSignExtendColorABD(std::stringstream& ssShader) const = 0;
+ virtual void _writeClamp(std::stringstream& ssShader) const = 0;
+ virtual void _writeCallDither(std::stringstream& ssShader) const = 0;
+ virtual void _writeBlender1(std::stringstream& ssShader) const = 0;
+ virtual void _writeBlender2(std::stringstream& ssShader) const = 0;
+ virtual void _writeBlenderAlpha(std::stringstream& ssShader) const = 0;
+ virtual void _writeLegacyBlender(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeader(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentGlobalVariablesTex(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderDither(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderNoise(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderWriteDepth(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderDepthCompare(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderReadMSTex(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderClampWrapMirrorEngine(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderMipMap(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderReadTex(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderReadTexCopyMode(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentGlobalVariablesNotex(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentHeaderCalcLight(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentMain2Cycle(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentMain(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentBlendMux(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderCoverage(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentCorrectTexCoords(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentClampWrapMirrorEngineTex0(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentClampWrapMirrorEngineTex1(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentReadTexMipmap(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentReadTex0(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentReadTex1(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentReadTexCopyMode(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentCallN64Depth(std::stringstream& ssShader) const = 0;
+ virtual void _writeFragmentRenderTarget(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderFragmentMainEnd(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderCalcLight(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderClampWrapMirrorEngine(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderMipmap(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderReadtex(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderReadtexCopyMode(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderNoise(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderDither(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderWriteDepth(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderN64DepthCompare(std::stringstream& ssShader) const = 0;
+ virtual void _writeShaderN64DepthRender(std::stringstream& ssShader) const = 0;
+
+ virtual GLuint _getVertexShaderRect() const = 0;
+ virtual GLuint _getVertexShaderTriangle() const = 0;
+ virtual GLuint _getVertexShaderTexturedRect() const = 0;
+ virtual GLuint _getVertexShaderTexturedTriangle() const = 0;
+
+ std::unique_ptr m_uniformFactory;
+ opengl::CachedUseProgram * m_useProgram;
+ bool m_useCoverage = false;
+
+};
}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.cpp
new file mode 100644
index 00000000..ef594ade
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.cpp
@@ -0,0 +1,1238 @@
+#include // for setprecision
+#include
+#include
+#include
+#include "glsl_Utils.h"
+#include "glsl_CombinerInputs.h"
+#include "glsl_CombinerProgramImpl.h"
+#include "glsl_CombinerProgramBuilderAccurate.h"
+#include "glsl_CombinerProgramUniformFactoryAccurate.h"
+#include "GraphicsDrawer.h"
+
+namespace glsl {
+
+class VertexShaderTexturedTriangle : public ShaderPart
+{
+public:
+ VertexShaderTexturedTriangle(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "IN highp vec4 aPosition; \n"
+ "IN lowp vec4 aColor; \n"
+ "IN highp vec2 aTexCoord; \n"
+ "IN lowp float aNumLights; \n"
+ "IN highp vec4 aModify; \n"
+ "IN highp vec2 aBaryCoords; \n"
+ " \n"
+ "uniform int uTexturePersp; \n"
+ "uniform lowp int uTextureFilterMode; \n"
+ " \n"
+ "uniform lowp int uFogUsage; \n"
+ "uniform mediump vec2 uFogScale; \n"
+ "uniform mediump vec2 uScreenCoordsScale; \n"
+ " \n"
+ "uniform mediump vec2 uTexScale; \n"
+ "uniform mediump vec2 uVTrans; \n"
+ "uniform mediump vec2 uVScale; \n"
+ "uniform mediump vec2 uAdjustTrans; \n"
+ "uniform mediump vec2 uAdjustScale; \n"
+ "uniform lowp ivec2 uCacheFrameBuffer; \n"
+ "OUT highp vec2 vTexCoord0; \n"
+ "OUT highp vec2 vTexCoord1; \n"
+ "OUT mediump vec2 vLodTexCoord; \n"
+ "OUT lowp float vNumLights; \n"
+ "OUT lowp vec4 vShadeColor; \n"
+ "OUT highp vec4 vBaryCoords; \n"
+ ;
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
+ else
+ m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
+ m_part +=
+ "void main() \n"
+ "{ \n"
+ " gl_Position = aPosition; \n"
+ " vShadeColor = aColor; \n"
+ " vec2 texCoord = aTexCoord; \n"
+ " texCoord *= uTexScale; \n"
+ " if (uTexturePersp == 0 && aModify[2] == 0.0) texCoord *= 0.5;\n"
+ " vTexCoord0 = texCoord; \n"
+ " vTexCoord1 = texCoord; \n"
+ " vLodTexCoord = texCoord; \n"
+ " vNumLights = aNumLights; \n"
+ " if ((aModify[0]) != 0.0) { \n"
+ " gl_Position.xy *= gl_Position.w; \n"
+ " } \n"
+ " else { \n"
+ " gl_Position.xy = gl_Position.xy * uVScale.xy + uVTrans.xy * gl_Position.ww; \n"
+ " gl_Position.xy = floor(gl_Position.xy * vec2(4.0)) * vec2(0.25); \n"
+ " gl_Position.xy = gl_Position.xy * uAdjustScale + gl_Position.ww * uAdjustTrans; \n"
+ " } \n"
+ " if ((aModify[1]) != 0.0) \n"
+ " gl_Position.z *= gl_Position.w; \n"
+ " if ((aModify[3]) != 0.0) \n"
+ " vNumLights = 0.0; \n"
+ " if (uFogUsage > 0) { \n"
+ " lowp float fp; \n"
+ " if (aPosition.z < -aPosition.w && aModify[1] == 0.0) \n"
+ " fp = -uFogScale.s + uFogScale.t; \n"
+ " else \n"
+ " fp = aPosition.z/aPosition.w*uFogScale.s + uFogScale.t; \n"
+ " fp = clamp(fp, 0.0, 1.0); \n"
+ " if (uFogUsage == 1) \n"
+ " vShadeColor.a = fp; \n"
+ " else \n"
+ " vShadeColor.rgb = vec3(fp); \n"
+ " } \n"
+ " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n"
+ " vShadeColorNoperspective = vShadeColor; \n"
+ ;
+ }
+};
+
+class ShaderFragmentGlobalVariablesTex : public ShaderPart
+{
+public:
+ ShaderFragmentGlobalVariablesTex(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "uniform sampler2D uTex0; \n"
+ "uniform sampler2D uTex1; \n"
+ "uniform lowp vec4 uFogColor; \n"
+ "uniform lowp vec4 uCenterColor;\n"
+ "uniform lowp vec4 uScaleColor; \n"
+ "uniform lowp vec4 uBlendColor; \n"
+ "uniform lowp vec4 uEnvColor; \n"
+ "uniform lowp vec4 uPrimColor; \n"
+ "uniform lowp float uPrimLod; \n"
+ "uniform lowp float uK4; \n"
+ "uniform lowp float uK5; \n"
+ "uniform lowp int uAlphaCompareMode; \n"
+ "uniform lowp ivec2 uFbMonochrome; \n"
+ "uniform lowp ivec2 uFbFixedAlpha; \n"
+ "uniform lowp int uEnableAlphaTest; \n"
+ "uniform lowp int uCvgXAlpha; \n"
+ "uniform lowp int uAlphaCvgSel; \n"
+ "uniform lowp float uAlphaTestValue; \n"
+ "uniform lowp int uDepthSource; \n"
+ "uniform highp float uPrimDepth; \n"
+ "uniform mediump vec2 uScreenScale; \n"
+ "uniform highp vec2 uTexClamp[2]; \n"
+ "uniform highp vec2 uTexWrap[2]; \n"
+ "uniform lowp vec2 uTexWrapEn[2]; \n"
+ "uniform lowp vec2 uTexMirrorEn[2]; \n"
+ "uniform lowp vec2 uTexClampEn[2]; \n"
+ "uniform highp vec2 uTexSize[2]; \n"
+ "uniform highp vec2 uShiftScale[2]; \n"
+ "uniform highp vec2 uTexOffset[2]; \n"
+ "uniform highp vec2 uHDRatio[2]; \n"
+ "uniform highp vec2 uTexCoordOffset[2]; \n"
+ "uniform highp vec2 uBilinearOffset; \n"
+ "uniform highp vec2 uCacheOffset[2]; \n"
+ "uniform lowp int uUseTexCoordBounds; \n"
+ "uniform highp vec4 uTexCoordBounds0; \n"
+ "uniform highp vec4 uTexCoordBounds1; \n"
+ "uniform lowp int uScreenSpaceTriangle; \n"
+ "highp vec2 texCoord0; \n"
+ "highp vec2 texCoord1; \n"
+ "highp vec2 tcData0[5]; \n"
+ "highp vec2 tcData1[5]; \n"
+ "uniform lowp int uCvgDest; \n"
+ "uniform lowp int uBlendAlphaMode; \n"
+ "lowp float cvg; \n"
+ ;
+
+ if (config.generalEmulation.enableLegacyBlending != 0) {
+ m_part +=
+ "uniform lowp int uFogUsage; \n"
+ ;
+ } else {
+ m_part +=
+ "uniform lowp ivec4 uBlendMux1; \n"
+ "uniform lowp int uForceBlendCycle1;\n"
+ ;
+ }
+
+ if (!_glinfo.isGLES2) {
+ m_part +=
+ "uniform sampler2D uDepthTex; \n"
+ "uniform lowp int uAlphaDitherMode; \n"
+ "uniform lowp int uColorDitherMode; \n"
+ "uniform lowp int uRenderTarget; \n"
+ "uniform mediump vec2 uDepthScale; \n"
+ ;
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part +=
+ "uniform lowp int uEnableDepthCompare; \n"
+ ;
+ }
+ } else {
+ m_part +=
+ "lowp int nCurrentTile; \n"
+ ;
+ }
+
+ if (config.video.multisampling > 0) {
+ m_part +=
+ "uniform lowp ivec2 uMSTexEnabled; \n"
+ "uniform lowp sampler2DMS uMSTex0; \n"
+ "uniform lowp sampler2DMS uMSTex1; \n"
+ ;
+ }
+
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective IN lowp vec4 vShadeColorNoperspective; \n";
+ else
+ m_part += "IN lowp vec4 vShadeColorNoperspective; \n";
+
+ m_part +=
+ "IN lowp vec4 vShadeColor; \n"
+ "IN highp vec2 vTexCoord0; \n"
+ "IN highp vec2 vTexCoord1; \n"
+ "IN mediump vec2 vLodTexCoord; \n"
+ "IN lowp float vNumLights; \n"
+ "IN highp vec4 vBaryCoords; \n"
+ ;
+
+ if (_glinfo.dual_source_blending) {
+ m_part +=
+ "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" // SECONDARY FRAGMENT SHADER OUTPUT
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ } else if (_glinfo.ext_fetch) {
+ m_part +=
+ "layout(location = 0) inout lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR fragColor \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA fragColor.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else if (_glinfo.ext_fetch_arm) {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR gl_LastFragColorARM \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA gl_LastFragColorARM.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ }
+
+ if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ "layout(location = 1) inout highp vec4 depthZ; \n"
+ "layout(location = 2) inout highp vec4 depthDeltaZ; \n"
+ ;
+ }
+
+ if (_glinfo.isGLES2)
+ m_part +=
+ "uniform mediump vec2 uTextureSize[2]; \n"
+ ;
+ }
+};
+
+class ShaderFragmentHeaderTextureEngine : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderTextureEngine(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, \n"
+ " in highp vec2 vWrap, in highp vec2 vClamp, \n"
+ " in lowp vec2 vClampEn, in lowp vec2 vMirrorEn ); \n"
+ "void textureEngine0(in highp vec2 texCoord, out highp vec2 tcData[5]); \n"
+ "void textureEngine1(in highp vec2 texCoord, out highp vec2 tcData[5]); \n"
+ ;
+ }
+};
+
+class ShaderFragmentHeaderReadMSTex : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadMSTex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ if (!m_glinfo.isGLES2 &&
+ config.video.multisampling > 0 &&
+ (CombinerProgramBuilder::s_cycleType == G_CYC_COPY || CombinerProgramBuilder::s_textureConvert.useTextureFiltering()))
+ {
+ shader <<
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha);\n";
+ }
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentHeaderReadTex : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadTex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+ if (!m_glinfo.isGLES2) {
+
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart += "uniform lowp int uTextureFilterMode; \n";
+ shaderPart += "#define TEX_NEAREST(name, tex, tcData) \\\n"
+ "{ \\\n"
+ " name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ "} \n"
+ ;
+ switch (config.texture.bilinearMode + config.texture.enableHalosRemoval * 2) {
+ case BILINEAR_3POINT:
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ shaderPart +=
+ "#define TEX_FILTER(name, tex, tcData) \\\n"
+ " { \\\n"
+ " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
+ " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
+ " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
+ " name = c0 + bottomRightTri * (c1-c0); \\\n"
+ " } \n"
+ ;
+ break;
+ case BILINEAR_STANDARD:
+ shaderPart +=
+ "#define TEX_FILTER(name, tex, tcData) \\\n"
+ "{ \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
+ " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
+ " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
+ " name = c0 + tcData[4].t * (c1-c0); \\\n"
+ "} \n"
+ ;
+ break;
+ case BILINEAR_3POINT_WITH_COLOR_BLEEDING:
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ shaderPart +=
+ "#define TEX_FILTER(name, tex, tcData) \\\n"
+ "{ \\\n"
+ " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
+ " if(uEnableAlphaTest == 1 ){ \\\n" // Calculate premultiplied color values
+ " c00.rgb *= c00.a; \\\n"
+ " c01.rgb *= c01.a; \\\n"
+ " c10.rgb *= c10.a; \\\n"
+ " c11.rgb *= c11.a; \\\n"
+ " } \\\n"
+ " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
+ " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
+ " name = c0 + bottomRightTri * (c1-c0); \\\n"
+ " if(uEnableAlphaTest == 1 ) name.rgb /= name.a; \\\n" // Divide alpha to get actual color value
+ "} \n"
+ ;
+ break;
+ case BILINEAR_STANDARD_WITH_COLOR_BLEEDING_AND_PREMULTIPLIED_ALPHA:
+ shaderPart +=
+ "#define TEX_FILTER(name, tex, tcData) \\\n"
+ "{ \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
+ " if(uEnableAlphaTest == 1){ \\\n" // Calculate premultiplied color values
+ " c00.rgb *= c00.a; \\\n"
+ " c01.rgb *= c01.a; \\\n"
+ " c10.rgb *= c10.a; \\\n"
+ " c11.rgb *= c11.a; \\\n"
+ " } \\\n"
+ " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
+ " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
+ " name = c0 + tcData[4].t * (c1-c0); \\\n"
+ " if(uEnableAlphaTest == 1) name.rgb /= name.a; \\\n"
+ "} \n"
+ ;
+ break;
+ }
+ shaderPart +=
+ "#define READ_TEX(name, tex, tcData, fbMonochrome, fbFixedAlpha) \\\n"
+ " { \\\n"
+ " if (fbMonochrome == 3) { \\\n"
+ " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
+ " name = texelFetch(tex, coord, 0); \\\n"
+ " } else { \\\n"
+ " if (uTextureFilterMode == 0) \\\n"
+ " { \\\n"
+ " TEX_NEAREST(name, tex, tcData); \\\n"
+ " } \\\n"
+ " else TEX_FILTER(name, tex, tcData); \\\n"
+ " } \\\n"
+ " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
+ " else if (fbMonochrome == 2) \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " else if (fbMonochrome == 3) { \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " name.a = 0.0; \\\n"
+ " } \\\n"
+ " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
+ " } \n"
+ ;
+ }
+
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion()) {
+ shaderPart +=
+ "uniform lowp ivec2 uTextureFormat; \n"
+ "uniform lowp int uTextureConvert; \n"
+ "uniform mediump ivec4 uConvertParams; \n"
+ "#define YUVCONVERT(name, format) \\\n"
+ " mediump ivec4 icolor = ivec4(name*255.0); \\\n"
+ " if (format == 1) \\\n"
+ " icolor.rg -= 128; \\\n"
+ " mediump ivec4 iconvert; \\\n"
+ " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \\\n"
+ " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \\\n"
+ " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \\\n"
+ " iconvert.a = icolor.b; \\\n"
+ " name = vec4(iconvert)/255.0; \n"
+ "#define YUVCONVERT_TEX0(name, tex, tcData, format) \\\n"
+ " { \\\n"
+ " name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " YUVCONVERT(name, format) \\\n"
+ " } \n"
+ "#define YUVCONVERT_TEX1(name, tex, tcData, format, prev) \\\n"
+ " { \\\n"
+ " if (uTextureConvert != 0) name = prev; \\\n"
+ " else name = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " YUVCONVERT(name, format) \\\n"
+ " } \n"
+ ;
+ }
+
+ } else {
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart +=
+ "uniform lowp int uTextureFilterMode; \n"
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
+ ;
+ }
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion()) {
+ shaderPart +=
+ "uniform lowp ivec2 uTextureFormat; \n"
+ "uniform lowp int uTextureConvert; \n"
+ "uniform mediump ivec4 uConvertParams; \n"
+ "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 tcData[5], in lowp int convert, in lowp int format, in lowp vec4 prev); \n"
+ ;
+ }
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentHeaderReadTexCopyMode : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadTexCopyMode (const opengl::GLInfo & _glinfo)
+ {
+ if (!_glinfo.isGLES2) {
+ m_part =
+ "#define READ_TEX(name, tex, tcData, fbMonochrome, fbFixedAlpha) \\\n"
+ " { \\\n"
+ " if (fbMonochrome == 3) { \\\n"
+ " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
+ " name = texelFetch(tex, coord, 0); \\\n"
+ " } else { \\\n"
+ " name = texelFetch(tex, ivec2(tcData[0]),0); \\\n"
+ " } \\\n"
+ " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
+ " else if (fbMonochrome == 2) \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " else if (fbMonochrome == 3) { \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " name.a = 0.0; \\\n"
+ " } \\\n"
+ " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
+ " } \n"
+ ;
+ } else {
+ m_part =
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentReadTexCopyMode : public ShaderPart
+{
+public:
+ ShaderFragmentReadTexCopyMode(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ " nCurrentTile = 0; \n"
+ " lowp vec4 readtex0 = readTex(uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n"
+ ;
+ } else {
+ if (config.video.multisampling > 0) {
+ m_part =
+ " lowp vec4 readtex0; \n"
+ " if (uMSTexEnabled[0] == 0) { \n"
+ " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ " } else readtex0 = readTexMS(uMSTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]);\n"
+ ;
+ } else {
+ m_part =
+ " lowp vec4 readtex0; \n"
+ " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderFragmentReadTex0 : public ShaderPart
+{
+public:
+ ShaderFragmentReadTex0(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+
+ shaderPart = " nCurrentTile = 0; \n";
+ if (CombinerProgramBuilder::s_textureConvert.getBilerp0()) {
+ shaderPart += " lowp vec4 readtex0 = readTex(uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
+ } else {
+ shaderPart += " lowp vec4 tmpTex = vec4(0.0); \n"
+ " lowp vec4 readtex0 = YUV_Convert(uTex0, tcData0, 0, uTextureFormat[0], tmpTex); \n";
+ }
+
+ } else {
+
+ if (!CombinerProgramBuilder::s_textureConvert.getBilerp0()) {
+ shaderPart = " lowp vec4 readtex0; \n"
+ " YUVCONVERT_TEX0(readtex0, uTex0, tcData0, uTextureFormat[0]) \n";
+ } else {
+ if (config.video.multisampling > 0) {
+ shaderPart =
+ " lowp vec4 readtex0; \n"
+ " if (uMSTexEnabled[0] == 0) { \n"
+ " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ " } else readtex0 = readTexMS(uMSTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
+ } else {
+ shaderPart = " lowp vec4 readtex0; \n"
+ " READ_TEX(readtex0, uTex0, tcData0, uFbMonochrome[0], uFbFixedAlpha[0]) \n";
+ }
+ }
+
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentReadTex1 : public ShaderPart
+{
+public:
+ ShaderFragmentReadTex1(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+
+ shaderPart = " nCurrentTile = 1; \n";
+
+ if (CombinerProgramBuilder::s_textureConvert.getBilerp1()) {
+ shaderPart += " lowp vec4 readtex1 = readTex(uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
+ } else {
+ shaderPart += " lowp vec4 readtex1 = YUV_Convert(uTex1, tcData1, uTextureConvert, uTextureFormat[1], readtex0); \n";
+ }
+
+ } else {
+
+ if (!CombinerProgramBuilder::s_textureConvert.getBilerp1()) {
+ shaderPart =
+ " lowp vec4 readtex1; \n"
+ " YUVCONVERT_TEX1(readtex1, uTex1, tcData1, uTextureFormat[1], readtex0) \n";
+ } else {
+ if (config.video.multisampling > 0) {
+ shaderPart =
+ " lowp vec4 readtex1; \n"
+ " if (uMSTexEnabled[1] == 0) { \n"
+ " READ_TEX(readtex1, uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]) \n"
+ " } else readtex1 = readTexMS(uMSTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
+ } else {
+ shaderPart = " lowp vec4 readtex1; \n"
+ " READ_TEX(readtex1, uTex1, tcData1, uFbMonochrome[1], uFbFixedAlpha[1]) \n";
+
+ }
+ }
+
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderMipmap : public ShaderPart
+{
+public:
+ ShaderMipmap(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ static const std::string strReadTex0 =
+ "lowp vec4 TextureMipMap0(in sampler2D tex, in highp vec2 tcData[5]) \n"
+ "{ \n"
+ " mediump vec2 texSize = uTextureSize[0]; \n"
+ " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
+ " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
+ " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
+ " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
+ ;
+ static const std::string strReadTex1 =
+ "lowp vec4 TextureMipMap1(in sampler2D tex, in highp vec2 tcData[5], in lowp float lod) \n"
+ "{ \n"
+ // Fetch from texture atlas
+ // First 8 texels contain info about tile size and offset, 1 texel per tile
+ " mediump vec2 texSize = uTextureSize[1]; \n"
+ " mediump vec4 texWdthAndOff0 = 255.0 * texture2D(tex, vec2(0.5, 0.5)/texSize); \n"
+ " mediump vec4 texWdthAndOff = 255.0 * texture2D(tex, vec2(lod + 0.5, 0.5)/texSize); \n"
+ " mediump vec2 lod_scale = texWdthAndOff.ba / texWdthAndOff0.ba; \n"
+ " mediump float offset = texWdthAndOff.r + texWdthAndOff.g * 256.0; \n"
+ " mediump float width = texWdthAndOff.b; \n"
+ " mediump vec2 Coords00 = floor(tcData[0] * lod_scale); \n"
+ " mediump float offset00 = offset + width * Coords00.t + Coords00.s; \n"
+ " mediump float Y00 = floor(offset00 / mipmapTileWidth); \n"
+ " lowp vec4 c00 = texture2D(tex, (vec2(offset00 - mipmapTileWidth * Y00, Y00) + 0.5)/texSize); \n"
+ " mediump vec2 Coords01 = floor(tcData[1] * lod_scale); \n"
+ " mediump float offset01 = offset + width * Coords01.t + Coords01.s; \n"
+ " mediump float Y01 = floor(offset01 / mipmapTileWidth); \n"
+ " lowp vec4 c01 = texture2D(tex, (vec2(offset01 - mipmapTileWidth * Y01, Y01) + 0.5)/texSize); \n"
+ " mediump vec2 Coords10 = floor(tcData[2] * lod_scale); \n"
+ " mediump float offset10 = offset + width * Coords10.t + Coords10.s; \n"
+ " mediump float Y10 = floor(offset10 / mipmapTileWidth); \n"
+ " lowp vec4 c10 = texture2D(tex, (vec2(offset10 - mipmapTileWidth * Y10, Y10) + 0.5)/texSize); \n"
+ " mediump vec2 Coords11 = floor(tcData[3] * lod_scale); \n"
+ " mediump float offset11 = offset + width * Coords11.t + Coords11.s; \n"
+ " mediump float Y11 = floor(offset11 / mipmapTileWidth); \n"
+ " lowp vec4 c11 = texture2D(tex, (vec2(offset11 - mipmapTileWidth * Y11, Y11) + 0.5)/texSize); \n"
+ ;
+ static const std::string strBillinear3PointEndGles2 =
+ " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \n"
+ " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \n"
+ " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \n"
+ " return c0 + bottomRightTri * (c1-c0); \n"
+ " return c00; \n"
+ "} \n"
+ ;
+ static const std::string strBillinearStandardEndGles2 =
+ " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \n"
+ " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \n"
+ " return c0 + tcData[4].t * (c1-c0); \n"
+ " return c00; \n"
+ "} \n"
+ ;
+
+ if (config.texture.bilinearMode == BILINEAR_3POINT) {
+ m_part = strReadTex0;
+ m_part += strBillinear3PointEndGles2;
+ m_part += strReadTex1;
+ m_part += strBillinear3PointEndGles2;
+ } else {
+ m_part = strReadTex0;
+ m_part += strBillinearStandardEndGles2;
+ m_part += strReadTex1;
+ m_part += strBillinearStandardEndGles2;
+ }
+
+ m_part +=
+ "uniform lowp int uEnableLod; \n"
+ "uniform mediump float uMinLod; \n"
+ "uniform lowp int uMaxTile; \n"
+ "uniform lowp int uTextureDetail; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ ;
+
+ if (config.generalEmulation.enableLOD == 0) {
+ m_part +=
+ " mediump float lod = 1.0; \n"
+ ;
+ } else {
+ m_part +=
+ " mediump vec2 dx = abs(dFdx(vLodTexCoord)) * uScreenScale; \n"
+ " mediump vec2 dy = abs(dFdy(vLodTexCoord)) * uScreenScale; \n"
+ " mediump float lod = max(max(dx.x, dx.y), max(dy.x, dy.y)); \n"
+ ;
+ }
+ m_part +=
+ "#define MIN(x, y) y < x ? y : x \n"
+ "#define MAX(x, y) x < y ? y : x \n"
+ " lowp int lod_max_tile = uTextureDetail != 2 ? 7 : 6; \n"
+ " lowp int max_tile = MIN(lod_max_tile, uMaxTile); \n"
+ " mediump float min_lod = uTextureDetail != 0 ? uMinLod : 1.0; \n"
+ " mediump float max_lod = pow(2.0, float(max_tile)) - 1.0 / 32.0; \n"
+ " mediump float lod_clamp = min(max(lod, min_lod), max_lod); \n"
+ // Simulate clamp function, needed for GLES 2.0 and integer types
+ " mediump int lod_clamp_int = int(log2(lod_clamp)); \n"
+ " mediump int lod_clamp_max = MAX(lod_clamp_int, 0); \n"
+ " lowp int lod_tile = MIN(lod_clamp_max, max_tile); \n"
+ " lowp int tile0 = 0; \n"
+ " lowp int tile1 = 1; \n"
+ " if (uEnableLod != 0) { \n"
+ " if (lod_clamp < 1.0 && uTextureDetail == 0) { \n"
+ " tile0 = 0; \n"
+ " tile1 = 0; \n"
+ " } else if (lod_clamp >= 1.0 && uTextureDetail == 2) { \n"
+ " tile0 = lod_tile + 1; \n"
+ " tile1 = lod_tile + 2; \n"
+ " } else { \n"
+ " tile0 = lod_tile; \n"
+ " tile1 = lod_tile + 1; \n"
+ " } \n"
+ " } \n"
+ " mediump float lod_frac = lod_clamp / pow(2.0, float(lod_tile)); \n"
+ " if (uTextureDetail == 1 || lod_clamp >= 1.0) { \n"
+ " lod_frac = clamp(lod_frac - 1.0, -1.0, 1.0); \n"
+ " } \n"
+ " \n"
+ " if (tile0 == 0) \n"
+ " readtex0 = TextureMipMap0(uTex0, tcData0); \n"
+ " else if (uEnableLod == 0 || uMaxTile == 1) \n"
+ " readtex0 = TextureMipMap0(uTex1, tcData1); \n"
+ " else readtex0 = TextureMipMap1(uTex1, tcData1, float(tile0 - 1)); \n"
+ " if (tile1 == 0) \n"
+ " readtex1 = TextureMipMap0(uTex0, tcData0); \n"
+ " else if (uEnableLod == 0 || uMaxTile == 1) \n"
+ " readtex1 = TextureMipMap0(uTex1, tcData1); \n"
+ " else readtex1 = TextureMipMap1(uTex1, tcData1, float(tile1 - 1)); \n"
+ " return lod_frac; \n"
+ "} \n"
+ ;
+ }
+ else {
+ static const std::string strReadTex0 =
+ "#define READ_TEX0_MIPMAP(name, tex, tcData) \\\n"
+ "{ \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(tcData[0]), 0); \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(tcData[1]), 0); \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(tcData[2]), 0); \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(tcData[3]), 0); \\\n"
+ ;
+ static const std::string strReadTex1 =
+ "#define READ_TEX1_MIPMAP(name, tex, tcData, tile) \\\n"
+ "{ \\\n"
+ // Fetch from texture atlas
+ // First 8 texels contain info about tile size and offset, 1 texel per tile
+ " mediump vec4 texWdthAndOff0 = 255.0 * texelFetch(tex, ivec2(0, 0), 0); \\\n"
+ " mediump vec4 texWdthAndOff = 255.0 * texelFetch(tex, ivec2(int(tile), 0), 0); \\\n"
+ " mediump vec2 lod_scale = texWdthAndOff.ba / texWdthAndOff0.ba; \\\n"
+ " mediump int offset = int(texWdthAndOff.r) + int(texWdthAndOff.g) * 256; \\\n"
+ " mediump int width = int(texWdthAndOff.b); \\\n"
+ " mediump ivec2 iCoords00 = ivec2(tcData[0] * lod_scale); \\\n"
+ " mediump int offset00 = offset + width * iCoords00.t + iCoords00.s; \\\n"
+ " mediump int Y00 = offset00/mipmapTileWidth; \\\n"
+ " lowp vec4 c00 = texelFetch(tex, ivec2(offset00 - mipmapTileWidth * Y00, Y00), 0); \\\n"
+ " mediump ivec2 iCoords01 = ivec2(tcData[1] * lod_scale); \\\n"
+ " mediump int offset01 = offset + width * iCoords01.t + iCoords01.s; \\\n"
+ " mediump int Y01 = offset01/mipmapTileWidth; \\\n"
+ " lowp vec4 c01 = texelFetch(tex, ivec2(offset01 - mipmapTileWidth * Y01, Y01), 0); \\\n"
+ " mediump ivec2 iCoords10 = ivec2(tcData[2] * lod_scale); \\\n"
+ " mediump int offset10 = offset + width * iCoords10.t + iCoords10.s; \\\n"
+ " mediump int Y10 = offset10/mipmapTileWidth; \\\n"
+ " lowp vec4 c10 = texelFetch(tex, ivec2(offset10 - mipmapTileWidth * Y10, Y10), 0); \\\n"
+ " mediump ivec2 iCoords11 = ivec2(tcData[3] * lod_scale); \\\n"
+ " mediump int offset11 = offset + width * iCoords11.t + iCoords11.s; \\\n"
+ " mediump int Y11 = offset11/mipmapTileWidth; \\\n"
+ " lowp vec4 c11 = texelFetch(tex, ivec2(offset11 - mipmapTileWidth * Y11, Y11), 0); \\\n"
+ ;
+ static const std::string strBillinear3PointEnd =
+ " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \\\n"
+ " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \\\n"
+ " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \\\n"
+ " name = c0 + bottomRightTri * (c1-c0); \\\n"
+ "} \n"
+ ;
+ static const std::string strBillinearStandardEnd =
+ " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \\\n"
+ " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \\\n"
+ " name = c0 + tcData[4].t * (c1-c0); \\\n"
+ "} \n"
+ ;
+ if (config.texture.bilinearMode == BILINEAR_3POINT) {
+ m_part = strReadTex0;
+ m_part += strBillinear3PointEnd;
+ m_part += strReadTex1;
+ m_part += strBillinear3PointEnd;
+ } else {
+ m_part = strReadTex0;
+ m_part += strBillinearStandardEnd;
+ m_part += strReadTex1;
+ m_part += strBillinearStandardEnd;
+ }
+ m_part +=
+ "uniform lowp int uEnableLod; \n"
+ "uniform mediump float uMinLod; \n"
+ "uniform lowp int uMaxTile; \n"
+ "uniform lowp int uTextureDetail; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ ;
+ if (config.generalEmulation.enableLOD == 0) {
+ m_part +=
+ " mediump float lod = 1.0; \n"
+ ;
+ } else {
+ m_part +=
+ " mediump vec2 dx = abs(dFdx(vLodTexCoord)) * uScreenScale; \n"
+ " mediump vec2 dy = abs(dFdy(vLodTexCoord)) * uScreenScale; \n"
+ " mediump float lod = max(max(dx.x, dx.y), max(dy.x, dy.y)); \n"
+ ;
+ }
+ m_part +=
+ " lowp int max_tile = min(uTextureDetail != 2 ? 7 : 6, uMaxTile); \n"
+ " mediump float min_lod = uTextureDetail != 0 ? uMinLod : 1.0; \n"
+ " mediump float max_lod = pow(2.0, float(max_tile)) - 1.0 / 32.0; \n"
+ " mediump float lod_clamp = min(max(lod, min_lod), max_lod); \n"
+ " lowp int lod_tile = clamp(int(log2(lod_clamp)), 0 , max_tile); \n"
+ " lowp int tile0 = 0; \n"
+ " lowp int tile1 = 1; \n"
+ " if (uEnableLod != 0) { \n"
+ " if (lod_clamp < 1.0 && uTextureDetail == 0) { \n"
+ " tile0 = 0; \n"
+ " tile1 = 0; \n"
+ " } else if (lod_clamp >= 1.0 && uTextureDetail == 2) { \n"
+ " tile0 = lod_tile + 1; \n"
+ " tile1 = lod_tile + 2; \n"
+ " } else { \n"
+ " tile0 = lod_tile; \n"
+ " tile1 = lod_tile + 1; \n"
+ " } \n"
+ " } \n"
+ " mediump float lod_frac = lod_clamp / pow(2.0, float(lod_tile)); \n"
+ " if (uTextureDetail == 1 || lod_clamp >= 1.0) { \n"
+ " lod_frac = clamp(lod_frac - 1.0, -1.0, 1.0); \n"
+ " } \n"
+ " \n"
+ " if(tile0 == 0) {READ_TEX0_MIPMAP(readtex0, uTex0, tcData0);} \n"
+ " else if (uEnableLod == 0 || uMaxTile == 1) {READ_TEX0_MIPMAP(readtex0, uTex1, tcData1);}\n"
+ " else {READ_TEX1_MIPMAP(readtex0, uTex1, tcData1, tile0 - 1);} \n"
+ " if(tile1 == 0) {READ_TEX0_MIPMAP(readtex1, uTex0, tcData0);} \n"
+ " else if (uEnableLod == 0 || uMaxTile == 1) {READ_TEX0_MIPMAP(readtex1, uTex1, tcData1);}\n"
+ " else {READ_TEX1_MIPMAP(readtex1, uTex1, tcData1, tile1 - 1);} \n"
+ " return lod_frac; \n"
+ "} \n"
+ ;
+ }
+ }
+};
+
+class ShaderReadtex : public ShaderPart
+{
+public:
+ ShaderReadtex(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+ shaderPart +=
+ "lowp vec4 TextureNearest(in sampler2D tex, in highp vec2 tcData[5]) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " return texture2D(tex, (tcData[0] + 0.5) / texSize); \n"
+ " } \n"
+ ;
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion())
+ shaderPart +=
+ "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 tcData[5], in lowp int convert, in lowp int format, in lowp vec4 prev) \n"
+ "{ \n"
+ " lowp vec4 texColor; \n"
+ " if (convert != 0) texColor = prev; \n"
+ " else texColor = TextureNearest(tex, tcData); \n"
+ " mediump ivec4 icolor = ivec4(texColor*255.0); \n"
+ " if (format == 1) \n"
+ " icolor.rg -= 128; \n"
+ " mediump ivec4 iconvert; \n"
+ " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \n"
+ " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \n"
+ " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \n"
+ " iconvert.a = icolor.b; \n"
+ " return vec4(iconvert)/255.0; \n"
+ " } \n"
+ ;
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ if (config.texture.bilinearMode == BILINEAR_3POINT) {
+ shaderPart +=
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 tcData[5]) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " lowp float bottomRightTri = step(1.0, tcData[4].s + tcData[4].t); \n"
+ " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
+ " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
+ " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
+ " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
+ " lowp vec4 c0 = c00 + tcData[4].s*(c10-c00) + tcData[4].t*(c01-c00); \n"
+ " lowp vec4 c1 = c11 + (1.0-tcData[4].s)*(c01-c11) + (1.0-tcData[4].t)*(c10-c11); \n"
+ " return c0 + bottomRightTri * (c1-c0); \n"
+ " } \n"
+ ;
+ } else {
+ shaderPart +=
+ // bilinear filtering.
+ "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 tcData[5]) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " lowp vec4 c00 = texture2D(tex, (tcData[0] + 0.5)/texSize); \n"
+ " lowp vec4 c01 = texture2D(tex, (tcData[1] + 0.5)/texSize); \n"
+ " lowp vec4 c10 = texture2D(tex, (tcData[2] + 0.5)/texSize); \n"
+ " lowp vec4 c11 = texture2D(tex, (tcData[3] + 0.5)/texSize); \n"
+ " lowp vec4 c0 = c00 + tcData[4].s * (c10-c00); \n"
+ " lowp vec4 c1 = c01 + tcData[4].s * (c11-c01); \n"
+ " return c0 + tcData[4].t * (c1-c0); \n"
+ " } \n"
+ ;
+ }
+ shaderPart +=
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " lowp vec4 texColor; \n"
+ " if (uTextureFilterMode == 0) texColor = TextureNearest(tex, tcData); \n"
+ " else texColor = TextureFilter(tex, tcData); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ } else {
+ if (config.video.multisampling > 0 && CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart =
+ "uniform lowp int uMSAASamples; \n"
+ "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
+ "{ \n"
+ " lowp vec4 texel = vec4(0.0); \n"
+ " for (int i = 0; i < uMSAASamples; ++i) \n"
+ " texel += texelFetch(mstex, ipos, i); \n"
+ " return texel / float(uMSAASamples); \n"
+ "} \n"
+ " \n"
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " mediump ivec2 itexCoord; \n"
+ " if (fbMonochrome == 3) { \n"
+ " itexCoord = ivec2(gl_FragCoord.xy); \n"
+ " } else { \n"
+ " itexCoord = ivec2(tcData[0]); \n"
+ " } \n"
+ " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " else if (fbMonochrome == 3) { \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " texColor.a = 0.0; \n"
+ " } \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderReadtexCopyMode : public ShaderPart
+{
+public:
+ ShaderReadtexCopyMode(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ "lowp vec4 TextureNearest(in sampler2D tex, in highp vec2 tcData[5]) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " return texture2D(tex, (tcData[0] + 0.5) / texSize); \n"
+ " } \n"
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " lowp vec4 texColor = TextureNearest(tex, tcData); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ } else {
+ if (config.video.multisampling > 0) {
+ m_part =
+ "uniform lowp int uMSAASamples; \n"
+ "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
+ "{ \n"
+ " lowp vec4 texel = vec4(0.0); \n"
+ " for (int i = 0; i < uMSAASamples; ++i) \n"
+ " texel += texelFetch(mstex, ipos, i); \n"
+ " return texel / float(uMSAASamples); \n"
+ "} \n"
+ " \n"
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 tcData[5], in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " mediump ivec2 itexCoord; \n"
+ " if (fbMonochrome == 3) { \n"
+ " itexCoord = ivec2(gl_FragCoord.xy); \n"
+ " } else { \n"
+ " itexCoord = ivec2(tcData[0]); \n"
+ " } \n"
+ " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " else if (fbMonochrome == 3) { \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " texColor.a = 0.0; \n"
+ " } \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderTextureEngine : public ShaderPart
+{
+public:
+ ShaderTextureEngine(const opengl::GLInfo _glinfo) {
+ m_part =
+ "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, in highp vec2 vWrap, \n"
+ " in highp vec2 vClamp, in lowp vec2 vWrapEn, in lowp vec2 vClampEn, in lowp vec2 vMirrorEn) \n"
+ "{ \n"
+ " highp vec2 texCoord = vTexCoord; \n"
+ " highp vec2 clampedCoord = clamp(texCoord, vec2(0.0), vClamp); \n"
+ " texCoord += vClampEn*(clampedCoord-texCoord); \n"
+ " lowp vec2 needMirror = step(vWrap, mod(texCoord, 2.0*vWrap)); \n"
+ " highp vec2 invertedCoord = mod(-texCoord-vec2(1.0), vWrap); \n"
+ " texCoord += vMirrorEn*needMirror*(invertedCoord-texCoord); \n"
+ " highp vec2 wrappedCoord = mod(texCoord,vWrap); \n"
+ " texCoord += vWrapEn*(wrappedCoord-texCoord); \n"
+ " return texCoord; \n"
+ "} \n"
+
+ "highp vec2 wrap2D(in highp vec2 tc, in highp vec2 size) \n"
+ "{ \n"
+ " highp float divs = floor(tc.s / size.s); \n"
+ " highp float divt = floor((tc.t + divs) / size.t); \n"
+ " return vec2(tc.s - divs * size.s, tc.t + divs - divt*size.t); \n"
+ "} \n"
+
+ "void textureEngine0(in highp vec2 texCoord, out highp vec2 tcData[5]) \n"
+ "{ \n"
+ " highp vec2 tileCoord = (WRAP(texCoord * uShiftScale[0] - uTexOffset[0], -1024.0, 1024.0));\n"
+ " tileCoord = (tileCoord + uBilinearOffset) * uHDRatio[0] - uBilinearOffset; \n"
+ " mediump vec2 intPart = floor(tileCoord); \n"
+ " highp vec2 tc00 = clampWrapMirror(intPart, uTexWrap[0], uTexClamp[0], uTexWrapEn[0], uTexClampEn[0], uTexMirrorEn[0]); \n"
+ " highp vec2 tc11 = clampWrapMirror(intPart + vec2(1.0,1.0), uTexWrap[0], uTexClamp[0], uTexWrapEn[0], uTexClampEn[0], uTexMirrorEn[0]); \n"
+ " tcData[0] = wrap2D(tc00, uTexSize[0]) + uCacheOffset[0]; \n"
+ " tcData[3] = wrap2D(tc11, uTexSize[0]) + uCacheOffset[0]; \n"
+ " tcData[1] = vec2(tcData[0].s, tcData[3].t); \n"
+ " tcData[2] = vec2(tcData[3].s, tcData[0].t); \n"
+ " tcData[4] = tileCoord - intPart; \n"
+ "} \n"
+
+ "void textureEngine1(in highp vec2 texCoord, out highp vec2 tcData[5]) \n"
+ "{ \n"
+ " highp vec2 tileCoord = (WRAP(texCoord * uShiftScale[1] - uTexOffset[1], -1024.0, 1024.0)); \n"
+ " tileCoord = (tileCoord + uBilinearOffset) * uHDRatio[1] - uBilinearOffset; \n"
+ " mediump vec2 intPart = floor(tileCoord); \n"
+ " highp vec2 tc00 = clampWrapMirror(intPart, uTexWrap[1], uTexClamp[1], uTexWrapEn[1], uTexClampEn[1], uTexMirrorEn[1]); \n"
+ " highp vec2 tc11 = clampWrapMirror(intPart + vec2(1.0,1.0), uTexWrap[1], uTexClamp[1], uTexWrapEn[1], uTexClampEn[1], uTexMirrorEn[1]); \n"
+ " tcData[0] = wrap2D(tc00, uTexSize[1]) + uCacheOffset[1]; \n"
+ " tcData[3] = wrap2D(tc11, uTexSize[1]) + uCacheOffset[1]; \n"
+ " tcData[1] = vec2(tcData[0].s, tcData[3].t); \n"
+ " tcData[2] = vec2(tcData[3].s, tcData[0].t); \n"
+ " tcData[4] = tileCoord - intPart; \n"
+ "} \n"
+
+ ;
+ }
+};
+
+class ShaderFragmentTextureEngineTex0 : public ShaderPart {
+public:
+ ShaderFragmentTextureEngineTex0(const opengl::GLInfo _glinfo)
+ {
+ m_part =
+ "textureEngine0(mTexCoord0, tcData0); \n"
+ ;
+ }
+};
+
+class ShaderFragmentTextureEngineTex1 : public ShaderPart {
+public:
+ ShaderFragmentTextureEngineTex1(const opengl::GLInfo _glinfo)
+ {
+ m_part =
+ "textureEngine1(mTexCoord1, tcData1); \n"
+ ;
+ }
+};
+
+CombinerProgramBuilderAccurate::CombinerProgramBuilderAccurate(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram)
+: CombinerProgramBuilderCommon(_glinfo, _useProgram, std::make_unique(_glinfo),
+ std::make_unique(_glinfo))
+, m_fragmentGlobalVariablesTex(new ShaderFragmentGlobalVariablesTex(_glinfo))
+, m_fragmentHeaderTextureEngine(new ShaderFragmentHeaderTextureEngine(_glinfo))
+, m_fragmentHeaderReadMSTex(new ShaderFragmentHeaderReadMSTex(_glinfo))
+, m_fragmentHeaderReadTex(new ShaderFragmentHeaderReadTex(_glinfo))
+, m_fragmentHeaderReadTexCopyMode(new ShaderFragmentHeaderReadTexCopyMode(_glinfo))
+, m_fragmentReadTex0(new ShaderFragmentReadTex0(_glinfo))
+, m_fragmentReadTex1(new ShaderFragmentReadTex1(_glinfo))
+, m_fragmentTextureEngineTex0(new ShaderFragmentTextureEngineTex0(_glinfo))
+, m_fragmentTextureEngineTex1(new ShaderFragmentTextureEngineTex1(_glinfo))
+, m_fragmentReadTexCopyMode(new ShaderFragmentReadTexCopyMode(_glinfo))
+, m_shaderMipmap(new ShaderMipmap(_glinfo))
+, m_shaderReadtex(new ShaderReadtex(_glinfo))
+, m_shaderReadtexCopyMode(new ShaderReadtexCopyMode(_glinfo))
+, m_shaderTextureEngine(new ShaderTextureEngine(_glinfo))
+{
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentGlobalVariablesTex(std::stringstream& ssShader) const
+{
+ m_fragmentGlobalVariablesTex->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentHeaderReadMSTex(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadMSTex->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentHeaderReadTex(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadTex->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentHeaderReadTexCopyMode(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadTexCopyMode->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentHeaderClampWrapMirrorEngine(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderTextureEngine->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentClampWrapMirrorEngineTex0(std::stringstream& ssShader) const
+{
+ m_fragmentTextureEngineTex0->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentClampWrapMirrorEngineTex1(std::stringstream& ssShader) const
+{
+ m_fragmentTextureEngineTex1->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentReadTex0(std::stringstream& ssShader) const
+{
+ m_fragmentReadTex0->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentReadTex1(std::stringstream& ssShader) const
+{
+ m_fragmentReadTex1->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeFragmentReadTexCopyMode(std::stringstream& ssShader) const
+{
+ m_fragmentReadTexCopyMode->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeShaderClampWrapMirrorEngine(std::stringstream& ssShader) const
+{
+ m_shaderTextureEngine->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeShaderMipmap(std::stringstream& ssShader) const
+{
+ m_shaderMipmap->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeShaderReadtex(std::stringstream& ssShader) const
+{
+ m_shaderReadtex->write(ssShader);
+}
+
+void CombinerProgramBuilderAccurate::_writeShaderReadtexCopyMode(std::stringstream& ssShader) const
+{
+ m_shaderReadtexCopyMode->write(ssShader);
+}
+
+}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.h
new file mode 100644
index 00000000..bb12510d
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderAccurate.h
@@ -0,0 +1,46 @@
+#pragma once
+#include
+
+namespace glsl {
+
+class CombinerProgramUniformFactory;
+
+class CombinerProgramBuilderAccurate : public glsl::CombinerProgramBuilderCommon
+{
+public:
+ CombinerProgramBuilderAccurate(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram);
+
+private:
+ void _writeFragmentGlobalVariablesTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadMSTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadTexCopyMode(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderClampWrapMirrorEngine(std::stringstream& ssShader) const override;
+ void _writeFragmentClampWrapMirrorEngineTex0(std::stringstream& ssShader) const override;
+ void _writeFragmentClampWrapMirrorEngineTex1(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTex0(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTex1(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTexCopyMode(std::stringstream& ssShader) const override;
+ void _writeShaderClampWrapMirrorEngine(std::stringstream& ssShader) const override;
+ void _writeShaderMipmap(std::stringstream& ssShader) const override;
+ void _writeShaderReadtex(std::stringstream& ssShader) const override;
+ void _writeShaderReadtexCopyMode(std::stringstream& ssShader) const override;
+
+ ShaderPartPtr m_fragmentGlobalVariablesTex;
+ ShaderPartPtr m_fragmentHeaderTextureEngine;
+ ShaderPartPtr m_fragmentHeaderReadMSTex;
+ ShaderPartPtr m_fragmentHeaderReadTex;
+ ShaderPartPtr m_fragmentHeaderReadTexCopyMode;
+ ShaderPartPtr m_fragmentReadTexCopyMode;
+ ShaderPartPtr m_fragmentReadTex0;
+ ShaderPartPtr m_fragmentReadTex1;
+ ShaderPartPtr m_shaderMipmap;
+ ShaderPartPtr m_shaderReadtex;
+ ShaderPartPtr m_shaderReadtexCopyMode;
+ ShaderPartPtr m_shaderTextureEngine;
+ ShaderPartPtr m_fragmentTextureEngineTex0;
+ ShaderPartPtr m_fragmentTextureEngineTex1;
+};
+
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
new file mode 100644
index 00000000..a2286793
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.cpp
@@ -0,0 +1,1681 @@
+#include // for setprecision
+#include
+#include
+#include
+#include "glsl_Utils.h"
+#include "glsl_CombinerInputs.h"
+#include "glsl_CombinerProgramImpl.h"
+#include "glsl_CombinerProgramBuilderCommon.h"
+#include "glsl_CombinerProgramUniformFactory.h"
+#include "GraphicsDrawer.h"
+
+namespace glsl {
+
+/*---------------ShaderParts-------------*/
+
+class VertexShaderHeader : public ShaderPart
+{
+public:
+ VertexShaderHeader(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part = "#version 100 \n";
+ m_part +=
+ "#if (__VERSION__ > 120) \n"
+ "# define IN in \n"
+ "# define OUT out \n"
+ "#else \n"
+ "# define IN attribute \n"
+ "# define OUT varying \n"
+ "#ifndef GL_FRAGMENT_PRECISION_HIGH \n"
+ "# define highp mediump \n"
+ "#endif \n"
+ "#endif // __VERSION \n"
+ ;
+ }
+ else if (_glinfo.isGLESX) {
+ std::stringstream ss;
+ ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 es " << std::endl;
+ ss << "# define IN in" << std::endl << "# define OUT out" << std::endl;
+ if (_glinfo.noPerspective) {
+ ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl
+ << "noperspective OUT highp float vZCoord;" << std::endl
+ << "uniform lowp int uClampMode;" << std::endl;
+ }
+ m_part = ss.str();
+ }
+ else {
+ std::stringstream ss;
+ ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 core " << std::endl;
+ ss << "# define IN in" << std::endl << "# define OUT out" << std::endl;
+ m_part = ss.str();
+ }
+ m_part += "uniform lowp vec2 uVertexOffset; \n";
+ std::stringstream ss;
+ ss << "const lowp float screenSizeDims = " << std::setprecision(1) << std::fixed << SCREEN_SIZE_DIM << ";" << std::endl;
+ m_part += ss.str();
+ }
+};
+
+class VertexShaderTriangle : public ShaderPart
+{
+public:
+ VertexShaderTriangle(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "IN highp vec4 aPosition; \n"
+ "IN lowp vec4 aColor; \n"
+ "IN lowp float aNumLights; \n"
+ "IN highp vec4 aModify; \n"
+ "IN highp vec2 aBaryCoords; \n"
+ " \n"
+ "uniform lowp int uFogUsage; \n"
+ "uniform mediump vec2 uFogScale; \n"
+ "uniform mediump vec2 uScreenCoordsScale; \n"
+ "uniform mediump vec2 uVTrans; \n"
+ "uniform mediump vec2 uVScale; \n"
+ "uniform mediump vec2 uAdjustTrans; \n"
+ "uniform mediump vec2 uAdjustScale; \n"
+ " \n"
+ "OUT lowp float vNumLights; \n"
+ "OUT lowp vec4 vShadeColor; \n"
+ "OUT highp vec4 vBaryCoords; \n"
+ ;
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
+ else
+ m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
+ m_part +=
+ " \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = aPosition; \n"
+ " vShadeColor = aColor; \n"
+ " vNumLights = aNumLights; \n"
+ " if ((aModify[0]) != 0.0) { \n"
+ " gl_Position.xy *= gl_Position.w; \n"
+ " } \n"
+ " else { \n"
+ " gl_Position.xy = gl_Position.xy * uVScale.xy + uVTrans.xy * gl_Position.ww; \n"
+ " gl_Position.xy = floor(gl_Position.xy * vec2(4.0)) * vec2(0.25); \n"
+ " gl_Position.xy = gl_Position.xy * uAdjustScale + gl_Position.ww * uAdjustTrans; \n"
+ " } \n"
+ " if ((aModify[1]) != 0.0) \n"
+ " gl_Position.z *= gl_Position.w; \n"
+ " if ((aModify[3]) != 0.0) \n"
+ " vNumLights = 0.0; \n"
+ " if (uFogUsage > 0) { \n"
+ " lowp float fp; \n"
+ " if (aPosition.z < -aPosition.w && aModify[1] == 0.0) \n"
+ " fp = -uFogScale.s + uFogScale.t; \n"
+ " else \n"
+ " fp = aPosition.z/aPosition.w*uFogScale.s + uFogScale.t; \n"
+ " fp = clamp(fp, 0.0, 1.0); \n"
+ " if (uFogUsage == 1) \n"
+ " vShadeColor.a = fp; \n"
+ " else \n"
+ " vShadeColor.rgb = vec3(fp); \n"
+ " } \n"
+ " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n"
+ " vShadeColorNoperspective = vShadeColor; \n"
+ ;
+ }
+};
+
+class VertexShaderTexturedRect : public ShaderPart
+{
+public:
+ VertexShaderTexturedRect(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "IN highp vec4 aRectPosition; \n"
+ "IN highp vec2 aTexCoord0; \n"
+ "IN highp vec2 aTexCoord1; \n"
+ "IN highp vec2 aBaryCoords; \n"
+ " \n"
+ "OUT highp vec2 vTexCoord0; \n"
+ "OUT highp vec2 vTexCoord1; \n"
+ "OUT lowp vec4 vShadeColor; \n"
+ "OUT highp vec4 vBaryCoords;"
+ ;
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
+ else
+ m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
+ m_part +=
+ "uniform lowp vec4 uRectColor; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = aRectPosition; \n"
+ " vShadeColor = uRectColor; \n"
+ " vShadeColorNoperspective = uRectColor; \n"
+ " vTexCoord0 = aTexCoord0; \n"
+ " vTexCoord1 = aTexCoord1; \n"
+ " vBaryCoords = vec4(aBaryCoords, vec2(1.0) - aBaryCoords); \n"
+ ;
+ }
+};
+
+class VertexShaderRect : public ShaderPart
+{
+public:
+ VertexShaderRect(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "IN highp vec4 aRectPosition; \n"
+ "IN highp vec2 aBaryCoords; \n"
+ " \n"
+ "OUT lowp vec4 vShadeColor; \n"
+ "OUT highp vec4 vBaryCoords; \n"
+ ;
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
+ else
+ m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
+ m_part +=
+ "uniform lowp vec4 uRectColor; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = aRectPosition; \n"
+ " vShadeColor = uRectColor; \n"
+ " vShadeColorNoperspective = uRectColor; \n"
+ " vBaryCoords = vec4(aBaryCoords, vec2(1.0) - aBaryCoords);\n"
+ ;
+ }
+};
+
+class VertexShaderEnd : public ShaderPart
+{
+public:
+ VertexShaderEnd(const opengl::GLInfo & _glinfo)
+ {
+ if (!_glinfo.isGLESX) {
+ m_part =
+ " gl_ClipDistance[0] = gl_Position.w - gl_Position.z; \n"
+ ;
+ } else if (config.generalEmulation.enableFragmentDepthWrite != 0 && _glinfo.noPerspective) {
+ m_part =
+ " vZCoord = gl_Position.z / gl_Position.w; \n"
+ " if (uClampMode > 0) \n"
+ " gl_Position.z = 0.0; \n"
+ ;
+ } else if (config.generalEmulation.enableClipping != 0) {
+ // Move the near plane towards the camera.
+ // It helps to avoid issues with near-plane clipping in games, which do not use it.
+ // Z must be scaled back in fragment shader.
+ m_part = " gl_Position.z /= 8.0; \n";
+ }
+ m_part +=
+ " gl_Position.xy += uVertexOffset * vec2(gl_Position.w); \n"
+ " gl_Position.xy -= vec2(0.5*screenSizeDims) * gl_Position.ww; \n"
+ " gl_Position.xy /= vec2(0.5*screenSizeDims); \n"
+ "} \n"
+ ;
+ }
+};
+
+class FragmentShaderHeader : public ShaderPart
+{
+public:
+ FragmentShaderHeader(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part = "#version 100 \n";
+ if (config.generalEmulation.enableLOD) {
+ m_part += "#extension GL_EXT_shader_texture_lod : enable \n";
+ m_part += "#extension GL_OES_standard_derivatives : enable \n";
+ }
+ m_part +=
+ "#if (__VERSION__ > 120) \n"
+ "# define IN in \n"
+ "# define OUT out \n"
+ "# define texture2D texture \n"
+ "#else \n"
+ "# define IN varying \n"
+ "# define OUT \n"
+ "#ifndef GL_FRAGMENT_PRECISION_HIGH \n"
+ "# define highp mediump \n"
+ "#endif \n"
+ "#endif // __VERSION __ \n"
+ ;
+ } else if (_glinfo.isGLESX) {
+ std::stringstream ss;
+ ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 es " << std::endl;
+ if (_glinfo.noPerspective)
+ ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl;
+ if (_glinfo.dual_source_blending)
+ ss << "#extension GL_EXT_blend_func_extended : enable" << std::endl;
+ if (_glinfo.ext_fetch)
+ ss << "#extension GL_EXT_shader_framebuffer_fetch : enable" << std::endl;
+ if (_glinfo.ext_fetch_arm)
+ ss << "#extension GL_ARM_shader_framebuffer_fetch : enable" << std::endl;
+
+ if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast) {
+ if (_glinfo.imageTextures && _glinfo.fragment_interlockNV) {
+ ss << "#extension GL_NV_fragment_shader_interlock : enable" << std::endl
+ << "layout(pixel_interlock_ordered) in;" << std::endl;
+ }
+ } else if (_glinfo.fetch_depth)
+ ss << "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : enable" << std::endl;
+
+ ss << "# define IN in" << std::endl
+ << "# define OUT out" << std::endl
+ << "# define texture2D texture" << std::endl;
+ m_part = ss.str();
+ } else {
+ std::stringstream ss;
+ ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 core " << std::endl;
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ if (_glinfo.n64DepthWithFbFetch)
+ ss << "#extension GL_EXT_shader_framebuffer_fetch : enable" << std::endl;
+ else if (_glinfo.imageTextures) {
+ if (_glinfo.majorVersion * 10 + _glinfo.minorVersion < 42) {
+ ss << "#extension GL_ARB_shader_image_load_store : enable" << std::endl
+ << "#extension GL_ARB_shading_language_420pack : enable" << std::endl;
+ }
+ if (_glinfo.fragment_interlock)
+ ss << "#extension GL_ARB_fragment_shader_interlock : enable" << std::endl
+ << "layout(pixel_interlock_ordered) in;" << std::endl;
+ else if (_glinfo.fragment_interlockNV)
+ ss << "#extension GL_NV_fragment_shader_interlock : enable" << std::endl
+ << "layout(pixel_interlock_ordered) in;" << std::endl;
+ else if (_glinfo.fragment_ordering)
+ ss << "#extension GL_INTEL_fragment_shader_ordering : enable" << std::endl;
+ }
+ }
+
+ ss << "# define IN in" << std::endl
+ << "# define OUT out" << std::endl
+ << "# define texture2D texture" << std::endl;
+ m_part = ss.str();
+ }
+ m_part +=
+ // Return the vector of the standard basis of R^4 with a 1 at position and 0 otherwise.
+ " #define STVEC(pos) (step(float(pos), vec4(0.5,1.5,2.5,3.5)) - step(float(pos), vec4(-0.5,0.5,1.5,2.5))) \n";
+
+ std::stringstream ss;
+ if (_glinfo.isGLES2)
+ ss << "const mediump float mipmapTileWidth = " << std::setprecision(1) << std::fixed << f32(MIPMAP_TILE_WIDTH) << ";" << std::endl;
+ else
+ ss << "const mediump int mipmapTileWidth = " << MIPMAP_TILE_WIDTH << ";" << std::endl;
+ m_part += ss.str();
+ }
+};
+
+class ShaderBlender1 : public ShaderPart
+{
+public:
+ ShaderBlender1(const opengl::GLInfo & _glinfo)
+ {
+#if 1
+ m_part =
+ " srcColor1 = vec4(0.0); \n"
+ " dstFactor1 = 0.0; \n"
+ " muxPM[0] = clampedColor; \n"
+ " muxA[0] = clampedColor.a; \n"
+ " muxa = MUXA(uBlendMux1[1]); \n"
+ " muxB[0] = 1.0 - muxa; \n"
+ " muxb = MUXB(uBlendMux1[3]); \n"
+ " muxp = MUXPM(uBlendMux1[0]); \n"
+ " muxm = MUXPM(uBlendMux1[2]); \n"
+ " muxaf = MUXF(uBlendMux1[0]); \n"
+ " muxbf = MUXF(uBlendMux1[2]); \n"
+ " if (uForceBlendCycle1 != 0) { \n"
+ " srcColor1 = muxp * muxa + muxm * muxb; \n"
+ " dstFactor1 = muxaf * muxa + muxbf * muxb; \n"
+ " srcColor1 = clamp(srcColor1, 0.0, 1.0); \n"
+ " } else { \n"
+ " srcColor1 = muxp; \n"
+ " dstFactor1 = muxaf; \n"
+ " } \n"
+ " fragColor = srcColor1; \n"
+ " fragColor1 = vec4(dstFactor1); \n"
+ ;
+#else
+ // Keep old code for reference
+ m_part =
+ " muxPM[0] = clampedColor; \n"
+ " if (uForceBlendCycle1 != 0) { \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"
+ " } else clampedColor.rgb = muxPM[uBlendMux1[0]].rgb; \n"
+ ;
+#endif
+ }
+};
+
+class ShaderBlender2 : public ShaderPart
+{
+public:
+ ShaderBlender2(const opengl::GLInfo & _glinfo)
+ {
+#if 1
+ m_part =
+ " srcColor2 = vec4(0.0); \n"
+ " dstFactor2 = 0.0; \n"
+ " muxPM[0] = srcColor1; \n"
+ " muxa = MUXA(uBlendMux2[1]); \n"
+ " muxB[0] = 1.0 - muxa; \n"
+ " muxb = MUXB(uBlendMux2[3]); \n"
+ " muxp = MUXPM(uBlendMux2[0]); \n"
+ " muxm = MUXPM(uBlendMux2[2]); \n"
+ " muxF[0] = dstFactor1; \n"
+ " muxaf = MUXF(uBlendMux2[0]); \n"
+ " muxbf = MUXF(uBlendMux2[2]); \n"
+ " if (uForceBlendCycle2 != 0) { \n"
+ " srcColor2 = muxp * muxa + muxm * muxb; \n"
+ " dstFactor2 = muxaf * muxa + muxbf * muxb; \n"
+ " srcColor2 = clamp(srcColor2, 0.0, 1.0); \n"
+ " } else { \n"
+ " srcColor2 = muxp; \n"
+ " dstFactor2 = muxaf; \n"
+ " } \n"
+ " fragColor = srcColor2; \n"
+ " fragColor1 = vec4(dstFactor2); \n"
+ ;
+
+#else
+ // Keep old code for reference
+ m_part =
+ " muxPM[0] = clampedColor; \n"
+ " muxPM[1] = vec4(0.0); \n"
+ " if (uForceBlendCycle2 != 0) { \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"
+ " } else clampedColor.rgb = muxPM[uBlendMux2[0]].rgb; \n"
+ ;
+#endif
+ }
+};
+
+class ShaderBlenderAlpha : public ShaderPart
+{
+public:
+ ShaderBlenderAlpha(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.dual_source_blending || _glinfo.ext_fetch || _glinfo.ext_fetch_arm) {
+ m_part +=
+ "if (uBlendAlphaMode != 2) { \n"
+ " lowp vec4 srcAlpha = vec4(cvg, cvg, 1.0, 0.0); \n"
+ " lowp vec4 dstFactorAlpha = vec4(1.0, 1.0, 0.0, 1.0); \n"
+ " if (uBlendAlphaMode == 0) \n"
+ " dstFactorAlpha[0] = 0.0; \n"
+ " fragColor1.a = dstFactorAlpha[uCvgDest]; \n"
+ " fragColor.a = srcAlpha[uCvgDest] + lastFragColor.a * fragColor1.a;\n"
+ "} else fragColor.a = clampedColor.a; \n";
+ }
+ else {
+ m_part +=
+ "fragColor.a = clampedColor.a;"
+ ;
+ }
+ }
+};
+
+class ShaderLegacyBlender : public ShaderPart
+{
+public:
+ ShaderLegacyBlender()
+ {
+ m_part =
+ " if (uFogUsage == 1) \n"
+ " fragColor.rgb = mix(fragColor.rgb, uFogColor.rgb, vShadeColor.a); \n"
+ ;
+ }
+};
+
+
+/*
+// 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;
+*/
+class ShaderClamp : public ShaderPart
+{
+public:
+ ShaderClamp()
+ {
+ m_part =
+ " lowp vec4 wrappedColor = WRAP(cmbRes, -0.51, 1.51); \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;
+*/
+class ShaderSignExtendColorC : public ShaderPart
+{
+public:
+ ShaderSignExtendColorC()
+ {
+ m_part =
+ " color1 = WRAP(color1, -1.01, 1.01); \n"
+ ;
+ }
+};
+
+class ShaderSignExtendAlphaC : public ShaderPart
+{
+public:
+ ShaderSignExtendAlphaC()
+ {
+ m_part =
+ " alpha1 = WRAP(alpha1, -1.01, 1.01); \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;
+*/
+class ShaderSignExtendColorABD : public ShaderPart
+{
+public:
+ ShaderSignExtendColorABD()
+ {
+ m_part =
+ " color1 = WRAP(color1, -0.51, 1.51); \n"
+ ;
+ }
+};
+
+class ShaderSignExtendAlphaABD : public ShaderPart
+{
+public:
+ ShaderSignExtendAlphaABD()
+ {
+ m_part =
+ " alpha1 = WRAP(alpha1, -0.51,1.51); \n"
+ ;
+ }
+};
+
+class ShaderAlphaTest : public ShaderPart
+{
+public:
+ ShaderAlphaTest()
+ {
+ m_part =
+ " 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"
+ ;
+ }
+};
+
+class ShaderDithering : public ShaderPart
+{
+public:
+ ShaderDithering(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2)
+ return;
+
+ if (config.generalEmulation.enableDitheringPattern == 0) {
+ m_part =
+ " if (uColorDitherMode == 2) { \n"
+ " colorDither(snoiseRGB(), clampedColor.rgb); \n"
+ " } \n"
+ " if (uAlphaDitherMode == 2) { \n"
+ " alphaDither(snoiseA(), clampedColor.a); \n"
+ " } \n"
+ ;
+ return;
+ }
+
+ m_part +=
+ " lowp mat4 bayer = mat4( 0.0, 0.5, 0.125, 0.625, \n"
+ " 0.5, 0.0, 0.625, 0.125, \n"
+ " 0.375, 0.875, 0.25, 0.75, \n"
+ " 0.875, 0.375, 0.75, 0.25); \n"
+ " lowp mat4 mSquare = mat4( 0.0, 0.75, 0.125, 0.875, \n"
+ " 0.5, 0.25, 0.625, 0.375, \n"
+ " 0.375, 0.625, 0.25, 0.5, \n"
+ " 0.875, 0.125, 0.75, 0.0); \n"
+ // Try to keep dithering visible even at higher resolutions
+ " lowp float divider = 1.0 + step(3.0, uScreenScale.x); \n"
+ " mediump ivec2 position = ivec2(mod((gl_FragCoord.xy - 0.5) / divider,4.0));\n"
+ " lowp vec4 posX = STVEC(position.x); \n"
+ " lowp vec4 posY = STVEC(position.y); \n"
+ " lowp float bayerThreshold = dot(bayer*posY, posX); \n"
+ " lowp float mSquareThreshold = dot(mSquare*posY, posX); \n"
+ " switch (uColorDitherMode) { \n"
+ " case 0: \n"
+ " colorDither(vec3(mSquareThreshold), clampedColor.rgb); \n"
+ " break; \n"
+ " case 1: \n"
+ " colorDither(vec3(bayerThreshold), clampedColor.rgb); \n"
+ " break; \n"
+ " case 2: \n"
+ " colorDither(snoiseRGB(), clampedColor.rgb); \n"
+ " break; \n"
+ " case 3: \n"
+ " break; \n"
+ " } \n"
+ " switch (uAlphaDitherMode) { \n"
+ " case 0: \n"
+ " switch (uColorDitherMode) { \n"
+ " case 0: \n"
+ " case 2: \n"
+ " alphaDither(mSquareThreshold, clampedColor.a); \n"
+ " break; \n"
+ " case 1: \n"
+ " case 3: \n"
+ " alphaDither(bayerThreshold, clampedColor.a); \n"
+ " break; \n"
+ " } \n"
+ " break; \n"
+ " case 1: \n"
+ " switch (uColorDitherMode) { \n"
+ " case 0: \n"
+ " case 2: \n"
+ " alphaDither(bayerThreshold, clampedColor.a); \n"
+ " break; \n"
+ " case 1: \n"
+ " case 3: \n"
+ " alphaDither(mSquareThreshold, clampedColor.a); \n"
+ " break; \n"
+ " } \n"
+ " break; \n"
+ " case 2: \n"
+ " alphaDither(snoiseA(), clampedColor.a); \n"
+ " break; \n"
+ " case 3: \n"
+ " break; \n"
+ " } \n"
+ ;
+ }
+};
+
+class ShaderFragmentGlobalVariablesNotex : public ShaderPart
+{
+public:
+ ShaderFragmentGlobalVariablesNotex(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "uniform lowp vec4 uFogColor; \n"
+ "uniform lowp vec4 uCenterColor;\n"
+ "uniform lowp vec4 uScaleColor; \n"
+ "uniform lowp vec4 uBlendColor; \n"
+ "uniform lowp vec4 uEnvColor; \n"
+ "uniform lowp vec4 uPrimColor; \n"
+ "uniform lowp float uPrimLod; \n"
+ "uniform lowp float uK4; \n"
+ "uniform lowp float uK5; \n"
+ "uniform lowp int uAlphaCompareMode; \n"
+ "uniform lowp ivec2 uFbMonochrome; \n"
+ "uniform lowp ivec2 uFbFixedAlpha; \n"
+ "uniform lowp int uEnableAlphaTest; \n"
+ "uniform lowp int uCvgXAlpha; \n"
+ "uniform lowp int uAlphaCvgSel; \n"
+ "uniform lowp float uAlphaTestValue; \n"
+ "uniform lowp int uDepthSource; \n"
+ "uniform highp float uPrimDepth; \n"
+ "uniform mediump vec2 uScreenScale; \n"
+ "uniform lowp int uScreenSpaceTriangle; \n"
+ "uniform lowp int uCvgDest; \n"
+ "uniform lowp int uBlendAlphaMode; \n"
+ "lowp float cvg; \n"
+ ;
+
+ if (config.generalEmulation.enableLegacyBlending != 0) {
+ m_part +=
+ "uniform lowp int uFogUsage; \n"
+ ;
+ } else {
+ m_part +=
+ "uniform lowp ivec4 uBlendMux1; \n"
+ "uniform lowp int uForceBlendCycle1;\n"
+ ;
+ }
+
+ if (!_glinfo.isGLES2) {
+ m_part +=
+ "uniform sampler2D uDepthTex; \n"
+ "uniform lowp int uAlphaDitherMode; \n"
+ "uniform lowp int uColorDitherMode; \n"
+ "uniform lowp int uRenderTarget; \n"
+ "uniform mediump vec2 uDepthScale; \n"
+ ;
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part +=
+ "uniform lowp int uEnableDepthCompare; \n"
+ ;
+ }
+ } else {
+ m_part +=
+ "lowp int nCurrentTile; \n"
+ ;
+ }
+
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective IN lowp vec4 vShadeColorNoperspective; \n";
+ else
+ m_part += "IN lowp vec4 vShadeColorNoperspective; \n";
+
+ m_part +=
+ "IN lowp vec4 vShadeColor; \n"
+ "IN lowp float vNumLights; \n"
+ "IN highp vec4 vBaryCoords; \n"
+ ;
+
+ if (_glinfo.dual_source_blending) {
+ m_part +=
+ "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" // SECONDARY FRAGMENT SHADER OUTPUT
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ } else if (_glinfo.ext_fetch) {
+ m_part +=
+ "layout(location = 0) inout lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR fragColor \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA fragColor.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else if (_glinfo.ext_fetch_arm) {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR gl_LastFragColorARM \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA gl_LastFragColorARM.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ }
+
+ if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ "layout(location = 1) inout highp vec4 depthZ; \n"
+ "layout(location = 2) inout highp vec4 depthDeltaZ; \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentHeaderNoise : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderNoise(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "lowp float snoise();\n"
+ ;
+ }
+};
+
+class ShaderFragmentHeaderWriteDepth : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderWriteDepth(const opengl::GLInfo & _glinfo)
+ {
+ if (!_glinfo.isGLES2) {
+ m_part =
+ "highp float writeDepth();\n";
+ ;
+ }
+ if (_glinfo.isGLESX && _glinfo.noPerspective) {
+ m_part =
+ "noperspective IN highp float vZCoord; \n"
+ "uniform lowp float uPolygonOffset; \n"
+ "uniform lowp int uClampMode; \n"
+ + m_part
+ ;
+ }
+
+ }
+};
+
+class ShaderFragmentHeaderCalcLight : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderCalcLight(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "void calc_light(in lowp float fLights, in lowp vec3 input_color, out lowp vec3 output_color);\n";
+ ;
+ }
+};
+
+class ShaderFragmentHeaderMipMap : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderMipMap(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1);\n";
+ ;
+ }
+};
+
+class ShaderFragmentHeaderDither : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderDither(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2)
+ return;
+
+ m_part =
+ "void colorDither(in lowp vec3 _threshold, inout lowp vec3 _color);\n"
+ "void alphaDither(in lowp float _threshold, inout lowp float _alpha);\n"
+ "lowp vec3 snoiseRGB();\n"
+ "lowp float snoiseA();\n"
+ ;
+ }
+};
+
+class ShaderFragmentHeaderDepthCompare : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderDepthCompare(const opengl::GLInfo & _glinfo)
+ {
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part =
+ "bool depth_compare(highp float curZ); \n"
+ "bool depth_render(highp float Z, highp float curZ); \n"
+ ;
+ if (_glinfo.imageTextures & !_glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ "layout(binding = 2, r32f) highp uniform restrict image2D uDepthImageZ; \n"
+ "layout(binding = 3, r32f) highp uniform restrict image2D uDepthImageDeltaZ; \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderFragmentMain : public ShaderPart
+{
+public:
+ ShaderFragmentMain(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "void main() \n"
+ "{ \n"
+ ;
+ if (!_glinfo.isGLES2) {
+ m_part +=
+ " highp float fragDepth = writeDepth(); \n"
+ ;
+ }
+ m_part +=
+ " lowp vec4 vec_color; \n"
+ " lowp float alpha1; \n"
+ " lowp vec3 color1, input_color; \n"
+ ;
+ if (config.generalEmulation.enableClipping != 0)
+ m_part +=
+ " lowp vec4 shadeColor = vShadeColorNoperspective; \n"
+ ;
+ else
+ m_part +=
+ " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n"
+ ;
+
+ m_part += "#define WRAP(x, low, high) (mod((x)-(low), (high)-(low)) + (low)) \n"; // Return wrapped value of x in interval [low, high)
+ // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible?
+ // m_part += "#define WRAP(x, low, high) (x) + ((high)-(low)) * (1.0-step(low,x)) - ((high)-(low)) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code.
+ }
+
+};
+
+class ShaderFragmentMain2Cycle : public ShaderPart
+{
+public:
+ ShaderFragmentMain2Cycle(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "void main() \n"
+ "{ \n"
+ ;
+ if (!_glinfo.isGLES2) {
+ m_part +=
+ " highp float fragDepth = writeDepth(); \n"
+ ;
+ }
+ m_part +=
+ " lowp vec4 vec_color, combined_color; \n"
+ " lowp float alpha1, alpha2; \n"
+ " lowp vec3 color1, color2, input_color; \n"
+ ;
+ if (config.generalEmulation.enableClipping != 0)
+ m_part +=
+ " lowp vec4 shadeColor = vShadeColorNoperspective; \n"
+ ;
+ else
+ m_part +=
+ " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n"
+ ;
+
+ m_part += "#define WRAP(x, low, high) mod((x)-(low), (high)-(low)) + (low) \n"; // Return wrapped value of x in interval [low, high)
+ // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible?
+ // m_part += "#define WRAP(x, low, high) (x) + (2.0) * (1.0-step(low,x)) - (2.0) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code.
+ }
+};
+
+class ShaderFragmentBlendMux : public ShaderPart
+{
+public:
+ ShaderFragmentBlendMux(const opengl::GLInfo & _glinfo)
+ {
+ if (config.generalEmulation.enableLegacyBlending == 0) {
+ m_part =
+ " #define MUXA(pos) dot(muxA, STVEC(pos)) \n"
+ " #define MUXB(pos) dot(muxB, STVEC(pos)) \n"
+ " #define MUXPM(pos) muxPM*(STVEC(pos)) \n"
+ " #define MUXF(pos) dot(muxF, STVEC(pos)) \n"
+ " lowp vec4 lastFragColor = LAST_FRAG_COLOR; \n"
+ " lowp float lastFragAlpha = LAST_FRAG_ALPHA; \n"
+ " lowp mat4 muxPM = mat4(vec4(0.0), lastFragColor, uBlendColor, uFogColor); \n"
+ " lowp vec4 muxA = vec4(0.0, uFogColor.a, shadeColor.a, 0.0); \n"
+ " lowp vec4 muxB = vec4(0.0, lastFragAlpha, 1.0, 0.0); \n"
+ " lowp vec4 muxF = vec4(0.0, 1.0, 0.0, 0.0); \n"
+ " lowp vec4 muxp, muxm, srcColor1, srcColor2; \n"
+ " lowp float muxa, muxb, dstFactor1, dstFactor2, muxaf, muxbf; \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentReadTexMipmap : public ShaderPart
+{
+public:
+ ShaderFragmentReadTexMipmap(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ " lowp vec4 readtex0, readtex1; \n"
+ " lowp float lod_frac = mipmap(readtex0, readtex1); \n"
+ ;
+ }
+};
+
+class ShaderFragmentCallN64Depth : public ShaderPart
+{
+public:
+ ShaderFragmentCallN64Depth(const opengl::GLInfo & _glinfo)
+ {
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part = " bool should_discard = false; \n";
+
+ if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
+ if (_glinfo.fragment_interlock)
+ m_part += " beginInvocationInterlockARB(); \n";
+ else if (_glinfo.fragment_interlockNV)
+ m_part += " beginInvocationInterlockNV(); \n";
+ else if (_glinfo.fragment_ordering)
+ m_part += " beginFragmentShaderOrderingINTEL(); \n";
+ }
+
+ m_part +=
+ " if (uRenderTarget != 0) { if (!depth_render(fragColor.r, fragDepth)) should_discard = true; } \n"
+ " else if (!depth_compare(fragDepth)) should_discard = true; \n"
+ ;
+
+ if (_glinfo.imageTextures & !_glinfo.n64DepthWithFbFetch) {
+ if (_glinfo.fragment_interlock)
+ m_part += " endInvocationInterlockARB(); \n";
+ else if (_glinfo.fragment_interlockNV)
+ m_part += " endInvocationInterlockNV(); \n";
+ }
+
+ m_part += " if (should_discard) discard; \n";
+
+ }
+ }
+};
+
+class ShaderFragmentRenderTarget : public ShaderPart
+{
+public:
+ ShaderFragmentRenderTarget(const opengl::GLInfo & _glinfo)
+ {
+ if (config.generalEmulation.enableFragmentDepthWrite != 0) {
+ m_part =
+ " if (uRenderTarget != 0) { \n"
+ " if (uRenderTarget > 1) { \n"
+ " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
+ " if (fragDepth >= texelFetch(uDepthTex, coord, 0).r) discard; \n"
+ " } \n"
+ " fragDepth = fragColor.r; \n"
+ " } \n"
+ " gl_FragDepth = fragDepth; \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentMainEnd : public ShaderPart
+{
+public:
+ ShaderFragmentMainEnd(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ " gl_FragColor = fragColor; \n"
+ "} \n\n";
+ } else {
+ m_part =
+ "} \n\n"
+ ;
+ }
+ }
+};
+
+class ShaderNoise : public ShaderPart
+{
+public:
+ ShaderNoise(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ "uniform sampler2D uTexNoise; \n"
+ "lowp float snoise() \n"
+ "{ \n"
+ " mediump vec2 texSize = vec2(640.0, 580.0); \n"
+ " mediump vec2 coord = gl_FragCoord.xy/uScreenScale/texSize; \n"
+ " return texture2D(uTexNoise, coord).r; \n"
+ "} \n"
+ ;
+ } else {
+ m_part =
+ "uniform sampler2D uTexNoise; \n"
+ "lowp float snoise() \n"
+ "{ \n"
+ " ivec2 coord = ivec2(gl_FragCoord.xy/uScreenScale); \n"
+ " return texelFetch(uTexNoise, coord, 0).r; \n"
+ "} \n"
+ ;
+ }
+ }
+};
+
+class ShaderDither : public ShaderPart
+{
+public:
+ ShaderDither(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2)
+ return;
+
+ if (config.generalEmulation.enableDitheringQuantization != 0) {
+ m_part =
+ "void quantizeRGB(inout lowp vec3 _color) \n"
+ "{ \n"
+ " _color.rgb = round(_color.rgb * 32.0)/32.0; \n"
+ "} \n"
+ "void quantizeA(inout lowp float _alpha) \n"
+ "{ \n"
+ " _alpha = round(_alpha * 32.0)/32.0; \n"
+ "} \n"
+ ;
+ } else {
+ m_part =
+ "void quantizeRGB(inout lowp vec3 _color){}\n"
+ "void quantizeA(inout lowp float _alpha){}\n"
+ ;
+ }
+
+ m_part +=
+ "void colorDither(in lowp vec3 _noise, inout lowp vec3 _color)\n"
+ "{ \n"
+ " mediump vec3 threshold = 7.0 / 255.0 * (_noise - 0.5); \n"
+ " _color = clamp(_color + threshold,0.0,1.0); \n"
+ " quantizeRGB(_color); \n"
+ "} \n"
+ "void alphaDither(in lowp float _noise, inout lowp float _alpha)\n"
+ "{ \n"
+ " mediump float threshold = 7.0 / 255.0 * (_noise - 0.5); \n"
+ " _alpha = clamp(_alpha + threshold,0.0,1.0); \n"
+ " quantizeA(_alpha); \n"
+ "} \n"
+ "lowp vec3 snoiseRGB() \n"
+ "{ \n"
+ " mediump vec2 texSize = vec2(640.0, 580.0); \n"
+ ;
+ if (config.generalEmulation.enableHiresNoiseDithering != 0)
+ // multiplier for higher res noise effect
+ m_part +=
+ " lowp float mult = 1.0 + step(2.0, uScreenScale.x); \n";
+ else
+ m_part +=
+ " lowp float mult = 1.0; \n";
+ m_part +=
+ " mediump vec2 coordR = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n"
+ " mediump vec2 coordG = mult * ((gl_FragCoord.xy + vec2( 0.0, texSize.y / 2.0 ))/uScreenScale/texSize);\n"
+ " mediump vec2 coordB = mult * ((gl_FragCoord.xy + vec2( texSize.x / 2.0, 0.0))/uScreenScale/texSize);\n"
+ // Only red channel of noise texture contains noise.
+ " lowp float r = texture(uTexNoise,coordR).r; \n"
+ " lowp float g = texture(uTexNoise,coordG).r; \n"
+ " lowp float b = texture(uTexNoise,coordB).r; \n"
+ " \n"
+ " return vec3(r,g,b); \n"
+ "} \n"
+ "lowp float snoiseA() \n"
+ "{ \n"
+ " mediump vec2 texSize = vec2(640.0, 580.0); \n"
+ ;
+ if (config.generalEmulation.enableHiresNoiseDithering != 0)
+ // multiplier for higher res noise effect
+ m_part +=
+ " lowp float mult = 1.0 + step(2.0, uScreenScale.x); \n";
+ else
+ m_part +=
+ " lowp float mult = 1.0; \n";
+ m_part +=
+ " \n"
+ " mediump vec2 coord = mult * ((gl_FragCoord.xy)/uScreenScale/texSize);\n"
+ " \n"
+ // Only red channel of noise texture contains noise.
+ " return texture(uTexNoise,coord).r; \n"
+ "} \n"
+ ;
+ }
+};
+
+class ShaderWriteDepth : public ShaderPart
+{
+public:
+ ShaderWriteDepth(const opengl::GLInfo & _glinfo)
+ {
+ if (!_glinfo.isGLES2) {
+ if (config.generalEmulation.enableFragmentDepthWrite == 0 &&
+ config.frameBufferEmulation.N64DepthCompare == Config::dcDisable) {
+ // Dummy write depth
+ m_part =
+ "highp float writeDepth() \n"
+ "{ \n"
+ " return 0.0; \n"
+ "} \n"
+ ;
+ } else {
+ if ((config.generalEmulation.hacks & hack_RE2) != 0) {
+ m_part =
+ "uniform lowp usampler2D uZlutImage;\n"
+ "highp float writeDepth() \n"
+ "{ \n"
+ ;
+ if (_glinfo.isGLESX) {
+ if (_glinfo.noPerspective) {
+ m_part +=
+ " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n"
+ " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
+ " clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ ;
+ } else if (config.generalEmulation.enableClipping != 0) {
+ m_part +=
+ " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
+ " clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ ;
+ } else {
+ m_part +=
+ " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
+ " clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ ;
+ }
+ } else {
+ m_part +=
+ " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n"
+ " clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ ;
+ }
+ m_part +=
+ " highp int iZ = FragDepth > 0.999 ? 262143 : int(floor(FragDepth * 262143.0)); \n"
+ " mediump int y0 = clamp(iZ/512, 0, 511); \n"
+ " mediump int x0 = iZ - 512*y0; \n"
+ " highp uint iN64z = texelFetch(uZlutImage,ivec2(x0,y0), 0).r; \n"
+ " return clamp(float(iN64z)/65532.0, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ } else {
+ if (_glinfo.isGLESX) {
+ if (_glinfo.noPerspective) {
+ m_part =
+ "highp float writeDepth() \n"
+ "{ \n"
+ " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n"
+ " if (uDepthSource != 0) return uPrimDepth; \n"
+ " return clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ } else if (config.generalEmulation.enableClipping != 0) {
+ m_part =
+ "highp float writeDepth() \n"
+ "{ \n"
+ " if (uDepthSource != 0) return uPrimDepth; \n"
+ " return clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ } else {
+ m_part =
+ "highp float writeDepth() \n"
+ "{ \n"
+ " if (uDepthSource != 0) return uPrimDepth; \n"
+ " return clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ }
+ } else {
+ m_part =
+ "highp float writeDepth() \n"
+ "{ \n"
+ " if (uDepthSource != 0) return uPrimDepth; \n"
+ " return clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ }
+ }
+ }
+ }
+ }
+};
+
+class ShaderCalcLight : public ShaderPart
+{
+public:
+ ShaderCalcLight(const opengl::GLInfo & /*_glinfo*/)
+ {
+ m_part =
+ "uniform mediump vec3 uLightDirection[8]; \n"
+ "uniform lowp vec3 uLightColor[8]; \n"
+ "void calc_light(in lowp float fLights, in lowp vec3 input_color, out lowp vec3 output_color) {\n"
+ " output_color = input_color; \n"
+ " lowp int nLights = int(floor(fLights + 0.5)); \n"
+ " if (nLights == 0) \n"
+ " return; \n"
+ " output_color = uLightColor[nLights]; \n"
+ " mediump float intensity; \n"
+ " for (int i = 0; i < nLights; i++) { \n"
+ " intensity = max(dot(input_color, uLightDirection[i]), 0.0);\n"
+ " output_color += intensity*uLightColor[i]; \n"
+ " }; \n"
+ " output_color = clamp(output_color, 0.0, 1.0); \n"
+ "} \n"
+ ;
+ }
+};
+
+class ShaderN64DepthCompare : public ShaderPart
+{
+public:
+ ShaderN64DepthCompare(const opengl::GLInfo & _glinfo)
+ {
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part =
+ "uniform lowp int uEnableDepth; \n"
+ "uniform lowp int uDepthMode; \n"
+ "uniform lowp int uEnableDepthUpdate; \n"
+ "uniform mediump float uDeltaZ; \n"
+ "bool depth_compare(highp float curZ) \n"
+ "{ \n"
+ " if (uEnableDepth == 0) return true; \n"
+ ;
+ if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
+ " highp vec4 depthZ = imageLoad(uDepthImageZ,coord); \n"
+ " highp vec4 depthDeltaZ = imageLoad(uDepthImageDeltaZ,coord);\n"
+ ;
+ }
+ m_part +=
+ " highp float bufZ = depthZ.r; \n"
+ " highp float dz, dzMin; \n"
+ " if (uDepthSource == 1) { \n"
+ " dzMin = dz = uDeltaZ; \n"
+ " } else { \n"
+ " dz = 4.0*fwidth(curZ); \n"
+ " dzMin = min(dz, depthDeltaZ.r); \n"
+ " } \n"
+ " bool bInfront = curZ < bufZ; \n"
+ " bool bFarther = (curZ + dzMin) >= bufZ; \n"
+ " bool bNearer = (curZ - dzMin) <= bufZ; \n"
+ " bool bMax = bufZ == 1.0; \n"
+ " bool bRes = false; \n"
+ " switch (uDepthMode) { \n"
+ " case 1: \n"
+ " bRes = bMax || bNearer; \n"
+ " break; \n"
+ " case 0: \n"
+ " case 2: \n"
+ " bRes = bMax || bInfront; \n"
+ " break; \n"
+ " case 3: \n"
+ " bRes = bFarther && bNearer && !bMax; \n"
+ " break; \n"
+ " } \n"
+ " bRes = bRes || (uEnableDepthCompare == 0); \n"
+ " if (uEnableDepthUpdate != 0 && bRes) { \n"
+ ;
+ if (_glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ " depthZ.r = curZ; \n"
+ " depthDeltaZ.r = dz; \n"
+ ;
+ } else if (_glinfo.imageTextures) {
+ m_part +=
+ " highp vec4 depthOutZ = vec4(curZ, 1.0, 1.0, 1.0); \n"
+ " highp vec4 depthOutDeltaZ = vec4(dz, 1.0, 1.0, 1.0); \n"
+ " imageStore(uDepthImageZ, coord, depthOutZ); \n"
+ " imageStore(uDepthImageDeltaZ, coord, depthOutDeltaZ); \n"
+ ;
+ }
+
+ m_part +=
+ " } \n"
+ " return bRes; \n"
+ "} \n"
+ ;
+ }
+ }
+};
+
+class ShaderN64DepthRender : public ShaderPart
+{
+public:
+ ShaderN64DepthRender(const opengl::GLInfo & _glinfo)
+ {
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part =
+ "bool depth_render(highp float Z, highp float curZ) \n"
+ "{ \n"
+ " ivec2 coord = ivec2(gl_FragCoord.xy); \n"
+ " if (uEnableDepthCompare != 0) { \n"
+ ;
+ if (_glinfo.imageTextures && !_glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ " highp vec4 depthZ = imageLoad(uDepthImageZ,coord); \n"
+ ;
+ }
+ m_part +=
+ " highp float bufZ = depthZ.r; \n"
+ " if (curZ >= bufZ) return false; \n"
+ " } \n"
+ ;
+ if (_glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ " depthZ.r = Z; \n"
+ " depthDeltaZ.r = 0.0; \n"
+ ;
+ } else if (_glinfo.imageTextures) {
+ m_part +=
+ " highp vec4 depthOutZ = vec4(Z, 1.0, 1.0, 1.0); \n"
+ " highp vec4 depthOutDeltaZ = vec4(0.0, 1.0, 1.0, 1.0);\n"
+ " imageStore(uDepthImageZ,coord, depthOutZ); \n"
+ " imageStore(uDepthImageDeltaZ,coord, depthOutDeltaZ); \n"
+ ;
+ }
+
+ m_part +=
+ " return true; \n"
+ "} \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentCorrectTexCoords : public ShaderPart {
+public:
+ ShaderFragmentCorrectTexCoords() {
+ m_part +=
+ " highp vec2 mTexCoord0 = vTexCoord0 + vec2(0.0001); \n"
+ " highp vec2 mTexCoord1 = vTexCoord1 + vec2(0.0001); \n"
+ " mTexCoord0 += uTexCoordOffset[0]; \n"
+ " mTexCoord1 += uTexCoordOffset[1]; \n"
+ " if (uUseTexCoordBounds != 0) { \n"
+ " mTexCoord0 = clamp(mTexCoord0, uTexCoordBounds0.xy, uTexCoordBounds0.zw); \n"
+ " mTexCoord1 = clamp(mTexCoord1, uTexCoordBounds1.xy, uTexCoordBounds1.zw); \n"
+ " } \n"
+ ;
+ }
+};
+
+class ShaderCoverage : public ShaderPart {
+public:
+ ShaderCoverage() {
+ m_part =
+ "const highp vec2 bias[8] = vec2[8] (vec2(-0.5,-0.5), vec2(0.0, -0.5), vec2(-0.25,-0.25), vec2(0.25, -0.25), \n"
+ " vec2(-0.5, 0.0), vec2(0.0,0.0), vec2(-0.25,0.25), vec2(0.25,0.25)); \n"
+ "highp vec4 dBCdx = dFdx(vBaryCoords); \n"
+ "highp vec4 dBCdy = dFdy(vBaryCoords); \n"
+ "cvg = 0.0; \n"
+ "for (int i = 0; i<8; i++) { \n"
+ " highp vec2 currentBias = bias[i]; \n"
+ " highp vec4 baryCoordsBiased = vBaryCoords + dBCdx*currentBias.x + dBCdy * currentBias.y; \n"
+ " lowp vec4 inside = step(0.0, baryCoordsBiased); \n"
+ " cvg += 0.125 * inside[0] * inside[1] * inside[2] * inside[3]; \n"
+ "} \n"
+ ;
+ }
+};
+
+/*---------------ShaderPartsEnd-------------*/
+
+static
+GLuint _createVertexShader(ShaderPart * _header, ShaderPart * _body, ShaderPart * _footer)
+{
+ std::stringstream ssShader;
+ _header->write(ssShader);
+ _body->write(ssShader);
+ _footer->write(ssShader);
+ const std::string strShader(ssShader.str());
+ const GLchar * strShaderData = strShader.data();
+
+ GLuint shader_object = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(shader_object, 1, &strShaderData, nullptr);
+ glCompileShader(shader_object);
+ if (!Utils::checkShaderCompileStatus(shader_object))
+ Utils::logErrorShader(GL_VERTEX_SHADER, strShaderData);
+ return shader_object;
+}
+
+CombinerProgramBuilderCommon::CombinerProgramBuilderCommon(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram,
+ std::unique_ptr _uniformFactory,
+ std::unique_ptr _vertexTexturedTriangle)
+: CombinerProgramBuilder(_glinfo, _useProgram, std::move(_uniformFactory))
+, m_blender1(new ShaderBlender1(_glinfo))
+, m_blender2(new ShaderBlender2(_glinfo))
+, m_blenderAlpha (new ShaderBlenderAlpha(_glinfo))
+, m_legacyBlender(new ShaderLegacyBlender)
+, m_clamp(new ShaderClamp)
+, m_signExtendColorC(new ShaderSignExtendColorC)
+, m_signExtendAlphaC(new ShaderSignExtendAlphaC)
+, m_signExtendColorABD(new ShaderSignExtendColorABD)
+, m_signExtendAlphaABD(new ShaderSignExtendAlphaABD)
+, m_alphaTest(new ShaderAlphaTest)
+, m_callDither(new ShaderDithering(_glinfo))
+, m_vertexHeader(new VertexShaderHeader(_glinfo))
+, m_vertexEnd(new VertexShaderEnd(_glinfo))
+, m_vertexRect(new VertexShaderRect(_glinfo))
+, m_vertexTexturedRect(new VertexShaderTexturedRect(_glinfo))
+, m_vertexTriangle(new VertexShaderTriangle(_glinfo))
+, m_fragmentHeader(new FragmentShaderHeader(_glinfo))
+, m_fragmentGlobalVariablesNotex(new ShaderFragmentGlobalVariablesNotex(_glinfo))
+, m_fragmentHeaderNoise(new ShaderFragmentHeaderNoise(_glinfo))
+, m_fragmentHeaderWriteDepth(new ShaderFragmentHeaderWriteDepth(_glinfo))
+, m_fragmentHeaderCalcLight(new ShaderFragmentHeaderCalcLight(_glinfo))
+, m_fragmentHeaderMipMap(new ShaderFragmentHeaderMipMap(_glinfo))
+, m_fragmentHeaderDither(new ShaderFragmentHeaderDither(_glinfo))
+, m_fragmentHeaderDepthCompare(new ShaderFragmentHeaderDepthCompare(_glinfo))
+, m_fragmentMain(new ShaderFragmentMain(_glinfo))
+, m_fragmentMain2Cycle(new ShaderFragmentMain2Cycle(_glinfo))
+, m_fragmentBlendMux(new ShaderFragmentBlendMux(_glinfo))
+, m_fragmentCorrectTexCoords(new ShaderFragmentCorrectTexCoords())
+, m_fragmentReadTexMipmap(new ShaderFragmentReadTexMipmap(_glinfo))
+, m_fragmentCallN64Depth(new ShaderFragmentCallN64Depth(_glinfo))
+, m_fragmentRenderTarget(new ShaderFragmentRenderTarget(_glinfo))
+, m_shaderFragmentMainEnd(new ShaderFragmentMainEnd(_glinfo))
+, m_shaderNoise(new ShaderNoise(_glinfo))
+, m_shaderDither(new ShaderDither(_glinfo))
+, m_shaderWriteDepth(new ShaderWriteDepth(_glinfo))
+, m_shaderCalcLight(new ShaderCalcLight(_glinfo))
+, m_shaderN64DepthCompare(new ShaderN64DepthCompare(_glinfo))
+, m_shaderN64DepthRender(new ShaderN64DepthRender(_glinfo))
+, m_shaderCoverage(new ShaderCoverage())
+, m_combinerOptionsBits(graphics::CombinerProgram::getShaderCombinerOptionsBits())
+{
+ m_vertexShaderRect = _createVertexShader(m_vertexHeader.get(), m_vertexRect.get(), m_vertexEnd.get());
+ m_vertexShaderTriangle = _createVertexShader(m_vertexHeader.get(), m_vertexTriangle.get(), m_vertexEnd.get());
+ m_vertexShaderTexturedRect = _createVertexShader(m_vertexHeader.get(), m_vertexTexturedRect.get(), m_vertexEnd.get());
+ m_vertexShaderTexturedTriangle = _createVertexShader(m_vertexHeader.get(), _vertexTexturedTriangle.get(), m_vertexEnd.get());
+}
+
+CombinerProgramBuilderCommon::~CombinerProgramBuilderCommon()
+{
+ glDeleteShader(m_vertexShaderRect);
+ glDeleteShader(m_vertexShaderTriangle);
+ glDeleteShader(m_vertexShaderTexturedRect);
+ glDeleteShader(m_vertexShaderTexturedTriangle);
+}
+
+const ShaderPart * CombinerProgramBuilderCommon::getVertexShaderHeader() const
+{
+ return m_vertexHeader.get();
+}
+
+const ShaderPart * CombinerProgramBuilderCommon::getFragmentShaderHeader() const
+{
+ return m_fragmentHeader.get();
+}
+
+const ShaderPart * CombinerProgramBuilderCommon::getFragmentShaderEnd() const
+{
+ return m_shaderFragmentMainEnd.get();
+}
+
+bool CombinerProgramBuilderCommon::isObsolete() const
+{
+ return m_combinerOptionsBits != graphics::CombinerProgram::getShaderCombinerOptionsBits();
+}
+
+GLuint CombinerProgramBuilderCommon::_getVertexShaderRect() const
+{
+ return m_vertexShaderRect;
+}
+
+GLuint CombinerProgramBuilderCommon::_getVertexShaderTriangle() const
+{
+ return m_vertexShaderTriangle;
+}
+
+GLuint CombinerProgramBuilderCommon::_getVertexShaderTexturedRect() const
+{
+ return m_vertexShaderTexturedRect;
+}
+
+GLuint CombinerProgramBuilderCommon::_getVertexShaderTexturedTriangle() const
+{
+ return m_vertexShaderTexturedTriangle;
+}
+
+void CombinerProgramBuilderCommon::_writeSignExtendAlphaC(std::stringstream& ssShader)const
+{
+ m_signExtendAlphaC->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeSignExtendAlphaABD(std::stringstream& ssShader)const
+{
+ m_signExtendAlphaABD->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeAlphaTest(std::stringstream& ssShader)const
+{
+ m_alphaTest->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeSignExtendColorC(std::stringstream& ssShader)const
+{
+ m_signExtendColorC->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeSignExtendColorABD(std::stringstream& ssShader)const
+{
+ m_signExtendColorABD->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeClamp(std::stringstream& ssShader)const
+{
+ m_clamp->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeCallDither(std::stringstream& ssShader)const
+{
+ m_callDither->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeBlender1(std::stringstream& ssShader)const
+{
+ m_blender1->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeBlender2(std::stringstream& ssShader)const
+{
+ m_blender2->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeBlenderAlpha(std::stringstream& ssShader)const
+{
+ m_blenderAlpha->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeLegacyBlender(std::stringstream& ssShader)const
+{
+ m_legacyBlender->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeader(std::stringstream& ssShader)const
+{
+ m_fragmentHeader->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderDither(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderDither->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderNoise(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderNoise->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderWriteDepth(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderWriteDepth->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderDepthCompare(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderDepthCompare->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderMipMap(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderMipMap->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentGlobalVariablesNotex(std::stringstream& ssShader)const
+{
+ m_fragmentGlobalVariablesNotex->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentHeaderCalcLight(std::stringstream& ssShader)const
+{
+ m_fragmentHeaderCalcLight->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentMain2Cycle(std::stringstream& ssShader)const
+{
+ m_fragmentMain2Cycle->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentMain(std::stringstream& ssShader)const
+{
+ m_fragmentMain->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentBlendMux(std::stringstream& ssShader)const
+{
+ m_fragmentBlendMux->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderCoverage(std::stringstream& ssShader)const
+{
+ m_shaderCoverage->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentCorrectTexCoords(std::stringstream& ssShader)const
+{
+ m_fragmentCorrectTexCoords->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentReadTexMipmap(std::stringstream& ssShader)const
+{
+ m_fragmentReadTexMipmap->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentCallN64Depth(std::stringstream& ssShader)const
+{
+ m_fragmentCallN64Depth->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeFragmentRenderTarget(std::stringstream& ssShader)const
+{
+ m_fragmentRenderTarget->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderFragmentMainEnd(std::stringstream& ssShader)const
+{
+ m_shaderFragmentMainEnd->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderCalcLight(std::stringstream& ssShader)const
+{
+ m_shaderCalcLight->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderNoise(std::stringstream& ssShader)const
+{
+ m_shaderNoise->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderDither(std::stringstream& ssShader)const
+{
+ m_shaderDither->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderWriteDepth(std::stringstream& ssShader)const
+{
+ m_shaderWriteDepth->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderN64DepthCompare(std::stringstream& ssShader)const
+{
+ m_shaderN64DepthCompare->write(ssShader);
+}
+
+void CombinerProgramBuilderCommon::_writeShaderN64DepthRender(std::stringstream& ssShader)const
+{
+ m_shaderN64DepthRender->write(ssShader);
+}
+
+}
\ No newline at end of file
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.h
new file mode 100644
index 00000000..d7fa6177
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderCommon.h
@@ -0,0 +1,118 @@
+#pragma once
+#include
+
+namespace glsl {
+
+class CombinerProgramUniformFactory;
+
+class CombinerProgramBuilderCommon : public glsl::CombinerProgramBuilder
+{
+public:
+ CombinerProgramBuilderCommon(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram,
+ std::unique_ptr _uniformFactory,
+ std::unique_ptr _vertexTexturedTriangle);
+ ~CombinerProgramBuilderCommon();
+
+ const ShaderPart * getVertexShaderHeader() const override;
+
+ const ShaderPart * getFragmentShaderHeader() const override;
+
+ const ShaderPart * getFragmentShaderEnd() const override;
+
+ bool isObsolete() const override;
+
+private:
+
+ GLuint _getVertexShaderRect() const override;
+ GLuint _getVertexShaderTriangle() const override;
+ GLuint _getVertexShaderTexturedRect() const override;
+ GLuint _getVertexShaderTexturedTriangle() const override;
+
+ void _writeSignExtendAlphaC(std::stringstream& ssShader) const override;
+ void _writeSignExtendAlphaABD(std::stringstream& ssShader) const override;
+ void _writeAlphaTest(std::stringstream& ssShader) const override;
+ void _writeSignExtendColorC(std::stringstream& ssShader) const override;
+ void _writeSignExtendColorABD(std::stringstream& ssShader) const override;
+ void _writeClamp(std::stringstream& ssShader) const override;
+ void _writeCallDither(std::stringstream& ssShader) const override;
+ void _writeBlender1(std::stringstream& ssShader) const override;
+ void _writeBlender2(std::stringstream& ssShader) const override;
+ void _writeBlenderAlpha(std::stringstream& ssShader) const override;
+ void _writeLegacyBlender(std::stringstream& ssShader) const override;
+ void _writeFragmentHeader(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderDither(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderNoise(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderWriteDepth(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderDepthCompare(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderMipMap(std::stringstream& ssShader) const override;
+ void _writeFragmentGlobalVariablesNotex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderCalcLight(std::stringstream& ssShader) const override;
+ void _writeFragmentMain2Cycle(std::stringstream& ssShader) const override;
+ void _writeFragmentMain(std::stringstream& ssShader) const override;
+ void _writeFragmentBlendMux(std::stringstream& ssShader) const override;
+ void _writeShaderCoverage(std::stringstream& ssShader) const override;
+ void _writeFragmentCorrectTexCoords(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTexMipmap(std::stringstream& ssShader) const override;
+ void _writeFragmentCallN64Depth(std::stringstream& ssShader) const override;
+ void _writeFragmentRenderTarget(std::stringstream& ssShader) const override;
+ void _writeShaderFragmentMainEnd(std::stringstream& ssShader) const override;
+ void _writeShaderCalcLight(std::stringstream& ssShader) const override;
+ void _writeShaderNoise(std::stringstream& ssShader) const override;
+ void _writeShaderDither(std::stringstream& ssShader) const override;
+ void _writeShaderWriteDepth(std::stringstream& ssShader) const override;
+ void _writeShaderN64DepthCompare(std::stringstream& ssShader) const override;
+ void _writeShaderN64DepthRender(std::stringstream& ssShader) const override;
+
+ ShaderPartPtr m_blender1;
+ ShaderPartPtr m_blender2;
+ ShaderPartPtr m_blenderAlpha;
+ ShaderPartPtr m_legacyBlender;
+ ShaderPartPtr m_clamp;
+ ShaderPartPtr m_signExtendColorC;
+ ShaderPartPtr m_signExtendAlphaC;
+ ShaderPartPtr m_signExtendColorABD;
+ ShaderPartPtr m_signExtendAlphaABD;
+ ShaderPartPtr m_alphaTest;
+ ShaderPartPtr m_callDither;
+
+ ShaderPartPtr m_vertexHeader;
+ ShaderPartPtr m_vertexEnd;
+ ShaderPartPtr m_vertexRect;
+ ShaderPartPtr m_vertexTexturedRect;
+ ShaderPartPtr m_vertexTriangle;
+
+ ShaderPartPtr m_fragmentHeader;
+ ShaderPartPtr m_fragmentGlobalVariablesNotex;
+ ShaderPartPtr m_fragmentHeaderNoise;
+ ShaderPartPtr m_fragmentHeaderWriteDepth;
+ ShaderPartPtr m_fragmentHeaderCalcLight;
+ ShaderPartPtr m_fragmentHeaderMipMap;
+ ShaderPartPtr m_fragmentHeaderDither;
+ ShaderPartPtr m_fragmentHeaderDepthCompare;
+ ShaderPartPtr m_fragmentMain;
+ ShaderPartPtr m_fragmentMain2Cycle;
+ ShaderPartPtr m_fragmentBlendMux;
+ ShaderPartPtr m_fragmentCorrectTexCoords;
+ ShaderPartPtr m_fragmentReadTexMipmap;
+ ShaderPartPtr m_fragmentCallN64Depth;
+ ShaderPartPtr m_fragmentRenderTarget;
+ ShaderPartPtr m_shaderFragmentMainEnd;
+
+ ShaderPartPtr m_shaderNoise;
+ ShaderPartPtr m_shaderDither;
+ ShaderPartPtr m_shaderWriteDepth;
+ ShaderPartPtr m_shaderCalcLight;
+ ShaderPartPtr m_shaderN64DepthCompare;
+ ShaderPartPtr m_shaderN64DepthRender;
+ ShaderPartPtr m_shaderCoverage;
+
+ u32 m_combinerOptionsBits;
+
+ GLuint m_vertexShaderRect;
+ GLuint m_vertexShaderTriangle;
+ GLuint m_vertexShaderTexturedRect;
+ GLuint m_vertexShaderTexturedTriangle;
+};
+
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.cpp
new file mode 100644
index 00000000..2dfcdbb9
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.cpp
@@ -0,0 +1,1186 @@
+#include // for setprecision
+#include
+#include
+#include
+#include "glsl_Utils.h"
+#include "glsl_CombinerInputs.h"
+#include "glsl_CombinerProgramImpl.h"
+#include "glsl_CombinerProgramBuilderFast.h"
+#include "glsl_CombinerProgramUniformFactoryFast.h"
+#include "GraphicsDrawer.h"
+
+namespace glsl {
+
+class VertexShaderTexturedTriangleFast : public ShaderPart
+{
+public:
+ VertexShaderTexturedTriangleFast(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "IN highp vec4 aPosition; \n"
+ "IN lowp vec4 aColor; \n"
+ "IN highp vec2 aTexCoord; \n"
+ "IN lowp float aNumLights; \n"
+ "IN highp vec4 aModify; \n"
+ "IN highp vec2 aBaryCoords; \n"
+ " \n"
+ "uniform int uTexturePersp; \n"
+ "uniform lowp int uTextureFilterMode; \n"
+ " \n"
+ "uniform lowp int uFogUsage; \n"
+ "uniform mediump vec2 uFogScale; \n"
+ "uniform mediump vec2 uScreenCoordsScale; \n"
+ " \n"
+ "uniform mediump vec2 uTexScale; \n"
+ "uniform mediump vec2 uTexOffset[2]; \n"
+ "uniform mediump vec2 uCacheScale[2]; \n"
+ "uniform mediump vec2 uCacheOffset[2]; \n"
+ "uniform mediump vec2 uCacheShiftScale[2]; \n"
+ "uniform mediump vec2 uVTrans; \n"
+ "uniform mediump vec2 uVScale; \n"
+ "uniform mediump vec2 uAdjustTrans; \n"
+ "uniform mediump vec2 uAdjustScale; \n"
+ "uniform lowp ivec2 uCacheFrameBuffer; \n"
+ "OUT highp vec2 vTexCoord0; \n"
+ "OUT highp vec2 vTexCoord1; \n"
+ "OUT mediump vec2 vLodTexCoord; \n"
+ "OUT lowp float vNumLights; \n"
+ "OUT lowp vec4 vShadeColor; \n"
+ "OUT highp vec4 vBaryCoords; \n"
+ ;
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective OUT lowp vec4 vShadeColorNoperspective;\n";
+ else
+ m_part += "OUT lowp vec4 vShadeColorNoperspective; \n";
+ m_part +=
+ "mediump vec2 calcTexCoord(in vec2 texCoord, in int idx) \n"
+ "{ \n"
+ " vec2 texCoordOut = texCoord*uCacheShiftScale[idx]; \n"
+ " texCoordOut -= uTexOffset[idx]; \n"
+ " texCoordOut += uCacheOffset[idx]; \n"
+ " if (uTextureFilterMode != 0 && uCacheFrameBuffer[idx] != 0) \n" /* Workaround for framebuffer textures. */
+ " texCoordOut -= vec2(0.0,1.0); \n" /* They contain garbage at the bottom. */
+ " return texCoordOut * uCacheScale[idx]; \n"
+ "} \n"
+ " \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = aPosition; \n"
+ " vShadeColor = aColor; \n"
+ " vec2 texCoord = aTexCoord; \n"
+ " texCoord *= uTexScale; \n"
+ " if (uTexturePersp == 0 && aModify[2] == 0.0) texCoord *= 0.5;\n"
+ " vTexCoord0 = calcTexCoord(texCoord, 0); \n"
+ " vTexCoord1 = calcTexCoord(texCoord, 1); \n"
+ " vLodTexCoord = texCoord; \n"
+ " vNumLights = aNumLights; \n"
+ " if ((aModify[0]) != 0.0) { \n"
+ " gl_Position.xy *= gl_Position.w; \n"
+ " } \n"
+ " else { \n"
+ " gl_Position.xy = gl_Position.xy * uVScale.xy + uVTrans.xy * gl_Position.ww; \n"
+ " gl_Position.xy = floor(gl_Position.xy * vec2(4.0)) * vec2(0.25); \n"
+ " gl_Position.xy = gl_Position.xy * uAdjustScale + gl_Position.ww * uAdjustTrans; \n"
+ " } \n"
+ " if ((aModify[1]) != 0.0) \n"
+ " gl_Position.z *= gl_Position.w; \n"
+ " if ((aModify[3]) != 0.0) \n"
+ " vNumLights = 0.0; \n"
+ " if (uFogUsage > 0) { \n"
+ " lowp float fp; \n"
+ " if (aPosition.z < -aPosition.w && aModify[1] == 0.0) \n"
+ " fp = -uFogScale.s + uFogScale.t; \n"
+ " else \n"
+ " fp = aPosition.z/aPosition.w*uFogScale.s + uFogScale.t; \n"
+ " fp = clamp(fp, 0.0, 1.0); \n"
+ " if (uFogUsage == 1) \n"
+ " vShadeColor.a = fp; \n"
+ " else \n"
+ " vShadeColor.rgb = vec3(fp); \n"
+ " } \n"
+ " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n"
+ " vShadeColorNoperspective = vShadeColor; \n"
+ ;
+ }
+};
+
+class ShaderFragmentGlobalVariablesTexFast : public ShaderPart
+{
+public:
+ ShaderFragmentGlobalVariablesTexFast(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "uniform sampler2D uTex0; \n"
+ "uniform sampler2D uTex1; \n"
+ "uniform lowp vec4 uFogColor; \n"
+ "uniform lowp vec4 uCenterColor;\n"
+ "uniform lowp vec4 uScaleColor; \n"
+ "uniform lowp vec4 uBlendColor; \n"
+ "uniform lowp vec4 uEnvColor; \n"
+ "uniform lowp vec4 uPrimColor; \n"
+ "uniform lowp float uPrimLod; \n"
+ "uniform lowp float uK4; \n"
+ "uniform lowp float uK5; \n"
+ "uniform lowp int uAlphaCompareMode; \n"
+ "uniform lowp ivec2 uFbMonochrome; \n"
+ "uniform lowp ivec2 uFbFixedAlpha; \n"
+ "uniform lowp int uEnableAlphaTest; \n"
+ "uniform lowp int uCvgXAlpha; \n"
+ "uniform lowp int uAlphaCvgSel; \n"
+ "uniform lowp float uAlphaTestValue; \n"
+ "uniform lowp int uDepthSource; \n"
+ "uniform highp float uPrimDepth; \n"
+ "uniform mediump vec2 uScreenScale; \n"
+ "uniform highp vec4 uTexClamp0; \n"
+ "uniform highp vec4 uTexClamp1; \n"
+ "uniform highp vec2 uTexWrap0; \n"
+ "uniform highp vec2 uTexWrap1; \n"
+ "uniform lowp vec2 uTexMirror0; \n"
+ "uniform lowp vec2 uTexMirror1; \n"
+ "uniform highp vec2 uTexScale0; \n"
+ "uniform highp vec2 uTexScale1; \n"
+ "uniform highp vec2 uTexCoordOffset[2]; \n"
+ "uniform lowp int uUseTexCoordBounds; \n"
+ "uniform highp vec4 uTexCoordBounds0; \n"
+ "uniform highp vec4 uTexCoordBounds1; \n"
+ "uniform lowp int uScreenSpaceTriangle; \n"
+ "highp vec2 texCoord0; \n"
+ "highp vec2 texCoord1; \n"
+ "uniform lowp int uCvgDest; \n"
+ "uniform lowp int uBlendAlphaMode; \n"
+ "lowp float cvg; \n"
+ ;
+
+ if (config.generalEmulation.enableLegacyBlending != 0) {
+ m_part +=
+ "uniform lowp int uFogUsage; \n"
+ ;
+ } else {
+ m_part +=
+ "uniform lowp ivec4 uBlendMux1; \n"
+ "uniform lowp int uForceBlendCycle1;\n"
+ ;
+ }
+
+ if (!_glinfo.isGLES2) {
+ m_part +=
+ "uniform sampler2D uDepthTex; \n"
+ "uniform lowp int uAlphaDitherMode; \n"
+ "uniform lowp int uColorDitherMode; \n"
+ "uniform lowp int uRenderTarget; \n"
+ "uniform mediump vec2 uDepthScale; \n"
+ ;
+ if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable) {
+ m_part +=
+ "uniform lowp int uEnableDepthCompare; \n"
+ ;
+ }
+ } else {
+ m_part +=
+ "lowp int nCurrentTile; \n"
+ ;
+ }
+
+ if (config.video.multisampling > 0) {
+ m_part +=
+ "uniform lowp ivec2 uMSTexEnabled; \n"
+ "uniform lowp sampler2DMS uMSTex0; \n"
+ "uniform lowp sampler2DMS uMSTex1; \n"
+ ;
+ }
+
+ if (!_glinfo.isGLESX || _glinfo.noPerspective)
+ m_part += "noperspective IN lowp vec4 vShadeColorNoperspective; \n";
+ else
+ m_part += "IN lowp vec4 vShadeColorNoperspective; \n";
+
+ m_part +=
+ "IN lowp vec4 vShadeColor; \n"
+ "IN highp vec2 vTexCoord0; \n"
+ "IN highp vec2 vTexCoord1; \n"
+ "IN mediump vec2 vLodTexCoord; \n"
+ "IN lowp float vNumLights; \n"
+ "IN highp vec4 vBaryCoords; \n"
+ ;
+
+ if (_glinfo.dual_source_blending) {
+ m_part +=
+ "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" // SECONDARY FRAGMENT SHADER OUTPUT
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ } else if (_glinfo.ext_fetch) {
+ m_part +=
+ "layout(location = 0) inout lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR fragColor \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA fragColor.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else if (_glinfo.ext_fetch_arm) {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR gl_LastFragColorARM \n" // CURRENT FRAMEBUFFER COLOR/ALPHA
+ "#define LAST_FRAG_ALPHA gl_LastFragColorARM.a \n" // CURRENT FRAMEBUFFER ALPHA
+ ;
+ } else {
+ m_part +=
+ "OUT lowp vec4 fragColor; \n" // MAIN FRAGMENT SHADER OUTPUT
+ "lowp vec4 fragColor1; \n" // DUMMY
+ "#define LAST_FRAG_COLOR vec4(0.0) \n" // DUMMY
+ "#define LAST_FRAG_ALPHA 1.0 \n" // DUMMY
+ ;
+ }
+
+ if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.n64DepthWithFbFetch) {
+ m_part +=
+ "layout(location = 1) inout highp vec4 depthZ; \n"
+ "layout(location = 2) inout highp vec4 depthDeltaZ; \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentHeaderClampWrapMirror : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderClampWrapMirror(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, \n"
+ " in highp vec4 vClamp, in highp vec2 vWrap, \n"
+ " in lowp vec2 vMirror, in highp vec2 vOffset); \n"
+ ;
+ }
+};
+
+class ShaderFragmentHeaderReadMSTexFast : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadMSTexFast(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ if (!m_glinfo.isGLES2 &&
+ config.video.multisampling > 0 &&
+ (CombinerProgramBuilder::s_cycleType == G_CYC_COPY || CombinerProgramBuilder::s_textureConvert.useTextureFiltering()))
+ {
+ shader <<
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha);\n";
+ }
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentHeaderReadTexFast : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadTexFast(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+ if (!m_glinfo.isGLES2) {
+
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart += "uniform lowp int uTextureFilterMode; \n";
+ switch (config.texture.bilinearMode + config.texture.enableHalosRemoval * 2) {
+ case BILINEAR_3POINT:
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ shaderPart +=
+ "#define TEX_OFFSET(off, tex, texCoord) texture(tex, texCoord - (off)/texSize) \n"
+ "#define TEX_FILTER(name, tex, texCoord) \\\n"
+ " { \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex,0)); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 c0 = TEX_OFFSET(offset, tex, texCoord); \\\n"
+ " lowp vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord); \\\n"
+ " lowp vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " name = c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \\\n"
+ " } \n"
+ ;
+ break;
+ case BILINEAR_STANDARD:
+ shaderPart +=
+ "#define TEX_OFFSET(off, tex, texCoord) texture(tex, texCoord - (off)/texSize) \n"
+ "#define TEX_FILTER(name, tex, texCoord) \\\n"
+ "{ \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex,0)); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 zero = vec4(0.0); \\\n"
+ " \\\n"
+ " lowp vec4 p0q0 = TEX_OFFSET(offset, tex, texCoord); \\\n"
+ " lowp vec4 p1q0 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord); \\\n"
+ " \\\n"
+ " lowp vec4 p0q1 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " lowp vec4 p1q1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " \\\n"
+ " mediump vec2 interpolationFactor = abs(offset); \\\n"
+ " lowp vec4 pInterp_q0 = mix( p0q0, p1q0, interpolationFactor.x ); \\\n" // Interpolates top row in X direction.
+ " lowp vec4 pInterp_q1 = mix( p0q1, p1q1, interpolationFactor.x ); \\\n" // Interpolates bottom row in X direction.
+ " name = mix( pInterp_q0, pInterp_q1, interpolationFactor.y ); \\\n" // Interpolate in Y direction.
+ "} \n"
+ ;
+ break;
+ case BILINEAR_3POINT_WITH_COLOR_BLEEDING:
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ shaderPart +=
+ "#define TEX_OFFSET(off, tex, texCoord) texture(tex, texCoord - (off)/texSize) \n"
+ "#define TEX_FILTER(name, tex, texCoord) \\\n"
+ "{ \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex,0)); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 c0 = TEX_OFFSET(offset, tex, texCoord); \\\n"
+ " lowp vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord); \\\n"
+ " lowp vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " \\\n"
+ " if(uEnableAlphaTest == 1 ){ \\\n" // Calculate premultiplied color values
+ " c0.rgb *= c0.a; \\\n"
+ " c1.rgb *= c1.a; \\\n"
+ " c2.rgb *= c2.a; \\\n"
+ " name = c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \\\n"
+ " name.rgb /= name.a; \\\n" // Divide alpha to get actual color value
+ " } \\\n"
+ " else name = c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \\\n"
+ "} \n"
+ ;
+ break;
+ case BILINEAR_STANDARD_WITH_COLOR_BLEEDING_AND_PREMULTIPLIED_ALPHA:
+ shaderPart +=
+ "#define TEX_OFFSET(off, tex, texCoord) texture(tex, texCoord - (off)/texSize) \n"
+ "#define TEX_FILTER(name, tex, texCoord) \\\n"
+ "{ \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex,0)); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 zero = vec4(0.0); \\\n"
+ " \\\n"
+ " lowp vec4 p0q0 = TEX_OFFSET(offset, tex, texCoord); \\\n"
+ " lowp vec4 p1q0 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord); \\\n"
+ " \\\n"
+ " lowp vec4 p0q1 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " lowp vec4 p1q1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y - sign(offset.y)), tex, texCoord); \\\n"
+ " \\\n"
+ " if(uEnableAlphaTest == 1){ \\\n" // Calculate premultiplied color values
+ " p0q0.rgb *= p0q0.a; \\\n"
+ " p1q0.rgb *= p1q0.a; \\\n"
+ " p0q1.rgb *= p0q1.a; \\\n"
+ " p1q1.rgb *= p1q1.a; \\\n"
+ " \\\n"
+ " mediump vec2 interpolationFactor = abs(offset); \\\n"
+ " lowp vec4 pInterp_q0 = mix( p0q0, p1q0, interpolationFactor.x ); \\\n" // Interpolates top row in X direction.
+ " lowp vec4 pInterp_q1 = mix( p0q1, p1q1, interpolationFactor.x ); \\\n" // Interpolates bottom row in X direction.
+ " name = mix( pInterp_q0, pInterp_q1, interpolationFactor.y ); \\\n" // Interpolate in Y direction.
+ " name.rgb /= name.a; \\\n" // Divide alpha to get actual color value
+ " } \\\n"
+ " else if(uCvgXAlpha == 1){ \\\n" // Use texture bleeding for mk64
+ " if(p0q0.a > p1q0.a) p1q0.rgb = p0q0.rgb; \\\n"
+ " if(p1q0.a > p0q0.a) p0q0.rgb = p1q0.rgb; \\\n"
+ " if(p0q1.a > p1q1.a) p1q1.rgb = p0q1.rgb; \\\n"
+ " if(p1q1.a > p0q1.a) p0q1.rgb = p1q1.rgb; \\\n"
+ " if(p0q0.a > p0q1.a) p0q1.rgb = p0q0.rgb; \\\n"
+ " if(p0q1.a > p0q0.a) p0q0.rgb = p0q1.rgb; \\\n"
+ " if(p1q0.a > p1q1.a) p1q1.rgb = p1q0.rgb; \\\n"
+ " if(p1q1.a > p1q0.a) p1q0.rgb = p1q1.rgb; \\\n"
+ " \\\n"
+ " mediump vec2 interpolationFactor = abs(offset); \\\n"
+ " lowp vec4 pInterp_q0 = mix( p0q0, p1q0, interpolationFactor.x ); \\\n" // Interpolates top row in X direction.
+ " lowp vec4 pInterp_q1 = mix( p0q1, p1q1, interpolationFactor.x ); \\\n" // Interpolates bottom row in X direction.
+ " name = mix( pInterp_q0, pInterp_q1, interpolationFactor.y ); \\\n"
+ " } \\\n"
+ " else{ \\\n"
+ " mediump vec2 interpolationFactor = abs(offset); \\\n"
+ " lowp vec4 pInterp_q0 = mix( p0q0, p1q0, interpolationFactor.x ); \\\n" // Interpolates top row in X direction.
+ " lowp vec4 pInterp_q1 = mix( p0q1, p1q1, interpolationFactor.x ); \\\n" // Interpolates bottom row in X direction.
+ " name = mix( pInterp_q0, pInterp_q1, interpolationFactor.y ); \\\n" // Interpolate in Y direction.
+ " } \\\n"
+ "} \n"
+ ;
+ break;
+ }
+ shaderPart +=
+ "#define READ_TEX(name, tex, texCoord, fbMonochrome, fbFixedAlpha) \\\n"
+ " { \\\n"
+ " if (fbMonochrome == 3) { \\\n"
+ " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
+ " name = texelFetch(tex, coord, 0); \\\n"
+ " } else { \\\n"
+ " if (uTextureFilterMode == 0) name = texture(tex, texCoord); \\\n"
+ " else TEX_FILTER(name, tex, texCoord); \\\n"
+ " } \\\n"
+ " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
+ " else if (fbMonochrome == 2) \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " else if (fbMonochrome == 3) { \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " name.a = 0.0; \\\n"
+ " } \\\n"
+ " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
+ " } \n"
+ ;
+ }
+
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion()) {
+ shaderPart +=
+ "uniform lowp ivec2 uTextureFormat; \n"
+ "uniform lowp int uTextureConvert; \n"
+ "uniform mediump ivec4 uConvertParams; \n"
+ "#define YUVCONVERT(name, format) \\\n"
+ " mediump ivec4 icolor = ivec4(name*255.0); \\\n"
+ " if (format == 1) \\\n"
+ " icolor.rg -= 128; \\\n"
+ " mediump ivec4 iconvert; \\\n"
+ " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \\\n"
+ " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \\\n"
+ " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \\\n"
+ " iconvert.a = icolor.b; \\\n"
+ " name = vec4(iconvert)/255.0; \n"
+ "#define YUVCONVERT_TEX0(name, tex, texCoord, format) \\\n"
+ " { \\\n"
+ " name = texture(tex, texCoord); \\\n"
+ " YUVCONVERT(name, format) \\\n"
+ " } \n"
+ "#define YUVCONVERT_TEX1(name, tex, texCoord, format, prev) \\\n"
+ " { \\\n"
+ " if (uTextureConvert != 0) name = prev; \\\n"
+ " else name = texture(tex, texCoord); \\\n"
+ " YUVCONVERT(name, format) \\\n"
+ " } \n"
+ ;
+ }
+
+ } else {
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart +=
+ "uniform lowp int uTextureFilterMode; \n"
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
+ ;
+ }
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion()) {
+ shaderPart +=
+ "uniform lowp ivec2 uTextureFormat; \n"
+ "uniform lowp int uTextureConvert; \n"
+ "uniform mediump ivec4 uConvertParams; \n"
+ "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 texCoord, in lowp int convert, in lowp int format, in lowp vec4 prev); \n"
+ ;
+ }
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentHeaderReadTexCopyModeFast : public ShaderPart
+{
+public:
+ ShaderFragmentHeaderReadTexCopyModeFast (const opengl::GLInfo & _glinfo)
+ {
+ if (!_glinfo.isGLES2) {
+ m_part =
+ "#define READ_TEX(name, tex, texCoord, fbMonochrome, fbFixedAlpha) \\\n"
+ " { \\\n"
+ " if (fbMonochrome == 3) { \\\n"
+ " mediump ivec2 coord = ivec2(gl_FragCoord.xy); \\\n"
+ " name = texelFetch(tex, coord, 0); \\\n"
+ " } else { \\\n"
+ " name = texture(tex, texCoord); \\\n"
+ " } \\\n"
+ " if (fbMonochrome == 1) name = vec4(name.r); \\\n"
+ " else if (fbMonochrome == 2) \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " else if (fbMonochrome == 3) { \\\n"
+ " name.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), name.rgb)); \\\n"
+ " name.a = 0.0; \\\n"
+ " } \\\n"
+ " if (fbFixedAlpha == 1) name.a = 0.825; \\\n"
+ " } \n"
+ ;
+ } else {
+ m_part =
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha); \n"
+ ;
+ }
+ }
+};
+
+class ShaderFragmentClampWrapMirrorTex0 : public ShaderPart
+{
+public:
+ ShaderFragmentClampWrapMirrorTex0(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ " texCoord0 = clampWrapMirror(vTexCoord0, uTexClamp0, uTexWrap0, uTexMirror0, uTexScale0); \n"
+ ;
+ }
+};
+
+class ShaderFragmentClampWrapMirrorTex1 : public ShaderPart
+{
+public:
+ ShaderFragmentClampWrapMirrorTex1(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ " texCoord1 = clampWrapMirror(vTexCoord1, uTexClamp1, uTexWrap1, uTexMirror1, uTexScale1); \n"
+ ;
+ }
+};
+
+class ShaderFragmentReadTexCopyModeFast : public ShaderPart
+{
+public:
+ ShaderFragmentReadTexCopyModeFast(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ " nCurrentTile = 0; \n"
+ " lowp vec4 readtex0 = readTex(uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]); \n"
+ ;
+ } else {
+ if (config.video.multisampling > 0) {
+ m_part =
+ " lowp vec4 readtex0; \n"
+ " if (uMSTexEnabled[0] == 0) { \n"
+ " READ_TEX(readtex0, uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ " } else readtex0 = readTexMS(uMSTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]);\n"
+ ;
+ } else {
+ m_part =
+ " lowp vec4 readtex0; \n"
+ " READ_TEX(readtex0, uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderFragmentReadTex0Fast : public ShaderPart
+{
+public:
+ ShaderFragmentReadTex0Fast(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+
+ shaderPart = " nCurrentTile = 0; \n";
+ if (CombinerProgramBuilder::s_textureConvert.getBilerp0()) {
+ shaderPart += " lowp vec4 readtex0 = readTex(uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
+ } else {
+ shaderPart += " lowp vec4 tmpTex = vec4(0.0); \n"
+ " lowp vec4 readtex0 = YUV_Convert(uTex0, texCoord0, 0, uTextureFormat[0], tmpTex); \n";
+ }
+
+ } else {
+
+ if (!CombinerProgramBuilder::s_textureConvert.getBilerp0()) {
+ shaderPart = " lowp vec4 readtex0; \n"
+ " YUVCONVERT_TEX0(readtex0, uTex0, texCoord0, uTextureFormat[0]) \n";
+ } else {
+ if (config.video.multisampling > 0) {
+ shaderPart =
+ " lowp vec4 readtex0; \n"
+ " if (uMSTexEnabled[0] == 0) { \n"
+ " READ_TEX(readtex0, uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]) \n"
+ " } else readtex0 = readTexMS(uMSTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]); \n";
+ } else {
+ shaderPart = " lowp vec4 readtex0; \n"
+ " READ_TEX(readtex0, uTex0, texCoord0, uFbMonochrome[0], uFbFixedAlpha[0]) \n";
+ }
+ }
+
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderFragmentReadTex1Fast : public ShaderPart
+{
+public:
+ ShaderFragmentReadTex1Fast(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+
+ shaderPart = " nCurrentTile = 1; \n";
+
+ if (CombinerProgramBuilder::s_textureConvert.getBilerp1()) {
+ shaderPart += " lowp vec4 readtex1 = readTex(uTex1, texCoord1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
+ } else {
+ shaderPart += " lowp vec4 readtex1 = YUV_Convert(uTex1, texCoord1, uTextureConvert, uTextureFormat[1], readtex0); \n";
+ }
+
+ } else {
+
+ if (!CombinerProgramBuilder::s_textureConvert.getBilerp1()) {
+ shaderPart =
+ " lowp vec4 readtex1; \n"
+ " YUVCONVERT_TEX1(readtex1, uTex1, texCoord1, uTextureFormat[1], readtex0) \n";
+ } else {
+ if (config.video.multisampling > 0) {
+ shaderPart =
+ " lowp vec4 readtex1; \n"
+ " if (uMSTexEnabled[1] == 0) { \n"
+ " READ_TEX(readtex1, uTex1, texCoord1, uFbMonochrome[1], uFbFixedAlpha[1]) \n"
+ " } else readtex1 = readTexMS(uMSTex1, texCoord1, uFbMonochrome[1], uFbFixedAlpha[1]); \n";
+ } else {
+ shaderPart = " lowp vec4 readtex1; \n"
+ " READ_TEX(readtex1, uTex1, texCoord1, uFbMonochrome[1], uFbFixedAlpha[1]) \n";
+ }
+ }
+
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderMipmapFast : public ShaderPart
+{
+public:
+ ShaderMipmapFast(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ if (config.generalEmulation.enableLOD == 0) {
+ // Fake mipmap
+ m_part =
+ "uniform lowp int uMaxTile; \n"
+ "uniform mediump float uMinLod; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ " readtex0 = texture2D(uTex0, texCoord0); \n"
+ " readtex1 = texture2D(uTex1, texCoord1); \n"
+ " if (uMaxTile == 0) return 1.0; \n"
+ " return uMinLod; \n"
+ "} \n"
+ ;
+ } else {
+ m_part =
+ "uniform lowp int uEnableLod; \n"
+ "uniform mediump float uMinLod; \n"
+ "uniform lowp int uMaxTile; \n"
+ "uniform lowp int uTextureDetail; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ " readtex0 = texture2D(uTex0, texCoord0); \n"
+ " readtex1 = texture2DLodEXT(uTex1, texCoord1, 0.0); \n"
+ " \n"
+ " mediump float fMaxTile = float(uMaxTile); \n"
+ " mediump vec2 dx = abs(dFdx(vLodTexCoord)) * uScreenScale; \n"
+ " mediump vec2 dy = abs(dFdy(vLodTexCoord)) * uScreenScale; \n"
+ " mediump float lod = max(dx.x + dx.y, dy.x + dy.y); \n" /*LINEAR*/
+ " bool magnify = lod < 1.0; \n"
+ " mediump float lod_tile = magnify ? 0.0 : floor(log2(floor(lod))); \n"
+ " bool distant = lod > 128.0 || lod_tile >= fMaxTile; \n"
+ " mediump float lod_frac = fract(lod/pow(2.0, lod_tile)); \n"
+ " if (magnify) lod_frac = max(lod_frac, uMinLod); \n"
+ " if (uTextureDetail == 0) { \n"
+ " if (distant) lod_frac = 1.0; \n"
+ " else if (magnify) lod_frac = 0.0; \n"
+ " } \n"
+ " if (magnify && (uTextureDetail == 1 || uTextureDetail == 3)) \n"
+ " lod_frac = 1.0 - lod_frac; \n"
+ " if (uMaxTile == 0) { \n"
+ " if (uEnableLod != 0) { \n"
+ " if (uTextureDetail < 2) readtex1 = readtex0; \n"
+ " else if (!magnify) readtex0 = readtex1; \n"
+ " } \n"
+ " return lod_frac; \n"
+ " } \n"
+ " if (uEnableLod == 0) return lod_frac; \n"
+ " \n"
+ " lod_tile = min(lod_tile, fMaxTile); \n"
+ " lowp float lod_tile_m1 = max(0.0, lod_tile - 1.0); \n"
+ " lowp float lod_tile_p1 = min(fMaxTile - 1.0, lod_tile + 1.0); \n"
+ " lowp vec4 lodT = texture2DLodEXT(uTex1, texCoord1, lod_tile); \n"
+ " lowp vec4 lodT_m1 = texture2DLodEXT(uTex1, texCoord1, lod_tile_m1); \n"
+ " lowp vec4 lodT_p1 = texture2DLodEXT(uTex1, texCoord1, lod_tile_p1); \n"
+ " if (lod_tile < 1.0) { \n"
+ " if (magnify) { \n"
+ // !sharpen && !detail
+ " if (uTextureDetail == 0) readtex1 = readtex0; \n"
+ " } else { \n"
+ // detail
+ " if (uTextureDetail > 1) { \n"
+ " readtex0 = lodT; \n"
+ " readtex1 = lodT_p1; \n"
+ " } \n"
+ " } \n"
+ " } else { \n"
+ " if (uTextureDetail > 1) { \n"
+ " readtex0 = lodT; \n"
+ " readtex1 = lodT_p1; \n"
+ " } else { \n"
+ " readtex0 = lodT_m1; \n"
+ " readtex1 = lodT; \n"
+ " } \n"
+ " } \n"
+ " return lod_frac; \n"
+ "} \n"
+ ;
+ }
+ }
+ else {
+ if (config.generalEmulation.enableLOD == 0) {
+ // Fake mipmap
+ m_part =
+ "uniform lowp int uMaxTile; \n"
+ "uniform mediump float uMinLod; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ " readtex0 = texture(uTex0, texCoord0); \n"
+ " readtex1 = texture(uTex1, texCoord1); \n"
+ " if (uMaxTile == 0) return 1.0; \n"
+ " return uMinLod; \n"
+ "} \n"
+ ;
+ } else {
+ if (config.texture.bilinearMode == BILINEAR_3POINT)
+ m_part =
+ "#define TEX_OFFSET_NORMAL(off, tex, texCoord, lod) texture(tex, texCoord - (off)/texSize) \n"
+ "#define TEX_OFFSET_MIPMAP(off, tex, texCoord, lod) textureLod(tex, texCoord - (off)/texSize, lod) \n"
+ "#define READ_TEX_NORMAL(name, tex, texCoord, lod) \\\n"
+ " { \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex, int(lod))); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 c0 = TEX_OFFSET_NORMAL(offset, tex, texCoord, lod); \\\n"
+ " lowp vec4 c1 = TEX_OFFSET_NORMAL(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord, lod); \\\n"
+ " lowp vec4 c2 = TEX_OFFSET_NORMAL(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord, lod); \\\n"
+ " name = c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \\\n"
+ " } \n"
+ "#define READ_TEX_MIPMAP(name, tex, texCoord, lod) \\\n"
+ " { \\\n"
+ " mediump vec2 texSize = vec2(textureSize(tex, int(lod))); \\\n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \\\n"
+ " offset -= step(1.0, offset.x + offset.y); \\\n"
+ " lowp vec4 c0 = TEX_OFFSET_MIPMAP(offset, tex, texCoord, lod); \\\n"
+ " lowp vec4 c1 = TEX_OFFSET_MIPMAP(vec2(offset.x - sign(offset.x), offset.y), tex, texCoord, lod); \\\n"
+ " lowp vec4 c2 = TEX_OFFSET_MIPMAP(vec2(offset.x, offset.y - sign(offset.y)), tex, texCoord, lod); \\\n"
+ " name = c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \\\n"
+ " } \n"
+ ;
+ else
+ m_part =
+ "#define TEX_FETCH_NORMAL(tex, texCoord, lod) texture(tex, texCoord) \n"
+ "#define TEX_FETCH_MIPMAP(tex, texCoord, lod) textureLod(tex, texCoord, lod) \n"
+ "#define READ_TEX_NORMAL(name, tex, texCoord, lod) name = TEX_FETCH_NORMAL(tex, texCoord, lod) \n"
+ "#define READ_TEX_MIPMAP(name, tex, texCoord, lod) name = TEX_FETCH_MIPMAP(tex, texCoord, lod) \n"
+ ;
+ m_part +=
+ "uniform lowp int uEnableLod; \n"
+ "uniform mediump float uMinLod; \n"
+ "uniform lowp int uMaxTile; \n"
+ "uniform lowp int uTextureDetail; \n"
+ " \n"
+ "mediump float mipmap(out lowp vec4 readtex0, out lowp vec4 readtex1) { \n"
+ " READ_TEX_NORMAL(readtex0, uTex0, texCoord0, 0.0); \n"
+ " READ_TEX_MIPMAP(readtex1, uTex1, texCoord1, 0.0); \n"
+ " \n"
+ " mediump float fMaxTile = float(uMaxTile); \n"
+ " mediump vec2 dx = abs(dFdx(vLodTexCoord)); \n"
+ " dx *= uScreenScale; \n"
+ " mediump float lod = max(dx.x, dx.y); \n"
+ " bool magnify = lod < 1.0; \n"
+ " mediump float lod_tile = magnify ? 0.0 : floor(log2(floor(lod))); \n"
+ " bool distant = lod > 128.0 || lod_tile >= fMaxTile; \n"
+ " mediump float lod_frac = fract(lod/pow(2.0, lod_tile)); \n"
+ " if (magnify) lod_frac = max(lod_frac, uMinLod); \n"
+ " if (uTextureDetail == 0) { \n"
+ " if (distant) lod_frac = 1.0; \n"
+ " else if (magnify) lod_frac = 0.0; \n"
+ " } \n"
+ " if (magnify && ((uTextureDetail & 1) != 0)) \n"
+ " lod_frac = 1.0 - lod_frac; \n"
+ " if (uMaxTile == 0) { \n"
+ " if (uEnableLod != 0) { \n"
+ " if ((uTextureDetail & 2) == 0) readtex1 = readtex0; \n"
+ " else if (!magnify) readtex0 = readtex1; \n"
+ " } \n"
+ " return lod_frac; \n"
+ " } \n"
+ " if (uEnableLod == 0) return lod_frac; \n"
+ " \n"
+ " lod_tile = min(lod_tile, fMaxTile - 1.0); \n"
+ " lowp float lod_tile_m1 = max(0.0, lod_tile - 1.0); \n"
+ " lowp float lod_tile_p1 = min(fMaxTile - 1.0, lod_tile + 1.0); \n"
+ " lowp vec4 lodT, lodT_m1, lodT_p1; \n"
+ " READ_TEX_MIPMAP(lodT, uTex1, texCoord1, lod_tile); \n"
+ " READ_TEX_MIPMAP(lodT_m1, uTex1, texCoord1, lod_tile_m1); \n"
+ " READ_TEX_MIPMAP(lodT_p1, uTex1, texCoord1, lod_tile_p1); \n"
+ " if (lod_tile < 1.0) { \n"
+ " if (magnify) { \n"
+ // !sharpen && !detail
+ " if (uTextureDetail == 0) readtex1 = readtex0; \n"
+ " } else { \n"
+ // detail
+ " if ((uTextureDetail & 2) != 0 ) { \n"
+ " readtex0 = lodT; \n"
+ " readtex1 = lodT_p1; \n"
+ " } \n"
+ " } \n"
+ " } else { \n"
+ " if ((uTextureDetail & 2) != 0 ) { \n"
+ " readtex0 = lodT; \n"
+ " readtex1 = lodT_p1; \n"
+ " } else { \n"
+ " readtex0 = lodT_m1; \n"
+ " readtex1 = lodT; \n"
+ " } \n"
+ " } \n"
+ " return lod_frac; \n"
+ "} \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderReadtexFast : public ShaderPart
+{
+public:
+ ShaderReadtexFast(const opengl::GLInfo & _glinfo) : m_glinfo(_glinfo)
+ {
+ }
+
+ void write(std::stringstream & shader) const override
+ {
+ std::string shaderPart;
+
+ if (m_glinfo.isGLES2) {
+ if (CombinerProgramBuilder::s_textureConvert.useYUVCoversion())
+ shaderPart +=
+ "lowp vec4 YUV_Convert(in sampler2D tex, in highp vec2 texCoord, in lowp int convert, in lowp int format, in lowp vec4 prev) \n"
+ "{ \n"
+ " lowp vec4 texColor; \n"
+ " if (convert != 0) texColor = prev; \n"
+ " else texColor = texture2D(tex, texCoord); \n"
+ " mediump ivec4 icolor = ivec4(texColor*255.0); \n"
+ " if (format == 1) \n"
+ " icolor.rg -= 128; \n"
+ " mediump ivec4 iconvert; \n"
+ " iconvert.r = icolor.b + (uConvertParams[0]*icolor.g + 128)/256; \n"
+ " iconvert.g = icolor.b + (uConvertParams[1]*icolor.r + uConvertParams[2]*icolor.g + 128)/256; \n"
+ " iconvert.b = icolor.b + (uConvertParams[3]*icolor.r + 128)/256; \n"
+ " iconvert.a = icolor.b; \n"
+ " return vec4(iconvert)/255.0; \n"
+ " } \n"
+ ;
+ if (CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ if (config.texture.bilinearMode == BILINEAR_3POINT) {
+ shaderPart +=
+ "uniform mediump vec2 uTextureSize[2]; \n"
+ // 3 point texture filtering.
+ // Original author: ArthurCarvalho
+ // GLSL implementation: twinaphex, mupen64plus-libretro project.
+ "#define TEX_OFFSET(off) texture2D(tex, texCoord - (off)/texSize) \n"
+ "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 texCoord) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \n"
+ " offset -= step(1.0, offset.x + offset.y); \n"
+ " lowp vec4 c0 = TEX_OFFSET(offset); \n"
+ " lowp vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y)); \n"
+ " lowp vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y))); \n"
+ " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0); \n"
+ "} \n"
+ ;
+ } else {
+ shaderPart +=
+ // bilinear filtering.
+ "uniform mediump vec2 uTextureSize[2]; \n"
+ "#define TEX_OFFSET(off) texture2D(tex, texCoord - (off)/texSize) \n"
+ "lowp vec4 TextureFilter(in sampler2D tex, in highp vec2 texCoord) \n"
+ "{ \n"
+ " mediump vec2 texSize; \n"
+ " if (nCurrentTile == 0) \n"
+ " texSize = uTextureSize[0]; \n"
+ " else \n"
+ " texSize = uTextureSize[1]; \n"
+ " mediump vec2 offset = fract(texCoord*texSize - vec2(0.5)); \n"
+ " offset -= step(1.0, offset.x + offset.y); \n"
+ " lowp vec4 zero = vec4(0.0); \n"
+ " \n"
+ " lowp vec4 p0q0 = TEX_OFFSET(offset); \n"
+ " lowp vec4 p1q0 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y)); \n"
+ " \n"
+ " lowp vec4 p0q1 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y))); \n"
+ " lowp vec4 p1q1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y - sign(offset.y)));\n"
+ " \n"
+ " mediump vec2 interpolationFactor = abs(offset); \n"
+ " lowp vec4 pInterp_q0 = mix( p0q0, p1q0, interpolationFactor.x ); \n" // Interpolates top row in X direction.
+ " lowp vec4 pInterp_q1 = mix( p0q1, p1q1, interpolationFactor.x ); \n" // Interpolates bottom row in X direction.
+ " return mix( pInterp_q0, pInterp_q1, interpolationFactor.y ); \n" // Interpolate in Y direction.
+ "} \n"
+ ;
+ }
+ shaderPart +=
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " lowp vec4 texColor; \n"
+ " if (uTextureFilterMode == 0) texColor = texture2D(tex, texCoord); \n"
+ " else texColor = TextureFilter(tex, texCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ } else {
+ if (config.video.multisampling > 0 && CombinerProgramBuilder::s_textureConvert.useTextureFiltering()) {
+ shaderPart =
+ "uniform lowp int uMSAASamples; \n"
+ "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
+ "{ \n"
+ " lowp vec4 texel = vec4(0.0); \n"
+ " for (int i = 0; i < uMSAASamples; ++i) \n"
+ " texel += texelFetch(mstex, ipos, i); \n"
+ " return texel / float(uMSAASamples); \n"
+ "} \n"
+ " \n"
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " mediump ivec2 itexCoord; \n"
+ " if (fbMonochrome == 3) { \n"
+ " itexCoord = ivec2(gl_FragCoord.xy); \n"
+ " } else { \n"
+ " mediump vec2 msTexSize = vec2(textureSize(mstex)); \n"
+ " itexCoord = ivec2(msTexSize * texCoord); \n"
+ " } \n"
+ " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " else if (fbMonochrome == 3) { \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " texColor.a = 0.0; \n"
+ " } \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ }
+
+ shader << shaderPart;
+ }
+
+private:
+ const opengl::GLInfo& m_glinfo;
+};
+
+class ShaderReadtexCopyModeFast : public ShaderPart
+{
+public:
+ ShaderReadtexCopyModeFast(const opengl::GLInfo & _glinfo)
+ {
+ if (_glinfo.isGLES2) {
+ m_part =
+ "lowp vec4 readTex(in sampler2D tex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " lowp vec4 texColor = texture2D(tex, texCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ } else {
+ if (config.video.multisampling > 0) {
+ m_part =
+ "uniform lowp int uMSAASamples; \n"
+ "lowp vec4 sampleMS(in lowp sampler2DMS mstex, in mediump ivec2 ipos) \n"
+ "{ \n"
+ " lowp vec4 texel = vec4(0.0); \n"
+ " for (int i = 0; i < uMSAASamples; ++i) \n"
+ " texel += texelFetch(mstex, ipos, i); \n"
+ " return texel / float(uMSAASamples); \n"
+ "} \n"
+ " \n"
+ "lowp vec4 readTexMS(in lowp sampler2DMS mstex, in highp vec2 texCoord, in lowp int fbMonochrome, in lowp int fbFixedAlpha) \n"
+ "{ \n"
+ " mediump ivec2 itexCoord; \n"
+ " if (fbMonochrome == 3) { \n"
+ " itexCoord = ivec2(gl_FragCoord.xy); \n"
+ " } else { \n"
+ " mediump vec2 msTexSize = vec2(textureSize(mstex)); \n"
+ " itexCoord = ivec2(msTexSize * texCoord); \n"
+ " } \n"
+ " lowp vec4 texColor = sampleMS(mstex, itexCoord); \n"
+ " if (fbMonochrome == 1) texColor = vec4(texColor.r); \n"
+ " else if (fbMonochrome == 2) \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " else if (fbMonochrome == 3) { \n"
+ " texColor.rgb = vec3(dot(vec3(0.2126, 0.7152, 0.0722), texColor.rgb)); \n"
+ " texColor.a = 0.0; \n"
+ " } \n"
+ " if (fbFixedAlpha == 1) texColor.a = 0.825; \n"
+ " return texColor; \n"
+ "} \n"
+ ;
+ }
+ }
+ }
+};
+
+class ShaderClampWrapMirror : public ShaderPart
+{
+public:
+ ShaderClampWrapMirror(const opengl::GLInfo & _glinfo)
+ {
+ m_part =
+ "highp vec2 clampWrapMirror(in highp vec2 vTexCoord, in highp vec4 vClamp, \n"
+ " in highp vec2 vWrap, in lowp vec2 vMirror, in highp vec2 vScale) \n"
+ "{ \n"
+ " highp vec2 texCoord = clamp(vTexCoord, vClamp.xy, vClamp.zw); \n"
+ " lowp vec2 one = vec2(1.0); \n"
+ " lowp vec2 clamped = step(vClamp.zw, texCoord); \n"
+ " lowp vec2 notClamped = one - clamped; \n"
+ " lowp vec2 wrapped = step(vWrap , texCoord); \n"
+ " lowp vec2 notWrapped = one - wrapped; \n"
+ " texCoord = clamped * texCoord + notClamped * (wrapped*mod(texCoord, vWrap) + notWrapped*texCoord); \n"
+ " highp vec2 intPart = floor(texCoord); \n"
+ " highp vec2 fractPart = fract(texCoord); \n"
+ " lowp vec2 needMirror = step(vec2(0.5), mod(intPart, vWrap)) * vMirror; \n"
+ " texCoord = clamped * texCoord + notClamped * fractPart; \n"
+ " texCoord = (one - vMirror) * texCoord + vMirror * fractPart; \n"
+ " texCoord = (one - texCoord) * needMirror + texCoord * (one - needMirror); \n"
+ " texCoord *= vScale; \n"
+ " return texCoord; \n"
+ "} \n"
+ ;
+ }
+};
+
+CombinerProgramBuilderFast::CombinerProgramBuilderFast(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram)
+: CombinerProgramBuilderCommon(_glinfo, _useProgram, std::make_unique(_glinfo),
+ std::make_unique(_glinfo))
+, m_fragmentGlobalVariablesTex(new ShaderFragmentGlobalVariablesTexFast(_glinfo))
+, m_fragmentHeaderClampWrapMirror(new ShaderFragmentHeaderClampWrapMirror(_glinfo))
+, m_fragmentHeaderReadMSTex(new ShaderFragmentHeaderReadMSTexFast(_glinfo))
+, m_fragmentHeaderReadTex(new ShaderFragmentHeaderReadTexFast(_glinfo))
+, m_fragmentHeaderReadTexCopyMode(new ShaderFragmentHeaderReadTexCopyModeFast(_glinfo))
+, m_fragmentReadTex0(new ShaderFragmentReadTex0Fast(_glinfo))
+, m_fragmentReadTex1(new ShaderFragmentReadTex1Fast(_glinfo))
+, m_fragmentClampWrapMirrorTex0(new ShaderFragmentClampWrapMirrorTex0(_glinfo))
+, m_fragmentClampWrapMirrorTex1(new ShaderFragmentClampWrapMirrorTex1(_glinfo))
+, m_fragmentReadTexCopyMode(new ShaderFragmentReadTexCopyModeFast(_glinfo))
+, m_shaderMipmap(new ShaderMipmapFast(_glinfo))
+, m_shaderReadtex(new ShaderReadtexFast(_glinfo))
+, m_shaderReadtexCopyMode(new ShaderReadtexCopyModeFast(_glinfo))
+, m_shaderClampWrapMirror(new ShaderClampWrapMirror(_glinfo))
+{
+
+}
+
+void CombinerProgramBuilderFast::_writeFragmentGlobalVariablesTex(std::stringstream& ssShader) const
+{
+ m_fragmentGlobalVariablesTex->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentHeaderReadMSTex(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadMSTex->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentHeaderReadTex(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadTex->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentHeaderReadTexCopyMode(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderReadTexCopyMode->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentHeaderClampWrapMirrorEngine(std::stringstream& ssShader) const
+{
+ m_fragmentHeaderClampWrapMirror->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentClampWrapMirrorEngineTex0(std::stringstream& ssShader) const
+{
+ m_fragmentClampWrapMirrorTex0->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentClampWrapMirrorEngineTex1(std::stringstream& ssShader) const
+{
+ m_fragmentClampWrapMirrorTex1->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentReadTex0(std::stringstream& ssShader) const
+{
+ m_fragmentReadTex0->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentReadTex1(std::stringstream& ssShader) const
+{
+ m_fragmentReadTex1->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeFragmentReadTexCopyMode(std::stringstream& ssShader) const
+{
+ m_fragmentReadTexCopyMode->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeShaderClampWrapMirrorEngine(std::stringstream& ssShader) const
+{
+ m_shaderClampWrapMirror->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeShaderMipmap(std::stringstream& ssShader) const
+{
+ m_shaderMipmap->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeShaderReadtex(std::stringstream& ssShader) const
+{
+ m_shaderReadtex->write(ssShader);
+}
+
+void CombinerProgramBuilderFast::_writeShaderReadtexCopyMode(std::stringstream& ssShader) const
+{
+ m_shaderReadtexCopyMode->write(ssShader);
+}
+
+
+}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.h
new file mode 100644
index 00000000..cf343967
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilderFast.h
@@ -0,0 +1,47 @@
+#pragma once
+#include
+
+namespace glsl {
+
+class CombinerProgramUniformFactory;
+
+class CombinerProgramBuilderFast : public glsl::CombinerProgramBuilderCommon
+{
+public:
+ CombinerProgramBuilderFast(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram);
+
+private:
+
+ void _writeFragmentGlobalVariablesTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadMSTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadTex(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderReadTexCopyMode(std::stringstream& ssShader) const override;
+ void _writeFragmentHeaderClampWrapMirrorEngine(std::stringstream& ssShader) const override;
+ void _writeFragmentClampWrapMirrorEngineTex0(std::stringstream& ssShader) const override;
+ void _writeFragmentClampWrapMirrorEngineTex1(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTex0(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTex1(std::stringstream& ssShader) const override;
+ void _writeFragmentReadTexCopyMode(std::stringstream& ssShader) const override;
+ void _writeShaderClampWrapMirrorEngine(std::stringstream& ssShader) const override;
+ void _writeShaderMipmap(std::stringstream& ssShader) const override;
+ void _writeShaderReadtex(std::stringstream& ssShader) const override;
+ void _writeShaderReadtexCopyMode(std::stringstream& ssShader) const override;
+
+ ShaderPartPtr m_fragmentGlobalVariablesTex;
+ ShaderPartPtr m_fragmentHeaderClampWrapMirror;
+ ShaderPartPtr m_fragmentHeaderReadMSTex;
+ ShaderPartPtr m_fragmentHeaderReadTex;
+ ShaderPartPtr m_fragmentHeaderReadTexCopyMode;
+ ShaderPartPtr m_fragmentReadTex0;
+ ShaderPartPtr m_fragmentReadTex1;
+ ShaderPartPtr m_fragmentClampWrapMirrorTex0;
+ ShaderPartPtr m_fragmentClampWrapMirrorTex1;
+ ShaderPartPtr m_fragmentReadTexCopyMode;
+ ShaderPartPtr m_shaderMipmap;
+ ShaderPartPtr m_shaderReadtex;
+ ShaderPartPtr m_shaderReadtexCopyMode;
+ ShaderPartPtr m_shaderClampWrapMirror;
+};
+
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp
index def95ba6..bd71aa5b 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp
@@ -22,1274 +22,103 @@
namespace glsl {
-/*---------------Uniform-------------*/
+CombinerProgramUniformFactory::CombinerProgramUniformFactory(const opengl::GLInfo &_glInfo)
+: m_glInfo(_glInfo) {
+}
-struct iUniform {
- GLint loc = -1;
- int val = -999;
- void set(int _val, bool _force) {
- if (loc >= 0 && (_force || val != _val)) {
- val = _val;
- glUniform1i(loc, _val);
- }
- }
-};
-
-struct fUniform {
- GLint loc = -1;
- float val = -9999.9f;
- void set(float _val, bool _force) {
- if (loc >= 0 && (_force || val != _val)) {
- val = _val;
- glUniform1f(loc, _val);
- }
- }
-};
-
-struct fv2Uniform {
- GLint loc = -1;
- float val1 = -9999.9f, val2 = -9999.9f;
- void set(float _val1, float _val2, bool _force) {
- if (loc >= 0 && (_force || val1 != _val1 || val2 != _val2)) {
- val1 = _val1;
- val2 = _val2;
- glUniform2f(loc, _val1, _val2);
- }
- }
-};
-
-struct fv3Uniform {
- GLint loc = -1;
- float val[3];
- void set(float * _pVal, bool _force) {
- const size_t szData = sizeof(float)* 3;
- if (loc >= 0 && (_force || memcmp(val, _pVal, szData) != 0)) {
- memcpy(val, _pVal, szData);
- glUniform3fv(loc, 1, _pVal);
- }
- }
-};
-
-struct fv4Uniform {
- GLint loc = -1;
- float val[4];
- void set(float * _pVal, bool _force) {
- const size_t szData = sizeof(float)* 4;
- if (loc >= 0 && (_force || memcmp(val, _pVal, szData) != 0)) {
- memcpy(val, _pVal, szData);
- glUniform4fv(loc, 1, _pVal);
- }
- }
-};
-
-struct iv2Uniform {
- GLint loc = -1;
- int val1 = -999, val2 = -999;
- void set(int _val1, int _val2, bool _force) {
- if (loc >= 0 && (_force || val1 != _val1 || val2 != _val2)) {
- val1 = _val1;
- val2 = _val2;
- glUniform2i(loc, _val1, _val2);
- }
- }
-};
-
-struct i4Uniform {
- GLint loc = -1;
- int val0 = -999, val1 = -999, val2 = -999, val3 = -999;
- void set(int _val0, int _val1, int _val2, int _val3, bool _force) {
- if (loc < 0)
- return;
- if (_force || _val0 != val0 || _val1 != val1 || _val2 != val2 || _val3 != val3) {
- val0 = _val0;
- val1 = _val1;
- val2 = _val2;
- val3 = _val3;
- glUniform4i(loc, val0, val1, val2, val3);
- }
- }
-};
-
-
-/*---------------UniformGroup-------------*/
-
-#define LocateUniform(A) \
- A.loc = glGetUniformLocation(_program, #A);
-
-class UNoiseTex : public UniformGroup
+CombinerProgramUniformFactory::~CombinerProgramUniformFactory()
{
-public:
- UNoiseTex(GLuint _program) {
- LocateUniform(uTexNoise);
- }
-
- void update(bool _force) override
- {
- uTexNoise.set(int(graphics::textureIndices::NoiseTex), _force);
- }
-
-private:
- iUniform uTexNoise;
-};
-
-class UDepthTex : public UniformGroup
-{
-public:
- UDepthTex(GLuint _program) {
- LocateUniform(uDepthTex);
- }
-
- void update(bool _force) override
- {
- uDepthTex.set(int(graphics::textureIndices::DepthTex), _force);
- }
-
-private:
- iUniform uDepthTex;
-};
-
-class UZLutTexture : public UniformGroup
-{
-public:
- UZLutTexture(GLuint _program) {
- LocateUniform(uZlutImage);
- }
-
- void update(bool _force) override
- {
- uZlutImage.set(int(graphics::textureIndices::ZLUTTex), _force);
- }
-
-private:
- iUniform uZlutImage;
-};
-
-class UTextures : public UniformGroup
-{
-public:
- UTextures(GLuint _program) {
- LocateUniform(uTex0);
- LocateUniform(uTex1);
- }
-
- void update(bool _force) override
- {
- uTex0.set(0, _force);
- uTex1.set(1, _force);
- }
-
-private:
- iUniform uTex0;
- iUniform uTex1;
-};
-
-class UMSAATextures : public UniformGroup
-{
-public:
- UMSAATextures(GLuint _program) {
- LocateUniform(uMSTex0);
- LocateUniform(uMSTex1);
- LocateUniform(uMSAASamples);
- }
-
- void update(bool _force) override
- {
- uMSTex0.set(int(graphics::textureIndices::MSTex[0]), _force);
- uMSTex1.set(int(graphics::textureIndices::MSTex[1]), _force);
- uMSAASamples.set(config.video.multisampling, _force);
- }
-
-private:
- iUniform uMSTex0;
- iUniform uMSTex1;
- iUniform uMSAASamples;
-};
-
-class UScreenSpaceTriangleInfo : public UniformGroup
-{
-public:
- UScreenSpaceTriangleInfo(GLuint _program) {
- LocateUniform(uScreenSpaceTriangle);
- }
-
- void update(bool _force) override
- {
- uScreenSpaceTriangle.set(
- (dwnd().getDrawer().getDrawingState() == DrawingState::ScreenSpaceTriangle) ? 1 : 0, _force);
- }
-
-private:
- iUniform uScreenSpaceTriangle;
-};
-
-class URasterInfo : public UniformGroup {
-public:
- URasterInfo(GLuint _program) {
- LocateUniform(uVertexOffset);
- LocateUniform(uTexCoordOffset[0]);
- LocateUniform(uTexCoordOffset[1]);
- LocateUniform(uUseTexCoordBounds);
- LocateUniform(uTexCoordBounds0);
- LocateUniform(uTexCoordBounds1);
- }
-
- void update(bool _force) override {
- const bool isNativeRes = config.frameBufferEmulation.nativeResFactor == 1 && config.video.multisampling == 0;
- const bool isTexRect = dwnd().getDrawer().getDrawingState() == DrawingState::TexRect;
- const bool useTexCoordBounds = isTexRect && !isNativeRes && config.graphics2D.enableTexCoordBounds;
- /* At rasterization stage, the N64 places samples on the top left of the fragment while OpenGL */
- /* places them in the fragment center. As a result, a normal approach results in shifted texture */
- /* coordinates. In native resolution, this difference can be negated by shifting vertices by 0.5. */
- /* In higher resolutions, there are more samples than the game intends, so shifting is not very */
- /* effective. Still, an heuristic is applied to render texture rectangles as correctly as possible */
- /* in higher resolutions too. See issue #2324 for details. */
- const float vertexOffset = isNativeRes ? 0.5f : 0.0f;
- float texCoordOffset[2][2] = { 0.0f, 0.0f };
- if (isTexRect && !isNativeRes) {
- float scale[2] = { 0.0f, 0.0f };
- if (config.graphics2D.enableNativeResTexrects != 0 && gDP.otherMode.textureFilter != G_TF_POINT) {
- scale[0] = scale[1] = 1.0f;
- } else {
- scale[0] = scale[1] = static_cast(config.frameBufferEmulation.nativeResFactor);
- }
-
- for (int t = 0; t < 2; t++) {
- const CachedTexture* _pTexture = textureCache().current[t];
- if (_pTexture != nullptr) {
- if (config.frameBufferEmulation.nativeResFactor != 0) {
- if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) {
- texCoordOffset[t][0] = -0.5f * gDP.lastTexRectInfo.dsdx;
- texCoordOffset[t][1] = -0.5f * gDP.lastTexRectInfo.dtdy;
- } else {
- texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? -0.5f / scale[0] : -1.0f + 0.5f / scale[0]) * gDP.lastTexRectInfo.dsdx;
- texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? -0.5f / scale[1] : -1.0f + 0.5f / scale[1]) * gDP.lastTexRectInfo.dtdy;
- }
- } else {
- texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dsdx;
- texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dtdy;
- if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) {
- texCoordOffset[t][0] -= 0.5f;
- texCoordOffset[t][1] -= 0.5f;
- }
- }
- }
- }
- }
- /* Hack for framebuffer textures. See #519 and #2112 */
- if ((config.generalEmulation.hacks & hack_fbTextureOffset) != 0) {
- for (int t = 0; t < 2; t++) {
- const CachedTexture* _pTexture = textureCache().current[t];
- if (_pTexture != nullptr) {
- if (gDP.otherMode.textureFilter != G_TF_POINT && _pTexture->frameBufferTexture != CachedTexture::fbNone) {
- texCoordOffset[t][0] -= 1.0f;
- texCoordOffset[t][1] -= 1.0f;
- }
- }
- }
- }
- float tcbounds[2][4] = {};
- if (useTexCoordBounds) {
- f32 uls, lrs, ult, lrt, S, T;
- for (int t = 0; t < 2; t++) {
- const CachedTexture * _pTexture = textureCache().current[t];
- if (_pTexture != nullptr) {
- S = _FIXED2FLOAT(gDP.lastTexRectInfo.s, 5);
- T = _FIXED2FLOAT(gDP.lastTexRectInfo.t, 5);
- uls = S + (ceilf(gDP.lastTexRectInfo.ulx) - gDP.lastTexRectInfo.ulx) * gDP.lastTexRectInfo.dsdx;
- lrs = S + (ceilf(gDP.lastTexRectInfo.lrx) - gDP.lastTexRectInfo.ulx - 1.0f) * gDP.lastTexRectInfo.dsdx;
- ult = T + (ceilf(gDP.lastTexRectInfo.uly) - gDP.lastTexRectInfo.uly) * gDP.lastTexRectInfo.dtdy;
- lrt = T + (ceilf(gDP.lastTexRectInfo.lry) - gDP.lastTexRectInfo.uly - 1.0f) * gDP.lastTexRectInfo.dtdy;
- tcbounds[t][0] = fmin(uls, lrs);
- tcbounds[t][1] = fmin(ult, lrt);
- tcbounds[t][2] = fmax(uls, lrs);
- tcbounds[t][3] = fmax(ult, lrt);
- }
- }
- }
-
- uVertexOffset.set(vertexOffset, vertexOffset, _force);
- uTexCoordOffset[0].set(texCoordOffset[0][0], texCoordOffset[0][1], _force);
- uTexCoordOffset[1].set(texCoordOffset[1][0], texCoordOffset[1][1], _force);
- uUseTexCoordBounds.set(useTexCoordBounds ? 1 : 0, _force);
- uTexCoordBounds0.set(tcbounds[0], _force);
- uTexCoordBounds1.set(tcbounds[1], _force);
- }
-
-private:
- fv2Uniform uVertexOffset;
- fv2Uniform uTexCoordOffset[2];
- iUniform uUseTexCoordBounds;
- fv4Uniform uTexCoordBounds0;
- fv4Uniform uTexCoordBounds1;
-};
-
-class UFrameBufferInfo : public UniformGroup
-{
-public:
- UFrameBufferInfo(GLuint _program) {
- LocateUniform(uFbMonochrome);
- LocateUniform(uFbFixedAlpha);
- LocateUniform(uMSTexEnabled);
- }
-
- void update(bool _force) override
- {
- int nFbMonochromeMode0 = 0, nFbMonochromeMode1 = 0;
- int nFbFixedAlpha0 = 0, nFbFixedAlpha1 = 0;
- int nMSTex0Enabled = 0, nMSTex1Enabled = 0;
- TextureCache & cache = textureCache();
- if (cache.current[0] != nullptr && cache.current[0]->frameBufferTexture != CachedTexture::fbNone) {
- if (cache.current[0]->size == G_IM_SIZ_8b) {
- nFbMonochromeMode0 = 1;
- if (gDP.otherMode.imageRead == 0)
- nFbFixedAlpha0 = 1;
- } else if (gSP.textureTile[0]->size == G_IM_SIZ_16b && gSP.textureTile[0]->format == G_IM_FMT_IA) {
- nFbMonochromeMode0 = 2;
- } else if ((config.generalEmulation.hacks & hack_ZeldaMonochrome) != 0 &&
- cache.current[0]->size == G_IM_SIZ_16b &&
- gSP.textureTile[0]->size == G_IM_SIZ_8b &&
- gSP.textureTile[0]->format == G_IM_FMT_CI) {
- // Zelda monochrome effect
- nFbMonochromeMode0 = 3;
- nFbMonochromeMode1 = 3;
- }
-
- nMSTex0Enabled = cache.current[0]->frameBufferTexture == CachedTexture::fbMultiSample ? 1 : 0;
- }
- if (cache.current[1] != nullptr && cache.current[1]->frameBufferTexture != CachedTexture::fbNone) {
- if (cache.current[1]->size == G_IM_SIZ_8b) {
- nFbMonochromeMode1 = 1;
- if (gDP.otherMode.imageRead == 0)
- nFbFixedAlpha1 = 1;
- }
- else if (gSP.textureTile[1]->size == G_IM_SIZ_16b && gSP.textureTile[1]->format == G_IM_FMT_IA)
- nFbMonochromeMode1 = 2;
- nMSTex1Enabled = cache.current[1]->frameBufferTexture == CachedTexture::fbMultiSample ? 1 : 0;
- }
- uFbMonochrome.set(nFbMonochromeMode0, nFbMonochromeMode1, _force);
- uFbFixedAlpha.set(nFbFixedAlpha0, nFbFixedAlpha1, _force);
- uMSTexEnabled.set(nMSTex0Enabled, nMSTex1Enabled, _force);
- gDP.changed &= ~CHANGED_FB_TEXTURE;
- }
-
-private:
- iv2Uniform uFbMonochrome;
- iv2Uniform uFbFixedAlpha;
- iv2Uniform uMSTexEnabled;
-};
-
-
-class UFog : public UniformGroup
-{
-public:
- UFog(GLuint _program) {
- LocateUniform(uFogUsage);
- LocateUniform(uFogScale);
- }
-
- void update(bool _force) override
- {
- if (RSP.LLE) {
- uFogUsage.set(0, _force);
- return;
- }
-
- int nFogUsage = ((gSP.geometryMode & G_FOG) != 0) ? 1 : 0;
- if (GBI.getMicrocodeType() == F3DAM) {
- const s16 fogMode = ((gSP.geometryMode >> 13) & 9) + 0xFFF8;
- if (fogMode == 0)
- nFogUsage = 1;
- else if (fogMode > 0)
- nFogUsage = 2;
- }
- uFogUsage.set(nFogUsage, _force);
- uFogScale.set(gSP.fog.multiplierf, gSP.fog.offsetf, _force);
- }
-
-private:
- iUniform uFogUsage;
- fv2Uniform uFogScale;
-};
-
-class UBlendMode1Cycle : public UniformGroup
-{
-public:
- UBlendMode1Cycle(GLuint _program) {
- LocateUniform(uBlendMux1);
- LocateUniform(uForceBlendCycle1);
- }
-
- void update(bool _force) override
- {
- uBlendMux1.set(gDP.otherMode.c1_m1a,
- gDP.otherMode.c1_m1b,
- gDP.otherMode.c1_m2a,
- gDP.otherMode.c1_m2b,
- _force);
-
- const int forceBlend1 = (int)gDP.otherMode.forceBlender;
- uForceBlendCycle1.set(forceBlend1, _force);
- }
-
-private:
- i4Uniform uBlendMux1;
- iUniform uForceBlendCycle1;
-};
-
-class UBlendMode2Cycle : public UniformGroup
-{
-public:
- UBlendMode2Cycle(GLuint _program) {
- LocateUniform(uBlendMux1);
- LocateUniform(uBlendMux2);
- LocateUniform(uForceBlendCycle1);
- LocateUniform(uForceBlendCycle2);
- }
-
- void update(bool _force) override
- {
- uBlendMux1.set(gDP.otherMode.c1_m1a,
- gDP.otherMode.c1_m1b,
- gDP.otherMode.c1_m2a,
- gDP.otherMode.c1_m2b,
- _force);
-
- uBlendMux2.set(gDP.otherMode.c2_m1a,
- gDP.otherMode.c2_m1b,
- gDP.otherMode.c2_m2a,
- gDP.otherMode.c2_m2b,
- _force);
-
- const int forceBlend1 = 1;
- uForceBlendCycle1.set(forceBlend1, _force);
- const int forceBlend2 = gDP.otherMode.forceBlender;
- uForceBlendCycle2.set(forceBlend2, _force);
-
- if (!(graphics::Context::DualSourceBlending || graphics::Context::FramebufferFetchColor) || dwnd().getDrawer().isTexrectDrawerMode()) {
- // Modes, which shader blender can't emulate
- const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16);
- switch (mode) {
- case 0x0040:
- // Mia Hamm Soccer
- // clr_in * a_in + clr_mem * (1-a)
- // clr_in * a_in + clr_in * (1-a)
- case 0x0050:
- // A Bug's Life
- // clr_in * a_in + clr_mem * (1-a)
- // clr_in * a_in + clr_mem * (1-a)
- uForceBlendCycle1.set(0, _force);
- uForceBlendCycle2.set(0, _force);
- break;
- case 0x0150:
- // Tony Hawk
- // clr_in * a_in + clr_mem * (1-a)
- // clr_in * a_fog + clr_mem * (1-a_fog)
- if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) {
- uForceBlendCycle1.set(0, _force);
- uForceBlendCycle2.set(0, _force);
- }
- break;
- }
- }
-
- }
-
-private:
- i4Uniform uBlendMux1;
- i4Uniform uBlendMux2;
- iUniform uForceBlendCycle1;
- iUniform uForceBlendCycle2;
-};
-
-class UBlendCvg : public UniformGroup
-{
-public:
- UBlendCvg(GLuint _program) {
- LocateUniform(uCvgDest);
- LocateUniform(uBlendAlphaMode);
- }
-
- void update(bool _force) override
- {
- uCvgDest.set(gDP.otherMode.cvgDest, _force);
- if (dwnd().getDrawer().isTexrectDrawerMode())
- uBlendAlphaMode.set(2, _force); // No alpha blend in texrect drawing mode
- else
- uBlendAlphaMode.set(gDP.otherMode.forceBlender, _force);
- }
-private:
- iUniform uCvgDest;
- iUniform uBlendAlphaMode;
-};
-
-class UDitherMode : public UniformGroup
-{
-public:
- UDitherMode(GLuint _program, bool _usesNoise)
- : m_usesNoise(_usesNoise)
- {
- LocateUniform(uAlphaCompareMode);
- LocateUniform(uAlphaDitherMode);
- LocateUniform(uColorDitherMode);
- }
-
- void update(bool _force) override
- {
- if (gDP.otherMode.cycleType < G_CYC_COPY) {
- uAlphaCompareMode.set(gDP.otherMode.alphaCompare, _force);
- uAlphaDitherMode.set(gDP.otherMode.alphaDither, _force);
- uColorDitherMode.set(gDP.otherMode.colorDither, _force);
- }
- else {
- uAlphaCompareMode.set(0, _force);
- uAlphaDitherMode.set(0, _force);
- uColorDitherMode.set(0, _force);
- }
-
- bool updateNoiseTex = m_usesNoise;
- updateNoiseTex |= (gDP.otherMode.cycleType < G_CYC_COPY) && (gDP.otherMode.colorDither == G_CD_NOISE || gDP.otherMode.alphaDither == G_AD_NOISE || gDP.otherMode.alphaCompare == G_AC_DITHER);
- if (updateNoiseTex)
- g_noiseTexture.update();
- }
-
-private:
- iUniform uAlphaCompareMode;
- iUniform uAlphaDitherMode;
- iUniform uColorDitherMode;
- bool m_usesNoise;
-};
-
-class UScreenScale : public UniformGroup
-{
-public:
- UScreenScale(GLuint _program) {
- LocateUniform(uScreenScale);
- }
-
- void update(bool _force) override
- {
- if (dwnd().getDrawer().isTexrectDrawerMode()) {
- uScreenScale.set(1.0f, 1.0f, _force);
- return;
- }
-
- FrameBuffer * pBuffer = frameBufferList().getCurrent();
- if (pBuffer == nullptr)
- uScreenScale.set(dwnd().getScaleX(), dwnd().getScaleY(), _force);
- else
- uScreenScale.set(pBuffer->m_scale, pBuffer->m_scale, _force);
- }
-
-private:
- fv2Uniform uScreenScale;
-};
-
-class UMipmap : public UniformGroup
-{
-public:
- UMipmap(GLuint _program) {
- LocateUniform(uMinLod);
- LocateUniform(uMaxTile);
- LocateUniform(uEnableLod);
- LocateUniform(uTextureDetail);
- }
-
- void update(bool _force) override
- {
- uMinLod.set(gDP.primColor.m, _force);
- const CachedTexture * _pTexture = textureCache().current[1];
- if (_pTexture == nullptr)
- uMaxTile.set(gSP.texture.level, _force);
- else
- uMaxTile.set(_pTexture->max_level > 0 ? gSP.texture.level : std::min(gSP.texture.level, 1u), _force);
- const int uCalcLOD = (gDP.otherMode.textureLOD == G_TL_LOD) ? 1 : 0;
- uEnableLod.set(uCalcLOD, _force);
- uTextureDetail.set(gDP.otherMode.textureDetail, _force);
- }
-
-private:
- fUniform uMinLod;
- iUniform uMaxTile;
- iUniform uEnableLod;
- iUniform uTextureDetail;
-};
-
-class UTexturePersp : public UniformGroup
-{
-public:
- UTexturePersp(GLuint _program) {
- LocateUniform(uTexturePersp);
- }
-
- void update(bool _force) override
- {
- const u32 texturePersp = (RSP.LLE || GBI.isTexturePersp()) ? gDP.otherMode.texturePersp : 1U;
- uTexturePersp.set(texturePersp, _force);
- }
-
-private:
- iUniform uTexturePersp;
-};
-
-class UTextureFetchMode : public UniformGroup
-{
-public:
- UTextureFetchMode(GLuint _program) {
- LocateUniform(uTextureFilterMode);
- LocateUniform(uTextureFormat);
- LocateUniform(uTextureConvert);
- LocateUniform(uConvertParams);
- }
-
- void update(bool _force) override
- {
- int textureFilter = gDP.otherMode.textureFilter;
- uTextureFilterMode.set(textureFilter, _force);
- uTextureFormat.set(gSP.textureTile[0]->format, gSP.textureTile[1]->format, _force);
- uTextureConvert.set(gDP.otherMode.convert_one, _force);
- if (gDP.otherMode.bi_lerp0 == 0 || gDP.otherMode.bi_lerp1 == 0)
- uConvertParams.set(gDP.convert.k0, gDP.convert.k1, gDP.convert.k2, gDP.convert.k3, _force);
- }
-
-private:
- iUniform uTextureFilterMode;
- iv2Uniform uTextureFormat;
- iUniform uTextureConvert;
- i4Uniform uConvertParams;
-};
-
-class UAlphaTestInfo : public UniformGroup
-{
-public:
- UAlphaTestInfo(GLuint _program) {
- LocateUniform(uEnableAlphaTest);
- LocateUniform(uAlphaCvgSel);
- LocateUniform(uCvgXAlpha);
- LocateUniform(uAlphaTestValue);
- }
-
- void update(bool _force) override
- {
- if (gDP.otherMode.cycleType == G_CYC_FILL) {
- uEnableAlphaTest.set(0, _force);
- uAlphaCvgSel.set(0, _force);
-
- } else if (gDP.otherMode.cycleType == G_CYC_COPY) {
- uAlphaCvgSel.set(0, _force);
- if (gDP.otherMode.alphaCompare & G_AC_THRESHOLD) {
- uEnableAlphaTest.set(1, _force);
- uAlphaTestValue.set(0.5f, _force);
- } else {
- uEnableAlphaTest.set(0, _force);
- }
- } else if ((gDP.otherMode.alphaCompare & G_AC_THRESHOLD) != 0) {
- uEnableAlphaTest.set(1, _force);
- uAlphaTestValue.set(gDP.blendColor.a, _force);
- uAlphaCvgSel.set(gDP.otherMode.alphaCvgSel, _force);
- } else {
- uEnableAlphaTest.set(0, _force);
- uAlphaCvgSel.set(gDP.otherMode.alphaCvgSel, _force);
- }
-
- uCvgXAlpha.set(gDP.otherMode.cvgXAlpha, _force);
- }
-
-private:
- iUniform uEnableAlphaTest;
- iUniform uAlphaCvgSel;
- iUniform uCvgXAlpha;
- fUniform uAlphaTestValue;
-};
-
-class UViewportInfo : public UniformGroup
-{
-public:
- UViewportInfo(GLuint _program) {
- LocateUniform(uVTrans);
- LocateUniform(uVScale);
- LocateUniform(uAdjustTrans);
- LocateUniform(uAdjustScale);
- }
-
- void update(bool _force) override
- {
- const bool isOrthographicProjection = gSP.matrix.projection[3][2] == -1.f;
- float adjustTrans[2] = { 0.0f, 0.0f };
- float adjustScale[2] = { 1.0f, 1.0f };
- if (dwnd().isAdjustScreen() && (gDP.colorImage.width > VI.width * 98 / 100)) {
- if (isOrthographicProjection) {
- adjustScale[1] = 1.0f / dwnd().getAdjustScale();
- adjustTrans[1] = static_cast(gDP.colorImage.width) * 3.0f / 4.0f * (1.0f - adjustScale[1]) / 2.0f;
- } else {
- adjustScale[0] = dwnd().getAdjustScale();
- adjustTrans[0] = static_cast(gDP.colorImage.width) * (1.0f - adjustScale[0]) / 2.0f;
- }
- }
- uVTrans.set(gSP.viewport.vtrans[0], gSP.viewport.vtrans[1], _force);
- uVScale.set(gSP.viewport.vscale[0], -gSP.viewport.vscale[1], _force);
- uAdjustTrans.set(adjustTrans[0], adjustTrans[1], _force);
- uAdjustScale.set(adjustScale[0], adjustScale[1], _force);
- }
-
-private:
- fv2Uniform uVTrans;
- fv2Uniform uVScale;
- fv2Uniform uAdjustTrans;
- fv2Uniform uAdjustScale;
-};
-
-class UDepthScale : public UniformGroup
-{
-public:
- UDepthScale(GLuint _program) {
- LocateUniform(uDepthScale);
- }
-
- void update(bool _force) override
- {
- if (RSP.LLE)
- uDepthScale.set(0.5f, 0.5f, _force);
- else
- uDepthScale.set(gSP.viewport.vscale[2], gSP.viewport.vtrans[2], _force);
- }
-
-private:
- fv2Uniform uDepthScale;
-};
-
-class UDepthInfo : public UniformGroup
-{
-public:
- UDepthInfo(GLuint _program) {
- LocateUniform(uEnableDepth);
- LocateUniform(uEnableDepthCompare);
- LocateUniform(uEnableDepthUpdate);
- LocateUniform(uDepthMode);
- LocateUniform(uDepthSource);
- LocateUniform(uPrimDepth);
- LocateUniform(uDeltaZ);
- }
-
- void update(bool _force) override
- {
- FrameBuffer * pBuffer = frameBufferList().getCurrent();
- if (pBuffer == nullptr || pBuffer->m_pDepthBuffer == nullptr)
- return;
-
- const bool nDepthEnabled = ((gSP.geometryMode & G_ZBUFFER) || gDP.otherMode.depthSource == G_ZS_PRIM) &&
- gDP.otherMode.cycleType <= G_CYC_2CYCLE;
- uEnableDepth.set(nDepthEnabled ? 1 : 0, _force);
- if (nDepthEnabled) {
- uEnableDepthCompare.set(gDP.otherMode.depthCompare, _force);
- uEnableDepthUpdate.set(gDP.otherMode.depthUpdate, _force);
- } else {
- uEnableDepthCompare.set(0, _force);
- uEnableDepthUpdate.set(0, _force);
- }
- uDepthMode.set(gDP.otherMode.depthMode, _force);
- uDepthSource.set(gDP.otherMode.depthSource, _force);
- if (gDP.otherMode.depthSource == G_ZS_PRIM) {
- uDeltaZ.set(gDP.primDepth.deltaZ, _force);
- uPrimDepth.set(gDP.primDepth.z, _force);
- }
- }
-
-private:
- iUniform uEnableDepth;
- iUniform uEnableDepthCompare;
- iUniform uEnableDepthUpdate;
- iUniform uDepthMode;
- iUniform uDepthSource;
- fUniform uPrimDepth;
- fUniform uDeltaZ;
-};
-
-class UDepthSource : public UniformGroup
-{
-public:
- UDepthSource(GLuint _program) {
- LocateUniform(uDepthSource);
- LocateUniform(uPrimDepth);
- }
-
- void update(bool _force) override
- {
- uDepthSource.set(gDP.otherMode.depthSource, _force);
- if (gDP.otherMode.depthSource == G_ZS_PRIM)
- uPrimDepth.set(gDP.primDepth.z, _force);
- }
-
-private:
- iUniform uDepthSource;
- fUniform uPrimDepth;
-};
-
-class URenderTarget : public UniformGroup
-{
-public:
- URenderTarget(GLuint _program) {
- LocateUniform(uRenderTarget);
- }
-
- void update(bool _force) override
- {
- int renderTarget = 0;
- if (isCurrentColorImageDepthImage()) {
- renderTarget = isDepthCompareEnabled() ? 2 : 1;
- }
- uRenderTarget.set(renderTarget, _force);
- }
-
-private:
- iUniform uRenderTarget;
-};
-
-class UClampMode : public UniformGroup
-{
-public:
- UClampMode(GLuint _program) {
- LocateUniform(uClampMode);
- }
-
- void update(bool _force) override
- {
- int clampMode = -1;
- switch (gfxContext.getClampMode())
- {
- case graphics::ClampMode::ClippingEnabled:
- clampMode = 0;
- break;
- case graphics::ClampMode::NoNearPlaneClipping:
- clampMode = 1;
- break;
- case graphics::ClampMode::NoClipping:
- clampMode = 2;
- break;
- }
- uClampMode.set(clampMode, _force);
- }
-
-private:
- iUniform uClampMode;
-};
-
-class UPolygonOffset : public UniformGroup
-{
-public:
- UPolygonOffset(GLuint _program) {
- LocateUniform(uPolygonOffset);
- }
-
- void update(bool _force) override
- {
- f32 offset = gfxContext.isEnabled(graphics::enable::POLYGON_OFFSET_FILL) ? 0.003f : 0.0f;
- uPolygonOffset.set(offset, _force);
- }
-
-private:
- fUniform uPolygonOffset;
-};
-
-class UScreenCoordsScale : public UniformGroup
-{
-public:
- UScreenCoordsScale(GLuint _program) {
- LocateUniform(uScreenCoordsScale);
- }
-
- void update(bool _force) override
- {
- f32 scaleX, scaleY;
- calcCoordsScales(frameBufferList().getCurrent(), scaleX, scaleY);
- uScreenCoordsScale.set(2.0f*scaleX, -2.0f*scaleY, _force);
- }
-
-private:
- fv2Uniform uScreenCoordsScale;
-};
-
-class UColors : public UniformGroup
-{
-public:
- UColors(GLuint _program) {
- LocateUniform(uFogColor);
- LocateUniform(uCenterColor);
- LocateUniform(uScaleColor);
- LocateUniform(uBlendColor);
- LocateUniform(uEnvColor);
- LocateUniform(uPrimColor);
- LocateUniform(uPrimLod);
- LocateUniform(uK4);
- LocateUniform(uK5);
- }
-
- void update(bool _force) override
- {
- uFogColor.set(&gDP.fogColor.r, _force);
- uCenterColor.set(&gDP.key.center.r, _force);
- uScaleColor.set(&gDP.key.scale.r, _force);
- uBlendColor.set(&gDP.blendColor.r, _force);
- uEnvColor.set(&gDP.envColor.r, _force);
- uPrimColor.set(&gDP.primColor.r, _force);
- uPrimLod.set(gDP.primColor.l, _force);
- uK4.set(_FIXED2FLOATCOLOR(gDP.convert.k4, 8 ), _force);
- uK5.set(_FIXED2FLOATCOLOR(gDP.convert.k5, 8 ), _force);
- }
-
-private:
- fv4Uniform uFogColor;
- fv4Uniform uCenterColor;
- fv4Uniform uScaleColor;
- fv4Uniform uBlendColor;
- fv4Uniform uEnvColor;
- fv4Uniform uPrimColor;
- fUniform uPrimLod;
- fUniform uK4;
- fUniform uK5;
-};
-
-class URectColor : public UniformGroup
-{
-public:
- URectColor(GLuint _program) {
- LocateUniform(uRectColor);
- }
-
- void update(bool _force) override
- {
- uRectColor.set(&gDP.rectColor.r, _force);
- }
-
-private:
- fv4Uniform uRectColor;
-};
-
-class UTextureSize : public UniformGroup
-{
-public:
- UTextureSize(GLuint _program, bool _useT0, bool _useT1)
- : m_useT0(_useT0)
- , m_useT1(_useT1)
- {
- LocateUniform(uTextureSize[0]);
- LocateUniform(uTextureSize[1]);
- }
-
- void update(bool _force) override
- {
- TextureCache & cache = textureCache();
- if (m_useT0 && cache.current[0] != nullptr)
- uTextureSize[0].set(static_cast(cache.current[0]->width),
- static_cast(cache.current[0]->height), _force);
- if (m_useT1 && cache.current[1] != nullptr) {
- CachedTexture * pTexture = cache.current[1];
- if (pTexture->max_level == 0)
- uTextureSize[1].set(static_cast(pTexture->width),
- static_cast(pTexture->height), _force);
- else
- uTextureSize[1].set(static_cast(pTexture->mipmapAtlasWidth),
- static_cast(pTexture->mipmapAtlasHeight), _force);
- }
- }
-
-private:
- fv2Uniform uTextureSize[2];
- bool m_useT0;
- bool m_useT1;
-};
-
-class UTextureParams : public UniformGroup
-{
-public:
- UTextureParams(GLuint _program, bool _useT0, bool _useT1)
- {
- m_useTile[0] = _useT0;
- m_useTile[1] = _useT1;
- LocateUniform(uTexScale);
- LocateUniform(uCacheFrameBuffer);
- }
-
- void update(bool _force) override
- {
- int nFB[2] = { 0, 0 };
- TextureCache & cache = textureCache();
- for (u32 t = 0; t < 2; ++t) {
- if (!m_useTile[t])
- continue;
- CachedTexture *_pTexture = cache.current[t];
- if (_pTexture != nullptr) {
- nFB[t] = _pTexture->frameBufferTexture;
- }
- }
-
- uCacheFrameBuffer.set(nFB[0], nFB[1], _force);
- uTexScale.set(gSP.texture.scales, gSP.texture.scalet, _force);
- }
-
-private:
- bool m_useTile[2];
- fv2Uniform uTexScale;
- iv2Uniform uCacheFrameBuffer;
-};
-
-class UTextureEngine : public UniformGroup
-{
-public:
- UTextureEngine(GLuint _program, bool _useT0, bool _useT1)
- {
- m_useTile[0] = _useT0;
- m_useTile[1] = _useT1;
- LocateUniform(uTexWrap[0]);
- LocateUniform(uTexWrap[1]);
- LocateUniform(uTexClamp[0]);
- LocateUniform(uTexClamp[1]);
- LocateUniform(uTexWrapEn[0]);
- LocateUniform(uTexWrapEn[1]);
- LocateUniform(uTexClampEn[0]);
- LocateUniform(uTexClampEn[1]);
- LocateUniform(uTexMirrorEn[0]);
- LocateUniform(uTexMirrorEn[1]);
- LocateUniform(uTexSize[0]);
- LocateUniform(uTexSize[1]);
- LocateUniform(uShiftScale[0]);
- LocateUniform(uShiftScale[1]);
- LocateUniform(uTexOffset[0]);
- LocateUniform(uTexOffset[1]);
- LocateUniform(uHDRatio[0]);
- LocateUniform(uHDRatio[1]);
- LocateUniform(uCacheOffset[0]);
- LocateUniform(uCacheOffset[1]);
- LocateUniform(uBilinearOffset);
- }
-
- void update(bool _force) override
- {
- std::array aTexWrap[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
- std::array aTexClamp[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
- std::array aTexWrapEn[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
- std::array aTexClampEn[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
- std::array aTexMirrorEn[2] = { { 0.0f, 0.0f }, { 0.0f,0.0f }};
- std::array aTexSize[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
-
- std::array aShiftScale[2] = { { 1.0f, 1.0f }, { 1.0f,1.0f } };
- std::array aTexOffset[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
-
- std::array aHDRatio[2] = { { 1.0f, 1.0f }, { 1.0f, 1.0f } };
- std::array aCacheOffset[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
-
- const float bilinearOffset = gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY ? 0.5f : 0.0f;
- uBilinearOffset.set(bilinearOffset, bilinearOffset, _force);
-
- TextureCache & cache = textureCache();
- for (u32 t = 0; t < 2; ++t) {
- if (!m_useTile[t])
- continue;
-
- const gDPTile * pTile = gSP.textureTile[t];
- CachedTexture * pTexture = cache.current[t];
- if (pTile == nullptr || pTexture == nullptr)
- continue;
-
- aTexSize[t][0] = pTexture->width * pTexture->hdRatioS;
- aTexSize[t][1] = pTexture->height * pTexture->hdRatioT;
-
- aShiftScale[t][0] = pTile->shifts > 10 ? static_cast(1 << (16 - pTile->shifts)) : 1.0f / static_cast(1 << pTile->shifts);
- aShiftScale[t][1] = pTile->shiftt > 10 ? static_cast(1 << (16 - pTile->shiftt)) : 1.0f / static_cast(1 << pTile->shiftt);
-
- if (pTile->textureMode != TEXTUREMODE_BGIMAGE && pTile->textureMode != TEXTUREMODE_FRAMEBUFFER_BG) {
- aTexOffset[t][0] = pTile->fuls;
- aTexOffset[t][1] = pTile->fult;
- }
-
- aHDRatio[t][0] = pTexture->hdRatioS;
- aHDRatio[t][1] = pTexture->hdRatioT;
-
- aCacheOffset[t][0] = pTexture->offsetS * pTexture->hdRatioS;
- aCacheOffset[t][1] = pTexture->offsetT * pTexture->hdRatioT;
-
- /* Not sure if special treatment of framebuffer textures is correct */
- if (pTexture->frameBufferTexture != CachedTexture::fbNone)
- {
- aTexClamp[t][0] = f32(pTexture->width) * pTexture->hdRatioS - 1.0f;
- aTexClamp[t][1] = f32(pTexture->height) * pTexture->hdRatioT - 1.0f;
- aTexWrapEn[t][0] = 0.0;
- aTexWrapEn[t][1] = 0.0;
- aTexClampEn[t][0] = 1.0;
- aTexClampEn[t][1] = 1.0;
- aTexMirrorEn[t][0] = 0.0;
- aTexMirrorEn[t][1] = 0.0;
- } else if (pTile->textureMode != TEXTUREMODE_NORMAL || g_debugger.isDebugMode()) {
- aTexWrapEn[t][0] = 0.0;
- aTexWrapEn[t][1] = 0.0;
- aTexClampEn[t][0] = 0.0;
- aTexClampEn[t][1] = 0.0;
- aTexMirrorEn[t][0] = 0.0;
- aTexMirrorEn[t][1] = 0.0;
- } else {
- aTexWrap[t][0] = f32(1 << pTile->masks) * pTexture->hdRatioS;
- aTexWrap[t][1] = f32(1 << pTile->maskt) * pTexture->hdRatioT;
- aTexClamp[t][0] = f32(pTile->lrs - pTile->uls + 1) * pTexture->hdRatioS - 1.0f;
- aTexClamp[t][1] = f32(pTile->lrt - pTile->ult + 1) * pTexture->hdRatioT - 1.0f;
- aTexWrapEn[t][0] = f32(pTile->masks == 0 ? 0 : 1);
- aTexWrapEn[t][1] = f32(pTile->maskt == 0 ? 0 : 1);
- aTexClampEn[t][0] = f32(gDP.otherMode.cycleType == G_CYC_COPY ? 0 : (pTile->masks == 0 ? 1 : pTile->clamps));
- aTexClampEn[t][1] = f32(gDP.otherMode.cycleType == G_CYC_COPY ? 0 : (pTile->maskt == 0 ? 1 : pTile->clampt));
- aTexMirrorEn[t][0] = f32(pTile->masks == 0 ? 0 : pTile->mirrors);
- aTexMirrorEn[t][1] = f32(pTile->maskt == 0 ? 0 : pTile->mirrort);
- }
-
- uTexWrap[t].set(aTexWrap[t][0], aTexWrap[t][1], _force);
- uTexClamp[t].set(aTexClamp[t][0], aTexClamp[t][1], _force);
- uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force);
- uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force);
- uTexClampEn[t].set(aTexClampEn[t][0], aTexClampEn[t][1], _force);
- uTexMirrorEn[t].set(aTexMirrorEn[t][0], aTexMirrorEn[t][1], _force);
- uTexSize[t].set(aTexSize[t][0], aTexSize[t][1], _force);
- uShiftScale[t].set(aShiftScale[t][0], aShiftScale[t][1], _force);
- uTexOffset[t].set(aTexOffset[t][0], aTexOffset[t][1], _force);
- uHDRatio[t].set(aHDRatio[t][0], aHDRatio[t][1], _force);
- uCacheOffset[t].set(aCacheOffset[t][0], aCacheOffset[t][1], _force);
- }
-
- }
-
-private:
- bool m_useTile[2];
- fv2Uniform uTexWrap[2];
- fv2Uniform uTexClamp[2];
- fv2Uniform uTexWrapEn[2];
- fv2Uniform uTexClampEn[2];
- fv2Uniform uTexMirrorEn[2];
- fv2Uniform uTexSize[2];
- fv2Uniform uShiftScale[2];
- fv2Uniform uTexOffset[2];
- fv2Uniform uHDRatio[2];
- fv2Uniform uCacheOffset[2];
- fv2Uniform uBilinearOffset;
-};
-
-class ULights : public UniformGroup
-{
-public:
- ULights(GLuint _program)
- {
- char buf[32];
- for (s32 i = 0; i < 8; ++i) {
- sprintf(buf, "uLightDirection[%d]", i);
- uLightDirection[i].loc = glGetUniformLocation(_program, buf);
- sprintf(buf, "uLightColor[%d]", i);
- uLightColor[i].loc = glGetUniformLocation(_program, buf);
- }
- }
-
- void update(bool _force) override
- {
- for (u32 i = 0; i <= gSP.numLights; ++i) {
- uLightDirection[i].set(gSP.lights.xyz[i], _force);
- uLightColor[i].set(gSP.lights.rgb[i], _force);
- }
- }
-
-private:
- fv3Uniform uLightDirection[8];
- fv3Uniform uLightColor[8];
-};
-
-
-/*---------------CombinerProgramUniformFactory-------------*/
+}
void CombinerProgramUniformFactory::buildUniforms(GLuint _program,
- const CombinerInputs & _inputs,
- const CombinerKey & _key,
- UniformGroups & _uniforms)
-{
- _uniforms.emplace_back(new UNoiseTex(_program));
- _uniforms.emplace_back(new UScreenSpaceTriangleInfo(_program));
- _uniforms.emplace_back(new URasterInfo(_program));
- _uniforms.emplace_back(new UViewportInfo(_program));
+ const CombinerInputs &_inputs,
+ const CombinerKey &_key,
+ UniformGroups &_uniforms) {
+
+ _addNoiseTex(_program, _uniforms);
+ _addScreenSpaceTriangleInfo(_program, _uniforms);
+ _addRasterInfo(_program, _uniforms);
+ _addViewportInfo(_program, _uniforms);
if (!m_glInfo.isGLES2) {
- _uniforms.emplace_back(new UDepthTex(_program));
- _uniforms.emplace_back(new UDepthScale(_program));
+ _addDepthTex(_program, _uniforms);
+ _addDepthScale(_program, _uniforms);
}
if (_inputs.usesTexture()) {
- _uniforms.emplace_back(new UTextures(_program));
+ _addTextures(_program, _uniforms);
if (config.video.multisampling != 0)
- _uniforms.emplace_back(new UMSAATextures(_program));
+ _addMSAATextures(_program, _uniforms);
- _uniforms.emplace_back(new UFrameBufferInfo(_program));
+ _addFrameBufferInfo(_program, _uniforms);
if (_inputs.usesLOD()) {
- _uniforms.emplace_back(new UMipmap(_program));
+ _addMipmap(_program, _uniforms);
+ if (config.generalEmulation.enableLOD != 0)
+ _addMipmap2(_program, _uniforms);
} else if (_key.getCycleType() < G_CYC_COPY) {
- _uniforms.emplace_back(new UTextureFetchMode(_program));
+ _addTextureFetchMode(_program, _uniforms);
}
- _uniforms.emplace_back(new UTexturePersp(_program));
+ _addTexturePersp(_program, _uniforms);
if (m_glInfo.isGLES2)
- _uniforms.emplace_back(new UTextureSize(_program, _inputs.usesTile(0), _inputs.usesTile(1)));
+ _addTextureSize(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));
if (!_key.isRectKey())
- _uniforms.emplace_back(new UTextureParams(_program, _inputs.usesTile(0), _inputs.usesTile(1)));
+ _addTextureParams(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));
- _uniforms.emplace_back(new UTextureEngine(_program, _inputs.usesTile(0), _inputs.usesTile(1)));
+ _addClampWrapMirrorEngine(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));
}
- _uniforms.emplace_back(new UFog(_program));
+ _addFog(_program, _uniforms);
if (config.generalEmulation.enableLegacyBlending == 0) {
switch (_key.getCycleType()) {
- case G_CYC_1CYCLE:
- _uniforms.emplace_back(new UBlendMode1Cycle(_program));
- break;
- case G_CYC_2CYCLE:
- _uniforms.emplace_back(new UBlendMode2Cycle(_program));
- break;
+ case G_CYC_1CYCLE:
+ _addBlendMode1Cycle(_program, _uniforms);
+ break;
+ case G_CYC_2CYCLE:
+ _addBlendMode2Cycle(_program, _uniforms);
+ break;
}
}
- _uniforms.emplace_back(new UBlendCvg(_program));
+ _addBlendCvg(_program, _uniforms);
- _uniforms.emplace_back(new UDitherMode(_program, _inputs.usesNoise()));
+ _addDitherMode(_program, _uniforms, _inputs.usesNoise());
- _uniforms.emplace_back(new UScreenScale(_program));
+ _addScreenScale(_program, _uniforms);
- _uniforms.emplace_back(new UAlphaTestInfo(_program));
+ _addAlphaTestInfo(_program, _uniforms);
if ((config.generalEmulation.hacks & hack_RE2) != 0 && config.generalEmulation.enableFragmentDepthWrite != 0)
- _uniforms.emplace_back(new UZLutTexture(_program));
+ _addZLutTexture(_program, _uniforms);
if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
- _uniforms.emplace_back(new UDepthInfo(_program));
+ _addDepthInfo(_program, _uniforms);
else
- _uniforms.emplace_back(new UDepthSource(_program));
+ _addDepthSource(_program, _uniforms);
if (config.generalEmulation.enableFragmentDepthWrite != 0 ||
config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
- _uniforms.emplace_back(new URenderTarget(_program));
+ _addRenderTarget(_program, _uniforms);
if (m_glInfo.isGLESX && m_glInfo.noPerspective) {
- _uniforms.emplace_back(new UClampMode(_program));
- _uniforms.emplace_back(new UPolygonOffset(_program));
+ _addClampMode(_program, _uniforms);
+ _addPolygonOffset(_program, _uniforms);
}
- _uniforms.emplace_back(new UScreenCoordsScale(_program));
+ _addScreenCoordsScale(_program, _uniforms);
- _uniforms.emplace_back(new UColors(_program));
+ _addColors(_program, _uniforms);
if (_key.isRectKey())
- _uniforms.emplace_back(new URectColor(_program));
+ _addRectColor(_program, _uniforms);
if (_inputs.usesHwLighting())
- _uniforms.emplace_back(new ULights(_program));
-}
-
-CombinerProgramUniformFactory::CombinerProgramUniformFactory(const opengl::GLInfo & _glInfo)
-: m_glInfo(_glInfo)
-{
+ _addLights(_program, _uniforms);
}
}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h
index f17ff1be..9f693a13 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.h
@@ -4,18 +4,85 @@
namespace glsl {
- class CombinerProgramUniformFactory
- {
- public:
- CombinerProgramUniformFactory(const opengl::GLInfo & _glInfo);
+class CombinerProgramUniformFactory {
+public:
+ CombinerProgramUniformFactory(const opengl::GLInfo & _glInfo);
+ virtual ~CombinerProgramUniformFactory();
- void buildUniforms(GLuint _program,
- const CombinerInputs & _inputs,
- const CombinerKey & _key,
- UniformGroups & _uniforms);
+ void buildUniforms(GLuint _program,
+ const CombinerInputs &_inputs,
+ const CombinerKey &_key,
+ UniformGroups &_uniforms);
- private:
- const opengl::GLInfo & m_glInfo;
- };
+private:
+ virtual void _addNoiseTex(GLuint _program, UniformGroups &_uniforms) const = 0;
+ virtual void _addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addRasterInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addViewportInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addDepthTex(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addDepthScale(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addTextures(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addMSAATextures(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addFrameBufferInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addMipmap(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addMipmap2(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addTextureFetchMode(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addTexturePersp(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addTextureSize(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const = 0;
+
+ virtual void _addTextureParams(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const = 0;
+
+ virtual void _addClampWrapMirrorEngine(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const = 0;
+
+ virtual void _addFog(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addBlendMode1Cycle(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addBlendMode2Cycle(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addBlendCvg(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const = 0;
+
+ virtual void _addScreenScale(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addAlphaTestInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addZLutTexture(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addDepthInfo(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addDepthSource(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addRenderTarget(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addClampMode(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addPolygonOffset(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addScreenCoordsScale(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addColors(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addRectColor(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+ virtual void _addLights(GLuint _program, UniformGroups &_uniforms) const = 0;
+
+protected:
+ const opengl::GLInfo & m_glInfo;
+};
}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp
new file mode 100644
index 00000000..bca677e6
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.cpp
@@ -0,0 +1,397 @@
+#include
+#include
+#include
+#include "glsl_CombinerProgramUniformFactoryAccurate.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef min
+#undef min
+#endif
+
+namespace glsl {
+
+class URasterInfo : public UniformGroup {
+public:
+ URasterInfo(GLuint _program) {
+ LocateUniform(uVertexOffset);
+ LocateUniform(uTexCoordOffset[0]);
+ LocateUniform(uTexCoordOffset[1]);
+ LocateUniform(uUseTexCoordBounds);
+ LocateUniform(uTexCoordBounds0);
+ LocateUniform(uTexCoordBounds1);
+ }
+
+ void update(bool _force) override {
+ const bool isNativeRes = config.frameBufferEmulation.nativeResFactor == 1 && config.video.multisampling == 0;
+ const bool isTexRect = dwnd().getDrawer().getDrawingState() == DrawingState::TexRect;
+ const bool useTexCoordBounds = isTexRect && !isNativeRes && config.graphics2D.enableTexCoordBounds;
+ /* At rasterization stage, the N64 places samples on the top left of the fragment while OpenGL */
+ /* places them in the fragment center. As a result, a normal approach results in shifted texture */
+ /* coordinates. In native resolution, this difference can be negated by shifting vertices by 0.5. */
+ /* In higher resolutions, there are more samples than the game intends, so shifting is not very */
+ /* effective. Still, an heuristic is applied to render texture rectangles as correctly as possible */
+ /* in higher resolutions too. See issue #2324 for details. */
+ const float vertexOffset = isNativeRes ? 0.5f : 0.0f;
+ float texCoordOffset[2][2] = { 0.0f, 0.0f };
+ if (isTexRect && !isNativeRes) {
+ float scale[2] = { 0.0f, 0.0f };
+ if (config.graphics2D.enableNativeResTexrects != 0 && gDP.otherMode.textureFilter != G_TF_POINT) {
+ scale[0] = scale[1] = 1.0f;
+ } else {
+ scale[0] = scale[1] = static_cast(config.frameBufferEmulation.nativeResFactor);
+ }
+
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture* _pTexture = textureCache().current[t];
+ if (_pTexture != nullptr) {
+ if (config.frameBufferEmulation.nativeResFactor != 0) {
+ if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) {
+ texCoordOffset[t][0] = -0.5f * gDP.lastTexRectInfo.dsdx;
+ texCoordOffset[t][1] = -0.5f * gDP.lastTexRectInfo.dtdy;
+ } else {
+ texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? -0.5f / scale[0] : -1.0f + 0.5f / scale[0]) * gDP.lastTexRectInfo.dsdx;
+ texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? -0.5f / scale[1] : -1.0f + 0.5f / scale[1]) * gDP.lastTexRectInfo.dtdy;
+ }
+ } else {
+ texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dsdx;
+ texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dtdy;
+ if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) {
+ texCoordOffset[t][0] -= 0.5f;
+ texCoordOffset[t][1] -= 0.5f;
+ }
+ }
+ }
+ }
+ }
+ /* Hack for framebuffer textures. See #519 and #2112 */
+ if ((config.generalEmulation.hacks & hack_fbTextureOffset) != 0) {
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture* _pTexture = textureCache().current[t];
+ if (_pTexture != nullptr) {
+ if (gDP.otherMode.textureFilter != G_TF_POINT && _pTexture->frameBufferTexture != CachedTexture::fbNone) {
+ texCoordOffset[t][0] -= 1.0f;
+ texCoordOffset[t][1] -= 1.0f;
+ }
+ }
+ }
+ }
+ float tcbounds[2][4] = {};
+ if (useTexCoordBounds) {
+ f32 uls, lrs, ult, lrt, S, T;
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture * _pTexture = textureCache().current[t];
+ if (_pTexture != nullptr) {
+ S = _FIXED2FLOAT(gDP.lastTexRectInfo.s, 5);
+ T = _FIXED2FLOAT(gDP.lastTexRectInfo.t, 5);
+ uls = S + (ceilf(gDP.lastTexRectInfo.ulx) - gDP.lastTexRectInfo.ulx) * gDP.lastTexRectInfo.dsdx;
+ lrs = S + (ceilf(gDP.lastTexRectInfo.lrx) - gDP.lastTexRectInfo.ulx - 1.0f) * gDP.lastTexRectInfo.dsdx;
+ ult = T + (ceilf(gDP.lastTexRectInfo.uly) - gDP.lastTexRectInfo.uly) * gDP.lastTexRectInfo.dtdy;
+ lrt = T + (ceilf(gDP.lastTexRectInfo.lry) - gDP.lastTexRectInfo.uly - 1.0f) * gDP.lastTexRectInfo.dtdy;
+ tcbounds[t][0] = fmin(uls, lrs);
+ tcbounds[t][1] = fmin(ult, lrt);
+ tcbounds[t][2] = fmax(uls, lrs);
+ tcbounds[t][3] = fmax(ult, lrt);
+ }
+ }
+ }
+
+ uVertexOffset.set(vertexOffset, vertexOffset, _force);
+ uTexCoordOffset[0].set(texCoordOffset[0][0], texCoordOffset[0][1], _force);
+ uTexCoordOffset[1].set(texCoordOffset[1][0], texCoordOffset[1][1], _force);
+ uUseTexCoordBounds.set(useTexCoordBounds ? 1 : 0, _force);
+ uTexCoordBounds0.set(tcbounds[0], _force);
+ uTexCoordBounds1.set(tcbounds[1], _force);
+ }
+
+private:
+ fv2Uniform uVertexOffset;
+ fv2Uniform uTexCoordOffset[2];
+ iUniform uUseTexCoordBounds;
+ fv4Uniform uTexCoordBounds0;
+ fv4Uniform uTexCoordBounds1;
+};
+
+class UMipmap : public UniformGroup
+{
+public:
+ UMipmap(GLuint _program) {
+ LocateUniform(uMinLod);
+ LocateUniform(uMaxTile);
+ LocateUniform(uEnableLod);
+ LocateUniform(uTextureDetail);
+ }
+
+ void update(bool _force) override
+ {
+ uMinLod.set(gDP.primColor.m, _force);
+ const CachedTexture * _pTexture = textureCache().current[1];
+ if (_pTexture == nullptr)
+ uMaxTile.set(gSP.texture.level, _force);
+ else
+ uMaxTile.set(_pTexture->max_level > 0 ? gSP.texture.level : std::min(gSP.texture.level, 1u), _force);
+ const int uCalcLOD = (gDP.otherMode.textureLOD == G_TL_LOD) ? 1 : 0;
+ uEnableLod.set(uCalcLOD, _force);
+ uTextureDetail.set(gDP.otherMode.textureDetail, _force);
+ }
+
+private:
+ fUniform uMinLod;
+ iUniform uMaxTile;
+ iUniform uEnableLod;
+ iUniform uTextureDetail;
+};
+
+class UTextureSize : public UniformGroup
+{
+public:
+ UTextureSize(GLuint _program, bool _useT0, bool _useT1)
+ : m_useT0(_useT0)
+ , m_useT1(_useT1)
+ {
+ LocateUniform(uTextureSize[0]);
+ LocateUniform(uTextureSize[1]);
+ }
+
+ void update(bool _force) override
+ {
+ TextureCache & cache = textureCache();
+ if (m_useT0 && cache.current[0] != nullptr)
+ uTextureSize[0].set(static_cast(cache.current[0]->width),
+ static_cast(cache.current[0]->height), _force);
+ if (m_useT1 && cache.current[1] != nullptr) {
+ CachedTexture * pTexture = cache.current[1];
+ if (pTexture->max_level == 0)
+ uTextureSize[1].set(static_cast(pTexture->width),
+ static_cast(pTexture->height), _force);
+ else
+ uTextureSize[1].set(static_cast(pTexture->mipmapAtlasWidth),
+ static_cast(pTexture->mipmapAtlasHeight), _force);
+ }
+ }
+
+private:
+ fv2Uniform uTextureSize[2];
+ bool m_useT0;
+ bool m_useT1;
+};
+
+class UTextureParams : public UniformGroup
+{
+public:
+ UTextureParams(GLuint _program, bool _useT0, bool _useT1)
+ {
+ m_useTile[0] = _useT0;
+ m_useTile[1] = _useT1;
+ LocateUniform(uTexScale);
+ LocateUniform(uCacheFrameBuffer);
+ }
+
+ void update(bool _force) override
+ {
+ int nFB[2] = { 0, 0 };
+ TextureCache & cache = textureCache();
+ for (u32 t = 0; t < 2; ++t) {
+ if (!m_useTile[t])
+ continue;
+ CachedTexture *_pTexture = cache.current[t];
+ if (_pTexture != nullptr) {
+ nFB[t] = _pTexture->frameBufferTexture;
+ }
+ }
+
+ uCacheFrameBuffer.set(nFB[0], nFB[1], _force);
+ uTexScale.set(gSP.texture.scales, gSP.texture.scalet, _force);
+ }
+
+private:
+ bool m_useTile[2];
+ fv2Uniform uTexScale;
+ iv2Uniform uCacheFrameBuffer;
+};
+
+class UTextureEngine : public UniformGroup
+{
+public:
+ UTextureEngine(GLuint _program, bool _useT0, bool _useT1)
+ {
+ m_useTile[0] = _useT0;
+ m_useTile[1] = _useT1;
+ LocateUniform(uTexWrap[0]);
+ LocateUniform(uTexWrap[1]);
+ LocateUniform(uTexClamp[0]);
+ LocateUniform(uTexClamp[1]);
+ LocateUniform(uTexWrapEn[0]);
+ LocateUniform(uTexWrapEn[1]);
+ LocateUniform(uTexClampEn[0]);
+ LocateUniform(uTexClampEn[1]);
+ LocateUniform(uTexMirrorEn[0]);
+ LocateUniform(uTexMirrorEn[1]);
+ LocateUniform(uTexSize[0]);
+ LocateUniform(uTexSize[1]);
+ LocateUniform(uShiftScale[0]);
+ LocateUniform(uShiftScale[1]);
+ LocateUniform(uTexOffset[0]);
+ LocateUniform(uTexOffset[1]);
+ LocateUniform(uHDRatio[0]);
+ LocateUniform(uHDRatio[1]);
+ LocateUniform(uCacheOffset[0]);
+ LocateUniform(uCacheOffset[1]);
+ LocateUniform(uBilinearOffset);
+ }
+
+ void update(bool _force) override
+ {
+ std::array aTexWrap[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
+ std::array aTexClamp[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
+ std::array aTexWrapEn[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
+ std::array aTexClampEn[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
+ std::array aTexMirrorEn[2] = { { 0.0f, 0.0f }, { 0.0f,0.0f }};
+ std::array aTexSize[2] = { { 1024.0f, 1024.0f }, { 1024.0f, 1024.0f } };
+
+ std::array aShiftScale[2] = { { 1.0f, 1.0f }, { 1.0f,1.0f } };
+ std::array aTexOffset[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
+
+ std::array aHDRatio[2] = { { 1.0f, 1.0f }, { 1.0f, 1.0f } };
+ std::array aCacheOffset[2] = { { 0.0f, 0.0f }, { 0.0f, 0.0f } };
+
+ const float bilinearOffset = gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY ? 0.5f : 0.0f;
+ uBilinearOffset.set(bilinearOffset, bilinearOffset, _force);
+
+ TextureCache & cache = textureCache();
+ for (u32 t = 0; t < 2; ++t) {
+ if (!m_useTile[t])
+ continue;
+
+ const gDPTile * pTile = gSP.textureTile[t];
+ CachedTexture * pTexture = cache.current[t];
+ if (pTile == nullptr || pTexture == nullptr)
+ continue;
+
+ aTexSize[t][0] = pTexture->width * pTexture->hdRatioS;
+ aTexSize[t][1] = pTexture->height * pTexture->hdRatioT;
+
+ aShiftScale[t][0] = pTile->shifts > 10 ? static_cast(1 << (16 - pTile->shifts)) : 1.0f / static_cast(1 << pTile->shifts);
+ aShiftScale[t][1] = pTile->shiftt > 10 ? static_cast(1 << (16 - pTile->shiftt)) : 1.0f / static_cast(1 << pTile->shiftt);
+
+ if (pTile->textureMode != TEXTUREMODE_BGIMAGE && pTile->textureMode != TEXTUREMODE_FRAMEBUFFER_BG) {
+ aTexOffset[t][0] = pTile->fuls;
+ aTexOffset[t][1] = pTile->fult;
+ }
+
+ aHDRatio[t][0] = pTexture->hdRatioS;
+ aHDRatio[t][1] = pTexture->hdRatioT;
+
+ aCacheOffset[t][0] = pTexture->offsetS * pTexture->hdRatioS;
+ aCacheOffset[t][1] = pTexture->offsetT * pTexture->hdRatioT;
+
+ /* Not sure if special treatment of framebuffer textures is correct */
+ if (pTexture->frameBufferTexture != CachedTexture::fbNone)
+ {
+ aTexClamp[t][0] = f32(pTexture->width) * pTexture->hdRatioS - 1.0f;
+ aTexClamp[t][1] = f32(pTexture->height) * pTexture->hdRatioT - 1.0f;
+ aTexWrapEn[t][0] = 0.0;
+ aTexWrapEn[t][1] = 0.0;
+ aTexClampEn[t][0] = 1.0;
+ aTexClampEn[t][1] = 1.0;
+ aTexMirrorEn[t][0] = 0.0;
+ aTexMirrorEn[t][1] = 0.0;
+ } else if (pTile->textureMode != TEXTUREMODE_NORMAL || g_debugger.isDebugMode()) {
+ aTexWrapEn[t][0] = 0.0;
+ aTexWrapEn[t][1] = 0.0;
+ aTexClampEn[t][0] = 0.0;
+ aTexClampEn[t][1] = 0.0;
+ aTexMirrorEn[t][0] = 0.0;
+ aTexMirrorEn[t][1] = 0.0;
+ } else {
+ aTexWrap[t][0] = f32(1 << pTile->masks) * pTexture->hdRatioS;
+ aTexWrap[t][1] = f32(1 << pTile->maskt) * pTexture->hdRatioT;
+ aTexClamp[t][0] = f32(pTile->lrs - pTile->uls + 1) * pTexture->hdRatioS - 1.0f;
+ aTexClamp[t][1] = f32(pTile->lrt - pTile->ult + 1) * pTexture->hdRatioT - 1.0f;
+ aTexWrapEn[t][0] = f32(pTile->masks == 0 ? 0 : 1);
+ aTexWrapEn[t][1] = f32(pTile->maskt == 0 ? 0 : 1);
+ aTexClampEn[t][0] = f32(gDP.otherMode.cycleType == G_CYC_COPY ? 0 : (pTile->masks == 0 ? 1 : pTile->clamps));
+ aTexClampEn[t][1] = f32(gDP.otherMode.cycleType == G_CYC_COPY ? 0 : (pTile->maskt == 0 ? 1 : pTile->clampt));
+ aTexMirrorEn[t][0] = f32(pTile->masks == 0 ? 0 : pTile->mirrors);
+ aTexMirrorEn[t][1] = f32(pTile->maskt == 0 ? 0 : pTile->mirrort);
+ }
+
+ uTexWrap[t].set(aTexWrap[t][0], aTexWrap[t][1], _force);
+ uTexClamp[t].set(aTexClamp[t][0], aTexClamp[t][1], _force);
+ uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force);
+ uTexWrapEn[t].set(aTexWrapEn[t][0], aTexWrapEn[t][1], _force);
+ uTexClampEn[t].set(aTexClampEn[t][0], aTexClampEn[t][1], _force);
+ uTexMirrorEn[t].set(aTexMirrorEn[t][0], aTexMirrorEn[t][1], _force);
+ uTexSize[t].set(aTexSize[t][0], aTexSize[t][1], _force);
+ uShiftScale[t].set(aShiftScale[t][0], aShiftScale[t][1], _force);
+ uTexOffset[t].set(aTexOffset[t][0], aTexOffset[t][1], _force);
+ uHDRatio[t].set(aHDRatio[t][0], aHDRatio[t][1], _force);
+ uCacheOffset[t].set(aCacheOffset[t][0], aCacheOffset[t][1], _force);
+ }
+
+ }
+
+private:
+ bool m_useTile[2];
+ fv2Uniform uTexWrap[2];
+ fv2Uniform uTexClamp[2];
+ fv2Uniform uTexWrapEn[2];
+ fv2Uniform uTexClampEn[2];
+ fv2Uniform uTexMirrorEn[2];
+ fv2Uniform uTexSize[2];
+ fv2Uniform uShiftScale[2];
+ fv2Uniform uTexOffset[2];
+ fv2Uniform uHDRatio[2];
+ fv2Uniform uCacheOffset[2];
+ fv2Uniform uBilinearOffset;
+};
+
+/*---------------CombinerProgramUniformFactoryCommon-------------*/
+
+void CombinerProgramUniformFactoryAccurate::_addRasterInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new URasterInfo(_program));
+}
+
+void CombinerProgramUniformFactoryAccurate::_addMipmap(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UMipmap(_program));
+}
+
+void CombinerProgramUniformFactoryAccurate::_addMipmap2(GLuint _program, UniformGroups &_uniforms) const
+{
+}
+
+void CombinerProgramUniformFactoryAccurate::_addTextureSize(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UTextureSize(_program, _usesTile0, _usesTile1));
+}
+
+void CombinerProgramUniformFactoryAccurate::_addTextureParams(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UTextureParams(_program, _usesTile0, _usesTile1));
+}
+
+void CombinerProgramUniformFactoryAccurate::_addClampWrapMirrorEngine(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UTextureEngine(_program, _usesTile0, _usesTile1));
+}
+
+CombinerProgramUniformFactoryAccurate::CombinerProgramUniformFactoryAccurate(const opengl::GLInfo & _glInfo)
+ : CombinerProgramUniformFactoryCommon(_glInfo)
+ , m_glInfo(_glInfo)
+{
+}
+
+}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.h
new file mode 100644
index 00000000..415e3d34
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryAccurate.h
@@ -0,0 +1,28 @@
+#pragma once
+#include
+#include "glsl_CombinerProgramImpl.h"
+#include "glsl_CombinerProgramUniformFactoryCommon.h"
+
+namespace glsl {
+ class CombinerProgramUniformFactoryAccurate : public CombinerProgramUniformFactoryCommon {
+
+ public:
+ CombinerProgramUniformFactoryAccurate(const opengl::GLInfo & _glInfo);
+ private:
+
+ virtual void _addRasterInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addMipmap(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addMipmap2(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addTextureSize(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+
+ virtual void _addTextureParams(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+
+ virtual void _addClampWrapMirrorEngine(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+
+ const opengl::GLInfo &m_glInfo;
+ };
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp
new file mode 100644
index 00000000..44f73306
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.cpp
@@ -0,0 +1,914 @@
+#include
+#include "glsl_CombinerProgramUniformFactoryCommon.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace glsl {
+
+/*---------------UniformGroup-------------*/
+
+class UNoiseTex : public UniformGroup
+{
+public:
+ UNoiseTex(GLuint _program) {
+ LocateUniform(uTexNoise);
+ }
+
+ void update(bool _force) override
+ {
+ uTexNoise.set(int(graphics::textureIndices::NoiseTex), _force);
+ }
+
+private:
+ iUniform uTexNoise;
+};
+
+class UDepthTex : public UniformGroup
+{
+public:
+ UDepthTex(GLuint _program) {
+ LocateUniform(uDepthTex);
+ }
+
+ void update(bool _force) override
+ {
+ uDepthTex.set(int(graphics::textureIndices::DepthTex), _force);
+ }
+
+private:
+ iUniform uDepthTex;
+};
+
+class UZLutTexture : public UniformGroup
+{
+public:
+ UZLutTexture(GLuint _program) {
+ LocateUniform(uZlutImage);
+ }
+
+ void update(bool _force) override
+ {
+ uZlutImage.set(int(graphics::textureIndices::ZLUTTex), _force);
+ }
+
+private:
+ iUniform uZlutImage;
+};
+
+class UTextures : public UniformGroup
+{
+public:
+ UTextures(GLuint _program) {
+ LocateUniform(uTex0);
+ LocateUniform(uTex1);
+ }
+
+ void update(bool _force) override
+ {
+ uTex0.set(0, _force);
+ uTex1.set(1, _force);
+ }
+
+private:
+ iUniform uTex0;
+ iUniform uTex1;
+};
+
+class UMSAATextures : public UniformGroup
+{
+public:
+ UMSAATextures(GLuint _program) {
+ LocateUniform(uMSTex0);
+ LocateUniform(uMSTex1);
+ LocateUniform(uMSAASamples);
+ }
+
+ void update(bool _force) override
+ {
+ uMSTex0.set(int(graphics::textureIndices::MSTex[0]), _force);
+ uMSTex1.set(int(graphics::textureIndices::MSTex[1]), _force);
+ uMSAASamples.set(config.video.multisampling, _force);
+ }
+
+private:
+ iUniform uMSTex0;
+ iUniform uMSTex1;
+ iUniform uMSAASamples;
+};
+
+class UScreenSpaceTriangleInfo : public UniformGroup
+{
+public:
+ UScreenSpaceTriangleInfo(GLuint _program) {
+ LocateUniform(uScreenSpaceTriangle);
+ }
+
+ void update(bool _force) override
+ {
+ uScreenSpaceTriangle.set(
+ (dwnd().getDrawer().getDrawingState() == DrawingState::ScreenSpaceTriangle) ? 1 : 0, _force);
+ }
+
+private:
+ iUniform uScreenSpaceTriangle;
+};
+
+class UFrameBufferInfo : public UniformGroup
+{
+public:
+ UFrameBufferInfo(GLuint _program) {
+ LocateUniform(uFbMonochrome);
+ LocateUniform(uFbFixedAlpha);
+ LocateUniform(uMSTexEnabled);
+ }
+
+ void update(bool _force) override
+ {
+ int nFbMonochromeMode0 = 0, nFbMonochromeMode1 = 0;
+ int nFbFixedAlpha0 = 0, nFbFixedAlpha1 = 0;
+ int nMSTex0Enabled = 0, nMSTex1Enabled = 0;
+ TextureCache & cache = textureCache();
+ if (cache.current[0] != nullptr && cache.current[0]->frameBufferTexture != CachedTexture::fbNone) {
+ if (cache.current[0]->size == G_IM_SIZ_8b) {
+ nFbMonochromeMode0 = 1;
+ if (gDP.otherMode.imageRead == 0)
+ nFbFixedAlpha0 = 1;
+ } else if (gSP.textureTile[0]->size == G_IM_SIZ_16b && gSP.textureTile[0]->format == G_IM_FMT_IA) {
+ nFbMonochromeMode0 = 2;
+ } else if ((config.generalEmulation.hacks & hack_ZeldaMonochrome) != 0 &&
+ cache.current[0]->size == G_IM_SIZ_16b &&
+ gSP.textureTile[0]->size == G_IM_SIZ_8b &&
+ gSP.textureTile[0]->format == G_IM_FMT_CI) {
+ // Zelda monochrome effect
+ nFbMonochromeMode0 = 3;
+ nFbMonochromeMode1 = 3;
+ }
+
+ nMSTex0Enabled = cache.current[0]->frameBufferTexture == CachedTexture::fbMultiSample ? 1 : 0;
+ }
+ if (cache.current[1] != nullptr && cache.current[1]->frameBufferTexture != CachedTexture::fbNone) {
+ if (cache.current[1]->size == G_IM_SIZ_8b) {
+ nFbMonochromeMode1 = 1;
+ if (gDP.otherMode.imageRead == 0)
+ nFbFixedAlpha1 = 1;
+ }
+ else if (gSP.textureTile[1]->size == G_IM_SIZ_16b && gSP.textureTile[1]->format == G_IM_FMT_IA)
+ nFbMonochromeMode1 = 2;
+ nMSTex1Enabled = cache.current[1]->frameBufferTexture == CachedTexture::fbMultiSample ? 1 : 0;
+ }
+ uFbMonochrome.set(nFbMonochromeMode0, nFbMonochromeMode1, _force);
+ uFbFixedAlpha.set(nFbFixedAlpha0, nFbFixedAlpha1, _force);
+ uMSTexEnabled.set(nMSTex0Enabled, nMSTex1Enabled, _force);
+ gDP.changed &= ~CHANGED_FB_TEXTURE;
+ }
+
+private:
+ iv2Uniform uFbMonochrome;
+ iv2Uniform uFbFixedAlpha;
+ iv2Uniform uMSTexEnabled;
+};
+
+
+class UFog : public UniformGroup
+{
+public:
+ UFog(GLuint _program) {
+ LocateUniform(uFogUsage);
+ LocateUniform(uFogScale);
+ }
+
+ void update(bool _force) override
+ {
+ if (RSP.LLE) {
+ uFogUsage.set(0, _force);
+ return;
+ }
+
+ int nFogUsage = ((gSP.geometryMode & G_FOG) != 0) ? 1 : 0;
+ if (GBI.getMicrocodeType() == F3DAM) {
+ const s16 fogMode = ((gSP.geometryMode >> 13) & 9) + 0xFFF8;
+ if (fogMode == 0)
+ nFogUsage = 1;
+ else if (fogMode > 0)
+ nFogUsage = 2;
+ }
+ uFogUsage.set(nFogUsage, _force);
+ uFogScale.set(gSP.fog.multiplierf, gSP.fog.offsetf, _force);
+ }
+
+private:
+ iUniform uFogUsage;
+ fv2Uniform uFogScale;
+};
+
+class UBlendMode1Cycle : public UniformGroup
+{
+public:
+ UBlendMode1Cycle(GLuint _program) {
+ LocateUniform(uBlendMux1);
+ LocateUniform(uForceBlendCycle1);
+ }
+
+ void update(bool _force) override
+ {
+ uBlendMux1.set(gDP.otherMode.c1_m1a,
+ gDP.otherMode.c1_m1b,
+ gDP.otherMode.c1_m2a,
+ gDP.otherMode.c1_m2b,
+ _force);
+
+ const int forceBlend1 = (int)gDP.otherMode.forceBlender;
+ uForceBlendCycle1.set(forceBlend1, _force);
+ }
+
+private:
+ i4Uniform uBlendMux1;
+ iUniform uForceBlendCycle1;
+};
+
+class UBlendMode2Cycle : public UniformGroup
+{
+public:
+ UBlendMode2Cycle(GLuint _program) {
+ LocateUniform(uBlendMux1);
+ LocateUniform(uBlendMux2);
+ LocateUniform(uForceBlendCycle1);
+ LocateUniform(uForceBlendCycle2);
+ }
+
+ void update(bool _force) override
+ {
+ uBlendMux1.set(gDP.otherMode.c1_m1a,
+ gDP.otherMode.c1_m1b,
+ gDP.otherMode.c1_m2a,
+ gDP.otherMode.c1_m2b,
+ _force);
+
+ uBlendMux2.set(gDP.otherMode.c2_m1a,
+ gDP.otherMode.c2_m1b,
+ gDP.otherMode.c2_m2a,
+ gDP.otherMode.c2_m2b,
+ _force);
+
+ const int forceBlend1 = 1;
+ uForceBlendCycle1.set(forceBlend1, _force);
+ const int forceBlend2 = gDP.otherMode.forceBlender;
+ uForceBlendCycle2.set(forceBlend2, _force);
+
+ if (!(graphics::Context::DualSourceBlending || graphics::Context::FramebufferFetchColor) || dwnd().getDrawer().isTexrectDrawerMode()) {
+ // Modes, which shader blender can't emulate
+ const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16);
+ switch (mode) {
+ case 0x0040:
+ // Mia Hamm Soccer
+ // clr_in * a_in + clr_mem * (1-a)
+ // clr_in * a_in + clr_in * (1-a)
+ case 0x0050:
+ // A Bug's Life
+ // clr_in * a_in + clr_mem * (1-a)
+ // clr_in * a_in + clr_mem * (1-a)
+ uForceBlendCycle1.set(0, _force);
+ uForceBlendCycle2.set(0, _force);
+ break;
+ case 0x0150:
+ // Tony Hawk
+ // clr_in * a_in + clr_mem * (1-a)
+ // clr_in * a_fog + clr_mem * (1-a_fog)
+ if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) {
+ uForceBlendCycle1.set(0, _force);
+ uForceBlendCycle2.set(0, _force);
+ }
+ break;
+ }
+ }
+
+ }
+
+private:
+ i4Uniform uBlendMux1;
+ i4Uniform uBlendMux2;
+ iUniform uForceBlendCycle1;
+ iUniform uForceBlendCycle2;
+};
+
+class UBlendCvg : public UniformGroup
+{
+public:
+ UBlendCvg(GLuint _program) {
+ LocateUniform(uCvgDest);
+ LocateUniform(uBlendAlphaMode);
+ }
+
+ void update(bool _force) override
+ {
+ uCvgDest.set(gDP.otherMode.cvgDest, _force);
+ if (dwnd().getDrawer().isTexrectDrawerMode())
+ uBlendAlphaMode.set(2, _force); // No alpha blend in texrect drawing mode
+ else
+ uBlendAlphaMode.set(gDP.otherMode.forceBlender, _force);
+ }
+private:
+ iUniform uCvgDest;
+ iUniform uBlendAlphaMode;
+};
+
+class UDitherMode : public UniformGroup
+{
+public:
+ UDitherMode(GLuint _program, bool _usesNoise)
+ : m_usesNoise(_usesNoise)
+ {
+ LocateUniform(uAlphaCompareMode);
+ LocateUniform(uAlphaDitherMode);
+ LocateUniform(uColorDitherMode);
+ }
+
+ void update(bool _force) override
+ {
+ if (gDP.otherMode.cycleType < G_CYC_COPY) {
+ uAlphaCompareMode.set(gDP.otherMode.alphaCompare, _force);
+ uAlphaDitherMode.set(gDP.otherMode.alphaDither, _force);
+ uColorDitherMode.set(gDP.otherMode.colorDither, _force);
+ }
+ else {
+ uAlphaCompareMode.set(0, _force);
+ uAlphaDitherMode.set(0, _force);
+ uColorDitherMode.set(0, _force);
+ }
+
+ bool updateNoiseTex = m_usesNoise;
+ updateNoiseTex |= (gDP.otherMode.cycleType < G_CYC_COPY) && (gDP.otherMode.colorDither == G_CD_NOISE || gDP.otherMode.alphaDither == G_AD_NOISE || gDP.otherMode.alphaCompare == G_AC_DITHER);
+ if (updateNoiseTex)
+ g_noiseTexture.update();
+ }
+
+private:
+ iUniform uAlphaCompareMode;
+ iUniform uAlphaDitherMode;
+ iUniform uColorDitherMode;
+ bool m_usesNoise;
+};
+
+class UScreenScale : public UniformGroup
+{
+public:
+ UScreenScale(GLuint _program) {
+ LocateUniform(uScreenScale);
+ }
+
+ void update(bool _force) override
+ {
+ if (dwnd().getDrawer().isTexrectDrawerMode()) {
+ uScreenScale.set(1.0f, 1.0f, _force);
+ return;
+ }
+
+ FrameBuffer * pBuffer = frameBufferList().getCurrent();
+ if (pBuffer == nullptr)
+ uScreenScale.set(dwnd().getScaleX(), dwnd().getScaleY(), _force);
+ else
+ uScreenScale.set(pBuffer->m_scale, pBuffer->m_scale, _force);
+ }
+
+private:
+ fv2Uniform uScreenScale;
+};
+
+class UTexturePersp : public UniformGroup
+{
+public:
+ UTexturePersp(GLuint _program) {
+ LocateUniform(uTexturePersp);
+ }
+
+ void update(bool _force) override
+ {
+ const u32 texturePersp = (RSP.LLE || GBI.isTexturePersp()) ? gDP.otherMode.texturePersp : 1U;
+ uTexturePersp.set(texturePersp, _force);
+ }
+
+private:
+ iUniform uTexturePersp;
+};
+
+class UTextureFetchMode : public UniformGroup
+{
+public:
+ UTextureFetchMode(GLuint _program) {
+ LocateUniform(uTextureFilterMode);
+ LocateUniform(uTextureFormat);
+ LocateUniform(uTextureConvert);
+ LocateUniform(uConvertParams);
+ }
+
+ void update(bool _force) override
+ {
+ int textureFilter = gDP.otherMode.textureFilter;
+ uTextureFilterMode.set(textureFilter, _force);
+ uTextureFormat.set(gSP.textureTile[0]->format, gSP.textureTile[1]->format, _force);
+ uTextureConvert.set(gDP.otherMode.convert_one, _force);
+ if (gDP.otherMode.bi_lerp0 == 0 || gDP.otherMode.bi_lerp1 == 0)
+ uConvertParams.set(gDP.convert.k0, gDP.convert.k1, gDP.convert.k2, gDP.convert.k3, _force);
+ }
+
+private:
+ iUniform uTextureFilterMode;
+ iv2Uniform uTextureFormat;
+ iUniform uTextureConvert;
+ i4Uniform uConvertParams;
+};
+
+class UAlphaTestInfo : public UniformGroup
+{
+public:
+ UAlphaTestInfo(GLuint _program) {
+ LocateUniform(uEnableAlphaTest);
+ LocateUniform(uAlphaCvgSel);
+ LocateUniform(uCvgXAlpha);
+ LocateUniform(uAlphaTestValue);
+ }
+
+ void update(bool _force) override
+ {
+ if (gDP.otherMode.cycleType == G_CYC_FILL) {
+ uEnableAlphaTest.set(0, _force);
+ uAlphaCvgSel.set(0, _force);
+
+ } else if (gDP.otherMode.cycleType == G_CYC_COPY) {
+ uAlphaCvgSel.set(0, _force);
+ if (gDP.otherMode.alphaCompare & G_AC_THRESHOLD) {
+ uEnableAlphaTest.set(1, _force);
+ uAlphaTestValue.set(0.5f, _force);
+ } else {
+ uEnableAlphaTest.set(0, _force);
+ }
+ } else if ((gDP.otherMode.alphaCompare & G_AC_THRESHOLD) != 0) {
+ uEnableAlphaTest.set(1, _force);
+ uAlphaTestValue.set(gDP.blendColor.a, _force);
+ uAlphaCvgSel.set(gDP.otherMode.alphaCvgSel, _force);
+ } else {
+ uEnableAlphaTest.set(0, _force);
+ uAlphaCvgSel.set(gDP.otherMode.alphaCvgSel, _force);
+ }
+
+ uCvgXAlpha.set(gDP.otherMode.cvgXAlpha, _force);
+ }
+
+private:
+ iUniform uEnableAlphaTest;
+ iUniform uAlphaCvgSel;
+ iUniform uCvgXAlpha;
+ fUniform uAlphaTestValue;
+};
+
+class UViewportInfo : public UniformGroup
+{
+public:
+ UViewportInfo(GLuint _program) {
+ LocateUniform(uVTrans);
+ LocateUniform(uVScale);
+ LocateUniform(uAdjustTrans);
+ LocateUniform(uAdjustScale);
+ }
+
+ void update(bool _force) override
+ {
+ const bool isOrthographicProjection = gSP.matrix.projection[3][2] == -1.f;
+ float adjustTrans[2] = { 0.0f, 0.0f };
+ float adjustScale[2] = { 1.0f, 1.0f };
+ if (dwnd().isAdjustScreen() && (gDP.colorImage.width > VI.width * 98 / 100)) {
+ if (isOrthographicProjection) {
+ adjustScale[1] = 1.0f / dwnd().getAdjustScale();
+ adjustTrans[1] = static_cast(gDP.colorImage.width) * 3.0f / 4.0f * (1.0f - adjustScale[1]) / 2.0f;
+ } else {
+ adjustScale[0] = dwnd().getAdjustScale();
+ adjustTrans[0] = static_cast(gDP.colorImage.width) * (1.0f - adjustScale[0]) / 2.0f;
+ }
+ }
+ uVTrans.set(gSP.viewport.vtrans[0], gSP.viewport.vtrans[1], _force);
+ uVScale.set(gSP.viewport.vscale[0], -gSP.viewport.vscale[1], _force);
+ uAdjustTrans.set(adjustTrans[0], adjustTrans[1], _force);
+ uAdjustScale.set(adjustScale[0], adjustScale[1], _force);
+ }
+
+private:
+ fv2Uniform uVTrans;
+ fv2Uniform uVScale;
+ fv2Uniform uAdjustTrans;
+ fv2Uniform uAdjustScale;
+};
+
+class UDepthScale : public UniformGroup
+{
+public:
+ UDepthScale(GLuint _program) {
+ LocateUniform(uDepthScale);
+ }
+
+ void update(bool _force) override
+ {
+ if (RSP.LLE)
+ uDepthScale.set(0.5f, 0.5f, _force);
+ else
+ uDepthScale.set(gSP.viewport.vscale[2], gSP.viewport.vtrans[2], _force);
+ }
+
+private:
+ fv2Uniform uDepthScale;
+};
+
+class UDepthInfo : public UniformGroup
+{
+public:
+ UDepthInfo(GLuint _program) {
+ LocateUniform(uEnableDepth);
+ LocateUniform(uEnableDepthCompare);
+ LocateUniform(uEnableDepthUpdate);
+ LocateUniform(uDepthMode);
+ LocateUniform(uDepthSource);
+ LocateUniform(uPrimDepth);
+ LocateUniform(uDeltaZ);
+ }
+
+ void update(bool _force) override
+ {
+ FrameBuffer * pBuffer = frameBufferList().getCurrent();
+ if (pBuffer == nullptr || pBuffer->m_pDepthBuffer == nullptr)
+ return;
+
+ const bool nDepthEnabled = ((gSP.geometryMode & G_ZBUFFER) || gDP.otherMode.depthSource == G_ZS_PRIM) &&
+ gDP.otherMode.cycleType <= G_CYC_2CYCLE;
+ uEnableDepth.set(nDepthEnabled ? 1 : 0, _force);
+ if (nDepthEnabled) {
+ uEnableDepthCompare.set(gDP.otherMode.depthCompare, _force);
+ uEnableDepthUpdate.set(gDP.otherMode.depthUpdate, _force);
+ } else {
+ uEnableDepthCompare.set(0, _force);
+ uEnableDepthUpdate.set(0, _force);
+ }
+ uDepthMode.set(gDP.otherMode.depthMode, _force);
+ uDepthSource.set(gDP.otherMode.depthSource, _force);
+ if (gDP.otherMode.depthSource == G_ZS_PRIM) {
+ uDeltaZ.set(gDP.primDepth.deltaZ, _force);
+ uPrimDepth.set(gDP.primDepth.z, _force);
+ }
+ }
+
+private:
+ iUniform uEnableDepth;
+ iUniform uEnableDepthCompare;
+ iUniform uEnableDepthUpdate;
+ iUniform uDepthMode;
+ iUniform uDepthSource;
+ fUniform uPrimDepth;
+ fUniform uDeltaZ;
+};
+
+class UDepthSource : public UniformGroup
+{
+public:
+ UDepthSource(GLuint _program) {
+ LocateUniform(uDepthSource);
+ LocateUniform(uPrimDepth);
+ }
+
+ void update(bool _force) override
+ {
+ uDepthSource.set(gDP.otherMode.depthSource, _force);
+ if (gDP.otherMode.depthSource == G_ZS_PRIM)
+ uPrimDepth.set(gDP.primDepth.z, _force);
+ }
+
+private:
+ iUniform uDepthSource;
+ fUniform uPrimDepth;
+};
+
+class URenderTarget : public UniformGroup
+{
+public:
+ URenderTarget(GLuint _program) {
+ LocateUniform(uRenderTarget);
+ }
+
+ void update(bool _force) override
+ {
+ int renderTarget = 0;
+ if (isCurrentColorImageDepthImage()) {
+ renderTarget = isDepthCompareEnabled() ? 2 : 1;
+ }
+ uRenderTarget.set(renderTarget, _force);
+ }
+
+private:
+ iUniform uRenderTarget;
+};
+
+class UClampMode : public UniformGroup
+{
+public:
+ UClampMode(GLuint _program) {
+ LocateUniform(uClampMode);
+ }
+
+ void update(bool _force) override
+ {
+ int clampMode = -1;
+ switch (gfxContext.getClampMode())
+ {
+ case graphics::ClampMode::ClippingEnabled:
+ clampMode = 0;
+ break;
+ case graphics::ClampMode::NoNearPlaneClipping:
+ clampMode = 1;
+ break;
+ case graphics::ClampMode::NoClipping:
+ clampMode = 2;
+ break;
+ }
+ uClampMode.set(clampMode, _force);
+ }
+
+private:
+ iUniform uClampMode;
+};
+
+class UPolygonOffset : public UniformGroup
+{
+public:
+ UPolygonOffset(GLuint _program) {
+ LocateUniform(uPolygonOffset);
+ }
+
+ void update(bool _force) override
+ {
+ f32 offset = gfxContext.isEnabled(graphics::enable::POLYGON_OFFSET_FILL) ? 0.003f : 0.0f;
+ uPolygonOffset.set(offset, _force);
+ }
+
+private:
+ fUniform uPolygonOffset;
+};
+
+class UScreenCoordsScale : public UniformGroup
+{
+public:
+ UScreenCoordsScale(GLuint _program) {
+ LocateUniform(uScreenCoordsScale);
+ }
+
+ void update(bool _force) override
+ {
+ f32 scaleX, scaleY;
+ calcCoordsScales(frameBufferList().getCurrent(), scaleX, scaleY);
+ uScreenCoordsScale.set(2.0f*scaleX, -2.0f*scaleY, _force);
+ }
+
+private:
+ fv2Uniform uScreenCoordsScale;
+};
+
+class UColors : public UniformGroup
+{
+public:
+ UColors(GLuint _program) {
+ LocateUniform(uFogColor);
+ LocateUniform(uCenterColor);
+ LocateUniform(uScaleColor);
+ LocateUniform(uBlendColor);
+ LocateUniform(uEnvColor);
+ LocateUniform(uPrimColor);
+ LocateUniform(uPrimLod);
+ LocateUniform(uK4);
+ LocateUniform(uK5);
+ }
+
+ void update(bool _force) override
+ {
+ uFogColor.set(&gDP.fogColor.r, _force);
+ uCenterColor.set(&gDP.key.center.r, _force);
+ uScaleColor.set(&gDP.key.scale.r, _force);
+ uBlendColor.set(&gDP.blendColor.r, _force);
+ uEnvColor.set(&gDP.envColor.r, _force);
+ uPrimColor.set(&gDP.primColor.r, _force);
+ uPrimLod.set(gDP.primColor.l, _force);
+ uK4.set(_FIXED2FLOATCOLOR(gDP.convert.k4, 8 ), _force);
+ uK5.set(_FIXED2FLOATCOLOR(gDP.convert.k5, 8 ), _force);
+ }
+
+private:
+ fv4Uniform uFogColor;
+ fv4Uniform uCenterColor;
+ fv4Uniform uScaleColor;
+ fv4Uniform uBlendColor;
+ fv4Uniform uEnvColor;
+ fv4Uniform uPrimColor;
+ fUniform uPrimLod;
+ fUniform uK4;
+ fUniform uK5;
+};
+
+class URectColor : public UniformGroup
+{
+public:
+ URectColor(GLuint _program) {
+ LocateUniform(uRectColor);
+ }
+
+ void update(bool _force) override
+ {
+ uRectColor.set(&gDP.rectColor.r, _force);
+ }
+
+private:
+ fv4Uniform uRectColor;
+};
+
+class ULights : public UniformGroup
+{
+public:
+ ULights(GLuint _program)
+ {
+ char buf[32];
+ for (s32 i = 0; i < 8; ++i) {
+ sprintf(buf, "uLightDirection[%d]", i);
+ uLightDirection[i].loc = glGetUniformLocation(_program, buf);
+ sprintf(buf, "uLightColor[%d]", i);
+ uLightColor[i].loc = glGetUniformLocation(_program, buf);
+ }
+ }
+
+ void update(bool _force) override
+ {
+ for (u32 i = 0; i <= gSP.numLights; ++i) {
+ uLightDirection[i].set(gSP.lights.xyz[i], _force);
+ uLightColor[i].set(gSP.lights.rgb[i], _force);
+ }
+ }
+
+private:
+ fv3Uniform uLightDirection[8];
+ fv3Uniform uLightColor[8];
+};
+
+
+/*---------------CombinerProgramUniformFactoryCommon-------------*/
+
+void CombinerProgramUniformFactoryCommon::_addNoiseTex(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UNoiseTex(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UScreenSpaceTriangleInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addRasterInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UScreenSpaceTriangleInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addViewportInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UViewportInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addDepthTex(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UDepthTex(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addDepthScale(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UDepthScale(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addTextures(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UTextures(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addMSAATextures(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UMSAATextures(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addFrameBufferInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UFrameBufferInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addTextureFetchMode(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UTextureFetchMode(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addTexturePersp(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UTexturePersp(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addFog(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UFog(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addBlendMode1Cycle(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UBlendMode1Cycle(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addBlendMode2Cycle(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UBlendMode2Cycle(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addBlendCvg(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UBlendCvg(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const
+{
+ _uniforms.emplace_back(new UDitherMode(_program, _usesNoise));
+}
+
+void CombinerProgramUniformFactoryCommon::_addScreenScale(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UScreenScale(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addAlphaTestInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UAlphaTestInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addZLutTexture(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UZLutTexture(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addDepthInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UDepthInfo(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addDepthSource(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UDepthSource(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addRenderTarget(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new URenderTarget(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addClampMode(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UClampMode(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addPolygonOffset(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UPolygonOffset(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addScreenCoordsScale(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UScreenCoordsScale(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addColors(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UColors(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addRectColor(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new URectColor(_program));
+}
+
+void CombinerProgramUniformFactoryCommon::_addLights(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new ULights(_program));
+}
+
+CombinerProgramUniformFactoryCommon::CombinerProgramUniformFactoryCommon(const opengl::GLInfo & _glInfo)
+: CombinerProgramUniformFactory(_glInfo)
+{
+
+}
+
+}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h
new file mode 100644
index 00000000..3457b8e8
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryCommon.h
@@ -0,0 +1,161 @@
+#pragma once
+#include "glsl_CombinerProgramUniformFactory.h"
+
+namespace glsl {
+
+/*---------------Uniform-------------*/
+
+struct iUniform {
+ GLint loc = -1;
+ int val = -999;
+ void set(int _val, bool _force) {
+ if (loc >= 0 && (_force || val != _val)) {
+ val = _val;
+ glUniform1i(loc, _val);
+ }
+ }
+};
+
+struct fUniform {
+ GLint loc = -1;
+ float val = -9999.9f;
+ void set(float _val, bool _force) {
+ if (loc >= 0 && (_force || val != _val)) {
+ val = _val;
+ glUniform1f(loc, _val);
+ }
+ }
+};
+
+struct fv2Uniform {
+ GLint loc = -1;
+ float val1 = -9999.9f, val2 = -9999.9f;
+ void set(float _val1, float _val2, bool _force) {
+ if (loc >= 0 && (_force || val1 != _val1 || val2 != _val2)) {
+ val1 = _val1;
+ val2 = _val2;
+ glUniform2f(loc, _val1, _val2);
+ }
+ }
+};
+
+struct fv3Uniform {
+ GLint loc = -1;
+ float val[3];
+ void set(float * _pVal, bool _force) {
+ const size_t szData = sizeof(float)* 3;
+ if (loc >= 0 && (_force || memcmp(val, _pVal, szData) != 0)) {
+ memcpy(val, _pVal, szData);
+ glUniform3fv(loc, 1, _pVal);
+ }
+ }
+};
+
+struct fv4Uniform {
+ GLint loc = -1;
+ float val[4];
+ void set(float * _pVal, bool _force) {
+ const size_t szData = sizeof(float)* 4;
+ if (loc >= 0 && (_force || memcmp(val, _pVal, szData) != 0)) {
+ memcpy(val, _pVal, szData);
+ glUniform4fv(loc, 1, _pVal);
+ }
+ }
+};
+
+struct iv2Uniform {
+ GLint loc = -1;
+ int val1 = -999, val2 = -999;
+ void set(int _val1, int _val2, bool _force) {
+ if (loc >= 0 && (_force || val1 != _val1 || val2 != _val2)) {
+ val1 = _val1;
+ val2 = _val2;
+ glUniform2i(loc, _val1, _val2);
+ }
+ }
+};
+
+struct i4Uniform {
+ GLint loc = -1;
+ int val0 = -999, val1 = -999, val2 = -999, val3 = -999;
+ void set(int _val0, int _val1, int _val2, int _val3, bool _force) {
+ if (loc < 0)
+ return;
+ if (_force || _val0 != val0 || _val1 != val1 || _val2 != val2 || _val3 != val3) {
+ val0 = _val0;
+ val1 = _val1;
+ val2 = _val2;
+ val3 = _val3;
+ glUniform4i(loc, val0, val1, val2, val3);
+ }
+ }
+};
+
+#define LocateUniform(A) \
+A.loc = glGetUniformLocation(_program, #A);
+
+class CombinerProgramUniformFactoryCommon : public CombinerProgramUniformFactory
+{
+public:
+ CombinerProgramUniformFactoryCommon(const opengl::GLInfo & _glInfo);
+
+private:
+ void _addNoiseTex(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addScreenSpaceTriangleInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addRasterInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addViewportInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addDepthTex(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addDepthScale(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addTextures(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addMSAATextures(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addFrameBufferInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addTextureFetchMode(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addTexturePersp(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addFog(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addBlendMode1Cycle(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addBlendMode2Cycle(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addBlendCvg(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addDitherMode(GLuint _program, UniformGroups &_uniforms, bool _usesNoise) const override;
+
+ void _addScreenScale(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addAlphaTestInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addZLutTexture(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addDepthInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addDepthSource(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addRenderTarget(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addClampMode(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addPolygonOffset(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addScreenCoordsScale(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addColors(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addRectColor(GLuint _program, UniformGroups &_uniforms) const override;
+
+ void _addLights(GLuint _program, UniformGroups &_uniforms) const override;
+};
+
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.cpp
new file mode 100644
index 00000000..d06f67be
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.cpp
@@ -0,0 +1,433 @@
+#include
+#include
+#include
+#include "glsl_CombinerProgramUniformFactoryFast.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef min
+#undef min
+#endif
+
+namespace glsl {
+
+class URasterInfoFast : public UniformGroup {
+public:
+ URasterInfoFast(GLuint _program) {
+ LocateUniform(uVertexOffset);
+ LocateUniform(uTexCoordOffset[0]);
+ LocateUniform(uTexCoordOffset[1]);
+ LocateUniform(uUseTexCoordBounds);
+ LocateUniform(uTexCoordBounds0);
+ LocateUniform(uTexCoordBounds1);
+ }
+
+ void update(bool _force) override {
+ const bool isNativeRes = config.frameBufferEmulation.nativeResFactor == 1 && config.video.multisampling == 0;
+ const bool isTexRect = dwnd().getDrawer().getDrawingState() == DrawingState::TexRect;
+ const bool useTexCoordBounds = isTexRect && !isNativeRes && config.graphics2D.enableTexCoordBounds;
+ /* At rasterization stage, the N64 places samples on the top left of the fragment while OpenGL */
+ /* places them in the fragment center. As a result, a normal approach results in shifted texture */
+ /* coordinates. In native resolution, this difference can be negated by shifting vertices by 0.5. */
+ /* In higher resolutions, there are more samples than the game intends, so shifting is not very */
+ /* effective. Still, an heuristic is applied to render texture rectangles as correctly as possible */
+ /* in higher resolutions too. See issue #2324 for details. */
+ const float vertexOffset = isNativeRes ? 0.5f : 0.0f;
+ float texCoordOffset[2][2] = { 0.0f, 0.0f };
+ if (isTexRect && !isNativeRes) {
+ float scale[2] = { 0.0f, 0.0f };
+ if (config.graphics2D.enableNativeResTexrects != 0 && gDP.otherMode.textureFilter != G_TF_POINT) {
+ scale[0] = scale[1] = 1.0f;
+ } else {
+ scale[0] = scale[1] = static_cast(config.frameBufferEmulation.nativeResFactor);
+ }
+
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture* _pTexture = textureCache().current[t];
+ if (_pTexture != nullptr) {
+ if (config.frameBufferEmulation.nativeResFactor != 0) {
+ texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? -0.5f / scale[0] : -1.0f + 0.5f / scale[0]) * gDP.lastTexRectInfo.dsdx * _pTexture->hdRatioS;
+ texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? -0.5f / scale[1] : -1.0f + 0.5f / scale[1]) * gDP.lastTexRectInfo.dtdy * _pTexture->hdRatioT;
+ } else {
+ texCoordOffset[t][0] = (gDP.lastTexRectInfo.dsdx >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dsdx * _pTexture->hdRatioS;
+ texCoordOffset[t][1] = (gDP.lastTexRectInfo.dtdy >= 0.0f ? 0.0f : -1.0f) * gDP.lastTexRectInfo.dtdy * _pTexture->hdRatioT;
+ if (gDP.otherMode.textureFilter != G_TF_POINT && gDP.otherMode.cycleType != G_CYC_COPY) {
+ texCoordOffset[t][0] -= 0.5f;
+ texCoordOffset[t][1] -= 0.5f;
+ }
+ }
+ }
+ }
+ }
+ /* Hack for framebuffer textures. See #519 and #2112 */
+ if ((config.generalEmulation.hacks & hack_fbTextureOffset) != 0) {
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture* _pTexture = textureCache().current[t];
+ if (_pTexture != nullptr) {
+ if (gDP.otherMode.textureFilter != G_TF_POINT && _pTexture->frameBufferTexture != CachedTexture::fbNone) {
+ texCoordOffset[t][0] -= 1.0f;
+ texCoordOffset[t][1] -= 1.0f;
+ }
+ }
+ }
+ }
+ float tcbounds[2][4] = {};
+ if (useTexCoordBounds) {
+ f32 uls, lrs, ult, lrt, S, T, shiftScaleS, shiftScaleT;
+ s16 shiftedS, shiftedT;
+ u32 shifts, shiftt;
+ for (int t = 0; t < 2; t++) {
+ const CachedTexture * _pTexture = textureCache().current[t];
+ const gDPTile * _pTile = gSP.textureTile[t];
+ if (_pTexture != nullptr && _pTile != nullptr){
+ if (_pTile->shifts > 10) {
+ shifts = 16 - _pTile->shifts;
+ shiftedS = static_cast(gDP.lastTexRectInfo.s << shifts);
+ shiftScaleS = static_cast(1 << shifts);
+ } else {
+ shifts = _pTile->shifts;
+ shiftedS = static_cast(gDP.lastTexRectInfo.s >> shifts);
+ shiftScaleS = 1.0f / static_cast(1 << shifts);
+ }
+ if (_pTile->shiftt > 10) {
+ shiftt = 16 - _pTile->shiftt;
+ shiftedT = static_cast(gDP.lastTexRectInfo.t << shiftt);
+ shiftScaleT = static_cast(1 << shiftt);
+ } else {
+ shiftt = _pTile->shiftt;
+ shiftedT = static_cast(gDP.lastTexRectInfo.t >> shiftt);
+ shiftScaleT = 1.0f / static_cast(1 << shiftt);
+ }
+
+ S = _FIXED2FLOAT(shiftedS, 5);
+ T = _FIXED2FLOAT(shiftedT, 5);
+ uls = S + (ceilf(gDP.lastTexRectInfo.ulx) - gDP.lastTexRectInfo.ulx) * gDP.lastTexRectInfo.dsdx * shiftScaleS;
+ lrs = S + (ceilf(gDP.lastTexRectInfo.lrx) - gDP.lastTexRectInfo.ulx - 1.0f) * gDP.lastTexRectInfo.dsdx * shiftScaleS;
+ ult = T + (ceilf(gDP.lastTexRectInfo.uly) - gDP.lastTexRectInfo.uly) * gDP.lastTexRectInfo.dtdy * shiftScaleT;
+ lrt = T + (ceilf(gDP.lastTexRectInfo.lry) - gDP.lastTexRectInfo.uly - 1.0f) * gDP.lastTexRectInfo.dtdy * shiftScaleT;
+
+ tcbounds[t][0] = (fmin(uls, lrs) - _pTile->fuls) * _pTexture->hdRatioS;
+ tcbounds[t][1] = (fmin(ult, lrt) - _pTile->fult) * _pTexture->hdRatioT;
+ tcbounds[t][2] = (fmax(uls, lrs) - _pTile->fuls) * _pTexture->hdRatioS;
+ tcbounds[t][3] = (fmax(ult, lrt) - _pTile->fult) * _pTexture->hdRatioT;
+ if (_pTexture->frameBufferTexture != CachedTexture::fbNone) {
+ tcbounds[t][0] += _pTexture->offsetS * _pTexture->hdRatioS;
+ tcbounds[t][1] += _pTexture->offsetT * _pTexture->hdRatioT;
+ tcbounds[t][2] += _pTexture->offsetS * _pTexture->hdRatioS;
+ tcbounds[t][3] += _pTexture->offsetT * _pTexture->hdRatioT;
+ }
+ }
+ }
+ }
+
+ uVertexOffset.set(vertexOffset, vertexOffset, _force);
+ uTexCoordOffset[0].set(texCoordOffset[0][0], texCoordOffset[0][1], _force);
+ uTexCoordOffset[1].set(texCoordOffset[1][0], texCoordOffset[1][1], _force);
+ uUseTexCoordBounds.set(useTexCoordBounds ? 1 : 0, _force);
+ uTexCoordBounds0.set(tcbounds[0], _force);
+ uTexCoordBounds1.set(tcbounds[1], _force);
+ }
+
+private:
+ fv2Uniform uVertexOffset;
+ fv2Uniform uTexCoordOffset[2];
+ iUniform uUseTexCoordBounds;
+ fv4Uniform uTexCoordBounds0;
+ fv4Uniform uTexCoordBounds1;
+};
+
+class UMipmap1 : public UniformGroup
+{
+public:
+ UMipmap1(GLuint _program) {
+ LocateUniform(uMinLod);
+ LocateUniform(uMaxTile);
+ }
+
+ void update(bool _force) override
+ {
+ uMinLod.set(gDP.primColor.m, _force);
+ uMaxTile.set(gSP.texture.level, _force);
+ }
+
+private:
+ fUniform uMinLod;
+ iUniform uMaxTile;
+};
+
+class UMipmap2 : public UniformGroup
+{
+public:
+ UMipmap2(GLuint _program) {
+ LocateUniform(uEnableLod);
+ LocateUniform(uTextureDetail);
+ }
+
+ void update(bool _force) override
+ {
+ const int uCalcLOD = (gDP.otherMode.textureLOD == G_TL_LOD) ? 1 : 0;
+ uEnableLod.set(uCalcLOD, _force);
+ uTextureDetail.set(gDP.otherMode.textureDetail, _force);
+ }
+
+private:
+ iUniform uEnableLod;
+ iUniform uTextureDetail;
+};
+
+class UTextureSizeFast : public UniformGroup
+{
+public:
+ UTextureSizeFast(GLuint _program, bool _useT0, bool _useT1)
+ : m_useT0(_useT0)
+ , m_useT1(_useT1)
+ {
+ LocateUniform(uTextureSize[0]);
+ LocateUniform(uTextureSize[1]);
+ }
+
+ void update(bool _force) override
+ {
+ TextureCache & cache = textureCache();
+ if (m_useT0 && cache.current[0] != NULL)
+ uTextureSize[0].set((float)cache.current[0]->width, (float)cache.current[0]->height, _force);
+ if (m_useT1 && cache.current[1] != NULL)
+ uTextureSize[1].set((float)cache.current[1]->width, (float)cache.current[1]->height, _force);
+ }
+
+private:
+ fv2Uniform uTextureSize[2];
+ bool m_useT0;
+ bool m_useT1;
+};
+
+class UTextureParamsFast : public UniformGroup
+{
+public:
+ UTextureParamsFast(GLuint _program, bool _useT0, bool _useT1)
+ {
+ m_useTile[0] = _useT0;
+ m_useTile[1] = _useT1;
+ LocateUniform(uTexOffset[0]);
+ LocateUniform(uTexOffset[1]);
+ LocateUniform(uCacheShiftScale[0]);
+ LocateUniform(uCacheShiftScale[1]);
+ LocateUniform(uCacheScale[0]);
+ LocateUniform(uCacheScale[1]);
+ LocateUniform(uCacheOffset[0]);
+ LocateUniform(uCacheOffset[1]);
+ LocateUniform(uTexScale);
+ LocateUniform(uCacheFrameBuffer);
+ }
+
+ void update(bool _force) override
+ {
+ int nFB[2] = { 0, 0 };
+ TextureCache & cache = textureCache();
+ for (u32 t = 0; t < 2; ++t) {
+ if (!m_useTile[t])
+ continue;
+
+ gDPTile * pTile = gSP.textureTile[t];
+ if (pTile != nullptr) {
+ if (pTile->textureMode == TEXTUREMODE_BGIMAGE || pTile->textureMode == TEXTUREMODE_FRAMEBUFFER_BG)
+ uTexOffset[t].set(0.0f, 0.0f, _force);
+ else {
+ float fuls = pTile->fuls;
+ float fult = pTile->fult;
+ if (pTile->frameBufferAddress > 0) {
+ FrameBuffer * pBuffer = frameBufferList().getBuffer(pTile->frameBufferAddress);
+ if (pBuffer != nullptr) {
+ if (pTile->masks > 0 && pTile->clamps == 0)
+ fuls = float(pTile->uls % (1 << pTile->masks));
+ if (pTile->maskt > 0 && pTile->clampt == 0)
+ fult = float(pTile->ult % (1 << pTile->maskt));
+ } else {
+ pTile->frameBufferAddress = 0;
+ }
+ }
+ uTexOffset[t].set(fuls, fult, _force);
+ }
+ }
+
+ CachedTexture *_pTexture = cache.current[t];
+ if (_pTexture != nullptr) {
+ f32 shiftScaleS = 1.0f;
+ f32 shiftScaleT = 1.0f;
+ getTextureShiftScale(t, cache, shiftScaleS, shiftScaleT);
+ uCacheShiftScale[t].set(shiftScaleS, shiftScaleT, _force);
+ uCacheScale[t].set(_pTexture->scaleS, _pTexture->scaleT, _force);
+ uCacheOffset[t].set(_pTexture->offsetS, _pTexture->offsetT, _force);
+ nFB[t] = _pTexture->frameBufferTexture;
+ }
+ }
+
+ uCacheFrameBuffer.set(nFB[0], nFB[1], _force);
+ uTexScale.set(gSP.texture.scales, gSP.texture.scalet, _force);
+ }
+
+private:
+ bool m_useTile[2];
+ fv2Uniform uTexOffset[2];
+ fv2Uniform uCacheShiftScale[2];
+ fv2Uniform uCacheScale[2];
+ fv2Uniform uCacheOffset[2];
+ fv2Uniform uTexScale;
+ iv2Uniform uCacheFrameBuffer;
+};
+
+class UClampWrapMirrorTex : public UniformGroup
+{
+public:
+ UClampWrapMirrorTex(GLuint _program, bool _useT0, bool _useT1)
+ {
+ m_useTile[0] = _useT0;
+ m_useTile[1] = _useT1;
+ LocateUniform(uTexClamp0);
+ LocateUniform(uTexClamp1);
+ LocateUniform(uTexWrap0);
+ LocateUniform(uTexWrap1);
+ LocateUniform(uTexMirror0);
+ LocateUniform(uTexMirror1);
+ LocateUniform(uTexScale0);
+ LocateUniform(uTexScale1);
+ }
+
+ void update(bool _force) override
+ {
+ std::array aTexClamp[2] = { { -10000.0f, -10000.0f, 10000.0f, 10000.0f },
+ { -10000.0f, -10000.0f, 10000.0f, 10000.0f } };
+ std::array aTexWrap[2] = { { 10000.0f, 10000.0f }, { 10000.0f, 10000.0f } };
+ std::array aTexMirror[2] = { { 0.0f, 0.0f}, { 0.0f, 0.0f } };
+ std::array aTexScale[2] = { { 1.0f, 1.0f },{ 1.0f, 1.0f } };
+ TextureCache & cache = textureCache();
+ const bool replaceTex1ByTex0 = needReplaceTex1ByTex0();
+ for (u32 t = 0; t < 2; ++t) {
+ if (!m_useTile[t])
+ continue;
+
+ const u32 tile = replaceTex1ByTex0 ? 0 : t;
+ const gDPTile * pTile = gSP.textureTile[tile];
+ CachedTexture * pTexture = cache.current[tile];
+ if (pTile == nullptr || pTexture == nullptr)
+ continue;
+
+ if (gDP.otherMode.cycleType != G_CYC_COPY) {
+ if (pTexture->clampS) {
+ aTexClamp[t][0] = 0.0f; // S lower bound
+ if (pTexture->frameBufferTexture != CachedTexture::fbNone ||
+ pTile->textureMode == TEXTUREMODE_BGIMAGE)
+ aTexClamp[t][2] = 1.0f;
+ else {
+ u32 tileWidth = ((pTile->lrs - pTile->uls) & 0x03FF) + 1;
+ if (pTile->size > pTexture->size)
+ tileWidth <<= pTile->size - pTexture->size;
+ // aTexClamp[t][2] = f32(tileWidth) / (pTexture->mirrorS ? f32(pTexture->width) : f32(pTexture->clampWidth)); // S upper bound
+ aTexClamp[t][2] = f32(tileWidth) / f32(pTexture->width); // S upper bound
+ }
+ }
+ if (pTexture->clampT) {
+ aTexClamp[t][1] = 0.0f; // T lower bound
+ if (pTexture->frameBufferTexture != CachedTexture::fbNone ||
+ pTile->textureMode == TEXTUREMODE_BGIMAGE)
+ aTexClamp[t][3] = 1.0f;
+ else {
+ const u32 tileHeight = ((pTile->lrt - pTile->ult) & 0x03FF) + 1;
+ // aTexClamp[t][3] = f32(tileHeight) / (pTexture->mirrorT ? f32(pTexture->height) : f32(pTexture->clampHeight)); // T upper bound
+ aTexClamp[t][3] = f32(tileHeight) / f32(pTexture->height); // T upper bound
+ }
+ }
+ }
+ if (pTexture->maskS) {
+ const f32 wrapWidth = static_cast(1 << pTile->originalMaskS);
+ const f32 pow2Width = static_cast(pow2(pTexture->width));
+ aTexWrap[t][0] = wrapWidth / pow2Width;
+ aTexScale[t][0] = pow2Width / f32(pTexture->width);
+ }
+ if (pTexture->maskT) {
+ const f32 wrapHeight = static_cast(1 << pTile->originalMaskT);
+ const f32 pow2Height = static_cast(pow2(pTexture->height));
+ aTexWrap[t][1] = wrapHeight / pow2Height;
+ aTexScale[t][1] = pow2Height / f32(pTexture->height);
+ }
+ if (pTexture->mirrorS) {
+ aTexMirror[t][0] = 1.0f;
+ aTexWrap[t][0] *= 2.0f;
+ }
+ if (pTexture->mirrorT) {
+ aTexMirror[t][1] = 1.0f;
+ aTexWrap[t][1] *= 2.0f;
+ }
+ }
+
+ uTexClamp0.set(aTexClamp[0].data(), _force);
+ uTexClamp1.set(aTexClamp[1].data(), _force);
+ uTexWrap0.set(aTexWrap[0][0], aTexWrap[0][1], _force);
+ uTexWrap1.set(aTexWrap[1][0], aTexWrap[1][1], _force);
+ uTexMirror0.set(aTexMirror[0][0], aTexMirror[0][1], _force);
+ uTexMirror1.set(aTexMirror[1][0], aTexMirror[1][1], _force);
+ uTexScale0.set(aTexScale[0][0], aTexScale[0][1], _force);
+ uTexScale1.set(aTexScale[1][0], aTexScale[1][1], _force);
+ }
+
+private:
+ bool m_useTile[2];
+ fv4Uniform uTexClamp0;
+ fv4Uniform uTexClamp1;
+ fv2Uniform uTexWrap0;
+ fv2Uniform uTexWrap1;
+ fv2Uniform uTexMirror0;
+ fv2Uniform uTexMirror1;
+ fv2Uniform uTexScale0;
+ fv2Uniform uTexScale1;
+};
+
+/*---------------CombinerProgramUniformFactoryCommon-------------*/
+
+void CombinerProgramUniformFactoryFast::_addRasterInfo(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new URasterInfoFast(_program));
+}
+
+void CombinerProgramUniformFactoryFast::_addMipmap(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UMipmap1(_program));
+}
+
+void CombinerProgramUniformFactoryFast::_addMipmap2(GLuint _program, UniformGroups &_uniforms) const
+{
+ _uniforms.emplace_back(new UMipmap2(_program));
+}
+
+void CombinerProgramUniformFactoryFast::_addTextureSize(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UTextureSizeFast(_program, _usesTile0, _usesTile1));
+}
+
+void CombinerProgramUniformFactoryFast::_addTextureParams(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UTextureParamsFast(_program, _usesTile0, _usesTile1));
+}
+
+void CombinerProgramUniformFactoryFast::_addClampWrapMirrorEngine(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const
+{
+ _uniforms.emplace_back(new UClampWrapMirrorTex(_program, _usesTile0, _usesTile1));
+}
+
+CombinerProgramUniformFactoryFast::CombinerProgramUniformFactoryFast(const opengl::GLInfo & _glInfo)
+: CombinerProgramUniformFactoryCommon(_glInfo)
+{
+}
+
+}
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.h
new file mode 100644
index 00000000..9f004b7a
--- /dev/null
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactoryFast.h
@@ -0,0 +1,26 @@
+#pragma once
+#include
+#include "glsl_CombinerProgramImpl.h"
+#include "glsl_CombinerProgramUniformFactoryCommon.h"
+
+namespace glsl {
+class CombinerProgramUniformFactoryFast : public CombinerProgramUniformFactoryCommon {
+
+public:
+ CombinerProgramUniformFactoryFast(const opengl::GLInfo & _glInfo);
+private:
+
+ virtual void _addRasterInfo(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addMipmap(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addMipmap2(GLuint _program, UniformGroups &_uniforms) const override;
+
+ virtual void _addTextureSize(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+
+ virtual void _addTextureParams(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+
+ virtual void _addClampWrapMirrorEngine(GLuint _program, UniformGroups &_uniforms, bool _usesTile0, bool _usesTile1) const override;
+};
+}
+
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp
index 0a3304ff..0cf4c91e 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp
@@ -14,10 +14,12 @@
#include
#include
#include
+#include
#include "glsl_Utils.h"
#include "glsl_ShaderStorage.h"
#include "glsl_CombinerProgramImpl.h"
-#include "glsl_CombinerProgramUniformFactory.h"
+#include "glsl_CombinerProgramUniformFactoryAccurate.h"
+#include "glsl_CombinerProgramUniformFactoryFast.h"
using namespace glsl;
@@ -203,7 +205,7 @@ bool ShaderStorage::saveShadersStorage(const graphics::Combiners & _combiners) c
static
CombinerProgramImpl * _readCombinerProgramFromStream(std::istream & _is,
CombinerKey& _cmbKey,
- CombinerProgramUniformFactory & _uniformFactory,
+ std::unique_ptr & _uniformFactory,
opengl::CachedUseProgram * _useProgram)
{
int inputs;
@@ -226,7 +228,7 @@ CombinerProgramImpl * _readCombinerProgramFromStream(std::istream & _is,
}
UniformGroups uniforms;
- _uniformFactory.buildUniforms(program, cmbInputs, _cmbKey, uniforms);
+ _uniformFactory->buildUniforms(program, cmbInputs, _cmbKey, uniforms);
return new CombinerProgramImpl(_cmbKey, program, _useProgram, cmbInputs, std::move(uniforms));
}
@@ -336,7 +338,14 @@ bool ShaderStorage::loadShadersStorage(graphics::Combiners & _combiners)
return _loadFromCombinerKeys(_combiners);
displayLoadProgress(L"LOAD COMBINER SHADERS %.1f%%", 0.0f);
- CombinerProgramUniformFactory uniformFactory(m_glinfo);
+
+ std::unique_ptr uniformFactory;
+
+ if (config.generalEmulation.enableInaccurateTextureCoordinates) {
+ uniformFactory = std::make_unique(m_glinfo);
+ } else {
+ uniformFactory = std::make_unique(m_glinfo);
+ }
fin.read((char*)&len, sizeof(len));
const f32 percent = len / 100.0f;
diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h b/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h
index 0fce4232..fe5a5173 100644
--- a/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h
+++ b/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h
@@ -20,7 +20,7 @@ namespace glsl {
bool _saveCombinerKeys(const graphics::Combiners & _combiners) const;
bool _loadFromCombinerKeys(graphics::Combiners & _combiners);
- const u32 m_formatVersion = 0x37U;
+ const u32 m_formatVersion = 0x38U;
const u32 m_keysFormatVersion = 0x05;
const opengl::GLInfo & m_glinfo;
opengl::CachedUseProgram * m_useProgram;
diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp
index 14f9e844..c02e2e52 100644
--- a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp
+++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp
@@ -13,7 +13,8 @@
#endif
#include "opengl_ColorBufferReaderWithReadPixels.h"
#include "opengl_Utils.h"
-#include "GLSL/glsl_CombinerProgramBuilder.h"
+#include "GLSL/glsl_CombinerProgramBuilderAccurate.h"
+#include "GLSL/glsl_CombinerProgramBuilderFast.h"
#include "GLSL/glsl_SpecialShadersFactory.h"
#include "GLSL/glsl_ShaderStorage.h"
@@ -408,7 +409,13 @@ void ContextImpl::resetCombinerProgramBuilder()
{
if (!isCombinerProgramBuilderObsolete())
return;
- m_combinerProgramBuilder.reset(new glsl::CombinerProgramBuilder(m_glInfo, m_cachedFunctions->getCachedUseProgram()));
+
+ if (config.generalEmulation.enableInaccurateTextureCoordinates) {
+ m_combinerProgramBuilder = std::make_unique(m_glInfo, m_cachedFunctions->getCachedUseProgram());
+ } else {
+ m_combinerProgramBuilder = std::make_unique(m_glInfo, m_cachedFunctions->getCachedUseProgram());
+ }
+
m_specialShadersFactory.reset(new glsl::SpecialShadersFactory(m_glInfo,
m_cachedFunctions->getCachedUseProgram(),
m_combinerProgramBuilder->getVertexShaderHeader(),
diff --git a/src/GraphicsDrawer.cpp b/src/GraphicsDrawer.cpp
index 2f5065e5..656fdb40 100644
--- a/src/GraphicsDrawer.cpp
+++ b/src/GraphicsDrawer.cpp
@@ -1277,15 +1277,59 @@ void GraphicsDrawer::drawTexturedRect(const TexturedRectParams & _params)
for (u32 t = 0; t < 2; ++t) {
if (pCurrentCombiner->usesTile(t) && cache.current[t] && gSP.textureTile[t]) {
- const f32 uls = _FIXED2FLOAT(_params.s, 5);
- const f32 lrs = uls + offsetX;
- const f32 ult = _FIXED2FLOAT(_params.t, 5);
- const f32 lrt = ult + offsetY;
- texST[t].s0 = uls;
- texST[t].s1 = lrs;
- texST[t].t0 = ult;
- texST[t].t1 = lrt;
+ if (config.generalEmulation.enableInaccurateTextureCoordinates) {
+ f32 shiftScaleS = 1.0f;
+ f32 shiftScaleT = 1.0f;
+
+ s16 S = _params.s;
+ if (gSP.textureTile[t]->shifts > 10) {
+ const u32 shifts = 16 - gSP.textureTile[t]->shifts;
+ S = static_cast(S << shifts);
+ shiftScaleS = static_cast(1 << shifts);
+ } else if (gSP.textureTile[t]->shifts > 0) {
+ const u32 shifts = gSP.textureTile[t]->shifts;
+ S = static_cast(S >> shifts);
+ shiftScaleS /= static_cast(1 << shifts);
+ }
+ const f32 uls = _FIXED2FLOAT(S, 5);
+ const f32 lrs = uls + offsetX * shiftScaleS;
+
+ s16 T = _params.t;
+ if (gSP.textureTile[t]->shiftt > 10) {
+ const u32 shiftt = 16 - gSP.textureTile[t]->shiftt;
+ T = static_cast(T << shiftt);
+ shiftScaleT = static_cast(1 << shiftt);
+ } else if (gSP.textureTile[t]->shiftt > 0) {
+ const u32 shiftt = gSP.textureTile[t]->shiftt;
+ T = static_cast(T >> shiftt);
+ shiftScaleT /= static_cast(1 << shiftt);
+ }
+ const f32 ult = _FIXED2FLOAT(T, 5);
+ const f32 lrt = ult + offsetY * shiftScaleT;
+
+ texST[t].s0 = uls - gSP.textureTile[t]->fuls;
+ texST[t].s1 = lrs - gSP.textureTile[t]->fuls;
+ texST[t].t0 = ult - gSP.textureTile[t]->fult;
+ texST[t].t1 = lrt - gSP.textureTile[t]->fult;
+
+ if (cache.current[t]->frameBufferTexture != CachedTexture::fbNone) {
+ texST[t].s0 = cache.current[t]->offsetS + texST[t].s0;
+ texST[t].t0 = cache.current[t]->offsetT + texST[t].t0;
+ texST[t].s1 = cache.current[t]->offsetS + texST[t].s1;
+ texST[t].t1 = cache.current[t]->offsetT + texST[t].t1;
+ }
+ } else {
+ const f32 uls = _FIXED2FLOAT(_params.s, 5);
+ const f32 lrs = uls + offsetX;
+ const f32 ult = _FIXED2FLOAT(_params.t, 5);
+ const f32 lrt = ult + offsetY;
+
+ texST[t].s0 = uls;
+ texST[t].s1 = lrs;
+ texST[t].t0 = ult;
+ texST[t].t1 = lrt;
+ }
if (cache.current[t]->frameBufferTexture != CachedTexture::fbMultiSample) {
Context::TexParameters texParams;
@@ -1310,6 +1354,13 @@ void GraphicsDrawer::drawTexturedRect(const TexturedRectParams & _params)
gfxContext.setTextureParameters(texParams);
}
}
+
+ if (config.generalEmulation.enableInaccurateTextureCoordinates) {
+ texST[t].s0 *= cache.current[t]->scaleS;
+ texST[t].t0 *= cache.current[t]->scaleT;
+ texST[t].s1 *= cache.current[t]->scaleS;
+ texST[t].t1 *= cache.current[t]->scaleT;
+ }
}
}
diff --git a/src/Textures.cpp b/src/Textures.cpp
index 4bf6a36c..b579f5ed 100644
--- a/src/Textures.cpp
+++ b/src/Textures.cpp
@@ -1100,7 +1100,206 @@ void doubleTexture(T* pTex, u32 width, u32 height)
}
}
-void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
+void TextureCache::_loadFast(u32 _tile, CachedTexture *_pTexture)
+{
+ u64 ricecrc = 0;
+ if (_loadHiresTexture(_tile, _pTexture, ricecrc))
+ return;
+
+ s32 mipLevel = 0;
+ bool force32bitFormat = false;
+ _pTexture->max_level = 0;
+
+ if (config.generalEmulation.enableLOD != 0 && gSP.texture.level > 1) {
+ if (_tile == 0) {
+ _pTexture->max_level = 0;
+ } else {
+ _pTexture->max_level = static_cast(gSP.texture.level - 1);
+ const u16 dim = std::max(_pTexture->width, _pTexture->height);
+ while (dim < static_cast(1 << _pTexture->max_level))
+ --_pTexture->max_level;
+
+ auto texFormat = gDP.tiles[gSP.texture.tile + 1].format;
+ auto texSize = gDP.tiles[gSP.texture.tile + 1].size;
+ u32 tileMipLevel = gSP.texture.tile + 2;
+ while (!force32bitFormat && (tileMipLevel < gSP.texture.tile + gSP.texture.level)) {
+ gDPTile const& mipTile = gDP.tiles[tileMipLevel++];
+ force32bitFormat = texFormat != mipTile.format || texSize != mipTile.size;
+ }
+ }
+ }
+
+ u32 sizeShift = 1;
+ {
+ const TextureLoadParameters & loadParams =
+ ImageFormat::get().tlp[gDP.otherMode.textureLUT][_pTexture->size][_pTexture->format];
+ if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8)
+ sizeShift = 2;
+ }
+ _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift;
+
+ unsigned int totalTexSize = std::max(static_cast(_pTexture->textureBytes/sizeof(u32) + 8), MIPMAP_TILE_WIDTH)
+ * (_pTexture->max_level + 1);
+
+ if (m_tempTextureHolder.size() < totalTexSize) {
+ m_tempTextureHolder.resize(totalTexSize);
+ }
+
+ GetTexelFunc GetTexel;
+ InternalColorFormatParam glInternalFormat;
+ DatatypeParam glType;
+
+ auto getLoadParams = [&](u16 _format, u16 _size)
+ {
+ const TextureLoadParameters & loadParams =
+ ImageFormat::get().tlp[gDP.otherMode.textureLUT][_size][_format];
+ if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) {
+ GetTexel = loadParams.Get32;
+ glInternalFormat = loadParams.glInternalFormat32;
+ glType = loadParams.glType32;
+ }
+ else {
+ GetTexel = loadParams.Get16;
+ glInternalFormat = loadParams.glInternalFormat16;
+ glType = loadParams.glType16;
+ }
+ };
+
+ CachedTexture tmptex = *_pTexture;
+ u16 line = tmptex.line;
+
+ while (true) {
+ getLoadParams(tmptex.format, tmptex.size);
+ {
+ const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1;
+ gDPTile & mipTile = gDP.tiles[tileMipLevel];
+ if (tmptex.max_level > 1 &&
+ tmptex.width == (mipTile.lrs - mipTile.uls + 1) * 2 &&
+ tmptex.height == (mipTile.lrt - mipTile.ult + 1) * 2)
+ {
+ // Special case for Southern Swamp grass texture, Zelda MM. See #2315
+ const u16 texWidth = tmptex.width;
+ const u16 texHeight = tmptex.height;
+ tmptex.width = mipTile.lrs - mipTile.uls + 1;
+ tmptex.height = mipTile.lrt - mipTile.ult + 1;
+ _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line);
+ if (sizeShift == 2)
+ doubleTexture(m_tempTextureHolder.data(), tmptex.width, tmptex.height);
+ else
+ doubleTexture((u16*)m_tempTextureHolder.data(), tmptex.width, tmptex.height);
+ tmptex.width = texWidth;
+ tmptex.height = texHeight;
+ } else {
+ _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line);
+ }
+ }
+
+ if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) {
+ _loadDepthTexture(_pTexture, (u16*)m_tempTextureHolder.data());
+ return;
+ }
+
+ if (m_toggleDumpTex &&
+ config.textureFilter.txHiresEnable != 0 &&
+ config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) {
+ txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height,
+ tmptex.width, (u16)u32(glInternalFormat),
+ (unsigned short)(_pTexture->format << 8 | _pTexture->size),
+ ricecrc);
+ }
+
+ bool bLoaded = false;
+ bool needEnhance = (config.textureFilter.txEnhancementMode | config.textureFilter.txFilterMode) != 0 &&
+ _pTexture->max_level == 0 &&
+ TFH.isInited();
+ if (needEnhance) {
+ if (config.textureFilter.txFilterIgnoreBG != 0) {
+ switch (GBI.getMicrocodeType()) {
+ case S2DEX_1_07:
+ case S2DEX_1_03:
+ case S2DEX_1_05:
+ needEnhance = RSP.cmd != 0x01 && RSP.cmd != 0x02;
+ break;
+ case S2DEX2:
+ needEnhance = RSP.cmd != 0x09 && RSP.cmd != 0x0A;
+ break;
+ }
+ }
+ }
+
+ if (needEnhance) {
+ GHQTexInfo ghqTexInfo;
+ if (txfilter_filter((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height,
+ (u16)u32(glInternalFormat), (uint64)_pTexture->crc,
+ &ghqTexInfo) != 0 && ghqTexInfo.data != nullptr) {
+ if (ghqTexInfo.width % 2 != 0 &&
+ ghqTexInfo.format != u32(internalcolorFormat::RGBA8) &&
+ m_curUnpackAlignment > 1)
+ gfxContext.setTextureUnpackAlignment(2);
+ ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format);
+ Context::InitTextureParams params;
+ params.handle = _pTexture->name;
+ params.textureUnitIndex = textureIndices::Tex[_tile];
+ params.mipMapLevel = 0;
+ params.msaaLevel = 0;
+ params.width = ghqTexInfo.width;
+ params.height = ghqTexInfo.height;
+ params.internalFormat = InternalColorFormatParam(ghqTexInfo.format);
+ params.format = ColorFormatParam(ghqTexInfo.texture_format);
+ params.dataType = DatatypeParam(ghqTexInfo.pixel_type);
+ params.data = ghqTexInfo.data;
+ gfxContext.init2DTexture(params);
+ _updateCachedTexture(ghqTexInfo, _pTexture, tmptex.width, tmptex.height);
+ bLoaded = true;
+ }
+ }
+ if (!bLoaded) {
+ if (tmptex.width % 2 != 0 &&
+ glInternalFormat != internalcolorFormat::RGBA8 &&
+ m_curUnpackAlignment > 1)
+ gfxContext.setTextureUnpackAlignment(2);
+ Context::InitTextureParams params;
+ params.handle = _pTexture->name;
+ params.textureUnitIndex = textureIndices::Tex[_tile];
+ params.mipMapLevel = mipLevel;
+ params.mipMapLevels = _pTexture->max_level + 1;
+ params.msaaLevel = 0;
+ params.width = tmptex.width;
+ params.height = tmptex.height;
+ params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat));
+ params.format = colorFormat::RGBA;
+ params.dataType = glType;
+ params.data = m_tempTextureHolder.data();
+ gfxContext.init2DTexture(params);
+ }
+ if (mipLevel == _pTexture->max_level)
+ break;
+ ++mipLevel;
+ const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1;
+ gDPTile & mipTile = gDP.tiles[tileMipLevel];
+ line = mipTile.line;
+ tmptex.tMem = mipTile.tmem;
+ tmptex.palette = mipTile.palette;
+ tmptex.maskS = mipTile.masks;
+ tmptex.maskT = mipTile.maskt;
+ tmptex.format = mipTile.format;
+ tmptex.size = mipTile.size;
+ TileSizes sizes;
+ _calcTileSizes(tileMipLevel, sizes, nullptr);
+ tmptex.clampWidth = sizes.clampWidth;
+ tmptex.clampHeight = sizes.clampHeight;
+ // Insure mip-map levels size consistency.
+ if (tmptex.width > 1)
+ tmptex.width >>= 1;
+ if (tmptex.height > 1)
+ tmptex.height >>= 1;
+ _pTexture->textureBytes += (tmptex.width * tmptex.height) << sizeShift;
+ }
+ if (m_curUnpackAlignment > 1)
+ gfxContext.setTextureUnpackAlignment(m_curUnpackAlignment);
+}
+
+void TextureCache::_loadAccurate(u32 _tile, CachedTexture *_pTexture)
{
u64 ricecrc = 0;
if (_loadHiresTexture(_tile, _pTexture, ricecrc))
@@ -1125,28 +1324,12 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
}
_pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift;
- // RAII holder for texture data
- class TexData
- {
- public:
- TexData(u32 bytes)
- {
- pData = (u32*)malloc(bytes);
- assert(pData != NULL);
- }
- ~TexData()
- {
- free(pData);
- pData = NULL;
- }
- u32 * get() const
- {
- return pData;
- }
- private:
- u32 *pData = NULL;
- } texData(std::max((_pTexture->textureBytes + 8*sizeof(u32)), MIPMAP_TILE_WIDTH * sizeof(u32))
- * (_pTexture->max_level + 1));
+ unsigned int totalTexSize = std::max(static_cast(_pTexture->textureBytes/sizeof(u32) + 8), MIPMAP_TILE_WIDTH)
+ * (_pTexture->max_level + 1);
+
+ if (m_tempTextureHolder.size() < totalTexSize) {
+ m_tempTextureHolder.resize(totalTexSize);
+ }
GetTexelFunc GetTexel;
InternalColorFormatParam glInternalFormat;
@@ -1180,15 +1363,15 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
while (true)
{
const u32 tileSizePacked = texDataOffset | (tmptex.width << 16) | (tmptex.height << 24);
- texData.get()[mipLevel] = tileSizePacked;
+ m_tempTextureHolder[mipLevel] = tileSizePacked;
getLoadParams(tmptex.format, tmptex.size);
- _getTextureDestData(tmptex, texData.get() + texDataOffset, glInternalFormat, GetTexel, &line);
+ _getTextureDestData(tmptex, &m_tempTextureHolder[texDataOffset], glInternalFormat, GetTexel, &line);
if (m_toggleDumpTex &&
config.textureFilter.txHiresEnable != 0 &&
config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) {
- txfilter_dmptx((u8*)texData.get() + texDataOffset, tmptex.width, tmptex.height,
+ txfilter_dmptx((u8*)(m_tempTextureHolder.data() + texDataOffset), tmptex.width, tmptex.height,
tmptex.width, (u16)u32(glInternalFormat),
(unsigned short)(_pTexture->format << 8 | _pTexture->size),
ricecrc);
@@ -1228,7 +1411,7 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat));
params.format = colorFormat::RGBA;
params.dataType = glType;
- params.data = texData.get();
+ params.data = m_tempTextureHolder.data();
gfxContext.init2DTexture(params);
_pTexture->mipmapAtlasWidth = params.width;
_pTexture->mipmapAtlasHeight = params.height;
@@ -1236,17 +1419,17 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
else
{
getLoadParams(tmptex.format, tmptex.size);
- _getTextureDestData(tmptex, texData.get(), glInternalFormat, GetTexel, &line);
+ _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line);
if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) {
- _loadDepthTexture(_pTexture, (u16*)texData.get());
+ _loadDepthTexture(_pTexture, (u16*)m_tempTextureHolder.data());
return;
}
if (m_toggleDumpTex &&
config.textureFilter.txHiresEnable != 0 &&
config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) {
- txfilter_dmptx((u8*)texData.get(), tmptex.width, tmptex.height,
+ txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height,
tmptex.width, (u16)u32(glInternalFormat),
(unsigned short)(_pTexture->format << 8 | _pTexture->size),
ricecrc);
@@ -1273,7 +1456,7 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
if (needEnhance) {
GHQTexInfo ghqTexInfo;
- if (txfilter_filter((u8*)texData.get(), tmptex.width, tmptex.height,
+ if (txfilter_filter((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height,
(u16)u32(glInternalFormat), (uint64)_pTexture->crc,
&ghqTexInfo) != 0 && ghqTexInfo.data != nullptr) {
if (ghqTexInfo.width % 2 != 0 &&
@@ -1313,7 +1496,7 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture)
params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat));
params.format = colorFormat::RGBA;
params.dataType = glType;
- params.data = texData.get();
+ params.data = m_tempTextureHolder.data();
gfxContext.init2DTexture(params);
}
}
@@ -1565,6 +1748,14 @@ void TextureCache::update(u32 _t)
return;
}
+ if (_t == 1 && needReplaceTex1ByTex0()) {
+ current[1] = current[0];
+ if (current[1] != nullptr) {
+ activateTexture(1, current[1]);
+ return;
+ }
+ }
+
if (gSP.texture.tile == 7 &&
_t == 0 &&
gSP.textureTile[0] == gDP.loadTile &&
@@ -1660,7 +1851,11 @@ void TextureCache::update(u32 _t)
pCurrent->offsetS = 0.0f;
pCurrent->offsetT = 0.0f;
- _load(_t, pCurrent);
+ if (config.generalEmulation.enableInaccurateTextureCoordinates) {
+ _loadFast(_t, pCurrent);
+ } else {
+ _loadAccurate(_t, pCurrent);
+ }
activateTexture( _t, pCurrent );
current[_t] = pCurrent;
@@ -1686,4 +1881,11 @@ void getTextureShiftScale(u32 t, const TextureCache & cache, f32 & shiftScaleS,
shiftScaleT = (f32)(1 << (16 - gSP.textureTile[t]->shiftt));
else if (gSP.textureTile[t]->shiftt > 0)
shiftScaleT /= (f32)(1 << gSP.textureTile[t]->shiftt);
-}
\ No newline at end of file
+}
+
+bool needReplaceTex1ByTex0()
+{
+ return config.generalEmulation.enableInaccurateTextureCoordinates &&
+ gSP.texture.level == 0 && gDP.otherMode.textureLOD == G_TL_LOD && gDP.otherMode.textureDetail == G_TD_CLAMP;
+}
+
diff --git a/src/Textures.h b/src/Textures.h
index cc28ae71..eed20677 100644
--- a/src/Textures.h
+++ b/src/Textures.h
@@ -5,6 +5,7 @@
#include
#include