From 848d19a80b81971c8b409151123ad272129071f9 Mon Sep 17 00:00:00 2001
From: Leon Styhre <leon@leonstyhre.com>
Date: Thu, 7 Sep 2023 21:02:38 +0200
Subject: [PATCH] Added support for binding multiple texture units for use in
 the shaders

---
 es-core/src/components/GIFAnimComponent.cpp   |  2 +-
 es-core/src/components/ImageComponent.cpp     | 16 +++++-----
 .../src/components/LottieAnimComponent.cpp    |  2 +-
 es-core/src/components/NinePatchComponent.cpp |  2 +-
 .../src/components/VideoFFmpegComponent.cpp   |  2 +-
 es-core/src/renderers/Renderer.cpp            |  4 +--
 es-core/src/renderers/Renderer.h              | 21 ++++++-------
 es-core/src/renderers/RendererOpenGL.cpp      | 31 +++++++++++++------
 es-core/src/renderers/RendererOpenGL.h        |  8 ++---
 es-core/src/renderers/ShaderOpenGL.cpp        | 13 ++++++++
 es-core/src/renderers/ShaderOpenGL.h          |  3 ++
 es-core/src/resources/Font.cpp                |  8 ++---
 es-core/src/resources/TextureData.cpp         |  8 ++---
 es-core/src/resources/TextureData.h           |  2 +-
 es-core/src/resources/TextureDataManager.cpp  |  6 ++--
 es-core/src/resources/TextureDataManager.h    |  2 +-
 es-core/src/resources/TextureResource.cpp     |  6 ++--
 es-core/src/resources/TextureResource.h       |  2 +-
 resources/shaders/glsl/blur_horizontal.glsl   | 20 ++++++------
 resources/shaders/glsl/blur_vertical.glsl     | 20 ++++++------
 resources/shaders/glsl/core.glsl              | 20 ++++++------
 resources/shaders/glsl/scanlines.glsl         |  4 +--
 22 files changed, 115 insertions(+), 87 deletions(-)

diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp
index 894a44d84..1cba9bac0 100644
--- a/es-core/src/components/GIFAnimComponent.cpp
+++ b/es-core/src/components/GIFAnimComponent.cpp
@@ -572,7 +572,7 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
     }
 
     if (mTexture->getSize().x != 0.0f) {
-        mTexture->bind();
+        mTexture->bind(0);
 
         Renderer::Vertex vertices[4];
 
diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp
index 2ed6965b8..e864a9789 100644
--- a/es-core/src/components/ImageComponent.cpp
+++ b/es-core/src/components/ImageComponent.cpp
@@ -420,14 +420,12 @@ void ImageComponent::render(const glm::mat4& parentTrans)
     if (mTexture && mOpacity > 0.0f) {
         if (Settings::getInstance()->getBool("DebugImage")) {
             if (mTargetIsMax) {
-                const glm::vec2 targetSizePos {
-                    glm::round((mTargetSize - mSize) * mOrigin * glm::vec2 {-1.0f})};
+                const glm::vec2 targetSizePos {(mTargetSize - mSize) * mOrigin * glm::vec2 {-1.0f}};
                 mRenderer->drawRect(targetSizePos.x, targetSizePos.y, mTargetSize.x, mTargetSize.y,
                                     0xFF000033, 0xFF000033);
             }
             if (mClipRegion == glm::vec4 {0.0f, 0.0f, 0.0f, 0.0f})
-                mRenderer->drawRect(0.0f, 0.0f, std::round(mSize.x), std::round(mSize.y),
-                                    0xFF000033, 0xFF000033);
+                mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
             else
                 mRenderer->drawRect(mClipRegion.x, mClipRegion.y, mClipRegion.z - mClipRegion.x,
                                     mClipRegion.w - mClipRegion.y, 0xFF000033, 0xFF000033);
@@ -441,9 +439,9 @@ void ImageComponent::render(const glm::mat4& parentTrans)
             // getting invalidated, in which case we want to make sure to not get a partially
             // faded texture rendered onto the new background.
             if (mWindow->isInvalidatingCachedBackground())
-                mTexture->bind();
+                mTexture->bind(0);
             else
-                fadeIn(mTexture->bind());
+                fadeIn(mTexture->bind(0));
 
             mVertices->brightness = mBrightness;
             mVertices->opacity = mThemeOpacity;
@@ -878,16 +876,16 @@ void ImageComponent::updateVertices()
 
     // We round the vertices already here in this component as we may otherwise end up with edge
     // cases at sizes near 0.5.
-    for (int i = 0; i < 4; ++i)
+    for (int i {0}; i < 4; ++i)
         mVertices[i].position = glm::round(mVertices[i].position);
 
     if (mFlipX) {
-        for (int i = 0; i < 4; ++i)
+        for (int i {0}; i < 4; ++i)
             mVertices[i].texcoord[0] = px - mVertices[i].texcoord[0];
     }
 
     if (mFlipY) {
-        for (int i = 0; i < 4; ++i)
+        for (int i {0}; i < 4; ++i)
             mVertices[i].texcoord[1] = py - mVertices[i].texcoord[1];
     }
 
diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp
index 993500df7..862a7ebc7 100644
--- a/es-core/src/components/LottieAnimComponent.cpp
+++ b/es-core/src/components/LottieAnimComponent.cpp
@@ -563,7 +563,7 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans)
     }
 
     if (mTexture->getSize().x != 0.0f) {
-        mTexture->bind();
+        mTexture->bind(0);
 
         Renderer::Vertex vertices[4];
 
diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp
index c1901be14..6a3b2ad6d 100644
--- a/es-core/src/components/NinePatchComponent.cpp
+++ b/es-core/src/components/NinePatchComponent.cpp
@@ -35,7 +35,7 @@ void NinePatchComponent::render(const glm::mat4& parentTrans)
         mRenderer->setMatrix(trans);
         (*mVertices)[0].opacity = mOpacity;
         (*mVertices)[0].shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
-        mTexture->bind();
+        mTexture->bind(0);
         mRenderer->drawTriangleStrips(&mVertices->at(0), 6 * 9);
     }
 
diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp
index bebe69b18..a1b5928df 100644
--- a/es-core/src/components/VideoFFmpegComponent.cpp
+++ b/es-core/src/components/VideoFFmpegComponent.cpp
@@ -257,7 +257,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
         }
 
         if (mTexture != nullptr)
-            mTexture->bind();
+            mTexture->bind(0);
 
         // Render scanlines if this option is enabled. However, if this is the media viewer
         // or the video screensaver, then skip this as the scanline rendering is then handled
diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp
index d4b542d00..ce60b5c94 100644
--- a/es-core/src/renderers/Renderer.cpp
+++ b/es-core/src/renderers/Renderer.cpp
@@ -487,12 +487,12 @@ void Renderer::drawRect(const float x,
     // clang-format on
 
     // Round vertices.
-    for (int i = 0; i < 4; ++i)
+    for (int i {0}; i < 4; ++i)
         vertices[i].position = glm::round(vertices[i].position);
 
     vertices->opacity = opacity;
     vertices->dimming = dimming;
 
-    bindTexture(0);
+    bindTexture(0, 0);
     drawTriangleStrips(vertices, 4, srcBlendFactor, dstBlendFactor);
 }
diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h
index 25949538b..feb958962 100644
--- a/es-core/src/renderers/Renderer.h
+++ b/es-core/src/renderers/Renderer.h
@@ -93,13 +93,11 @@ public:
 
         Vertex(const glm::vec2& positionArg,
                const glm::vec2& texcoordArg,
-               const unsigned int colorArg,
-               const glm::vec4& clipRegionArg = glm::vec4 {0.0f, 0.0f, 0.0f, 0.0f},
-               const glm::vec2& shadowOffsetArg = glm::vec2 {0.0f, 0.0f})
+               const unsigned int colorArg)
             : position {positionArg}
             , texcoord {texcoordArg}
             , color {colorArg}
-            , clipRegion {clipRegionArg}
+            , clipRegion {0.0f, 0.0f, 0.0f, 0.0f}
             , brightness {0.0f}
             , opacity {1.0f}
             , saturation {1.0f}
