From beb320bbd1bef82f579de6d3ed2ec22cd8269cb1 Mon Sep 17 00:00:00 2001
From: reuk <reuk@users.noreply.github.com>
Date: Thu, 1 Jul 2021 15:27:21 +0100
Subject: [PATCH] OpenGL:  Fix shader compilation on Ubuntu 18.04

Previously, we were always adding a #version 150 directive to shaders
when the GLSL version was higher than 1.2, which could cause compilation
to fail on platforms with GLSL versions between 1.2 and 1.5.
---
 BREAKING-CHANGES.txt                          |  20 ++++
 modules/juce_opengl/juce_opengl.h             |  10 --
 .../juce_opengl/opengl/juce_OpenGLHelpers.cpp | 105 +++++++++++++++++-
 .../juce_opengl/opengl/juce_OpenGLHelpers.h   |   5 +
 4 files changed, 126 insertions(+), 14 deletions(-)

diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt
index 12372b062..916b6e11d 100644
--- a/BREAKING-CHANGES.txt
+++ b/BREAKING-CHANGES.txt
@@ -4,6 +4,26 @@ JUCE breaking changes
 Develop
 =======
 
+Change
+------
+The JUCE_GLSL_VERSION preprocessor definition has been removed.
+
+Possible Issues
+---------------
+Code which used this definition will no longer compile.
+
+Workaround
+----------
+Use OpenGLHelpers::getGLSLVersionString to retrieve a version string which is
+consistent with the capabilities of the current OpenGL context.
+
+Rationale
+---------
+A compile-time version string is not very useful, as OpenGL versions and
+capabilities can change at runtime. Replacing this macro with a function allows
+querying the capabilities of the current context at runtime.
+
+
 Change
 ------
 The minimum support CMake version is now 3.15.
diff --git a/modules/juce_opengl/juce_opengl.h b/modules/juce_opengl/juce_opengl.h
index ea7e3d80e..32ee669bf 100644
--- a/modules/juce_opengl/juce_opengl.h
+++ b/modules/juce_opengl/juce_opengl.h
@@ -70,16 +70,6 @@
 
 #include <juce_gui_extra/juce_gui_extra.h>
 
-//==============================================================================
-/** This macro is a helper for use in GLSL shader code which needs to compile on both OpenGL 2.1 and OpenGL 3.0.
-    It's mandatory in OpenGL 3.0 to specify the GLSL version.
-*/
-#if JUCE_OPENGL_ES
- #define JUCE_GLSL_VERSION "#version 300 es"
-#else
- #define JUCE_GLSL_VERSION "#version 150"
-#endif
-
 //==============================================================================
 #if JUCE_OPENGL_ES || defined (DOXYGEN)
  /** This macro is a helper for use in GLSL shader code which needs to compile on both GLES and desktop GL.
diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp
index 599309735..5c74fee50 100644
--- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp
+++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp
@@ -26,6 +26,89 @@
 namespace juce
 {
 
+class Version
+{
+public:
+    constexpr Version() = default;
+
+    constexpr explicit Version (int majorIn)
+        : Version (majorIn, 0) {}
+
+    constexpr Version (int majorIn, int minorIn, int microIn = 0)
+        : major (majorIn), minor (minorIn), micro (microIn) {}
+
+    int major = 0, minor = 0, micro = 0;
+
+    constexpr bool operator== (const Version& other) const noexcept
+    {
+        return toTuple() == other.toTuple();
+    }
+
+    constexpr bool operator!= (const Version& other) const noexcept
+    {
+        return toTuple() != other.toTuple();
+    }
+
+    constexpr bool operator< (const Version& other) const noexcept
+    {
+        return toTuple() < other.toTuple();
+    }
+
+    constexpr bool operator<= (const Version& other) const noexcept
+    {
+        return toTuple() <= other.toTuple();
+    }
+
+    constexpr bool operator> (const Version& other) const noexcept
+    {
+        return toTuple() > other.toTuple();
+    }
+
+    constexpr bool operator>= (const Version& other) const noexcept
+    {
+        return toTuple() >= other.toTuple();
+    }
+
+private:
+    constexpr std::tuple<int, int, int> toTuple() const noexcept
+    {
+        return std::make_tuple (major, minor, micro);
+    }
+};
+
+
+template <typename Char>
+static auto* findNullTerminator (const Char* ptr)
+{
+    while (*ptr != 0)
+        ++ptr;
+
+    return ptr;
+}
+
+static Version getOpenGLVersion()
+{
+    const auto* versionBegin = glGetString (GL_VERSION);
+
+    if (versionBegin == nullptr)
+        return {};
+
+    const auto* versionEnd = findNullTerminator (versionBegin);
+    const std::string versionString (versionBegin, versionEnd);
+    const auto spaceSeparated = StringArray::fromTokens (versionString.c_str(), false);
+
+    if (spaceSeparated.isEmpty())
+        return {};
+
+    const auto pointSeparated = StringArray::fromTokens (spaceSeparated[0], ".", "");
+
+    const auto major = pointSeparated[0].getIntValue();
+    const auto minor = pointSeparated[1].getIntValue();
+    const auto micro = pointSeparated[2].getIntValue();
+
+    return { major, minor, micro };
+}
+
 void OpenGLHelpers::resetErrorState()
 {
     while (glGetError() != GL_NO_ERROR) {}
@@ -81,9 +164,23 @@ void OpenGLHelpers::enableScissorTest (Rectangle<int> clip)
     glScissor (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight());
 }
 
+String OpenGLHelpers::getGLSLVersionString()
+{
+    if (getOpenGLVersion() >= Version (3, 2))
+    {
+       #if JUCE_OPENGL_ES
+        return "#version 300 es";
+       #else
+        return "#version 150";
+       #endif
+    }
+
+    return "#version 110";
+}
+
 String OpenGLHelpers::translateVertexShaderToV3 (const String& code)
 {
-    if (OpenGLShaderProgram::getLanguageVersion() > 1.2)
+    if (getOpenGLVersion() >= Version (3, 2))
     {
         String output;
 
@@ -109,7 +206,7 @@ String OpenGLHelpers::translateVertexShaderToV3 (const String& code)
         output = code.replace ("attribute", "in");
        #endif
 
-        return JUCE_GLSL_VERSION "\n" + output.replace ("varying", "out");
+        return getGLSLVersionString() + "\n" + output.replace ("varying", "out");
     }
 
     return code;
@@ -117,8 +214,8 @@ String OpenGLHelpers::translateVertexShaderToV3 (const String& code)
 
 String OpenGLHelpers::translateFragmentShaderToV3 (const String& code)
 {
-    if (OpenGLShaderProgram::getLanguageVersion() > 1.2)
-        return JUCE_GLSL_VERSION "\n"
+    if (getOpenGLVersion() >= Version (3, 2))
+        return getGLSLVersionString() + "\n"
                "out " JUCE_MEDIUMP " vec4 fragColor;\n"
                 + code.replace ("varying", "in")
                       .replace ("texture2D", "texture")
diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h
index cef11c2e5..d07e93bf9 100644
--- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h
+++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h
@@ -52,6 +52,11 @@ public:
     /** Returns the address of a named GL extension function */
     static void* getExtensionFunction (const char* functionName);
 
+    /** Returns a version string such as "#version 150" suitable for prefixing a GLSL
+        shader on this platform.
+    */
+    static String getGLSLVersionString();
+
     /** Makes some simple textual changes to a shader program to try to convert old GLSL
         keywords to their v3 equivalents.
 
-- 
2.32.0

