From fd10aba8151a9dbba788eb1e5b4bc9f62605a537 Mon Sep 17 00:00:00 2001
From: Leon Styhre <leon@leonstyhre.com>
Date: Fri, 4 Sep 2020 18:59:19 +0200
Subject: [PATCH] Updated the GLSL shader logic and added a postprocessing
 function.

---
 es-core/src/renderers/Renderer.cpp          |  41 ++--
 es-core/src/renderers/Renderer.h            |  29 ++-
 es-core/src/renderers/Renderer_GL21.cpp     | 164 ++++++++++++---
 es-core/src/renderers/Shader_GL21.cpp       |  76 +++----
 es-core/src/renderers/Shader_GL21.h         |  23 +--
 resources/shaders/glsl/blur_horizontal.glsl | 137 +++++++++++++
 resources/shaders/glsl/blur_vertical.glsl   | 137 +++++++++++++
 resources/shaders/glsl/desaturate.glsl      |  16 +-
 resources/shaders/glsl/scanlines.glsl       | 208 ++++++++++++++++++++
 9 files changed, 724 insertions(+), 107 deletions(-)
 create mode 100644 resources/shaders/glsl/blur_horizontal.glsl
 create mode 100644 resources/shaders/glsl/blur_vertical.glsl
 create mode 100644 resources/shaders/glsl/scanlines.glsl

diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp
index 0e4744185..3834c5967 100644
--- a/es-core/src/renderers/Renderer.cpp
+++ b/es-core/src/renderers/Renderer.cpp
@@ -70,7 +70,7 @@ namespace Renderer
         LOG(LogInfo) << "Creating window...";
 
         if (SDL_Init(SDL_INIT_VIDEO) != 0) {
-            LOG(LogError) << "Error initializing SDL!\n	" << SDL_GetError();
+            LOG(LogError) << "Couldn't initialize SDL: " << SDL_GetError() << ".";
             return false;
         }
 
@@ -167,17 +167,25 @@ namespace Renderer
         #if defined(USE_OPENGL_21)
         LOG(LogInfo) << "Loading shaders...";
 
-        Shader* desaturateShader = new Shader();
+        std::vector<std::string> shaderFiles;
+        shaderFiles.push_back(":/shaders/glsl/desaturate.glsl");
+        shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl");
+        shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl");
+        shaderFiles.push_back(":/shaders/glsl/scanlines.glsl");
 
-        desaturateShader->loadShaderFile(":/shaders/glsl/desaturate.glsl", GL_VERTEX_SHADER);
-        desaturateShader->loadShaderFile(":/shaders/glsl/desaturate.glsl", GL_FRAGMENT_SHADER);
+        for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); it++) {
+            Shader* loadShader = new Shader();
 
-        if (!desaturateShader->createProgram()) {
-            LOG(LogError) << "Could not create shader program.";
-            return false;
+            loadShader->loadShaderFile(*it, GL_VERTEX_SHADER);
+            loadShader->loadShaderFile(*it, GL_FRAGMENT_SHADER);
+
+            if (!loadShader->createProgram()) {
+                LOG(LogError) << "Could not create shader program.";
+                return false;
+            }
+
+            sShaderProgramVector.push_back(loadShader);
         }
-
-        sShaderProgramVector.push_back(desaturateShader);
         #endif
 
         return true;
@@ -373,10 +381,19 @@ namespace Renderer
         return red << 24 | green << 16 | blue << 8 | alpha;
     }
 
-    Shader* getShaderProgram(unsigned int index)
+    Shader* getShaderProgram(unsigned int shaderID)
     {
-        if (sShaderProgramVector.size() > index)
-            return sShaderProgramVector[index];
+        unsigned int index = 0;
+
+        // Find the index in sShaderProgramVector by counting the number
+        // of shifts required to reach 0.
+        while (shaderID > 0) {
+            shaderID = shaderID >> 1;
+            index++;
+        }
+
+        if (sShaderProgramVector.size() > index-1)
+            return sShaderProgramVector[index-1];
         else
             return nullptr;
     };
diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h
index 6c5024922..057e7fbc0 100644
--- a/es-core/src/renderers/Renderer.h
+++ b/es-core/src/renderers/Renderer.h
@@ -21,7 +21,27 @@ struct SDL_Window;
 
 namespace Renderer
 {
+    const unsigned int SHADER_DESATURATE =          1;
+    const unsigned int SHADER_BLUR_HORIZONTAL =     2;
+    const unsigned int SHADER_BLUR_VERTICAL =       4;
+    const unsigned int SHADER_SCANLINES =           8;
+
+    struct shaderParameters {
+        std::array<GLfloat, 2> textureSize;
+        std::array<GLfloat, 4> textureCoordinates;
+        float fragmentSaturation;
+        unsigned int shaderPasses;
+
+        shaderParameters()
+                : textureSize({0.0, 0.0}),
+                textureCoordinates({0.0, 0.0, 0.0, 0.0}),
+                fragmentSaturation(1.0),
+                shaderPasses(1)
+                {};
+    };
+
     static std::vector<Shader*> sShaderProgramVector;
+    static GLuint shaderFBO;
 
     #if !defined(NDEBUG)
     #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function))
@@ -99,6 +119,7 @@ namespace Renderer
         Vector2f tex;
         unsigned int col;
         float saturation = 1.0;
+        unsigned int shaders = 0;
     };
 
     bool init();