@@ -195,15 +193,11 @@ public:
     static constexpr glm::mat4 getIdentity() { return glm::mat4 {1.0f}; }
     glm::mat4 mTrans {getIdentity()};
 
-    virtual void shaderPostprocessing(
-        const unsigned int shaders,
-        const Renderer::postProcessingParams& parameters = postProcessingParams(),
-        unsigned char* textureRGBA = nullptr) = 0;
-
     virtual void setup() = 0;
     virtual bool createContext() = 0;
     virtual void destroyContext() = 0;
-    virtual unsigned int createTexture(const TextureType type,
+    virtual unsigned int createTexture(const unsigned int texUnit,
+                                       const TextureType type,
                                        const bool linearMinify,
                                        const bool linearMagnify,
                                        const bool mipmapping,
@@ -213,18 +207,23 @@ public:
                                        void* data) = 0;
     virtual void destroyTexture(const unsigned int texture) = 0;
     virtual void updateTexture(const unsigned int texture,
+                               const unsigned int texUnit,
                                const TextureType type,
                                const unsigned int x,
                                const unsigned int y,
                                const unsigned int width,
                                const unsigned int height,
                                void* data) = 0;
-    virtual void bindTexture(const unsigned int texture) = 0;
+    virtual void bindTexture(const unsigned int texture, const unsigned int texUnit) = 0;
     virtual void drawTriangleStrips(
         const Vertex* vertices,
         const unsigned int numVertices,
         const BlendFactor srcBlendFactor = BlendFactor::ONE,
         const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) = 0;
+    virtual void shaderPostprocessing(
+        const unsigned int shaders,
+        const Renderer::postProcessingParams& parameters = postProcessingParams(),
+        unsigned char* textureRGBA = nullptr) = 0;
     virtual void setMatrix(const glm::mat4& matrix) = 0;
     virtual void setViewport(const Rect& viewport) = 0;
     virtual void setScissor(const Rect& scissor) = 0;
diff --git a/es-core/src/renderers/RendererOpenGL.cpp b/es-core/src/renderers/RendererOpenGL.cpp
index c6c18968a..8f62b705b 100644
--- a/es-core/src/renderers/RendererOpenGL.cpp
+++ b/es-core/src/renderers/RendererOpenGL.cpp
@@ -280,7 +280,7 @@ bool RendererOpenGL::createContext()
     GL_CHECK_ERROR(glBindVertexArray(mVertexBuffer2));
 
     uint8_t data[4] {255, 255, 255, 255};
-    mWhiteTexture = createTexture(TextureType::BGRA, false, false, false, true, 1, 1, data);
+    mWhiteTexture = createTexture(0, TextureType::BGRA, false, false, false, true, 1, 1, data);
 
     unsigned int textureWidth {0};
     unsigned int textureHeight {0};
@@ -294,9 +294,9 @@ bool RendererOpenGL::createContext()
         textureHeight = static_cast<unsigned int>(getScreenWidth());
     }
 
-    mPostProcTexture1 = createTexture(TextureType::BGRA, false, true, false, false, textureWidth,
+    mPostProcTexture1 = createTexture(0, TextureType::BGRA, false, true, false, false, textureWidth,
                                       textureHeight, nullptr);
-    mPostProcTexture2 = createTexture(TextureType::BGRA, false, true, false, false, textureWidth,
+    mPostProcTexture2 = createTexture(0, TextureType::BGRA, false, true, false, false, textureWidth,
                                       textureHeight, nullptr);
 
     // Attach textures to the shader framebuffers.
@@ -400,7 +400,8 @@ void RendererOpenGL::swapBuffers()
     GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
 }
 
-unsigned int RendererOpenGL::createTexture(const TextureType type,
+unsigned int RendererOpenGL::createTexture(const unsigned int texUnit,
+                                           const TextureType type,
                                            const bool linearMinify,
                                            const bool linearMagnify,
                                            const bool mipmapping,
@@ -409,9 +410,12 @@ unsigned int RendererOpenGL::createTexture(const TextureType type,
                                            const unsigned int height,
                                            void* data)
 {
+    assert(texUnit < 32);
+
     const GLenum textureType {convertTextureType(type)};
     unsigned int texture;
 
+    GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + texUnit));
     GL_CHECK_ERROR(glGenTextures(1, &texture));
     GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
 
@@ -454,6 +458,7 @@ void RendererOpenGL::destroyTexture(const unsigned int texture)
 }
 
 void RendererOpenGL::updateTexture(const unsigned int texture,
+                                   const unsigned int texUnit,
                                    const TextureType type,
                                    const unsigned int x,
                                    const unsigned int y,
@@ -461,8 +466,10 @@ void RendererOpenGL::updateTexture(const unsigned int texture,
                                    const unsigned int height,
                                    void* data)
 {
-    const GLenum textureType {convertTextureType(type)};
+    assert(texUnit < 32);
 
+    const GLenum textureType {convertTextureType(type)};
+    GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + texUnit));
     GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
     GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, textureType,
                                    GL_UNSIGNED_BYTE, data));
