소스 검색

GLRenderer: Improve compatibility with OpenGL 3.2 core profile
* Separate GLFbo and GLExt implementations. GLFbo can now be implemented either via vanilla OpenGL3 calls or GL_EXT_framebuffer_*** extensions (OpenGL2.1- only).
* Use modern way of getting supported extensions in core profile.
* Luminance and Alpha formats are not available when running in core profile.
* Bind a dummy vertex array object (VAO) when running in core profile.
* Point sprite mode is always enabled. Since both OpenGL ES 2.0 and OpenGL 3.2 core require it, jME3 is no longer capable of rendering regular points.

Kirill Vainer 10 년 전
부모
커밋
5b95f8a4b0

+ 2 - 1
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -54,6 +54,7 @@ import com.jme3.input.dummy.DummyMouseInput;
 import com.jme3.renderer.android.AndroidGL;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
 import com.jme3.renderer.opengl.GLRenderer;
 import com.jme3.system.*;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -196,7 +197,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         Object gl = new AndroidGL();
         // gl = GLTracer.createGlesTracer((GL)gl, (GLExt)gl);
         // gl = new GLDebugES((GL)gl, (GLExt)gl);
-        renderer = new GLRenderer((GL)gl, (GLExt)gl);
+        renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl);
         renderer.initialize();
 
         JmeSystem.setSoftTextDialogInput(this);

+ 13 - 1
jme3-core/src/main/java/com/jme3/renderer/Caps.java

@@ -337,7 +337,19 @@ public enum Caps {
      * <p>
      * Improves the quality of environment mapping.
      */
-    SeamlessCubemap;
+    SeamlessCubemap,
+    
+    /**
+     * Running with OpenGL 3.2+ core profile.
+     * 
+     * Compatibility features will not be available.
+     */
+    CoreProfile,
+    
+    /**
+     * GPU can provide and accept binary shaders.
+     */
+    BinaryShader;
 
     /**
      * Returns true if given the renderer capabilities, the texture

+ 0 - 1
jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java

@@ -32,7 +32,6 @@
 package com.jme3.renderer.opengl;
 
 import java.nio.ByteBuffer;
-import java.nio.DoubleBuffer;
 import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;

+ 7 - 3
jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java

@@ -35,14 +35,18 @@ import java.nio.IntBuffer;
 
 /**
  * GL functions only available on vanilla desktop OpenGL 3.0.
- * 
+ *
  * @author Kirill Vainer
  */
 public interface GL3 extends GL2 {
-    
+
     public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
-    public static final int GL_GEOMETRY_SHADER=0x8DD9;
+    public static final int GL_GEOMETRY_SHADER = 0x8DD9;
+    public static final int GL_NUM_EXTENSIONS = 0x821D;
+    
     public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
     public void glBindVertexArray(int param1); /// GL3+
+    public void glDeleteVertexArrays(IntBuffer arrays); /// GL3+
     public void glGenVertexArrays(IntBuffer param1); /// GL3+
+    public String glGetString(int param1, int param2); /// GL3+
 }

+ 16 - 3
jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java

@@ -3,13 +3,13 @@ package com.jme3.renderer.opengl;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 
-public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
+public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
 
     private final GL2 gl2;
     private final GL3 gl3;
     
-    public GLDebugDesktop(GL gl, GLFbo glfbo) {
-        super(gl, glfbo);
+    public GLDebugDesktop(GL gl, GLExt glext, GLFbo glfbo) {
+        super(gl, glext, glfbo);
         this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
         this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
     }
@@ -73,5 +73,18 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
         gl3.glGenVertexArrays(param1);
         checkError();
     }
+    
+    @Override
+    public String glGetString(int param1, int param2) {
+        String result = gl3.glGetString(param1, param2);
+        checkError();
+        return result;
+    }
+
+    @Override
+    public void glDeleteVertexArrays(IntBuffer arrays) {
+        gl3.glDeleteVertexArrays(arrays);
+        checkError();
+    }
 
 }

+ 4 - 6
jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java

@@ -10,12 +10,10 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
     private final GLFbo glfbo;
     private final GLExt glext;
 
-    public GLDebugES(GL gl, GLFbo glfbo) {
+    public GLDebugES(GL gl, GLExt glext, GLFbo glfbo) {
         this.gl = gl;
-//        this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
-//        this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
+        this.glext = glext;
         this.glfbo = glfbo;
-        this.glext = glfbo instanceof GLExt ? (GLExt) glfbo : null;
     }
 
     public void glActiveTexture(int texture) {
@@ -478,7 +476,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
     }
 
     public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
-        glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+        glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
         checkError();
     }
 
@@ -525,7 +523,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
     }
 
     public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
-        glext.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+        glfbo.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
         checkError();
     }
 

+ 1 - 3
jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java

@@ -41,7 +41,7 @@ import java.nio.IntBuffer;
  * 
  * @author Kirill Vainer
  */