@@ -127,7 +148,10 @@ namespace Renderer
     unsigned int rgbaToABGR(unsigned int color);
     unsigned int abgrToRGBA(unsigned int color);
 
-    Shader* getShaderProgram(unsigned int index);
+    Shader* getShaderProgram(unsigned int shaderID);
+    void shaderPostprocessing(unsigned int shaders,
+            const Renderer::shaderParameters& parameters = shaderParameters(),
+            unsigned char* textureRGBA = nullptr);
 
     // API specific.
     unsigned int convertColor(const unsigned int _color);
@@ -161,7 +185,8 @@ namespace Renderer
             const Vertex* _vertices,
             const unsigned int _numVertices,
             const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
-            const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
+            const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA,
+            const shaderParameters& parameters = shaderParameters());
     void setProjection(const Transform4x4f& _projection);
     void setMatrix(const Transform4x4f& _matrix);
     void setViewport(const Rect& _viewport);
diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp
index c7b05fcff..7efb8dfea 100644
--- a/es-core/src/renderers/Renderer_GL21.cpp
+++ b/es-core/src/renderers/Renderer_GL21.cpp
@@ -127,6 +127,13 @@ namespace Renderer
         else {
             LOG(LogInfo) << "GL_ARB_fragment_shader: OK";
         }
+        if (extensions.find("GL_EXT_framebuffer_blit") == std::string::npos) {
+            LOG(LogError) << "GL_EXT_framebuffer_blit: MISSING";
+            missingExtension = true;
+        }
+        else {
+            LOG(LogInfo) << "GL_EXT_framebuffer_blit: OK";
+        }
         if (missingExtension) {
             LOG(LogError) << "Required OpenGL extensions missing.";
             return false;
@@ -144,11 +151,15 @@ namespace Renderer
         GL_CHECK_ERROR(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
         GL_CHECK_ERROR(glEnableClientState(GL_COLOR_ARRAY));
 
+        // This is the framebuffer that will be used for shader rendering.
+        GL_CHECK_ERROR(glGenFramebuffers(1, &shaderFBO));
+
         return true;
     }
 
     void destroyContext()
     {
+        GL_CHECK_ERROR(glDeleteFramebuffers(1, &shaderFBO));
         SDL_GL_DeleteContext(sdlContext);
         sdlContext = nullptr;
     }
@@ -225,28 +236,17 @@ namespace Renderer
                 convertBlendFactor(_dstBlendFactor)));
 
         GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices));
-
-        // If saturation is set below the maximum (default) value, run the desaturation shader.
-        if (_vertices->saturation < 1.0) {
-            Shader* desaturateShader = getShaderProgram(Shader::Desaturate);
-
-            // Only try to use the shader if it has been loaded properly.
-            if (desaturateShader) {
-                desaturateShader->activateShaders();
-                desaturateShader->getVariableLocations(desaturateShader->getProgramID());
-                desaturateShader->setVariable(_vertices->saturation);
-                GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices));
-                desaturateShader->deactivateShaders();
-            }
-        }
     }
 
     void drawTriangleStrips(
             const Vertex* _vertices,
             const unsigned int _numVertices,
             const Blend::Factor _srcBlendFactor,
-            const Blend::Factor _dstBlendFactor)
+            const Blend::Factor _dstBlendFactor,
+            const shaderParameters& parameters)
     {
+        float width = _vertices[3].pos[0];
+        float height = _vertices[3].pos[1];
         GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos));
         GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex));
         GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col));
@@ -256,17 +256,56 @@ namespace Renderer
 
         GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
 