@@ -470,8 +477,12 @@ void RendererOpenGL::updateTexture(const unsigned int texture,
     GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, mWhiteTexture));
 }
 
-void RendererOpenGL::bindTexture(const unsigned int texture)
+void RendererOpenGL::bindTexture(const unsigned int texture, const unsigned int texUnit)
 {
+    assert(texUnit < 32);
+
+    GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + texUnit));
+
     if (texture == 0)
         GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, mWhiteTexture));
     else
@@ -500,6 +511,8 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
                 mCoreShader->setAttribPointers();
             GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * numVertices, vertices,
                                         GL_DYNAMIC_DRAW));
+            mCoreShader->setTextureSamplers();
+            mCoreShader->setTextureSize({width, height});
             mCoreShader->setClipRegion(vertices->clipRegion);
             mCoreShader->setBrightness(vertices->brightness);
             mCoreShader->setOpacity(vertices->opacity);
@@ -644,7 +657,7 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
         shaderList.push_back(Shader::SCANLINES);
 
     setMatrix(getIdentity());
-    bindTexture(mPostProcTexture1);
+    bindTexture(mPostProcTexture1, 0);
 
     GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1));
 
@@ -732,14 +745,14 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
                 break;
 
             if (firstFBO) {
-                bindTexture(mPostProcTexture2);
+                bindTexture(mPostProcTexture2, 0);
                 GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2));
                 GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO1));
                 GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
                 firstFBO = false;
             }
             else {
-                bindTexture(mPostProcTexture1);
+                bindTexture(mPostProcTexture1, 0);
                 GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO1));
                 GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mShaderFBO2));
                 GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
diff --git a/es-core/src/renderers/RendererOpenGL.h b/es-core/src/renderers/RendererOpenGL.h
index 8c31afbcf..416b4d89d 100644
--- a/es-core/src/renderers/RendererOpenGL.h
+++ b/es-core/src/renderers/RendererOpenGL.h
@@ -43,7 +43,8 @@ public:
     void setSwapInterval() override;
     void swapBuffers() override;
 
