Przeglądaj źródła

Add GL debug capabilities (#1790)

* Add GL debug capabilities

* Fix: check for null names
Riccardo Balbo 3 lat temu
rodzic
commit
488d9fa1e9

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

@@ -438,7 +438,12 @@ public enum Caps {
     /**
      * Supports unpack row length (stride).
      */
-    UnpackRowLength
+    UnpackRowLength,
+
+    /**
+     * Supports debugging capabilities
+     */
+    GLDebug
     ;
 
     /**

+ 10 - 1
jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

@@ -622,6 +622,7 @@ public class RenderManager {
      * @see com.jme3.material.Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
      */
     public void renderGeometry(Geometry geom) {
+        this.renderer.pushDebugGroup(geom.getName());
         if (geom.isIgnoreTransform()) {
             setWorldMatrix(Matrix4f.IDENTITY);
         } else {
@@ -680,6 +681,7 @@ public class RenderManager {
         } else {
             material.render(geom, lightList, this);
         }
+        this.renderer.popDebugGroup();
     }
 
     /**
@@ -955,7 +957,9 @@ public class RenderManager {
         if (prof != null) {
             prof.vpStep(VpStep.RenderBucket, vp, Bucket.Opaque);
         }
+        this.renderer.pushDebugGroup(Bucket.Opaque.name());
         rq.renderQueue(Bucket.Opaque, this, cam, flush);
+        this.renderer.popDebugGroup();
 
         // render the sky, with depth range set to the farthest
         if (!rq.isQueueEmpty(Bucket.Sky)) {
@@ -963,7 +967,9 @@ public class RenderManager {
                 prof.vpStep(VpStep.RenderBucket, vp, Bucket.Sky);
             }
             renderer.setDepthRange(1, 1);
+            this.renderer.pushDebugGroup(Bucket.Sky.name());
             rq.renderQueue(Bucket.Sky, this, cam, flush);
+            this.renderer.popDebugGroup();
             depthRangeChanged = true;
         }
 
@@ -979,8 +985,9 @@ public class RenderManager {
                 renderer.setDepthRange(0, 1);
                 depthRangeChanged = false;
             }
-
+            this.renderer.pushDebugGroup(Bucket.Transparent.name());
             rq.renderQueue(Bucket.Transparent, this, cam, flush);
+            this.renderer.popDebugGroup();
         }
 
         if (!rq.isQueueEmpty(Bucket.Gui)) {
@@ -989,7 +996,9 @@ public class RenderManager {
             }
             renderer.setDepthRange(0, 0);
             setCamera(cam, true);
+            this.renderer.pushDebugGroup(Bucket.Gui.name());
             rq.renderQueue(Bucket.Gui, this, cam, flush);
+            this.renderer.popDebugGroup();
             setCamera(cam, false);
             depthRangeChanged = true;
         }

+ 9 - 0
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -519,4 +519,13 @@ public interface Renderer {
      * @return true for conversion, false for no conversion
      */
     public boolean isMainFrameBufferSrgb();
+
+    public default void popDebugGroup() {
+     
+    }
+
+    public default void pushDebugGroup(String name) {
+ 
+    }
+    
 }

+ 5 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java

@@ -127,6 +127,11 @@ public interface GL3 extends GL2 {
      */
     public static final int GL_TRANSFORM_FEEDBACK_BUFFER = 0x8C8E;
 
+
+    public static final int GL_FRAMEBUFFER = 0x8D40;
+    public static final int GL_READ_FRAMEBUFFER = 0x8CA8;
+    public static final int GL_DRAW_FRAMEBUFFER = 0x8CA9;
+
     /**
      * <p><a target="_blank" href="http://docs.gl/gl4/glBindFragDataLocation">Reference Page</a></p>
      * <p>

+ 24 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java

@@ -108,6 +108,21 @@ public interface GLExt {
     public static final int GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C;
     public static final int GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D;
 
+    public static final int GL_DEBUG_SOURCE_API = 0x8246;
+    public static final int GL_DEBUG_SOURCE_WINDOW_SYSTEM = 0x8247;
+    public static final int GL_DEBUG_SOURCE_SHADER_COMPILER = 0x8248;
+    public static final int GL_DEBUG_SOURCE_THIRD_PARTY = 0x8249;
+    public static final int GL_DEBUG_SOURCE_APPLICATION = 0x824A;
+    public static final int GL_DEBUG_SOURCE_OTHER = 0x824B;
+
+    public static final int GL_BUFFER = 0x82E0;
+    public static final int GL_SHADER = 0x82E1;
+    public static final int GL_PROGRAM = 0x82E2;
+    public static final int GL_QUERY = 0x82E3;
+    public static final int GL_PROGRAM_PIPELINE = 0x82E4;
+    public static final int GL_SAMPLER = 0x82E6;
+    public static final int GL_DISPLAY_LIST = 0x82E7;
+
     /**
      * <p><a target="_blank" href="http://docs.gl/gl4/glBufferData">Reference Page</a></p>
      *
@@ -239,4 +254,13 @@ public interface GLExt {
      * @param divisor the divisor value.
      */
     public void glVertexAttribDivisorARB(int index, int divisor);
+
+    public default void glPushDebugGroup(int source, int id, String message) {
+    }
+
+    public default void glPopDebugGroup() {
+    }
+
+    public default void glObjectLabel(int identifier, int id, String label){
+    }
 }

+ 42 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -109,6 +109,9 @@ public final class GLRenderer implements Renderer {
     private final GLExt glext;
     private final GLFbo glfbo;
     private final TextureUtil texUtil;
+    private boolean debug = false;
+    private int debugGroupId = 0;
+
 
     public GLRenderer(GL gl, GLExt glext, GLFbo glfbo) {
         this.gl = gl;
@@ -128,6 +131,29 @@ public final class GLRenderer implements Renderer {
         generateMipmapsForFramebuffers = v;
     }
 
+    public void setDebugEnabled(boolean v) {
+        debug = v;
+    }
+
+    @Override
+    public void popDebugGroup() {
+        if (debug && caps.contains(Caps.GLDebug)) {
+            glext.glPopDebugGroup();
+            debugGroupId--;
+        }
+    }
+
+    @Override
+    public void pushDebugGroup(String name) {
+        if (debug && caps.contains(Caps.GLDebug)) {
+            if (name == null) name = "Group " + debugGroupId;
+            glext.glPushDebugGroup(GLExt.GL_DEBUG_SOURCE_APPLICATION, debugGroupId, name);
+            debugGroupId++;
+        }
+    }
+
+    
+
     @Override
     public Statistics getStatistics() {
         return statistics;
@@ -574,6 +600,10 @@ public final class GLRenderer implements Renderer {
             caps.add(Caps.UnpackRowLength);
         }
 
+        if (caps.contains(Caps.OpenGL43) || hasExtension("GL_KHR_debug")) {
+            caps.add(Caps.GLDebug);
+        }
+
         // Print context information
         logger.log(Level.INFO, "OpenGL Renderer Information\n" +
                         " * Vendor: {0}\n" +
@@ -1431,6 +1461,9 @@ public final class GLRenderer implements Renderer {
             }
 
             source.setId(id);
+            if (debug && caps.contains(Caps.GLDebug)) {
+                if(source.getName() != null) glext.glObjectLabel(GLExt.GL_SHADER, id, source.getName());
+            }
         } else {
             throw new RendererException("Cannot recompile shader source");
         }
@@ -2103,6 +2136,9 @@ public final class GLRenderer implements Renderer {
             assert context.boundFBO == fb.getId();
 
             context.boundFB = fb;
+            if (debug && caps.contains(Caps.GLDebug)) {
+                 if (fb.getName() != null) glext.glObjectLabel(GL3.GL_FRAMEBUFFER, fb.getId(), fb.getName());
+            }
         }
     }
 
@@ -2626,6 +2662,9 @@ public final class GLRenderer implements Renderer {
         assert texId != -1;
 
         setupTextureParams(unit, tex);
+        if (debug && caps.contains(Caps.GLDebug)) {
+            if (tex.getName() != null) glext.glObjectLabel(GL.GL_TEXTURE, tex.getImage().getId(), tex.getName());
+        }
     }
 
 
@@ -2995,6 +3034,9 @@ public final class GLRenderer implements Renderer {
                 attribs[slot] = vb.getWeakRef();
             }
         }
+        if (debug && caps.contains(Caps.GLDebug)) {
+            if (vb.getName() != null) glext.glObjectLabel(GLExt.GL_BUFFER, vb.getId(), vb.getName());
+        }
     }
 
     public void setVertexAttrib(VertexBuffer vb) {

+ 12 - 0
jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java

@@ -332,6 +332,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
     protected boolean normalized = false;
     protected int instanceSpan = 0;
     protected transient boolean dataSizeChanged = false;
+    protected String name;
 
     /**
      * Creates an empty, uninitialized buffer.
@@ -1189,4 +1190,15 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
                 throw new IOException("Unsupported import buffer format: " + format);
         }
     }
+
+    public String getName() {
+        if (name == null) {
+            name = getClass().getSimpleName() + "(" + getBufferType().name() + ")";
+        }
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
 }

+ 5 - 1
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -1299,7 +1299,9 @@ public final class AppSettings extends HashMap<String, Object> {
      * Determine if the renderer will be run in Graphics Debug mode, which means every openGL call is checked and
      * if it returns an error code, throw a {@link com.jme3.renderer.RendererException}.<br>
      * Without this, many openGL calls might fail without notice, so turning it on is recommended for development.
-     *
+     * Graphics Debug mode will also label native objects and group calls on supported renderers. Compatible
+     * graphics debuggers will be able to use this data to show a better outlook of your application
+     * 
      * @return whether the context will be run in Graphics Debug Mode or not
      * @see #setGraphicsDebug(boolean)
      */
@@ -1311,6 +1313,8 @@ public final class AppSettings extends HashMap<String, Object> {
      * Set whether the renderer will be run in Graphics Debug mode, which means every openGL call is checked and
      * if it returns an error code, throw a {@link com.jme3.renderer.RendererException}.<br>
      * Without this, many openGL calls might fail without notice, so turning it on is recommended for development.
+     * Graphics Debug mode will also label native objects and group calls on supported renderers. Compatible
+     * graphics debuggers will be able to use this data to show a better outlook of your application
      *
      * @param debug whether the context will be run in Graphics Debug Mode or not
      * @see #isGraphicsDebug()

+ 8 - 0
jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java

@@ -84,6 +84,7 @@ public class FrameBuffer extends NativeObject {
     private RenderBuffer depthBuf = null;
     private int colorBufIndex = 0;
     private boolean srgb;
+    private String name;
     private Boolean mipMapsGenerationHint = null;
 
     /**
@@ -844,6 +845,13 @@ public class FrameBuffer extends NativeObject {
         return srgb;
     }
 
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
 
     /**
      * Hints the renderer to generate mipmaps for this framebuffer if necessary

+ 16 - 0
jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java

@@ -102,4 +102,20 @@ public class LwjglGLExt extends LwjglRender implements GLExt {
     public void glDeleteSync(final Object sync) {
         ARBSync.glDeleteSync((Long) sync);
     }
+
+    @Override
+    public void glPushDebugGroup(int source, int id, String message) {
+        KHRDebug.glPushDebugGroup(source, id, message);
+    }
+
+    @Override
+    public void glPopDebugGroup() {
+        KHRDebug.glPopDebugGroup();
+    }
+
+    @Override
+    public void glObjectLabel(int identifier, int id, String label) {
+        assert label != null;
+        KHRDebug.glObjectLabel(identifier, id, label);
+    }
 }

+ 1 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@@ -237,6 +237,7 @@ public abstract class LwjglContext implements JmeContext {
             }
 
             this.renderer = new GLRenderer(gl, glext, glfbo);
+            if (this.settings.isGraphicsDebug()) ((GLRenderer)this.renderer).setDebugEnabled(true);
         }
         this.renderer.initialize();