Browse Source

Initial OpenGL 3.2 support.

Lasse Öörni 10 years ago
parent
commit
07a75dc37b

+ 9 - 3
Docs/Reference.dox

@@ -713,7 +713,7 @@ Screen resolution, fullscreen/windowed, vertical sync and hardware multisampling
 When setting the initial screen mode, Graphics does a few checks:
 
 - For Direct3D9, shader model 3.0 support is checked.
-- For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for. The ARB_instanced_arrays is not required, but will enable hardware instancing support when present.
+- For OpenGL, version 3.2+ support is checked for first and used if available. As a fallback, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil and EXT_texture_filter_anisotropic extensions is checked for. The ARB_instanced_arrays extension is also checked for but not required; it will enable hardware instancing support when present.
 - Are hardware shadow maps supported? Both AMD & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
 - Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and R32F texture format support.
 
@@ -1126,9 +1126,9 @@ uniform sampler2D sDetailMap2;
 uniform sampler2D sDetailMap3;
 \endcode
 
-\section Shaders_D3D11 Direct3D9 and Direct3D11 differences
+\section Shaders_API API differences
 
-Direct3D9 and Direct3D11 share the same HLSL shader code. Macros and some conditional code are used to hide the API differences where possible. When shaders are compiled for Direct3D11, the define D3D11 is present, and the following details need to be observed:
+Direct3D9 and Direct3D11 share the same HLSL shader code, and likewise OpenGL 2, OpenGL 3, OpenGL ES 2 and WebGL share the same GLSL code. Macros and some conditional code are used to hide the API differences where possible. When shaders are compiled for Direct3D11, the define D3D11 is present, and the following details need to be observed:
 
 - Uniforms are organized into constant buffers. See the file Uniforms.hlsl for the built-in uniforms. See TerrainBlend.hlsl for an example of defining your own uniforms into the "custom" constant buffer slot.
 - Both textures and samplers are defined for each texture unit. The macros in Samplers.hlsl (Sample2D, SampleCube etc.) can be used to write code that works on both APIs. These take the texture unit name without the 's' prefix.
@@ -1137,6 +1137,12 @@ Direct3D9 and Direct3D11 share the same HLSL shader code. Macros and some condit
 - Direct3D11 does not support luminance and luminance-alpha texture formats, but rather uses the R and RG channels. Therefore be prepared to perform swizzling in the texture reads as appropriate.
 - Direct3D11 will fail to render if the vertex shader refers to vertex elements that don't exist in the vertex buffers.
 
+For OpenGL, the define GL3 is present when shaders are being compiled for OpenGL 3+, the define GL_ES is present for OpenGL ES 2, WEBGL define is present for WebGL and RPI define is present for the Raspberry Pi. Observe the following differences:
+
+- On OpenGL 3 GLSL version 150 will be used if the shader source code does not define the version. The texture sampling functions are different but are worked around with defines in the file Samplers.glsl.
+- On OpenGL 3 luminance, alpha and luminance-alpha texture formats are deprecated, and are replaced with R and RG formats. Therefore be prepared to perform swizzling in the texture reads as appropriate.
+- On OpenGL ES 2 precision qualifiers need to be used.
+
 \section Shaders_Precaching Shader precaching
 
 The shader variations that are potentially used by a material technique in different lighting conditions and rendering passes are enumerated at material load time, but because of their large amount, they are not actually compiled or loaded from bytecode before being used in rendering. Especially on OpenGL the compiling of shaders just before rendering can cause hitches in the framerate. To avoid this, used shader combinations can be dumped out to an XML file, then preloaded. See \ref Graphics::BeginDumpShaders "BeginDumpShaders()", \ref Graphics::EndDumpShaders "EndDumpShaders()" and \ref Graphics::PrecacheShaders "PrecacheShaders()" in the Graphics subsystem. The command line parameters -ds <file> can be used to instruct the Engine to begin dumping shaders automatically on startup.

+ 93 - 20
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -217,6 +217,8 @@ static void GetGLPrimitiveType(unsigned elementCount, PrimitiveType type, unsign
 }
 
 const Vector2 Graphics::pixelUVOffset(0.0f, 0.0f);