-    unsigned int createTexture(const TextureType type,
+    unsigned int createTexture(const unsigned int texUnit,
+                               const TextureType type,
                                const bool linearMinify,
                                const bool linearMagnify,
                                const bool mipmapping,
@@ -53,20 +54,19 @@ public:
                                void* data) override;
     void destroyTexture(const unsigned int texture) override;
     void updateTexture(const unsigned int texture,
+                       const unsigned int texUnit,
                        const TextureType type,
                        const unsigned int x,
                        const unsigned int y,
                        const unsigned int width,
                        const unsigned int height,
                        void* data) override;
-    void bindTexture(const unsigned int texture) override;
-
+    void bindTexture(const unsigned int texture, const unsigned int texUnit) override;
     void drawTriangleStrips(
         const Vertex* vertices,
         const unsigned int numVertices,
         const BlendFactor srcBlendFactor = BlendFactor::ONE,
         const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) override;
-
     void shaderPostprocessing(
         const unsigned int shaders,
         const Renderer::postProcessingParams& parameters = postProcessingParams(),
diff --git a/es-core/src/renderers/ShaderOpenGL.cpp b/es-core/src/renderers/ShaderOpenGL.cpp
index 4511fc2cf..ffa4fe519 100644
--- a/es-core/src/renderers/ShaderOpenGL.cpp
+++ b/es-core/src/renderers/ShaderOpenGL.cpp
@@ -18,7 +18,10 @@ ShaderOpenGL::ShaderOpenGL()
     , mShaderPosition {0}
     , mShaderTextureCoord {0}
     , mShaderColor {0}
+    , mTextureSampler0 {0}
+    , mTextureSampler1 {0}
     , mShaderTextureSize {0}
+    , mShaderClipRegion {0}
     , mShaderBrightness {0}
     , mShaderOpacity {0}
     , mShaderSaturation {0}
@@ -123,6 +126,8 @@ void ShaderOpenGL::getVariableLocations(GLuint programID)
     mShaderPosition = glGetAttribLocation(mProgramID, "positionVertex");
     mShaderTextureCoord = glGetAttribLocation(mProgramID, "texCoordVertex");
     mShaderColor = glGetAttribLocation(mProgramID, "colorVertex");
+    mTextureSampler0 = glGetUniformLocation(mProgramID, "textureSampler0");
+    mTextureSampler1 = glGetUniformLocation(mProgramID, "textureSampler1");
     mShaderTextureSize = glGetUniformLocation(mProgramID, "texSize");
     mShaderClipRegion = glGetUniformLocation(mProgramID, "clipRegion");
     mShaderBrightness = glGetUniformLocation(mProgramID, "brightness");
@@ -159,6 +164,14 @@ void ShaderOpenGL::setAttribPointers()
             reinterpret_cast<const void*>(offsetof(Renderer::Vertex, color))));
 }
 
+void ShaderOpenGL::setTextureSamplers()
+{
+    if (mTextureSampler0 != -1)
+        GL_CHECK_ERROR(glUniform1i(mTextureSampler0, 0));
+    if (mTextureSampler1 != -1)
+        GL_CHECK_ERROR(glUniform1i(mTextureSampler1, 1));
+}
+
 void ShaderOpenGL::setTextureSize(std::array<GLfloat, 2> shaderVec2)
 {
     if (mShaderTextureSize != -1)
diff --git a/es-core/src/renderers/ShaderOpenGL.h b/es-core/src/renderers/ShaderOpenGL.h
index 338464e92..5216b9c9f 100644
--- a/es-core/src/renderers/ShaderOpenGL.h
+++ b/es-core/src/renderers/ShaderOpenGL.h
@@ -67,6 +67,7 @@ public:
     void setModelViewProjectionMatrix(glm::mat4 mvpMatrix);
 
     void setAttribPointers();
+    void setTextureSamplers();
     void setTextureSize(std::array<GLfloat, 2> shaderVec2);
     void setClipRegion(glm::vec4 clipRegion);
     void setBrightness(GLfloat brightness);
@@ -96,6 +97,8 @@ private:
     GLint mShaderPosition;
     GLint mShaderTextureCoord;
     GLint mShaderColor;
+    GLint mTextureSampler0;
+    GLint mTextureSampler1;
     GLint mShaderTextureSize;
     GLint mShaderClipRegion;
     GLint mShaderBrightness;
diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp
index 52d419766..8c9af90e2 100644
--- a/es-core/src/resources/Font.cpp
+++ b/es-core/src/resources/Font.cpp
@@ -255,7 +255,7 @@ void Font::renderTextCache(TextCache* cache)
             it->verts[0].clipRegion = cache->clipRegion;
         }
 
-        mRenderer->bindTexture(*it->textureIdPtr);
+        mRenderer->bindTexture(*it->textureIdPtr, 0);
         mRenderer->drawTriangleStrips(
             &it->verts[0], static_cast<const unsigned int>(it->verts.size()),
             Renderer::BlendFactor::SRC_ALPHA, Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
@@ -590,7 +590,7 @@ void Font::FontTexture::initTexture()
     // glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels
     // would get sampled during scaling.
     std::vector<uint8_t> texture(textureSize.x * textureSize.y * 4, 0);
-    textureId = Renderer::getInstance()->createTexture(Renderer::TextureType::RED, true,
+    textureId = Renderer::getInstance()->createTexture(0, Renderer::TextureType::RED, true,
                                                        linearMagnify, false, false, textureSize.x,
                                                        textureSize.y, &texture[0]);
 }
@@ -657,7 +657,7 @@ void Font::rebuildTextures()
                               static_cast<int>(it->second.texSize.y * tex->textureSize.y)};
 
         // Upload to texture.