-        // If saturation is set below the maximum (default) value, run the desaturation shader.
-        if (_vertices->saturation < 1.0) {
-            Shader* desaturateShader = getShaderProgram(Shader::Desaturate);
+        for (unsigned int i = 0; i < parameters.shaderPasses; i++) {
+            // If saturation is set below the maximum (default) value, run the desaturation shader.
+            if (_vertices->saturation < 1.0 || parameters.fragmentSaturation < 1.0) {
+                Shader* runShader = getShaderProgram(SHADER_DESATURATE);
+                // Only try to use the shader if it has been loaded properly.
+                if (runShader) {
+                    runShader->activateShaders();
+                    runShader->getVariableLocations(runShader->getProgramID());
+                    runShader->setSaturation(_vertices->saturation);
+                    GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
+                    runShader->deactivateShaders();
+                }
+            }
 
-            // Only try to use the shader if it has been loaded properly.
-            if (desaturateShader) {
-                desaturateShader->activateShaders();
-                desaturateShader->getVariableLocations(desaturateShader->getProgramID());
-                desaturateShader->setVariable(_vertices->saturation);
-                GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
-                desaturateShader->deactivateShaders();
+            // Check if any other shaders are set to be used and if so, run them.
+            if (_vertices->shaders & SHADER_BLUR_HORIZONTAL) {
+                Shader* runShader = getShaderProgram(SHADER_BLUR_HORIZONTAL);
+                if (runShader) {
+                    runShader->activateShaders();
+                    runShader->getVariableLocations(runShader->getProgramID());
+                    runShader->setTextureSize({width, height});
+                    GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
+                    runShader->deactivateShaders();
+                }
+            }
+
+            if (_vertices->shaders & SHADER_BLUR_VERTICAL) {
+                Shader* runShader = getShaderProgram(SHADER_BLUR_VERTICAL);
+                if (runShader) {
+                    runShader->activateShaders();
+                    runShader->getVariableLocations(runShader->getProgramID());
+                    runShader->setTextureSize({width, height});
+                    GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
+                    runShader->deactivateShaders();
+                }
+            }
+
+            if (_vertices->shaders & SHADER_SCANLINES) {
+                Shader* runShader = getShaderProgram(SHADER_SCANLINES);
+                float shaderWidth = width * 1.2;
+                // Workaround to get the scanlines to render somehow proportional to the
+                // resolution. A better solution is for sure needed.
+                float shaderHeight = height + height / ((int)height >> 7) * 1.5;
+                if (runShader) {
+                    runShader->activateShaders();
+                    runShader->getVariableLocations(runShader->getProgramID());
+                    runShader->setTextureSize({shaderWidth, shaderHeight});
+                    GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
+                    runShader->deactivateShaders();
+                }
             }
         }
     }
@@ -330,6 +369,81 @@ namespace Renderer
         GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
     }
 
+    void shaderPostprocessing(unsigned int shaders, const Renderer::shaderParameters& parameters,
+            unsigned char* textureRGBA)
+    {
+        Vertex vertices[4];
+        GLuint width = getScreenWidth();
+        GLuint height = getScreenHeight();
+        float widthf = static_cast<float>(width);
+        float heightf = static_cast<float>(height);
+
+        // Set vertex positions and texture coordinates to full screen as all
+        // postprocessing is applied to the complete screen area.
+        vertices[0] = { { 0, 0 }, { 0, 1 }, 0 };
+        vertices[1] = { { 0, heightf }, { 0, 0 }, 0 };
+        vertices[2] = { { widthf, 0 }, { 1, 1 }, 0 };
+        vertices[3] = { { widthf, heightf }, { 1, 0 }, 0};
+
+        vertices[0].shaders = shaders;
+        vertices[1].shaders = shaders;
+        vertices[2].shaders = shaders;
+        vertices[3].shaders = shaders;
+
+        if (parameters.fragmentSaturation < 1.0) {
+            vertices[0].saturation = parameters.fragmentSaturation;
+            vertices[1].saturation = parameters.fragmentSaturation;
+            vertices[2].saturation = parameters.fragmentSaturation;
+            vertices[3].saturation = parameters.fragmentSaturation;
+        }
+
+        setMatrix(Transform4x4f::Identity());
+
+        // The following method to apply the shaders is not optimal as it requires
+        // glBlitFramebuffer() to run twice. However, this function seems to be
+        // very fast so maybe it's not a practical issue.
+        GLuint screenTexture = createTexture(Texture::RGBA, false, false, width, height, nullptr);
+
+        GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
+        GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
+
+        // Attach the texture to the shader framebuffer.
+        GL_CHECK_ERROR(glFramebufferTexture2D(
+                GL_FRAMEBUFFER,
+                GL_COLOR_ATTACHMENT0,
+                GL_TEXTURE_2D,
+                screenTexture,
+                0));
+
+        // Blit the screen contents to screenTexture.
+        GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
+                GL_COLOR_BUFFER_BIT, GL_NEAREST));
+
+        // Apply/render the shaders.
+        drawTriangleStrips(vertices, 4, Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
+
+        // If textureRGBA has an address, it means that the output should go to this texture
+        // rather than to the screen. The glReadPixels() function is slow, but since this would
+        // typically only run every now and then to create a cached screen texture, it doesn't
+        // really matter.
+        if (textureRGBA) {
+            GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
+            GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
+                    GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
+            GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
+        }
+        else {
+            // Blit the resulting postprocessed texture back to the primary framebuffer.
+            GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
+            GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
+            GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
+                    GL_COLOR_BUFFER_BIT, GL_NEAREST));
+        }
+
+        GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0));
+        destroyTexture(screenTexture);
+    }
+
 } // Renderer::
 
 #endif // USE_OPENGL_21