+bool Graphics::gl3Support = false;
+bool Graphics::gl3SupportTested = false;
 
 Graphics::Graphics(Context* context_) :
     Object(context_),
@@ -362,7 +364,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
     }
 
     // Check fullscreen mode validity (desktop only). Use a closest match if not found
-    #if !defined(ANDROID) && !defined(IOS) && !defined(RPI)
+    #ifdef DESKTOP_GRAPHICS
     if (fullscreen)
     {
         PODVector<IntVector2> resolutions = GetResolutions();
@@ -403,6 +405,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         #endif
 
         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
         #ifndef GL_ES_VERSION_2_0
         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
@@ -415,10 +418,38 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
             SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
 
         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
-        #endif
+
+        // Test GL3 support first. Use a separate window for this because we may not be able to choose window pixel format
+        // several times
+        if (!gl3SupportTested)
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+
+            SDL_Window* dummyWindow = SDL_CreateWindow(windowTitle_.CString(), 0, 0, 1, 1, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
+            SDL_GLContext dummyContext = SDL_GL_CreateContext(dummyWindow);
+            if (dummyContext)
+            {
+                gl3Support = true;
+                apiName_ = "GL3";
+                SDL_GL_DeleteContext(dummyContext);
+            }
+            else
+            {
+                // Failed to create 3.1 context, fall back to 2.0 with extensions
+                SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+                SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+                SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
+            }
+            SDL_DestroyWindow(dummyWindow);
+            gl3SupportTested = true;
+        }
+        #else
         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
-        
+        #endif
+
         if (multiSample > 1)
         {
             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
@@ -449,11 +480,11 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
                 impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), x, y, width, height, flags);
             else
             {
-#if !defined(EMSCRIPTEN)
+                #ifndef EMSCRIPTEN
                 if (!impl_->window_)
                     impl_->window_ = SDL_CreateWindowFrom(externalWindow_, SDL_WINDOW_OPENGL);
                 fullscreen = false;
-#endif
+                #endif
             }
             
             if (impl_->window_)
@@ -494,6 +525,8 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         
         // If OpenGL extensions not yet initialized, initialize now
         #ifndef GL_ES_VERSION_2_0
+        // Work around GLEW failure to check extensions properly from a GL3 context
+        glewExperimental = GL_TRUE;
         GLenum err = glewInit();
         if (GLEW_OK != err)
         {
@@ -502,26 +535,51 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
             return false;
         }
         
-        if (!GLEW_VERSION_2_0)
+        if (!gl3Support)
         {
-            LOGERROR("OpenGL 2.0 is required");
-            Release(true, true);
-            return false;
+            if (!GLEW_VERSION_2_0)
+            {
+                LOGERROR("OpenGL 2.0 is required");
+                Release(true, true);
+                return false;
+            }
+
+            if (!GLEW_EXT_framebuffer_object || !GLEW_EXT_packed_depth_stencil)
+            {
+                LOGERROR("EXT_framebuffer_object and EXT_packed_depth_stencil OpenGL extensions are required");
+                Release(true, true);
+                return false;
+            }
+
+            instancingSupport_ = GLEW_ARB_instanced_arrays != 0;
+            dxtTextureSupport_ = GLEW_EXT_texture_compression_s3tc != 0;
+            anisotropySupport_ = GLEW_EXT_texture_filter_anisotropic != 0;
+            sRGBSupport_ = GLEW_EXT_texture_sRGB != 0;
+            sRGBWriteSupport_ = GLEW_EXT_framebuffer_sRGB != 0;
         }