-        mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y,
+        mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
                                  glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
     }
 }
@@ -781,7 +781,7 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
     glyph.rows = glyphSlot->bitmap.rows;
 
     // Upload glyph bitmap to texture.
-    mRenderer->updateTexture(tex->textureId, Renderer::TextureType::RED, cursor.x, cursor.y,
+    mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
                              glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
 
     return &glyph;
diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp
index 9fc15d658..b0caf0559 100644
--- a/es-core/src/resources/TextureData.cpp
+++ b/es-core/src/resources/TextureData.cpp
@@ -206,12 +206,12 @@ bool TextureData::isLoaded()
     return false;
 }
 
-bool TextureData::uploadAndBind()
+bool TextureData::uploadAndBind(const unsigned int texUnit)
 {
     // Check if it has already been uploaded.
     std::unique_lock<std::mutex> lock {mMutex};
     if (mTextureID != 0) {
-        mRenderer->bindTexture(mTextureID);
+        mRenderer->bindTexture(mTextureID, texUnit);
     }
     else {
         // Make sure we're ready to upload.
@@ -220,8 +220,8 @@ bool TextureData::uploadAndBind()
 
         // Upload texture.
         mTextureID =
-            mRenderer->createTexture(Renderer::TextureType::BGRA, true, mLinearMagnify, mMipmapping,
-                                     mTile, static_cast<const unsigned int>(mWidth),
+            mRenderer->createTexture(texUnit, Renderer::TextureType::BGRA, true, mLinearMagnify,
+                                     mMipmapping, mTile, static_cast<const unsigned int>(mWidth),
                                      static_cast<const unsigned int>(mHeight), mDataRGBA.data());
     }
     return true;
diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h
index d5df27762..1443c999c 100644
--- a/es-core/src/resources/TextureData.h
+++ b/es-core/src/resources/TextureData.h
@@ -41,7 +41,7 @@ public:
 
     // Upload the texture to VRAM if necessary and bind.
     // Returns true if bound correctly.
-    bool uploadAndBind();
+    bool uploadAndBind(const unsigned int texUnit);
 
     // Release the texture from VRAM.
     void releaseVRAM();
diff --git a/es-core/src/resources/TextureDataManager.cpp b/es-core/src/resources/TextureDataManager.cpp
index 23ea442cc..01bb39665 100644
--- a/es-core/src/resources/TextureDataManager.cpp
+++ b/es-core/src/resources/TextureDataManager.cpp
@@ -66,14 +66,14 @@ std::shared_ptr<TextureData> TextureDataManager::get(const TextureResource* key)
     return tex;
 }
 
-bool TextureDataManager::bind(const TextureResource* key)
+bool TextureDataManager::bind(const TextureResource* key, const unsigned int texUnit)
 {
     std::shared_ptr<TextureData> tex {get(key)};
     bool bound {false};
     if (tex != nullptr)
-        bound = tex->uploadAndBind();
+        bound = tex->uploadAndBind(texUnit);
     if (!bound)
-        mBlank->uploadAndBind();
+        mBlank->uploadAndBind(texUnit);
     return bound;
 }
 
diff --git a/es-core/src/resources/TextureDataManager.h b/es-core/src/resources/TextureDataManager.h
index a235314c9..c7c2f8c4d 100644
--- a/es-core/src/resources/TextureDataManager.h
+++ b/es-core/src/resources/TextureDataManager.h
@@ -72,7 +72,7 @@ public:
     void remove(const TextureResource* key);
 
     std::shared_ptr<TextureData> get(const TextureResource* key);
-    bool bind(const TextureResource* key);
+    bool bind(const TextureResource* key, const unsigned int texUnit);
 
     // Get the total size of all textures managed by this object, loaded and unloaded in bytes.
     size_t getTotalSize();
diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp
index ccc008223..dd9e1d5e4 100644
--- a/es-core/src/resources/TextureResource.cpp
+++ b/es-core/src/resources/TextureResource.cpp
@@ -146,14 +146,14 @@ bool TextureResource::isTiled() const
     return data->getTiled();
 }
 