diff --git a/es-core/src/renderers/Shader_GL21.cpp b/es-core/src/renderers/Shader_GL21.cpp
index 2a8d9f049..729d76f5f 100644
--- a/es-core/src/renderers/Shader_GL21.cpp
+++ b/es-core/src/renderers/Shader_GL21.cpp
@@ -16,12 +16,10 @@ namespace Renderer
 {
     Renderer::Shader::Shader()
             : mProgramID(-1),
-            shaderFloat_0(-1),
-            shaderFloat_1(-1),
-            shaderFloat_2(-1),
-            shaderVec4_0(-1),
-            shaderVec4_1(-1),
-            shaderVec4_2(-1)
+            shaderTextureSize(-1),
+            shaderTextureCoord(-1),
+            shaderColor(-1),
+            shaderSaturation(-1)
     {
     }
 
@@ -42,7 +40,7 @@ namespace Renderer
         // Define the GLSL version (version 120 = OpenGL 2.1).
         preprocessorDefines = "#version 120\n";
 
-        // Define the preprocessor macros that will let the shader compiler know whether
+        // Define the preprocessor constants that will let the shader compiler know whether
         // the VERTEX or FRAGMENT portion of the code should be used.
         if (shaderType == GL_VERTEX_SHADER)
             preprocessorDefines += "#define VERTEX\n";
@@ -73,7 +71,7 @@ namespace Renderer
             if (shaderCompiled != GL_TRUE) {
                 LOG(LogError) << "OpenGL error: Unable to compile shader " <<
                         currentShader << " (" << std::get<0>(*it) << ").";
-                printShaderInfoLog(currentShader);
+                printShaderInfoLog(currentShader, std::get<2>(*it));
                 return false;
             }
 
@@ -99,47 +97,34 @@ namespace Renderer
 
     void Renderer::Shader::getVariableLocations(GLuint programID)
     {
-        shaderFloat_0 = glGetUniformLocation(mProgramID, "shaderFloat_0");
-        shaderFloat_1 = glGetUniformLocation(mProgramID, "shaderFloat_1");
-        shaderFloat_2 = glGetUniformLocation(mProgramID, "shaderFloat_2");
-        shaderVec4_0 = glGetUniformLocation(mProgramID, "shaderVec4_0");
-        shaderVec4_1 = glGetUniformLocation(mProgramID, "shaderVec4_1");
-        shaderVec4_2 = glGetUniformLocation(mProgramID, "shaderVec4_2");
+        // Some of the variable names are chosen to be compatible with the RetroArch GLSL shaders.
+        shaderTextureSize = glGetUniformLocation(mProgramID, "TextureSize");
+        shaderTextureCoord = glGetAttribLocation(mProgramID, "TexCoord");
+        shaderColor = glGetAttribLocation(mProgramID, "COLOR");
+        shaderSaturation = glGetUniformLocation(mProgramID, "saturation");
     }
 
-    void Renderer::Shader::setVariable(GLfloat shaderFloat, int index)
+    void Renderer::Shader::setTextureSize(std::array<GLfloat, 2> shaderVec2)
     {
-        switch (index) {
-            case 0:
-                GL_CHECK_ERROR(glUniform1f(shaderFloat_0, shaderFloat));
-                break;
-            case 1:
-                GL_CHECK_ERROR(glUniform1f(shaderFloat_1, shaderFloat));
-                break;
-            case 2:
-                GL_CHECK_ERROR(glUniform1f(shaderFloat_2, shaderFloat));
-                break;
-            default:
-                break;
-        }
+        GL_CHECK_ERROR(glUniform2f(shaderTextureSize, shaderVec2[0], shaderVec2[1]));
     }
 
-    void Renderer::Shader::setVariable(std::array<GLfloat, 4> shaderVec4, int index)
+    void Renderer::Shader::setTextureCoordinates(std::array<GLfloat, 4> shaderVec4)
     {
-        switch (index) {
-            case 0:
-                GL_CHECK_ERROR(glUniform4f(shaderVec4_0, shaderVec4[0],
+        glEnableVertexAttribArray(shaderTextureCoord);
+        glVertexAttribPointer(shaderTextureCoord, 4, GL_FLOAT, GL_FALSE, 0,
+                (const GLvoid*)(uintptr_t)&shaderVec4);
+    }
+
+    void Renderer::Shader::setColor(std::array<GLfloat, 4> shaderVec4)
+    {
+        GL_CHECK_ERROR(glUniform4f(shaderColor, shaderVec4[0],
                         shaderVec4[1], shaderVec4[2], shaderVec4[3]));
-                break;
-            case 1:
-                GL_CHECK_ERROR(glUniform4f(shaderVec4_1, shaderVec4[0],
-                        shaderVec4[1], shaderVec4[2], shaderVec4[3]));
-            case 2:
-                GL_CHECK_ERROR(glUniform4f(shaderVec4_2, shaderVec4[0],
-                        shaderVec4[1], shaderVec4[2], shaderVec4[3]));
-            default:
-                break;
-        }
+    }
+
+    void Renderer::Shader::setSaturation(GLfloat saturation)
+    {
+        GL_CHECK_ERROR(glUniform1f(shaderSaturation, saturation));
     }
 
     void Renderer::Shader::activateShaders()
@@ -178,7 +163,7 @@ namespace Renderer
         }
     }
 
-    void Renderer::Shader::printShaderInfoLog(GLuint shaderID)
+    void Renderer::Shader::printShaderInfoLog(GLuint shaderID, GLenum shaderType)
     {
         if (glIsShader(shaderID)) {
             int logLength;
@@ -190,8 +175,9 @@ namespace Renderer
             glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front());
 
             if (logLength > 0) {
-                LOG(LogDebug) << "Renderer_GL21::printShaderLog():\n" <<
-                        std::string(infoLog.begin(), infoLog.end());
+                LOG(LogDebug) << "Renderer_GL21::printShaderLog(): Error in " <<
+                        (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" :
+                        "FRAGMENT section:\n") << std::string(infoLog.begin(), infoLog.end());
             }
         }
         else {
diff --git a/es-core/src/renderers/Shader_GL21.h b/es-core/src/renderers/Shader_GL21.h
index aff86e015..67c4b56ad 100644
--- a/es-core/src/renderers/Shader_GL21.h
+++ b/es-core/src/renderers/Shader_GL21.h
@@ -20,10 +20,6 @@ namespace Renderer
     class Shader
     {
     public:
-        enum shaderNames {
-            Desaturate
-        };
-
         Shader();
         ~Shader();
 
@@ -36,8 +32,10 @@ namespace Renderer
         // Get references to the variables inside the compiled shaders.
         void getVariableLocations(GLuint programID);
         // One-way communication with the compiled shaders.
-        void setVariable(GLfloat shaderFloat, int index = 0);
-        void setVariable(std::array<GLfloat, 4> shaderVec4, int index = 0);
+        void setTextureSize(std::array<GLfloat, 2> shaderVec2);
+        void setTextureCoordinates(std::array<GLfloat, 4> shaderVec4);
+        void setColor(std::array<GLfloat, 4> shaderVec4);
+        void setSaturation(GLfloat saturation);
         // Sets the shader program to use the loaded shaders.
         void activateShaders();
         // Sets the shader program to 0 which reverts to the fixed function pipeline.
@@ -46,20 +44,19 @@ namespace Renderer
         GLuint getProgramID();
         // Only used for error logging if the shaders fail to compile or link.
         void printProgramInfoLog(GLuint programID);
-        void printShaderInfoLog(GLuint shaderID);
+        void printShaderInfoLog(GLuint shaderID, GLenum shaderType);
 
     private:
         GLuint mProgramID;
         std::vector<std::tuple<std::string, std::string, GLenum>> shaderVector;
 
         // Variables used for communication with the compiled shaders.
-        GLint shaderFloat_0;
-        GLint shaderFloat_1;
-        GLint shaderFloat_2;
-        GLint shaderVec4_0;
-        GLint shaderVec4_1;
-        GLint shaderVec4_2;
+        GLint shaderTextureSize;
+        GLint shaderTextureCoord;
+        GLint shaderColor;
+        GLint shaderSaturation;
     };
+
 } // Renderer
 
 #endif // ES_CORE_RENDERER_SHADER_GL21_H
diff --git a/resources/shaders/glsl/blur_horizontal.glsl b/resources/shaders/glsl/blur_horizontal.glsl
new file mode 100644
index 000000000..f4897cbc8
--- /dev/null
+++ b/resources/shaders/glsl/blur_horizontal.glsl
@@ -0,0 +1,137 @@
+// Implementation based on the article "Efficient Gaussian blur with linear sampling"
+// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
+/* A version for MasterEffect Reborn, a standalone version, and a custom shader version for SweetFX can be
+   found at http://reshade.me/forum/shader-presentation/27-gaussian-blur-bloom-unsharpmask */
+ /*-----------------------------------------------------------.
+/                  Gaussian Blur settings                     /
+'-----------------------------------------------------------*/
+
+#define HW 1.00
+
+#if defined(VERTEX)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING out
+#define COMPAT_ATTRIBUTE in
+#define COMPAT_TEXTURE texture
+#else
+#define COMPAT_VARYING varying
+#define COMPAT_ATTRIBUTE attribute
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+COMPAT_ATTRIBUTE vec4 VertexCoord;
+COMPAT_ATTRIBUTE vec4 COLOR;
+COMPAT_ATTRIBUTE vec4 TexCoord;
+COMPAT_VARYING vec4 COL0;
+COMPAT_VARYING vec4 TEX0;
+
+vec4 _oPosition1;
+uniform mat4 MVPMatrix;
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+
+void main()
+{
+	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+//    gl_Position = MVPMatrix * VertexCoord;
+    COL0 = COLOR;
+	TEX0.xy = gl_MultiTexCoord0.xy;
+//    TEX0.xy = TexCoord.xy;
+}
+
+#elif defined(FRAGMENT)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING in
+#define COMPAT_TEXTURE texture
+out vec4 FragColor;
+#else
+#define COMPAT_VARYING varying
+#define FragColor gl_FragColor
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+uniform sampler2D Texture;
+COMPAT_VARYING vec4 TEX0;
+
+// compatibility #defines
+#define Source Texture
+#define vTexCoord TEX0.xy
+
+#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
+#define outsize vec4(OutputSize, 1.0 / OutputSize)
+
+void main()
+{
+	vec2 texcoord  = vTexCoord;
+//	vec2 PIXEL_SIZE = SourceSize.zw;
+	vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w);
+#if __VERSION__ < 130
+	float sampleOffsets1 = 0.0;
+	float sampleOffsets2 = 1.4347826;
+	float sampleOffsets3 = 3.3478260;
+	float sampleOffsets4 = 5.2608695;
+	float sampleOffsets5 = 7.1739130;
+
+	float sampleWeights1 = 0.16818994;
+	float sampleWeights2 = 0.27276957;
+	float sampleWeights3 = 0.11690125;
+	float sampleWeights4 = 0.024067905;
+	float sampleWeights5 = 0.0021112196;
+
+	vec4 color = COMPAT_TEXTURE(Source, texcoord) * sampleWeights1;
+
+// unroll the loop
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(sampleOffsets2* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights2;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(sampleOffsets2* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights2;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(sampleOffsets3* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights3;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(sampleOffsets3* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights3;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(sampleOffsets4* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights4;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(sampleOffsets4* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights4;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(sampleOffsets5* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights5;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(sampleOffsets5* HW * PIXEL_SIZE.x, 0.0)) * sampleWeights5;
+#else
+
+	float sampleOffsets[5] = { 0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130 };
+	float sampleWeights[5] = { 0.16818994, 0.27276957, 0.11690125, 0.024067905, 0.0021112196 };
+
+	vec4 color = COMPAT_TEXTURE(Source, texcoord) * sampleWeights[0];
+	for(int i = 1; i < 5; ++i) {
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(sampleOffsets[i]*HW * PIXEL_SIZE.x, 0.0)) * sampleWeights[i];
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(sampleOffsets[i]*HW * PIXEL_SIZE.x, 0.0)) * sampleWeights[i];
+	}
+#endif
+
+   FragColor = vec4(color);
+//   FragColor = vec4(0.4, 0.0, 0.2, 0.6);
+}
+#endif
diff --git a/resources/shaders/glsl/blur_vertical.glsl b/resources/shaders/glsl/blur_vertical.glsl
new file mode 100644
index 000000000..abad15bdd
--- /dev/null
+++ b/resources/shaders/glsl/blur_vertical.glsl
@@ -0,0 +1,137 @@
+// Implementation based on the article "Efficient Gaussian blur with linear sampling"
+// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
+/* A version for MasterEffect Reborn, a standalone version, and a custom shader version for SweetFX can be
+   found at http://reshade.me/forum/shader-presentation/27-gaussian-blur-bloom-unsharpmask */
+ /*-----------------------------------------------------------.
+/                  Gaussian Blur settings                     /
+'-----------------------------------------------------------*/
+
+#define VW 1.00
+
+#if defined(VERTEX)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING out
+#define COMPAT_ATTRIBUTE in
+#define COMPAT_TEXTURE texture
+#else
+#define COMPAT_VARYING varying
+#define COMPAT_ATTRIBUTE attribute
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+COMPAT_ATTRIBUTE vec4 VertexCoord;
+COMPAT_ATTRIBUTE vec4 COLOR;
+COMPAT_ATTRIBUTE vec4 TexCoord;
+COMPAT_VARYING vec4 COL0;
+COMPAT_VARYING vec4 TEX0;
+// out variables go here as COMPAT_VARYING whatever
+
+vec4 _oPosition1;
+uniform mat4 MVPMatrix;
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+
+void main()
+{
+	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+//    gl_Position = MVPMatrix * VertexCoord;
+    COL0 = COLOR;
+	TEX0.xy = gl_MultiTexCoord0.xy;
+//    TEX0.xy = TexCoord.xy;
+}
+
+#elif defined(FRAGMENT)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING in
+#define COMPAT_TEXTURE texture
+out vec4 FragColor;
+#else
+#define COMPAT_VARYING varying
+#define FragColor gl_FragColor
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+uniform sampler2D Texture;
+COMPAT_VARYING vec4 TEX0;
+
+// compatibility #defines
+#define Source Texture
+#define vTexCoord TEX0.xy
+
+#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
+#define outsize vec4(OutputSize, 1.0 / OutputSize)
+
+void main()
+{
+	vec2 texcoord  = vTexCoord;
+//	vec2 PIXEL_SIZE = SourceSize.zw;
+	vec2 PIXEL_SIZE = vec2(SourceSize.z, SourceSize.w);
+#if __VERSION__ < 130
+	float sampleOffsets1 = 0.0;
+	float sampleOffsets2 = 1.4347826;
+	float sampleOffsets3 = 3.3478260;
+	float sampleOffsets4 = 5.2608695;
+	float sampleOffsets5 = 7.1739130;
+
+	float sampleWeights1 = 0.16818994;
+	float sampleWeights2 = 0.27276957;
+	float sampleWeights3 = 0.11690125;
+	float sampleWeights4 = 0.024067905;
+	float sampleWeights5 = 0.0021112196;
+
+	vec4 color = COMPAT_TEXTURE(Source, texcoord) * sampleWeights1;
+
+// unroll the loop
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(0.0, sampleOffsets2* VW * PIXEL_SIZE.y)) * sampleWeights2;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(0.0, sampleOffsets2* VW * PIXEL_SIZE.y)) * sampleWeights2;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(0.0, sampleOffsets3* VW * PIXEL_SIZE.y)) * sampleWeights3;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(0.0, sampleOffsets3* VW * PIXEL_SIZE.y)) * sampleWeights3;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(0.0, sampleOffsets4* VW * PIXEL_SIZE.y)) * sampleWeights4;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(0.0, sampleOffsets4* VW * PIXEL_SIZE.y)) * sampleWeights4;
+
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(0.0, sampleOffsets5* VW * PIXEL_SIZE.y)) * sampleWeights5;
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(0.0, sampleOffsets5* VW * PIXEL_SIZE.y)) * sampleWeights5;
+#else
+
+	float sampleOffsets[5] = { 0.0, 1.4347826, 3.3478260, 5.2608695, 7.1739130 };
+	float sampleWeights[5] = { 0.16818994, 0.27276957, 0.11690125, 0.024067905, 0.0021112196 };
+
+	vec4 color = COMPAT_TEXTURE(Source, texcoord) * sampleWeights[0];
+	for(int i = 1; i < 5; ++i) {
+		color += COMPAT_TEXTURE(Source, texcoord + vec2(0.0, sampleOffsets[i]*VW * PIXEL_SIZE.y)) * sampleWeights[i];
+		color += COMPAT_TEXTURE(Source, texcoord - vec2(0.0, sampleOffsets[i]*VW * PIXEL_SIZE.y)) * sampleWeights[i];
+	}
+#endif
+
+   FragColor = vec4(color);
+}
+#endif
diff --git a/resources/shaders/glsl/desaturate.glsl b/resources/shaders/glsl/desaturate.glsl
index 83ce046c7..267918ad8 100644
--- a/resources/shaders/glsl/desaturate.glsl
+++ b/resources/shaders/glsl/desaturate.glsl
@@ -2,34 +2,30 @@
 // desaturate.glsl
 //
 // Desaturates textures such as game images.
