From dac1f270cee6bded498e6d4845d201256389a9ec Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 22 Apr 2020 00:13:23 +0100 Subject: [PATCH] Restore legacy OpenGL 2.1 support. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 1 + OpenRA.Game/Settings.cs | 3 +- OpenRA.Platforms.Default/OpenGL.cs | 76 ++++++++++++----- .../Sdl2GraphicsContext.cs | 17 ++-- .../Sdl2PlatformWindow.cs | 8 +- OpenRA.Platforms.Default/Shader.cs | 34 ++++++-- glsl/combined.frag | 81 ++++++++++++++++++- glsl/combined.vert | 16 ++++ glsl/model.frag | 18 +++++ glsl/model.vert | 9 +++ 10 files changed, 224 insertions(+), 39 deletions(-) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 5821d9ba92..92c3ae5973 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -19,6 +19,7 @@ namespace OpenRA { Modern, Embedded, + Legacy } public interface IPlatform diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index 0f3401d8f4..ddbf3be8ed 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -177,7 +177,8 @@ namespace OpenRA [Desc("Preferred OpenGL profile to use.", "Modern: OpenGL Core Profile 3.2 or greater.", - "Embedded: OpenGL ES 3.0 or greater.")] + "Embedded: OpenGL ES 3.0 or greater.", + "Legacy: OpenGL 2.1 with framebuffer_object extension.")] public GLProfile GLProfile = GLProfile.Modern; public int BatchSize = 8192; diff --git a/OpenRA.Platforms.Default/OpenGL.cs b/OpenRA.Platforms.Default/OpenGL.cs index 99ab42165b..af9b995518 100644 --- a/OpenRA.Platforms.Default/OpenGL.cs +++ b/OpenRA.Platforms.Default/OpenGL.cs @@ -478,7 +478,7 @@ namespace OpenRA.Platforms.Default #endregion - public static void Initialize() + public static void Initialize(bool preferLegacyProfile) { try { @@ -488,13 +488,14 @@ namespace OpenRA.Platforms.Default glGetError = Bind("glGetError"); glGetStringInternal = Bind("glGetString"); glGetStringiInternal = Bind("glGetStringi"); + glGetIntegerv = Bind("glGetIntegerv"); } catch (Exception e) { throw new InvalidProgramException("Failed to initialize low-level OpenGL bindings. GPU information is not available.", e); } - if (!DetectGLFeatures()) + if (!DetectGLFeatures(preferLegacyProfile)) { WriteGraphicsLog("Unsupported OpenGL version: " + glGetString(GL_VERSION)); throw new InvalidProgramException("OpenGL Version Error: See graphics.log for details."); @@ -542,7 +543,6 @@ namespace OpenRA.Platforms.Default glViewport = Bind("glViewport"); glClear = Bind("glClear"); glClearColor = Bind("glClearColor"); - glGetIntegerv = Bind("glGetIntegerv"); glFinish = Bind("glFinish"); glCreateProgram = Bind("glCreateProgram"); glUseProgram = Bind("glUseProgram"); @@ -571,10 +571,7 @@ namespace OpenRA.Platforms.Default glBufferData = Bind("glBufferData"); glBufferSubData = Bind("glBufferSubData"); glDeleteBuffers = Bind("glDeleteBuffers"); - glGenVertexArrays = Bind("glGenVertexArrays"); - glBindVertexArray = Bind("glBindVertexArray"); glBindAttribLocation = Bind("glBindAttribLocation"); - glBindFragDataLocation = Bind("glBindFragDataLocation"); glVertexAttribPointer = Bind("glVertexAttribPointer"); glEnableVertexAttribArray = Bind("glEnableVertexAttribArray"); glDisableVertexAttribArray = Bind("glDisableVertexAttribArray"); @@ -594,16 +591,39 @@ namespace OpenRA.Platforms.Default glGetTexImage = Bind("glGetTexImage"); glTexParameteri = Bind("glTexParameteri"); glTexParameterf = Bind("glTexParameterf"); - glGenFramebuffers = Bind("glGenFramebuffers"); - glBindFramebuffer = Bind("glBindFramebuffer"); - glFramebufferTexture2D = Bind("glFramebufferTexture2D"); - glDeleteFramebuffers = Bind("glDeleteFramebuffers"); - glGenRenderbuffers = Bind("glGenRenderbuffers"); - glBindRenderbuffer = Bind("glBindRenderbuffer"); - glRenderbufferStorage = Bind("glRenderbufferStorage"); - glDeleteRenderbuffers = Bind("glDeleteRenderbuffers"); - glFramebufferRenderbuffer = Bind("glFramebufferRenderbuffer"); - glCheckFramebufferStatus = Bind("glCheckFramebufferStatus"); + + if (Profile != GLProfile.Legacy) + { + glGenVertexArrays = Bind("glGenVertexArrays"); + glBindVertexArray = Bind("glBindVertexArray"); + glBindFragDataLocation = Bind("glBindFragDataLocation"); + glGenFramebuffers = Bind("glGenFramebuffers"); + glBindFramebuffer = Bind("glBindFramebuffer"); + glFramebufferTexture2D = Bind("glFramebufferTexture2D"); + glDeleteFramebuffers = Bind("glDeleteFramebuffers"); + glGenRenderbuffers = Bind("glGenRenderbuffers"); + glBindRenderbuffer = Bind("glBindRenderbuffer"); + glRenderbufferStorage = Bind("glRenderbufferStorage"); + glDeleteRenderbuffers = Bind("glDeleteRenderbuffers"); + glFramebufferRenderbuffer = Bind("glFramebufferRenderbuffer"); + glCheckFramebufferStatus = Bind("glCheckFramebufferStatus"); + } + else + { + glGenVertexArrays = null; + glBindVertexArray = null; + glBindFragDataLocation = null; + glGenFramebuffers = Bind("glGenFramebuffersEXT"); + glBindFramebuffer = Bind("glBindFramebufferEXT"); + glFramebufferTexture2D = Bind("glFramebufferTexture2DEXT"); + glDeleteFramebuffers = Bind("glDeleteFramebuffersEXT"); + glGenRenderbuffers = Bind("glGenRenderbuffersEXT"); + glBindRenderbuffer = Bind("glBindRenderbufferEXT"); + glRenderbufferStorage = Bind("glRenderbufferStorageEXT"); + glDeleteRenderbuffers = Bind("glDeleteRenderbuffersEXT"); + glFramebufferRenderbuffer = Bind("glFramebufferRenderbufferEXT"); + glCheckFramebufferStatus = Bind("glCheckFramebufferStatusEXT"); + } } catch (Exception e) { @@ -617,7 +637,7 @@ namespace OpenRA.Platforms.Default return (T)(object)Marshal.GetDelegateForFunctionPointer(SDL.SDL_GL_GetProcAddress(name), typeof(T)); } - public static bool DetectGLFeatures() + public static bool DetectGLFeatures(bool preferLegacyProfile) { var hasValidConfiguration = false; try @@ -654,6 +674,15 @@ namespace OpenRA.Platforms.Default var hasDebugMessagesCallback = SDL.SDL_GL_ExtensionSupported("GL_KHR_debug") == SDL.SDL_bool.SDL_TRUE; if (hasDebugMessagesCallback) Features |= GLFeatures.DebugMessagesCallback; + + if (preferLegacyProfile || (major == 2 && minor == 1) || (major == 3 && minor < 2)) + { + if (SDL.SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object") == SDL.SDL_bool.SDL_TRUE) + { + hasValidConfiguration = true; + Profile = GLProfile.Legacy; + } + } } catch (Exception) { } @@ -680,10 +709,15 @@ namespace OpenRA.Platforms.Default Log.Write("graphics", "Shader Version: {0}", glGetString(GL_SHADING_LANGUAGE_VERSION)); Log.Write("graphics", "Available extensions:"); - int extensionCount; - glGetIntegerv(GL_NUM_EXTENSIONS, out extensionCount); - for (var i = 0; i < extensionCount; i++) - Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)i)); + if (Profile != GLProfile.Legacy) + { + int extensionCount; + glGetIntegerv(GL_NUM_EXTENSIONS, out extensionCount); + for (var i = 0; i < extensionCount; i++) + Log.Write("graphics", glGetStringi(GL_EXTENSIONS, (uint)i)); + } + else + Log.Write("graphics", glGetString(GL_EXTENSIONS)); } public static void CheckGLError() diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index 03c3cb1914..2bc18f7211 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -35,14 +35,17 @@ namespace OpenRA.Platforms.Default if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window.Window, context) < 0) throw new InvalidOperationException("Can not create OpenGL context. (Error: {0})".F(SDL.SDL_GetError())); - OpenGL.Initialize(); + OpenGL.Initialize(window.GLProfile == GLProfile.Legacy); + OpenGL.CheckGLError(); - uint vao; - OpenGL.CheckGLError(); - OpenGL.glGenVertexArrays(1, out vao); - OpenGL.CheckGLError(); - OpenGL.glBindVertexArray(vao); - OpenGL.CheckGLError(); + if (OpenGL.Profile != GLProfile.Legacy) + { + uint vao; + OpenGL.glGenVertexArrays(1, out vao); + OpenGL.CheckGLError(); + OpenGL.glBindVertexArray(vao); + OpenGL.CheckGLError(); + } OpenGL.glEnableVertexAttribArray(Shader.VertexPosAttributeIndex); OpenGL.CheckGLError(); diff --git a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs index 9384d0d362..e88f1d1c41 100644 --- a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs +++ b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs @@ -151,9 +151,9 @@ namespace OpenRA.Platforms.Default // We first need to query the available profiles on Windows/Linux. // On macOS, known/consistent OpenGL support is provided by the OS. if (Platform.CurrentPlatform == PlatformType.OSX) - supportedProfiles = new[] { GLProfile.Modern }; + supportedProfiles = new[] { GLProfile.Modern, GLProfile.Legacy }; else - supportedProfiles = new[] { GLProfile.Modern, GLProfile.Embedded } + supportedProfiles = new[] { GLProfile.Modern, GLProfile.Embedded, GLProfile.Legacy } .Where(CanCreateGLWindow) .ToArray(); @@ -487,6 +487,10 @@ namespace OpenRA.Platforms.Default SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES); break; + case GLProfile.Legacy: + SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1); + break; } } diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index e2fc45c36c..8e46d5f3f0 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -23,6 +23,7 @@ namespace OpenRA.Platforms.Default public const int TexMetadataAttributeIndex = 2; readonly Dictionary samplers = new Dictionary(); + readonly Dictionary legacySizeUniforms = new Dictionary(); readonly Dictionary textures = new Dictionary(); readonly Queue unbindTextures = new Queue(); readonly uint program; @@ -33,7 +34,9 @@ namespace OpenRA.Platforms.Default var filename = Path.Combine(Platform.GameDir, "glsl", name + "." + ext); var code = File.ReadAllText(filename); - var version = OpenGL.Profile == GLProfile.Embedded ? "300 es" : "140"; + var version = OpenGL.Profile == GLProfile.Embedded ? "300 es" : + OpenGL.Profile == GLProfile.Legacy ? "120" : "140"; + code = code.Replace("{VERSION}", version); var shader = OpenGL.glCreateShader(type); @@ -80,8 +83,12 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata"); OpenGL.CheckGLError(); - OpenGL.glBindFragDataLocation(program, 0, "fragColor"); - OpenGL.CheckGLError(); + + if (OpenGL.Profile != GLProfile.Legacy) + { + OpenGL.glBindFragDataLocation(program, 0, "fragColor"); + OpenGL.CheckGLError(); + } OpenGL.glAttachShader(program, vertexShader); OpenGL.CheckGLError(); @@ -132,6 +139,13 @@ namespace OpenRA.Platforms.Default OpenGL.glUniform1i(loc, nextTexUnit); OpenGL.CheckGLError(); + if (OpenGL.Profile == GLProfile.Legacy) + { + var sizeLoc = OpenGL.glGetUniformLocation(program, sampler + "Size"); + if (sizeLoc >= 0) + legacySizeUniforms.Add(nextTexUnit, sizeLoc); + } + nextTexUnit++; } } @@ -146,13 +160,21 @@ namespace OpenRA.Platforms.Default // bind the textures foreach (var kv in textures) { - var id = ((ITextureInternal)kv.Value).ID; + var texture = (ITextureInternal)kv.Value; // Evict disposed textures from the cache - if (OpenGL.glIsTexture(id)) + if (OpenGL.glIsTexture(texture.ID)) { OpenGL.glActiveTexture(OpenGL.GL_TEXTURE0 + kv.Key); - OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, id); + OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, texture.ID); + + // Work around missing textureSize GLSL function by explicitly tracking sizes in a uniform + int param; + if (OpenGL.Profile == GLProfile.Legacy && legacySizeUniforms.TryGetValue(kv.Key, out param)) + { + OpenGL.glUniform2f(param, texture.Size.Width, texture.Size.Height); + OpenGL.CheckGLError(); + } } else unbindTextures.Enqueue(kv.Key); diff --git a/glsl/combined.frag b/glsl/combined.frag index c1aa426928..49c12c28f6 100644 --- a/glsl/combined.frag +++ b/glsl/combined.frag @@ -3,8 +3,6 @@ precision mediump float; #endif -in vec4 vColor; - uniform sampler2D Texture0; uniform sampler2D Texture1; uniform sampler2D Texture2; @@ -18,6 +16,27 @@ uniform bool EnableDepthPreview; uniform float DepthTextureScale; uniform float AntialiasPixelsPerTexel; +#if __VERSION__ == 120 +varying vec4 vTexCoord; +varying vec2 vTexMetadata; +varying vec4 vChannelMask; +varying vec4 vDepthMask; +varying vec2 vTexSampler; + +varying vec4 vColorFraction; +varying vec4 vRGBAFraction; +varying vec4 vPalettedFraction; + +uniform vec2 Texture0Size; +uniform vec2 Texture1Size; +uniform vec2 Texture2Size; +uniform vec2 Texture3Size; +uniform vec2 Texture4Size; +uniform vec2 Texture5Size; +uniform vec2 Texture6Size; +#else +in vec4 vColor; + in vec4 vTexCoord; in vec2 vTexMetadata; in vec4 vChannelMask; @@ -29,6 +48,7 @@ in vec4 vRGBAFraction; in vec4 vPalettedFraction; out vec4 fragColor; +#endif float jet_r(float x) { @@ -45,6 +65,43 @@ float jet_b(float x) return x < 0.3 ? 4.0 * x + 0.5 : -4.0 * x + 2.5; } +#if __VERSION__ == 120 +vec2 Size(float samplerIndex) +{ + if (samplerIndex < 0.5) + return Texture0Size; + else if (samplerIndex < 1.5) + return Texture1Size; + else if (samplerIndex < 2.5) + return Texture2Size; + else if (samplerIndex < 3.5) + return Texture3Size; + else if (samplerIndex < 4.5) + return Texture4Size; + else if (samplerIndex < 5.5) + return Texture5Size; + + return Texture6Size; +} + +vec4 Sample(float samplerIndex, vec2 pos) +{ + if (samplerIndex < 0.5) + return texture2D(Texture0, pos); + else if (samplerIndex < 1.5) + return texture2D(Texture1, pos); + else if (samplerIndex < 2.5) + return texture2D(Texture2, pos); + else if (samplerIndex < 3.5) + return texture2D(Texture3, pos); + else if (samplerIndex < 4.5) + return texture2D(Texture4, pos); + else if (samplerIndex < 5.5) + return texture2D(Texture5, pos); + + return texture2D(Texture6, pos); +} +#else ivec2 Size(float samplerIndex) { if (samplerIndex < 0.5) @@ -80,6 +137,7 @@ vec4 Sample(float samplerIndex, vec2 pos) return texture(Texture6, pos); } +#endif vec4 SamplePalettedBilinear(float samplerIndex, vec2 coords, vec2 textureSize) { @@ -93,10 +151,17 @@ vec4 SamplePalettedBilinear(float samplerIndex, vec2 coords, vec2 textureSize) vec4 x3 = Sample(samplerIndex, tl + vec2(0., px.y)); vec4 x4 = Sample(samplerIndex, tl + px); + #if __VERSION__ == 120 + vec4 c1 = texture2D(Palette, vec2(dot(x1, vChannelMask), vTexMetadata.s)); + vec4 c2 = texture2D(Palette, vec2(dot(x2, vChannelMask), vTexMetadata.s)); + vec4 c3 = texture2D(Palette, vec2(dot(x3, vChannelMask), vTexMetadata.s)); + vec4 c4 = texture2D(Palette, vec2(dot(x4, vChannelMask), vTexMetadata.s)); + #else vec4 c1 = texture(Palette, vec2(dot(x1, vChannelMask), vTexMetadata.s)); vec4 c2 = texture(Palette, vec2(dot(x2, vChannelMask), vTexMetadata.s)); vec4 c3 = texture(Palette, vec2(dot(x3, vChannelMask), vTexMetadata.s)); vec4 c4 = texture(Palette, vec2(dot(x4, vChannelMask), vTexMetadata.s)); + #endif return mix(mix(c1, c2, interp.x), mix(c3, c4, interp.x), interp.y); } @@ -127,7 +192,11 @@ void main() { vec4 x = Sample(vTexSampler.s, coords); vec2 p = vec2(dot(x, vChannelMask), vTexMetadata.s); + #if __VERSION__ == 120 + c = vPalettedFraction * texture2D(Palette, p) + vRGBAFraction * x + vColorFraction * vTexCoord; + #else c = vPalettedFraction * texture(Palette, p) + vRGBAFraction * x + vColorFraction * vTexCoord; + #endif } // Discard any transparent fragments (both color and depth) @@ -150,8 +219,16 @@ void main() float r = clamp(jet_r(x), 0.0, 1.0); float g = clamp(jet_g(x), 0.0, 1.0); float b = clamp(jet_b(x), 0.0, 1.0); + #if __VERSION__ == 120 + gl_FragColor = vec4(r, g, b, 1.0); + #else fragColor = vec4(r, g, b, 1.0); + #endif } else + #if __VERSION__ == 120 + gl_FragColor = c; + #else fragColor = c; + #endif } diff --git a/glsl/combined.vert b/glsl/combined.vert index db42199e82..87ea4e7814 100644 --- a/glsl/combined.vert +++ b/glsl/combined.vert @@ -3,6 +3,21 @@ uniform vec3 Scroll; uniform vec3 r1, r2; +#if __VERSION__ == 120 +attribute vec4 aVertexPosition; +attribute vec4 aVertexTexCoord; +attribute vec2 aVertexTexMetadata; + +varying vec4 vTexCoord; +varying vec2 vTexMetadata; +varying vec4 vChannelMask; +varying vec4 vDepthMask; +varying vec2 vTexSampler; + +varying vec4 vColorFraction; +varying vec4 vRGBAFraction; +varying vec4 vPalettedFraction; +#else in vec4 aVertexPosition; in vec4 aVertexTexCoord; in vec2 aVertexTexMetadata; @@ -16,6 +31,7 @@ out vec2 vTexSampler; out vec4 vColorFraction; out vec4 vRGBAFraction; out vec4 vPalettedFraction; +#endif vec4 UnpackChannelAttributes(float x) { diff --git a/glsl/model.frag b/glsl/model.frag index ce3e744c27..33bcfb50a8 100644 --- a/glsl/model.frag +++ b/glsl/model.frag @@ -9,13 +9,30 @@ uniform vec2 PaletteRows; uniform vec4 LightDirection; uniform vec3 AmbientLight, DiffuseLight; +#if __VERSION__ == 120 +varying vec4 vTexCoord; +varying vec4 vChannelMask; +varying vec4 vNormalsMask; +#else in vec4 vTexCoord; in vec4 vChannelMask; in vec4 vNormalsMask; out vec4 fragColor; +#endif void main() { + #if __VERSION__ == 120 + vec4 x = texture2D(DiffuseTexture, vTexCoord.st); + vec4 color = texture2D(Palette, vec2(dot(x, vChannelMask), PaletteRows.x)); + if (color.a < 0.01) + discard; + + vec4 y = texture2D(DiffuseTexture, vTexCoord.pq); + vec4 normal = (2.0 * texture2D(Palette, vec2(dot(y, vNormalsMask), PaletteRows.y)) - 1.0); + vec3 intensity = AmbientLight + DiffuseLight * max(dot(normal, LightDirection), 0.0); + gl_FragColor = vec4(intensity * color.rgb, color.a); + #else vec4 x = texture(DiffuseTexture, vTexCoord.st); vec4 color = texture(Palette, vec2(dot(x, vChannelMask), PaletteRows.x)); if (color.a < 0.01) @@ -25,4 +42,5 @@ void main() vec4 normal = (2.0 * texture(Palette, vec2(dot(y, vNormalsMask), PaletteRows.y)) - 1.0); vec3 intensity = AmbientLight + DiffuseLight * max(dot(normal, LightDirection), 0.0); fragColor = vec4(intensity * color.rgb, color.a); + #endif } diff --git a/glsl/model.vert b/glsl/model.vert index 30fdb34a94..16aa02496f 100644 --- a/glsl/model.vert +++ b/glsl/model.vert @@ -3,12 +3,21 @@ uniform mat4 View; uniform mat4 TransformMatrix; +#if __VERSION__ == 120 +attribute vec4 aVertexPosition; +attribute vec4 aVertexTexCoord; +attribute vec2 aVertexTexMetadata; +varying vec4 vTexCoord; +varying vec4 vChannelMask; +varying vec4 vNormalsMask; +#else in vec4 aVertexPosition; in vec4 aVertexTexCoord; in vec2 aVertexTexMetadata; out vec4 vTexCoord; out vec4 vChannelMask; out vec4 vNormalsMask; +#endif vec4 DecodeMask(float x) {