-bool TextureResource::bind()
+bool TextureResource::bind(const unsigned int texUnit)
 {
     if (mTextureData != nullptr) {
-        mTextureData->uploadAndBind();
+        mTextureData->uploadAndBind(texUnit);
         return true;
     }
     else {
-        return sTextureDataManager.bind(this);
+        return sTextureDataManager.bind(this, texUnit);
     }
 }
 
diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h
index 978c44c87..5fdf39a9b 100644
--- a/es-core/src/resources/TextureResource.h
+++ b/es-core/src/resources/TextureResource.h
@@ -73,7 +73,7 @@ public:
     }
 
     const glm::ivec2 getSize() const { return mSize; }
-    bool bind();
+    bool bind(const unsigned int texUnit);
 
     // Returns an approximation of total VRAM used by textures (in bytes).
     static size_t getTotalMemUsage();
diff --git a/resources/shaders/glsl/blur_horizontal.glsl b/resources/shaders/glsl/blur_horizontal.glsl
index 1747804fc..e35b8cb04 100644
--- a/resources/shaders/glsl/blur_horizontal.glsl
+++ b/resources/shaders/glsl/blur_horizontal.glsl
@@ -28,7 +28,7 @@ precision mediump float;
 #endif
 
 uniform uint shaderFlags;
-uniform sampler2D textureSampler;
+uniform sampler2D textureSampler0;
 uniform float blurStrength;
 in vec2 texCoord;
 out vec4 FragColor;
@@ -58,31 +58,31 @@ void main()
     }
 
     // 9-tap filter.
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 4.0 * blurStrength * hstep, tc.y - 4.0 * blurStrength * vstep)) *
              0.0162162162;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 3.0 * blurStrength * hstep, tc.y - 3.0 * blurStrength * vstep)) *
              0.0540540541;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 2.0 * blurStrength * hstep, tc.y - 2.0 * blurStrength * vstep)) *
              0.1216216216;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 1.0 * blurStrength * hstep, tc.y - 1.0 * blurStrength * vstep)) *
              0.1945945946;
 
-    color += texture(textureSampler, vec2(tc.x, tc.y)) * 0.2270270270;
+    color += texture(textureSampler0, vec2(tc.x, tc.y)) * 0.2270270270;
 
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 1.0 * blurStrength * hstep, tc.y + 1.0 * blurStrength * vstep)) *
              0.1945945946;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 2.0 * blurStrength * hstep, tc.y + 2.0 * blurStrength * vstep)) *
              0.1216216216;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 3.0 * blurStrength * hstep, tc.y + 3.0 * blurStrength * vstep)) *
              0.0540540541;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 4.0 * blurStrength * hstep, tc.y + 4.0 * blurStrength * vstep)) *
              0.0162162162;
 
diff --git a/resources/shaders/glsl/blur_vertical.glsl b/resources/shaders/glsl/blur_vertical.glsl
index 31e522ced..e23d5e8e1 100644
--- a/resources/shaders/glsl/blur_vertical.glsl
+++ b/resources/shaders/glsl/blur_vertical.glsl
@@ -28,7 +28,7 @@ precision mediump float;
 #endif
 
 uniform uint shaderFlags;
-uniform sampler2D textureSampler;
+uniform sampler2D textureSampler0;
 uniform float blurStrength;
 in vec2 texCoord;
 out vec4 FragColor;
@@ -58,31 +58,31 @@ void main()
     }
 
     // 9-tap filter.
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 4.0 * blurStrength * hstep, tc.y - 4.0 * blurStrength * vstep)) *
              0.0162162162;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 3.0 * blurStrength * hstep, tc.y - 3.0 * blurStrength * vstep)) *
              0.0540540541;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 2.0 * blurStrength * hstep, tc.y - 2.0 * blurStrength * vstep)) *
              0.1216216216;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x - 1.0 * blurStrength * hstep, tc.y - 1.0 * blurStrength * vstep)) *
              0.1945945946;
 