-// The uniform variable 'shaderFloat_0' sets the saturation intensity.
+// The uniform variable 'saturation' sets the saturation intensity.
 // Setting this to the value 0 results in complete desaturation (grayscale).
 //
 
-// Vertex section of code:
-// -----------------------
 #if defined(VERTEX)
+// Vertex section of code:
 
 varying vec2 vTexCoord;
 
 void main(void)
 {
-   vTexCoord = gl_MultiTexCoord0.xy;
-   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+    vTexCoord = gl_MultiTexCoord0.xy;
+    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
 }
-#endif
 
+#elif defined(FRAGMENT)
 // Fragment section of code:
-// -------------------------
-#ifdef FRAGMENT
 
-uniform float shaderFloat_0 = 1.0;
+uniform float saturation = 1.0;
 uniform sampler2D myTexture;
 varying vec2 vTexCoord;
 
 void main()
 {
-    float saturation = shaderFloat_0;
     vec4 color = texture2D(myTexture, vTexCoord);
     vec3 grayscale = vec3(dot(color.rgb, vec3(0.2125, 0.7154, 0.0721)));
 
diff --git a/resources/shaders/glsl/scanlines.glsl b/resources/shaders/glsl/scanlines.glsl
new file mode 100644
index 000000000..07537eae9
--- /dev/null
+++ b/resources/shaders/glsl/scanlines.glsl
@@ -0,0 +1,208 @@
+/*
+    Phosphor shader - Copyright (C) 2011 caligari.
+
+    Ported by Hyllian.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+// Parameter lines go here:
+// 0.5 = the spot stays inside the original pixel
+// 1.0 = the spot bleeds up to the center of next pixel
+#pragma parameter SPOT_WIDTH "CRTCaligari Spot Width" 0.9 0.1 1.5 0.05
+#pragma parameter SPOT_HEIGHT "CRTCaligari Spot Height" 0.75 0.1 1.5 0.05
+// Used to counteract the desaturation effect of weighting.
+#pragma parameter COLOR_BOOST "CRTCaligari Color Boost" 1.45 1.0 2.0 0.05
+// Constants used with gamma correction.
+#pragma parameter InputGamma "CRTCaligari Input Gamma" 2.4 0.0 5.0 0.1
+#pragma parameter OutputGamma "CRTCaligari Output Gamma" 2.2 0.0 5.0 0.1
+
+#if defined(VERTEX)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING out
+#define COMPAT_ATTRIBUTE in
+#define COMPAT_TEXTURE texture
+#else
+#define COMPAT_VARYING varying
+#define COMPAT_ATTRIBUTE attribute
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+COMPAT_ATTRIBUTE vec4 VertexCoord;
+COMPAT_ATTRIBUTE vec4 COLOR;
+COMPAT_ATTRIBUTE vec4 TexCoord;
+COMPAT_VARYING vec4 COL0;
+COMPAT_VARYING vec4 TEX0;
+COMPAT_VARYING vec2 onex;
+COMPAT_VARYING vec2 oney;
+
+vec4 _oPosition1;
+uniform mat4 MVPMatrix;
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+
+#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
+
+void main()
+{
+   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+//   gl_Position = MVPMatrix * VertexCoord;
+   COL0 = COLOR;
+   TEX0.xy = gl_MultiTexCoord0.xy;
+//   TEX0.xy = TexCoord.xy;
+   onex = vec2(SourceSize.z, 0.0);
+   oney = vec2(0.0, SourceSize.w);
+}
+
+#elif defined(FRAGMENT)
+
+#if __VERSION__ >= 130
+#define COMPAT_VARYING in
+#define COMPAT_TEXTURE texture
+out vec4 FragColor;
+#else
+#define COMPAT_VARYING varying
+#define FragColor gl_FragColor
+#define COMPAT_TEXTURE texture2D
+#endif
+
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#define COMPAT_PRECISION mediump
+#else
+#define COMPAT_PRECISION
+#endif
+
+uniform COMPAT_PRECISION int FrameDirection;
+uniform COMPAT_PRECISION int FrameCount;
+uniform COMPAT_PRECISION vec2 OutputSize;
+uniform COMPAT_PRECISION vec2 TextureSize;
+uniform COMPAT_PRECISION vec2 InputSize;
+uniform sampler2D Texture;
+COMPAT_VARYING vec4 TEX0;
+COMPAT_VARYING vec2 onex;
+COMPAT_VARYING vec2 oney;
+
+// compatibility #defines
+#define Source Texture
+#define vTexCoord TEX0.xy
+
+#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
+#define OutputSize vec4(OutputSize, 1.0 / OutputSize)
+
+#ifdef PARAMETER_UNIFORM
+// All parameter floats need to have COMPAT_PRECISION in front of them
+uniform COMPAT_PRECISION float SPOT_WIDTH;
+uniform COMPAT_PRECISION float SPOT_HEIGHT;
+uniform COMPAT_PRECISION float COLOR_BOOST;
+uniform COMPAT_PRECISION float InputGamma;
+uniform COMPAT_PRECISION float OutputGamma;
+#else
+#define SPOT_WIDTH 0.9
+#define SPOT_HEIGHT 0.75
+#define COLOR_BOOST 1.45
+#define InputGamma 2.4
+#define OutputGamma 2.2
+#endif
+
+#define GAMMA_IN(color)     pow(color,vec4(InputGamma))
+#define GAMMA_OUT(color)    pow(color, vec4(1.0 / OutputGamma))
+
+#define TEX2D(coords)	GAMMA_IN( COMPAT_TEXTURE(Source, coords) )
+
+// Macro for weights computing
+#define WEIGHT(w) \
+   if(w>1.0) w=1.0; \
+w = 1.0 - w * w; \
+w = w * w;
+
+void main()
+{
+   vec2 coords = ( vTexCoord * SourceSize.xy );
+   vec2 pixel_center = floor( coords ) + vec2(0.5, 0.5);
+   vec2 texture_coords = pixel_center * SourceSize.zw;
+
+   vec4 color = TEX2D( texture_coords );
+
+   float dx = coords.x - pixel_center.x;
+
+   float h_weight_00 = dx / SPOT_WIDTH;
+   WEIGHT( h_weight_00 );
+
+   color *= vec4( h_weight_00, h_weight_00, h_weight_00, h_weight_00  );
+
+   // get closest horizontal neighbour to blend
+   vec2 coords01;
+   if (dx>0.0) {
+      coords01 = onex;
+      dx = 1.0 - dx;
+   } else {
+      coords01 = -onex;
+      dx = 1.0 + dx;
+   }
+   vec4 colorNB = TEX2D( texture_coords + coords01 );
+
+   float h_weight_01 = dx / SPOT_WIDTH;
+   WEIGHT( h_weight_01 );
+
+   color = color + colorNB * vec4( h_weight_01 );
+
+   //////////////////////////////////////////////////////
+   // Vertical Blending
+   float dy = coords.y - pixel_center.y;
+   float v_weight_00 = dy / SPOT_HEIGHT;
+   WEIGHT( v_weight_00 );
+   color *= vec4( v_weight_00 );
+
+   // get closest vertical neighbour to blend
+   vec2 coords10;
+   if (dy>0.0) {
+      coords10 = oney;
+      dy = 1.0 - dy;
+   } else {
+      coords10 = -oney;
+      dy = 1.0 + dy;
+   }
+   colorNB = TEX2D( texture_coords + coords10 );
+
+   float v_weight_10 = dy / SPOT_HEIGHT;
+   WEIGHT( v_weight_10 );
+
+   color = color + colorNB * vec4( v_weight_10 * h_weight_00, v_weight_10 * h_weight_00, v_weight_10 * h_weight_00, v_weight_10 * h_weight_00 );
+
+   colorNB = TEX2D(  texture_coords + coords01 + coords10 );
+
+   color = color + colorNB * vec4( v_weight_10 * h_weight_01, v_weight_10 * h_weight_01, v_weight_10 * h_weight_01, v_weight_10 * h_weight_01 );
+
+   color *= vec4( COLOR_BOOST );
+
+   FragColor = clamp( GAMMA_OUT(color), 0.0, 1.0 );
+}
+#endif