-        
-        if (!GLEW_EXT_framebuffer_object || !GLEW_EXT_packed_depth_stencil)
+        else
         {
-            LOGERROR("EXT_framebuffer_object and EXT_packed_depth_stencil OpenGL extensions are required");
-            Release(true, true);
-            return false;
+            if (!GLEW_VERSION_3_2)
+            {
+                LOGERROR("OpenGL version mismatch: 3.2 context successfully requested from SDL, but version check failed");
+                Release(true, true);
+                return false;
+            }
+
+            // Create and bind a vertex array object that will stay in use throughout
+            /// \todo Investigate performance gain of using multiple VAO's
+            unsigned vertexArrayObject;
+            glGenVertexArrays(1, &vertexArrayObject);
+            glBindVertexArray(vertexArrayObject);
+
+            // Work around GLEW failure to check extensions properly from a GL3 context
+            instancingSupport_ = true;
+            dxtTextureSupport_ = true;
+            anisotropySupport_ = true;
+            sRGBSupport_ = true;
+            sRGBWriteSupport_ = true;
         }
         
-        instancingSupport_ = GLEW_ARB_instanced_arrays != 0;
-        dxtTextureSupport_ = GLEW_EXT_texture_compression_s3tc != 0;
-        anisotropySupport_ = GLEW_EXT_texture_filter_anisotropic != 0;
-        sRGBSupport_ = GLEW_EXT_texture_sRGB != 0;
-        sRGBWriteSupport_ = GLEW_EXT_framebuffer_sRGB != 0;
-        
         // Set up instancing divisors if supported
         if (instancingSupport_)
         {
@@ -2451,16 +2509,31 @@ void Graphics::MarkFBODirty()
 
 unsigned Graphics::GetAlphaFormat()
 {
+    #ifndef GL_ES_VERSION_2_0
+    // Alpha format is deprecated on OpenGL 3+
+    if (gl3Support)
+        return GL_R8;
+    #endif
     return GL_ALPHA;
 }
 
 unsigned Graphics::GetLuminanceFormat()
 {
+    #ifndef GL_ES_VERSION_2_0
+    // Luminance format is deprecated on OpenGL 3+
+    if (gl3Support)
+        return GL_R8;
+    #endif
     return GL_LUMINANCE;
 }
 
 unsigned Graphics::GetLuminanceAlphaFormat()
 {
+    #ifndef GL_ES_VERSION_2_0
+    // Luminance alpha format is deprecated on OpenGL 3+
+    if (gl3Support)
+        return GL_RG8;
+    #endif
     return GL_LUMINANCE_ALPHA;
 }
 

+ 6 - 0
Source/Urho3D/Graphics/OpenGL/OGLGraphics.h

@@ -428,6 +428,8 @@ public:
     static unsigned GetFormat(const String& formatName);
     /// Return UV offset required for pixel perfect rendering.
     static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return whether is using an OpenGL 3 context.
+    static bool GetGL3Support() { return gl3Support; }
 
 private:
     /// Create the application window icon.
@@ -600,6 +602,10 @@ private:
 
     /// Pixel perfect UV offset.
     static const Vector2 pixelUVOffset;
+    /// Flag for OpenGL 3 support.
+    static bool gl3Support;
+    /// Flag for OpenGL 3 support tested.
+    static bool gl3SupportTested;
 };
 
 /// Register Graphics library objects.

+ 5 - 0
Source/Urho3D/Graphics/OpenGL/OGLShaderVariation.cpp

@@ -121,6 +121,9 @@ bool ShaderVariation::Create()
             shaderCode += versionDefine + "\n";
         }
     }
+    // Force GLSL version 150 if no version define and GL3 is being used
+    if (!verEnd && Graphics::GetGL3Support())
+        shaderCode += "#version 150\n";
 
     // Distinguish between VS and PS compile in case the shader code wants to include/omit different things
     shaderCode += type_ == VS ? "#define COMPILEVS\n" : "#define COMPILEPS\n";
@@ -148,6 +151,8 @@ bool ShaderVariation::Create()
     #ifdef EMSCRIPTEN
     shaderCode += "#define WEBGL\n";
     #endif
+    if (Graphics::GetGL3Support())
+        shaderCode += "#define GL3\n";
 
     // When version define found, do not insert it a second time
     if (verEnd > 0)

+ 9 - 1
Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp

@@ -314,6 +314,12 @@ unsigned Texture::GetRowDataSize(int width) const
         return width * 4;
         
     #ifndef GL_ES_VERSION_2_0
+    case GL_R8:
+        return width;
+
+    case GL_RG8:
+        return width * 2;
+
     case GL_RGBA16:
         return width * 8;
         