-    color += texture(textureSampler, vec2(tc.x, tc.y)) * 0.2270270270;
+    color += texture(textureSampler0, vec2(tc.x, tc.y)) * 0.2270270270;
 
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 1.0 * blurStrength * hstep, tc.y + 1.0 * blurStrength * vstep)) *
              0.1945945946;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 2.0 * blurStrength * hstep, tc.y + 2.0 * blurStrength * vstep)) *
              0.1216216216;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 3.0 * blurStrength * hstep, tc.y + 3.0 * blurStrength * vstep)) *
              0.0540540541;
-    color += texture(textureSampler,
+    color += texture(textureSampler0,
                      vec2(tc.x + 4.0 * blurStrength * hstep, tc.y + 4.0 * blurStrength * vstep)) *
              0.0162162162;
 
diff --git a/resources/shaders/glsl/core.glsl b/resources/shaders/glsl/core.glsl
index 98300afac..12587cfcf 100644
--- a/resources/shaders/glsl/core.glsl
+++ b/resources/shaders/glsl/core.glsl
@@ -47,7 +47,8 @@ uniform float cornerRadius;
 uniform float reflectionsFalloff;
 uniform uint shaderFlags;
 
-uniform sampler2D textureSampler;
+uniform sampler2D textureSampler0;
+uniform sampler2D textureSampler1;
 out vec4 FragColor;
 
 // shaderFlags:
@@ -73,19 +74,20 @@ void main()
             discard;
     }
 
-    vec4 sampledColor = texture(textureSampler, texCoord);
+    vec4 sampledColor = texture(textureSampler0, texCoord);
 
     // Rounded corners.
     if (0x0u != (shaderFlags & 0x20u) || 0x0u != (shaderFlags & 0x40u)) {
-        float radius = cornerRadius;
+        float cornerRadiusClamped = cornerRadius;
         // Don't go beyond half the width and height.
-        if (radius > texSize.x / 2.0)
-            radius = texSize.x / 2.0;
-        if (radius > texSize.y / 2.0)
-            radius = texSize.y / 2.0;
+        if (cornerRadiusClamped > texSize.x / 2.0)
+            cornerRadiusClamped = texSize.x / 2.0;
+        if (cornerRadiusClamped > texSize.y / 2.0)
+            cornerRadiusClamped = texSize.y / 2.0;
 
-        vec2 q = abs(position - texSize / 2.0) - (vec2(texSize.x / 2.0, texSize.y / 2.0) - radius);
-        float pixelDistance = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
+        vec2 center = position - texSize / 2.0;
+        vec2 q = abs(center) - (vec2(texSize.x / 2.0, texSize.y / 2.0) - cornerRadiusClamped);
+        float pixelDistance = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - cornerRadiusClamped;
 
         if (pixelDistance > 0.0) {
             discard;
diff --git a/resources/shaders/glsl/scanlines.glsl b/resources/shaders/glsl/scanlines.glsl
index 75c365724..a69725e75 100644
--- a/resources/shaders/glsl/scanlines.glsl
+++ b/resources/shaders/glsl/scanlines.glsl
@@ -61,7 +61,7 @@ uniform float opacity;
 uniform float brightness;
 uniform float saturation;
 uniform uint shaderFlags;
-uniform sampler2D textureSampler;
+uniform sampler2D textureSampler0;
 in vec2 texCoord;
 in vec2 onex;
 in vec2 oney;
@@ -85,7 +85,7 @@ uniform float OutputGamma;
 #define GAMMA_IN(color) pow(color, vec4(InputGamma))
 #define GAMMA_OUT(color) pow(color, vec4(1.0 / OutputGamma))
 
-#define TEX2D(coords) GAMMA_IN(texture(textureSampler, coords))
+#define TEX2D(coords) GAMMA_IN(texture(textureSampler0, coords))
 
 // Macro for weights computing.
 #define WEIGHT(w)                                                                                  \