-public interface GLExt extends GLFbo {
+public interface GLExt {
 
         public static final int GL_ALREADY_SIGNALED = 0x911A;
 	public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
@@ -100,7 +100,6 @@ public interface GLExt extends GLFbo {
 	public static final int GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E;
         public static final int GL_WAIT_FAILED = 0x911D;
 
-	public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
         public void glBufferData(int target, IntBuffer data, int usage);
         public void glBufferSubData(int target, long offset, IntBuffer data);
         public int glClientWaitSync(Object sync, int flags, long timeout);
@@ -110,7 +109,6 @@ public interface GLExt extends GLFbo {
 	public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount);
         public Object glFenceSync(int condition, int flags);
 	public void glGetMultisample(int pname, int index, FloatBuffer val);
-        public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
 	public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations);
 	public void glVertexAttribDivisorARB(int index, int divisor);
 }

+ 2 - 1
jme3-core/src/main/java/com/jme3/renderer/opengl/GLFbo.java

@@ -83,6 +83,7 @@ public interface GLFbo {
     
     public void glBindFramebufferEXT(int param1, int param2);
     public void glBindRenderbufferEXT(int param1, int param2);
+    public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
     public int glCheckFramebufferStatusEXT(int param1);
     public void glDeleteFramebuffersEXT(IntBuffer param1);
     public void glDeleteRenderbuffersEXT(IntBuffer param1);
@@ -92,5 +93,5 @@ public interface GLFbo {
     public void glGenRenderbuffersEXT(IntBuffer param1);
     public void glGenerateMipmapEXT(int param1);
     public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4);
-    
+    public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
 }

+ 24 - 14
jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java

@@ -89,9 +89,11 @@ public final class GLImageFormats {
         GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length];
         
         if (caps.contains(Caps.OpenGL20)) {
-            format(formatToGL, Format.Alpha8,           GL2.GL_ALPHA8,            GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8,       GL2.GL_LUMINANCE8,        GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            if (!caps.contains(Caps.CoreProfile)) {
+                format(formatToGL, Format.Alpha8,           GL2.GL_ALPHA8,            GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8,       GL2.GL_LUMINANCE8,        GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            }
             format(formatToGL, Format.RGB8,             GL2.GL_RGB8,              GL.GL_RGB,             GL.GL_UNSIGNED_BYTE);
             format(formatToGL, Format.RGBA8,            GLExt.GL_RGBA8,           GL.GL_RGBA,            GL.GL_UNSIGNED_BYTE);
             format(formatToGL, Format.RGB565,           GL2.GL_RGB8,              GL.GL_RGB,             GL.GL_UNSIGNED_SHORT_5_6_5);
@@ -108,8 +110,10 @@ public final class GLImageFormats {
                 formatSrgb(formatToGL, Format.RGB565,           GLExt.GL_SRGB8_EXT,              GL.GL_RGB,             GL.GL_UNSIGNED_SHORT_5_6_5);
                 formatSrgb(formatToGL, Format.RGB5A1,           GLExt.GL_SRGB8_ALPHA8_EXT,       GL.GL_RGBA,            GL.GL_UNSIGNED_SHORT_5_5_5_1);
                 formatSrgb(formatToGL, Format.RGBA8,            GLExt.GL_SRGB8_ALPHA8_EXT,       GL.GL_RGBA,            GL.GL_UNSIGNED_BYTE);
-                formatSrgb(formatToGL, Format.Luminance8,       GLExt.GL_SLUMINANCE8_EXT,        GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
-                formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+                if (!caps.contains(Caps.CoreProfile)) {
+                    formatSrgb(formatToGL, Format.Luminance8,       GLExt.GL_SLUMINANCE8_EXT,        GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
+                    formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+                }
                 formatSrgb(formatToGL, Format.BGR8,             GLExt.GL_SRGB8_EXT,              GL2.GL_BGR,            GL.GL_UNSIGNED_BYTE);
                 formatSrgb(formatToGL, Format.ABGR8,            GLExt.GL_SRGB8_ALPHA8_EXT,       GL.GL_RGBA,            GL2.GL_UNSIGNED_INT_8_8_8_8);
                 formatSrgb(formatToGL, Format.ARGB8,            GLExt.GL_SRGB8_ALPHA8_EXT,       GL2.GL_BGRA,           GL2.GL_UNSIGNED_INT_8_8_8_8);
@@ -124,16 +128,20 @@ public final class GLImageFormats {
             }
         } else if (caps.contains(Caps.Rgba8)) {
             // A more limited form of 32-bit RGBA. Only GL_RGBA8 is available.
-            format(formatToGL, Format.Alpha8,           GLExt.GL_RGBA8, GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8,       GLExt.GL_RGBA8, GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            if (!caps.contains(Caps.CoreProfile)) {
+                format(formatToGL, Format.Alpha8,           GLExt.GL_RGBA8, GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8,       GLExt.GL_RGBA8, GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            }
             format(formatToGL, Format.RGB8,             GLExt.GL_RGBA8, GL.GL_RGB,             GL.GL_UNSIGNED_BYTE);
             format(formatToGL, Format.RGBA8,            GLExt.GL_RGBA8, GL.GL_RGBA,            GL.GL_UNSIGNED_BYTE);
         } else {
             // Actually, the internal format isn't used for OpenGL ES 2! This is the same as the above..
-            format(formatToGL, Format.Alpha8,           GL.GL_RGBA4,   GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8,       GL.GL_RGB565,  GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
-            format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4,   GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            if (!caps.contains(Caps.CoreProfile)) {
+                format(formatToGL, Format.Alpha8,           GL.GL_RGBA4,   GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8,       GL.GL_RGB565,  GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);
+                format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4,   GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+            }
             format(formatToGL, Format.RGB8,             GL.GL_RGB565,  GL.GL_RGB,             GL.GL_UNSIGNED_BYTE);
             format(formatToGL, Format.RGBA8,            GL.GL_RGBA4,   GL.GL_RGBA,            GL.GL_UNSIGNED_BYTE);
         }
@@ -145,9 +153,11 @@ public final class GLImageFormats {
         format(formatToGL, Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
         
         if (caps.contains(Caps.FloatTexture)) {
-            format(formatToGL, Format.Luminance16F,         GLExt.GL_LUMINANCE16F_ARB,       GL.GL_LUMINANCE,       GLExt.GL_HALF_FLOAT_ARB);
-            format(formatToGL, Format.Luminance32F,         GLExt.GL_LUMINANCE32F_ARB,       GL.GL_LUMINANCE,       GL.GL_FLOAT);
-            format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
+            if (!caps.contains(Caps.CoreProfile)) {
+                format(formatToGL, Format.Luminance16F,         GLExt.GL_LUMINANCE16F_ARB,       GL.GL_LUMINANCE,       GLExt.GL_HALF_FLOAT_ARB);
+                format(formatToGL, Format.Luminance32F,         GLExt.GL_LUMINANCE32F_ARB,       GL.GL_LUMINANCE,       GL.GL_FLOAT);
+                format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
+            }
             format(formatToGL, Format.RGB16F,               GLExt.GL_RGB16F_ARB,             GL.GL_RGB,             GLExt.GL_HALF_FLOAT_ARB);
             format(formatToGL, Format.RGB32F,               GLExt.GL_RGB32F_ARB,             GL.GL_RGB,             GL.GL_FLOAT);
             format(formatToGL, Format.RGBA16F,              GLExt.GL_RGBA16F_ARB,            GL.GL_RGBA,            GLExt.GL_HALF_FLOAT_ARB);

+ 75 - 85
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -112,13 +112,13 @@ public class GLRenderer implements Renderer {
     private final GLFbo glfbo;
     private final TextureUtil texUtil;
     
-    public GLRenderer(GL gl, GLFbo glfbo) {
+    public GLRenderer(GL gl, GLExt glext, GLFbo glfbo) {
         this.gl = gl;
         this.gl2 = gl instanceof GL2 ? (GL2)gl : null;
         this.gl3 = gl instanceof GL3 ? (GL3)gl : null;
         this.gl4 = gl instanceof GL4 ? (GL4)gl : null;
         this.glfbo = glfbo;
-        this.glext = glfbo instanceof GLExt ? (GLExt)glfbo : null;
+        this.glext = glext;
         this.texUtil = new TextureUtil(gl, gl2, glext, context);
     }
 
@@ -137,10 +137,19 @@ public class GLRenderer implements Renderer {
         return limits;
     }
 
-    private static HashSet<String> loadExtensions(String extensions) {
+    private HashSet<String> loadExtensions() {
         HashSet<String> extensionSet = new HashSet<String>(64);
-        for (String extension : extensions.split(" ")) {
-            extensionSet.add(extension);
+        if (gl3 != null) {
+            // If OpenGL3+ is available, use the non-deprecated way
+            // of getting supported extensions.
+            gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16);
+            int extensionCount = intBuf16.get(0);
+            for (int i = 0; i < extensionCount; i++) {
+                String extension = gl3.glGetString(GL.GL_EXTENSIONS, i);
+                extensionSet.add(extension);
+            }
+        } else {
+            extensionSet.addAll(Arrays.asList(gl.glGetString(GL.GL_EXTENSIONS).split(" ")));
         }
         return extensionSet;
     }
@@ -185,10 +194,12 @@ public class GLRenderer implements Renderer {
                         caps.add(Caps.OpenGL31);
                         if (oglVer >= 320) {
                             caps.add(Caps.OpenGL32);
-                        }if(oglVer>=330){
+                        }
+                        if (oglVer >= 330) {
                             caps.add(Caps.OpenGL33);
                             caps.add(Caps.GeometryShader);
-                        }if(oglVer>=400){
+                        }
+                        if (oglVer >= 400) {
                             caps.add(Caps.OpenGL40);
                             caps.add(Caps.TesselationShader);
                         }
@@ -243,7 +254,7 @@ public class GLRenderer implements Renderer {
     }
     
     private void loadCapabilitiesCommon() {
-        extensions = loadExtensions(gl.glGetString(GL.GL_EXTENSIONS));
+        extensions = loadExtensions();
         
         limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
         if (limits.get(Limits.VertexTextureUnits) > 0) {
@@ -251,7 +262,7 @@ public class GLRenderer implements Renderer {
         }
 
         limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
-
+        
 //        gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
 //        vertexUniforms = intBuf16.get(0);
 //        logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
@@ -279,7 +290,7 @@ public class GLRenderer implements Renderer {
         
         // == texture format extensions ==
         
-        boolean hasFloatTexture = false;
+        boolean hasFloatTexture;
 
         hasFloatTexture = hasExtension("GL_OES_texture_half_float") &&
                           hasExtension("GL_OES_texture_float");
@@ -375,11 +386,11 @@ public class GLRenderer implements Renderer {
             caps.add(Caps.TextureFilterAnisotropic);
         }
 
-        if (hasExtension("GL_EXT_framebuffer_object")) {
+        if (hasExtension("GL_EXT_framebuffer_object") || gl3 != null) {
             caps.add(Caps.FrameBuffer);
             
-            limits.put(Limits.RenderBufferSize, getInteger(GLExt.GL_MAX_RENDERBUFFER_SIZE_EXT));
-            limits.put(Limits.FrameBufferAttachments, getInteger(GLExt.GL_MAX_COLOR_ATTACHMENTS_EXT));
+            limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
+            limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
             
             if (hasExtension("GL_EXT_framebuffer_blit")) {
                 caps.add(Caps.FrameBufferBlit);
@@ -434,21 +445,30 @@ public class GLRenderer implements Renderer {
             caps.add(Caps.SeamlessCubemap);
         }
         
-//        if (hasExtension("GL_ARB_get_program_binary")) {
-//            int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
-//        }
+        if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) {
+            caps.add(Caps.CoreProfile);
+        }
+        
+        if (hasExtension("GL_ARB_get_program_binary")) {
+            int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
+            if (binaryFormats > 0) {
+                caps.add(Caps.BinaryShader);
+            }
+        }
         
         // Print context information
         logger.log(Level.INFO, "OpenGL Renderer Information\n" +
                                " * Vendor: {0}\n" +
                                " * Renderer: {1}\n" +
                                " * OpenGL Version: {2}\n" +
-                               " * GLSL Version: {3}",
+                               " * GLSL Version: {3}\n" +
+                               " * Profile: {4}",
                                new Object[]{ 
                                    gl.glGetString(GL.GL_VENDOR), 
                                    gl.glGetString(GL.GL_RENDERER),
                                    gl.glGetString(GL.GL_VERSION),
-                                   gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)
+                                   gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
+                                   caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility"
                                });
         
         // Print capabilities (if fine logging is enabled)
@@ -491,6 +511,20 @@ public class GLRenderer implements Renderer {
         
         // Initialize default state..
         gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
+        
+        if (caps.contains(Caps.CoreProfile)) {
+            // Core Profile requires VAO to be bound.
+            gl3.glGenVertexArrays(intBuf16);
+            int vaoId = intBuf16.get(0);
+            gl3.glBindVertexArray(vaoId);
+        } 
+        if (gl2 != null) {
+            gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
+            if (!caps.contains(Caps.CoreProfile)) {
+                gl2.glEnable(GL2.GL_POINT_SPRITE);
+                context.pointSprite = true;
+            }
+        }
     }
 
     public void invalidateState() {
@@ -610,31 +644,6 @@ public class GLRenderer implements Renderer {
             context.colorWriteEnabled = false;
         }
 
-        if (gl2 != null) {
-            if (state.isPointSprite() && !context.pointSprite) {
-                // Only enable/disable sprite
-                if (context.boundTextures[0] != null) {
-                    if (context.boundTextureUnit != 0) {
-                        gl.glActiveTexture(GL.GL_TEXTURE0);
-                        context.boundTextureUnit = 0;
-                    }
-                    gl2.glEnable(GL2.GL_POINT_SPRITE);
-                    gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
-                }
-                context.pointSprite = true;
-            } else if (!state.isPointSprite() && context.pointSprite) {
-                if (context.boundTextures[0] != null) {
-                    if (context.boundTextureUnit != 0) {
-                        gl.glActiveTexture(GL.GL_TEXTURE0);
-                        context.boundTextureUnit = 0;
-                    }
-                    gl2.glDisable(GL2.GL_POINT_SPRITE);
-                    gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
-                    context.pointSprite = false;
-                }
-            }
-        }
-
         if (state.isPolyOffset()) {
             if (!context.polyOffsetEnabled) {
                 gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
@@ -1288,24 +1297,24 @@ public class GLRenderer implements Renderer {
             }
 
             if (src == null) {
-                glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, 0);
+                glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, 0);
                 srcX0 = vpX;
                 srcY0 = vpY;
                 srcX1 = vpX + vpW;
                 srcY1 = vpY + vpH;
             } else {
-                glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, src.getId());
+                glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, src.getId());
                 srcX1 = src.getWidth();
                 srcY1 = src.getHeight();
             }
             if (dst == null) {
-                glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, 0);
+                glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, 0);
                 dstX0 = vpX;
                 dstY0 = vpY;
                 dstX1 = vpX + vpW;
                 dstY1 = vpY + vpH;
             } else {
-                glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
+                glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
                 dstX1 = dst.getWidth();
                 dstY1 = dst.getHeight();
             }
@@ -1313,12 +1322,12 @@ public class GLRenderer implements Renderer {
             if (copyDepth) {
                 mask |= GL.GL_DEPTH_BUFFER_BIT;
             }
-            glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
+            glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
                     dstX0, dstY0, dstX1, dstY1, mask,
                     GL.GL_NEAREST);
 
 
-            glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, prevFBO);
+            glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, prevFBO);
         } else {
             throw new RendererException("Framebuffer blitting not supported by the video hardware");
         }
@@ -1364,7 +1373,7 @@ public class GLRenderer implements Renderer {
         }
 
         if (context.boundRB != id) {
-            glfbo.glBindRenderbufferEXT(GLExt.GL_RENDERBUFFER_EXT, id);
+            glfbo.glBindRenderbufferEXT(GLFbo.GL_RENDERBUFFER_EXT, id);
             context.boundRB = id;
         }
 
@@ -1382,13 +1391,13 @@ public class GLRenderer implements Renderer {
             if (maxSamples < samples) {
                 samples = maxSamples;
             }
-            glext.glRenderbufferStorageMultisampleEXT(GLExt.GL_RENDERBUFFER_EXT,
+            glfbo.glRenderbufferStorageMultisampleEXT(GLFbo.GL_RENDERBUFFER_EXT,
                     samples,
                     glFmt.internalFormat,
                     fb.getWidth(),
                     fb.getHeight());
         } else {
-            glfbo.glRenderbufferStorageEXT(GLExt.GL_RENDERBUFFER_EXT,
+            glfbo.glRenderbufferStorageEXT(GLFbo.GL_RENDERBUFFER_EXT,
                     glFmt.internalFormat,
                     fb.getWidth(),
                     fb.getHeight());
@@ -1398,7 +1407,7 @@ public class GLRenderer implements Renderer {
     private int convertAttachmentSlot(int attachmentSlot) {
         // can also add support for stencil here
         if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
-            return GLExt.GL_DEPTH_ATTACHMENT_EXT;
+            return GLFbo.GL_DEPTH_ATTACHMENT_EXT;
         } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
             // NOTE: Using depth stencil format requires GL3, this is already
             // checked via render caps.
@@ -1407,7 +1416,7 @@ public class GLRenderer implements Renderer {
             throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
         }
         
-        return GLExt.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
+        return GLFbo.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
     }
 
     public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
@@ -1425,7 +1434,7 @@ public class GLRenderer implements Renderer {
             setupTextureParams(tex);
         }
 
-        glfbo.glFramebufferTexture2DEXT(GLExt.GL_FRAMEBUFFER_EXT,
+        glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
                 convertAttachmentSlot(rb.getSlot()),
                 convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
                 image.getId(),
@@ -1443,9 +1452,9 @@ public class GLRenderer implements Renderer {
             updateRenderTexture(fb, rb);
         }
         if (needAttach) {
-            glfbo.glFramebufferRenderbufferEXT(GLExt.GL_FRAMEBUFFER_EXT,
+            glfbo.glFramebufferRenderbufferEXT(GLFbo.GL_FRAMEBUFFER_EXT,
                     convertAttachmentSlot(rb.getSlot()),
-                    GLExt.GL_RENDERBUFFER_EXT,
+                    GLFbo.GL_RENDERBUFFER_EXT,
                     rb.getId());
         }
     }
@@ -1463,7 +1472,7 @@ public class GLRenderer implements Renderer {
         }
 
         if (context.boundFBO != id) {
-            glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, id);
+            glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, id);
             // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
             context.boundDrawBuf = 0;
             context.boundFBO = id;
@@ -1543,7 +1552,7 @@ public class GLRenderer implements Renderer {
         if (fb == null) {
             // unbind any fbos
             if (context.boundFBO != 0) {
-                glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
+                glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
                 statistics.onFrameBufferUse(null, true);
 
                 context.boundFBO = 0;
@@ -1575,7 +1584,7 @@ public class GLRenderer implements Renderer {
             setViewPort(0, 0, fb.getWidth(), fb.getHeight());
             
             if (context.boundFBO != fb.getId()) {
-                glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, fb.getId());
+                glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
                 statistics.onFrameBufferUse(fb, true);
 
                 context.boundFBO = fb.getId();
@@ -1615,7 +1624,7 @@ public class GLRenderer implements Renderer {
                     if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
                         intBuf16.clear();
                         for (int i = 0; i < fb.getNumColorBuffers(); i++) {
-                            intBuf16.put(GLExt.GL_COLOR_ATTACHMENT0_EXT + i);
+                            intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
                         }
 
                         intBuf16.flip();
@@ -1627,7 +1636,7 @@ public class GLRenderer implements Renderer {
                     // select this draw buffer
                     if (gl2 != null) {
                         if (context.boundDrawBuf != rb.getSlot()) {
-                            gl2.glDrawBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+                            gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
                             context.boundDrawBuf = rb.getSlot();
                         }
                     }
@@ -1656,7 +1665,7 @@ public class GLRenderer implements Renderer {
             setFrameBuffer(fb);
             if (gl2 != null) {
                 if (context.boundReadBuf != rb.getSlot()) {
-                    gl2.glReadBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+                    gl2.glReadBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
                     context.boundReadBuf = rb.getSlot();
                 }
             }
@@ -1680,7 +1689,7 @@ public class GLRenderer implements Renderer {
     public void deleteFrameBuffer(FrameBuffer fb) {
         if (fb.getId() != -1) {
             if (context.boundFBO == fb.getId()) {
-                glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
+                glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
                 context.boundFBO = 0;
             }
 
@@ -2618,32 +2627,13 @@ public class GLRenderer implements Renderer {
             return;
         }
 
-        if (context.pointSprite && mesh.getMode() != Mode.Points) {
-            // XXX: Hack, disable point sprite mode if mesh not in point mode
-            if (context.boundTextures[0] != null) {
-                if (context.boundTextureUnit != 0) {
-                    gl.glActiveTexture(GL.GL_TEXTURE0);
-                    context.boundTextureUnit = 0;
-                }
-                if (gl2 != null) {
-                    gl2.glDisable(GL2.GL_POINT_SPRITE);
-                    gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
-                }
-                context.pointSprite = false;
-            }
-        }
 
-        if (gl2 != null) {
-            if (context.pointSize != mesh.getPointSize()) {
-                gl2.glPointSize(mesh.getPointSize());
-                context.pointSize = mesh.getPointSize();
-            }
-        }
         if (context.lineWidth != mesh.getLineWidth()) {
             gl.glLineWidth(mesh.getLineWidth());
             context.lineWidth = mesh.getLineWidth();
         }
-        if(gl4!=null && mesh.getMode().equals(Mode.Patch)){
+        
+        if (gl4 != null && mesh.getMode().equals(Mode.Patch)) {
             gl4.glPatchParameter(mesh.getPatchVertexCount());
         }
         statistics.onMeshDrawn(mesh, lod, count);

+ 3 - 4
jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java

@@ -64,6 +64,7 @@ public final class GLTracer implements InvocationHandler {
         noEnumArgs("glScissor", 0, 1, 2, 3);
         noEnumArgs("glClear", 0);
         noEnumArgs("glGetInteger", 1);
+        noEnumArgs("glGetString", 1);
         
         noEnumArgs("glBindTexture", 1);
         noEnumArgs("glPixelStorei", 1);
@@ -95,8 +96,6 @@ public final class GLTracer implements InvocationHandler {
         noEnumArgs("glFramebufferTexture2DEXT", 3, 4);
         noEnumArgs("glBlitFramebufferEXT", 0, 1, 2, 3, 4, 5, 6, 7, 8);
         
-        
-        
         noEnumArgs("glCreateProgram", -1);
         noEnumArgs("glCreateShader", -1);
         noEnumArgs("glShaderSource", 0);
@@ -155,7 +154,7 @@ public final class GLTracer implements InvocationHandler {
      * @return A tracer that implements the given interface
      */
     public static Object createGlesTracer(Object glInterface, Class<?> glInterfaceClass) {
-        IntMap<String> constMap = generateConstantMap(GL.class, GLExt.class);
+        IntMap<String> constMap = generateConstantMap(GL.class, GLFbo.class, GLExt.class);
         return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
                                       new Class<?>[] { glInterfaceClass }, 
                                       new GLTracer(glInterface, constMap));
@@ -169,7 +168,7 @@ public final class GLTracer implements InvocationHandler {
      * @return A tracer that implements the given interface
      */
     public static Object createDesktopGlTracer(Object glInterface, Class<?> ... glInterfaceClasses) {
-        IntMap<String> constMap = generateConstantMap(GL2.class, GLExt.class);
+        IntMap<String> constMap = generateConstantMap(GL2.class, GL3.class, GL4.class, GLFbo.class, GLExt.class);
         return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
                                       glInterfaceClasses, 
                                       new GLTracer(glInterface, constMap));

+ 3 - 2
jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java

@@ -40,6 +40,7 @@ import com.jme3.renderer.ios.IosGL;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GLDebugES;
 import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
 import com.jme3.renderer.opengl.GLRenderer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
@@ -158,11 +159,11 @@ public class IGLESContext implements JmeContext {
         GLExt glext = (GLExt) gl;
 
 //        if (settings.getBoolean("GraphicsDebug")) {
-            gl = new GLDebugES(gl, glext);
+            gl = new GLDebugES(gl, glext, (GLFbo) glext);
             glext = (GLExt) gl;
 //        }
 
-        renderer = new GLRenderer(gl, glext);
+        renderer = new GLRenderer(gl, glext, (GLFbo) glext);
         renderer.initialize();
         
         input = new IosInputHandler();

+ 11 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -13,7 +13,7 @@ import java.nio.ShortBuffer;
 import com.jme3.renderer.opengl.GL4;
 import org.lwjgl.opengl.*;
 
-public class LwjglGL implements GL, GL2, GL3,GL4 {
+public class LwjglGL implements GL, GL2, GL3, GL4 {
     
     private static void checkLimit(Buffer buffer) {
         if (buffer == null) {
@@ -237,6 +237,10 @@ public class LwjglGL implements GL, GL2, GL3,GL4 {
     public String glGetString(int param1) {
         return GL11.glGetString(param1);
     }
+    
+    public String glGetString(int param1, int param2) {
+        return GL30.glGetStringi(param1, param2);
+    }
 
     public boolean glIsEnabled(int param1) {
         return GL11.glIsEnabled(param1);
@@ -444,4 +448,10 @@ public class LwjglGL implements GL, GL2, GL3,GL4 {
     public void glPatchParameter(int count) {
         GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES,count);
     }
+    
+    @Override
+    public void glDeleteVertexArrays(IntBuffer arrays) {
+        checkLimit(arrays);
+        ARBVertexArrayObject.glDeleteVertexArrays(arrays);
+    }
 }

+ 8 - 60
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java

@@ -7,12 +7,8 @@ import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 import org.lwjgl.opengl.ARBDrawInstanced;
 import org.lwjgl.opengl.ARBInstancedArrays;
-import org.lwjgl.opengl.ARBPixelBufferObject;
 import org.lwjgl.opengl.ARBSync;
 import org.lwjgl.opengl.ARBTextureMultisample;
-import org.lwjgl.opengl.EXTFramebufferBlit;
-import org.lwjgl.opengl.EXTFramebufferMultisample;
-import org.lwjgl.opengl.EXTFramebufferObject;
 import org.lwjgl.opengl.GL15;
 import org.lwjgl.opengl.GL20;
 import org.lwjgl.opengl.GLSync;
@@ -30,99 +26,51 @@ public class LwjglGLExt implements GLExt {
             throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
         }
     }
-    
-    public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
-        EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
-    }
 
+    @Override
     public void glBufferData(int target, IntBuffer data, int usage) {
         checkLimit(data);
         GL15.glBufferData(target, data, usage);
     }
 
+    @Override
     public void glBufferSubData(int target, long offset, IntBuffer data) {
         checkLimit(data);
         GL15.glBufferSubData(target, offset, data);
     }
 
+    @Override
     public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
         ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
     }
 
+    @Override
     public void glDrawBuffers(IntBuffer bufs) {
         checkLimit(bufs);
         GL20.glDrawBuffers(bufs);
     }
 
+    @Override
     public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
         ARBDrawInstanced.glDrawElementsInstancedARB(mode, indices_count, type, indices_buffer_offset, primcount);
     }
 
+    @Override
     public void glGetMultisample(int pname, int index, FloatBuffer val) {
         checkLimit(val);
         ARBTextureMultisample.glGetMultisample(pname, index, val);
     }
 
-    public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
-        EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
-    }
-
+    @Override
     public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
         ARBTextureMultisample.glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
     }
 
+    @Override
     public void glVertexAttribDivisorARB(int index, int divisor) {
         ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
     }
 
-    public void glBindFramebufferEXT(int param1, int param2) {
-        EXTFramebufferObject.glBindFramebufferEXT(param1, param2);
-    }
-
-    public void glBindRenderbufferEXT(int param1, int param2) {
-        EXTFramebufferObject.glBindRenderbufferEXT(param1, param2);
-    }
-
-    public int glCheckFramebufferStatusEXT(int param1) {
-        return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1);
-    }
-
-    public void glDeleteFramebuffersEXT(IntBuffer param1) {
-        checkLimit(param1);
-        EXTFramebufferObject.glDeleteFramebuffersEXT(param1);
-    }
-
-    public void glDeleteRenderbuffersEXT(IntBuffer param1) {
-        checkLimit(param1);
-        EXTFramebufferObject.glDeleteRenderbuffersEXT(param1);
-    }
-
-    public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
-        EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4);
-    }
-
-    public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
-        EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5);
-    }
-
-    public void glGenFramebuffersEXT(IntBuffer param1) {
-        checkLimit(param1);
-        EXTFramebufferObject.glGenFramebuffersEXT(param1);
-    }
-
-    public void glGenRenderbuffersEXT(IntBuffer param1) {
-        checkLimit(param1);
-        EXTFramebufferObject.glGenRenderbuffersEXT(param1);
-    }
-
-    public void glGenerateMipmapEXT(int param1) {
-        EXTFramebufferObject.glGenerateMipmapEXT(param1);
-    }
-
-    public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
-        EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4);
-    }
-    
     @Override
     public Object glFenceSync(int condition, int flags) {
         return ARBSync.glFenceSync(condition, flags);

+ 98 - 0
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java

@@ -0,0 +1,98 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import org.lwjgl.opengl.EXTFramebufferBlit;
+import org.lwjgl.opengl.EXTFramebufferMultisample;
+import org.lwjgl.opengl.EXTFramebufferObject;
+
+/**
+ * Implements GLFbo via GL_EXT_framebuffer_object.
+ * 
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboEXT implements GLFbo {
+
+    private static void checkLimit(Buffer buffer) {
+        if (buffer == null) {
+            return;
+        }
+        if (buffer.limit() == 0) {
+            throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+        }
+        if (buffer.remaining() == 0) {
+            throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+        }
+    }
+    
+    @Override
+    public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+        EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+    }
+    
+    @Override
+    public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+        EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+    }
+    
+    @Override
+    public void glBindFramebufferEXT(int param1, int param2) {
+        EXTFramebufferObject.glBindFramebufferEXT(param1, param2);
+    }
+    
+    @Override
+    public void glBindRenderbufferEXT(int param1, int param2) {
+        EXTFramebufferObject.glBindRenderbufferEXT(param1, param2);
+    }
+    
+    @Override
+    public int glCheckFramebufferStatusEXT(int param1) {
+        return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1);
+    }
+    
+    @Override
+    public void glDeleteFramebuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        EXTFramebufferObject.glDeleteFramebuffersEXT(param1);
+    }
+    
+    @Override
+    public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        EXTFramebufferObject.glDeleteRenderbuffersEXT(param1);
+    }
+    
+    @Override
+    public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+        EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4);
+    }
+    
+    @Override
+    public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+        EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5);
+    }
+    
+    @Override
+    public void glGenFramebuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        EXTFramebufferObject.glGenFramebuffersEXT(param1);
+    }
+    
+    @Override
+    public void glGenRenderbuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        EXTFramebufferObject.glGenRenderbuffersEXT(param1);
+    }
+    
+    @Override
+    public void glGenerateMipmapEXT(int param1) {
+        EXTFramebufferObject.glGenerateMipmapEXT(param1);
+    }
+    
+    @Override
+    public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+        EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4);
+    }
+}

+ 96 - 0
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java

@@ -0,0 +1,96 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import org.lwjgl.opengl.GL30;
+
+/**
+ * Implements GLFbo via OpenGL3+.
+ * 
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboGL3 implements GLFbo {
+
+    private static void checkLimit(Buffer buffer) {
+        if (buffer == null) {
+            return;
+        }
+        if (buffer.limit() == 0) {
+            throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+        }
+        if (buffer.remaining() == 0) {
+            throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+        }
+    }
+    
+    @Override
+    public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+        GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+    }
+    
+    @Override
+    public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+        GL30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
+    }
+    
+    @Override
+    public void glBindFramebufferEXT(int param1, int param2) {
+        GL30.glBindFramebuffer(param1, param2);
+    }
+    
+    @Override
+    public void glBindRenderbufferEXT(int param1, int param2) {
+        GL30.glBindRenderbuffer(param1, param2);
+    }
+    
+    @Override
+    public int glCheckFramebufferStatusEXT(int param1) {
+        return GL30.glCheckFramebufferStatus(param1);
+    }
+    
+    @Override
+    public void glDeleteFramebuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        GL30.glDeleteFramebuffers(param1);
+    }
+    
+    @Override
+    public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        GL30.glDeleteRenderbuffers(param1);
+    }
+    
+    @Override
+    public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+        GL30.glFramebufferRenderbuffer(param1, param2, param3, param4);
+    }
+    
+    @Override
+    public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+        GL30.glFramebufferTexture2D(param1, param2, param3, param4, param5);
+    }
+    
+    @Override
+    public void glGenFramebuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        GL30.glGenFramebuffers(param1);
+    }
+    
+    @Override
+    public void glGenRenderbuffersEXT(IntBuffer param1) {
+        checkLimit(param1);
+        GL30.glGenRenderbuffers(param1);
+    }
+    
+    @Override
+    public void glGenerateMipmapEXT(int param1) {
+        GL30.glGenerateMipmap(param1);
+    }
+    
+    @Override
+    public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+        GL30.glRenderbufferStorage(param1, param2, param3, param4);
+    }
+}

+ 20 - 6
jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@@ -39,13 +39,18 @@ import com.jme3.renderer.Renderer;
 import com.jme3.renderer.RendererException;
 import com.jme3.renderer.lwjgl.LwjglGL;
 import com.jme3.renderer.lwjgl.LwjglGLExt;
+import com.jme3.renderer.lwjgl.LwjglGLFboEXT;
+import com.jme3.renderer.lwjgl.LwjglGLFboGL3;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GL2;
 import com.jme3.renderer.opengl.GL3;
+import com.jme3.renderer.opengl.GL4;
 import com.jme3.renderer.opengl.GLDebugDesktop;
 import com.jme3.renderer.opengl.GLExt;
 import com.jme3.renderer.opengl.GLFbo;
 import com.jme3.renderer.opengl.GLRenderer;
+import com.jme3.renderer.opengl.GLTiming;
+import com.jme3.renderer.opengl.GLTimingState;
 import com.jme3.renderer.opengl.GLTracer;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
@@ -203,21 +208,30 @@ public abstract class LwjglContext implements JmeContext {
         }
         
         if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
-                || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
+         || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
             GL gl = new LwjglGL();
-            GLFbo glfbo = new LwjglGLExt();
+            GLExt glext = new LwjglGLExt();
+            GLFbo glfbo;
+            
+            if (GLContext.getCapabilities().OpenGL30) {
+                glfbo = new LwjglGLFboGL3();
+            } else {
+                glfbo = new LwjglGLFboEXT();
+            }
             
             if (settings.getBoolean("GraphicsDebug")) {
-                gl    = new GLDebugDesktop(gl, glfbo);
+                gl    = new GLDebugDesktop(gl, glext, glfbo);
+                glext = (GLExt) gl;
                 glfbo = (GLFbo) gl;
             }
             
             if (settings.getBoolean("GraphicsTrace")) {
-                gl    = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class);
-                glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLExt.class);
+                gl    = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class);
+                glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class);
+                glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class);
             }
             
-            renderer = new GLRenderer(gl, glfbo);
+            renderer = new GLRenderer(gl, glext, glfbo);
             renderer.initialize();
         } else {
             throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());