@@ -358,7 +364,9 @@ unsigned Texture::GetExternalFormat(unsigned format)
         return GL_LUMINANCE;
     else if (format == GL_SLUMINANCE_ALPHA_EXT)
         return GL_LUMINANCE_ALPHA;
-    else if (format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
+    else if (format == GL_R8)
+        return GL_RED;
+    else if (format == GL_RG8 || format == GL_RG16 || format == GL_RG16F || format == GL_RG32F)
         return GL_RG;
     else if (format == GL_RGBA16 || format == GL_RGBA16F_ARB || format == GL_RGBA32F_ARB || format == GL_SRGB_ALPHA_EXT)
         return GL_RGBA;

+ 5 - 1
bin/CoreData/Shaders/GLSL/Basic.glsl

@@ -43,7 +43,11 @@ void PS()
         gl_FragColor = diffColor * diffInput;
     #endif
     #ifdef ALPHAMAP
-        float alphaInput = texture2D(sDiffMap, vTexCoord).a;
+        #ifdef GL3
+            float alphaInput = texture2D(sDiffMap, vTexCoord).r;
+        #else
+            float alphaInput = texture2D(sDiffMap, vTexCoord).a;
+        #endif
         gl_FragColor = vec4(diffColor.rgb, diffColor.a * alphaInput);
     #endif
 }

+ 20 - 7
bin/CoreData/Shaders/GLSL/Lighting.glsl

@@ -135,16 +135,29 @@ float GetShadow(vec4 shadowPos)
             #else
                 vec2 offsets = cShadowMapInvSize;
             #endif
-            vec4 inLight = vec4(
-                shadow2DProj(sShadowMap, shadowPos).r,
-                shadow2DProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)).r,
-                shadow2DProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
-                shadow2DProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
-            );
+            #ifndef GL3
+                vec4 inLight = vec4(
+                    shadow2DProj(sShadowMap, shadowPos).r,
+                    shadow2DProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)).r,
+                    shadow2DProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)).r,
+                    shadow2DProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw)).r
+                );
+            #else
+                vec4 inLight = vec4(
+                    textureProj(sShadowMap, shadowPos),
+                    textureProj(sShadowMap, vec4(shadowPos.x + offsets.x, shadowPos.yzw)),
+                    textureProj(sShadowMap, vec4(shadowPos.x, shadowPos.y + offsets.y, shadowPos.zw)),
+                    textureProj(sShadowMap, vec4(shadowPos.xy + offsets.xy, shadowPos.zw))
+                );
+            #endif
             return cShadowIntensity.y + dot(inLight, vec4(cShadowIntensity.x));
         #else
             // Take one sample
-            float inLight = shadow2DProj(sShadowMap, shadowPos).r;
+            #ifndef GL3
+                float inLight = shadow2DProj(sShadowMap, shadowPos).r;
+            #else
+                float inLight = textureProj(sShadowMap, shadowPos);
+            #endif
             return cShadowIntensity.y + cShadowIntensity.x * inLight;
         #endif
     #else

+ 6 - 0
bin/CoreData/Shaders/GLSL/Samplers.glsl

@@ -24,6 +24,12 @@ uniform samplerCube sLightCubeMap;
     uniform sampler2D sShadowMap;
 #endif
 
+#ifdef GL3
+#define texture2D texture
+#define texture2DProj textureProj
+#define textureCube texture
+#endif
+
 vec3 DecodeNormal(vec4 normalInput)
 {
     #ifdef PACKEDNORMAL

+ 2 - 2
bin/CoreData/Shaders/GLSL/Transform.glsl

@@ -49,8 +49,8 @@ vec4 GetClipPos(vec3 worldPos)
 {
     vec4 ret = vec4(worldPos, 1.0) * cViewProj;
     // While getting the clip coordinate, also automatically set gl_ClipVertex for user clip planes
-    #ifndef GL_ES
-    gl_ClipVertex = ret;
+    #if !defined(GL_ES) && !defined(GL3)
+        gl_ClipVertex = ret;
     #endif
     return ret;
 }