diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java index 89ecf4e17f..961d78ae6f 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java +++ b/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 ; /** diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index fa97f38626..a1b453a096 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -622,6 +622,7 @@ public void updateUniformBindings(Shader shader) { * @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 void renderGeometry(Geometry geom) { } else { material.render(geom, lightList, this); } + this.renderer.popDebugGroup(); } /** @@ -955,7 +957,9 @@ public void renderViewPortQueues(ViewPort vp, boolean flush) { 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 void renderViewPortQueues(ViewPort vp, boolean flush) { 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 void renderViewPortQueues(ViewPort vp, boolean flush) { 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 void renderViewPortQueues(ViewPort vp, boolean flush) { } 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; } diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index f6deaa165e..d25e514781 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -519,4 +519,13 @@ public void setTexture(int unit, Texture tex) * @return true for conversion, false for no conversion */ public boolean isMainFrameBufferSrgb(); + + public default void popDebugGroup() { + + } + + public default void pushDebugGroup(String name) { + + } + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java index 0ecc8432e5..cf8aeb790f 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java +++ b/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; + /** *

Reference Page

*

diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java index 39184475e1..ea9add1583 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java +++ b/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; + /** *

Reference Page

* @@ -239,4 +254,13 @@ public void glTexImage2DMultisample(int target, int samples, int internalFormat, * @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){ + } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 91ba96d9bb..6a6ada666c 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/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 void setGenerateMipmapsForFrameBuffer(boolean v) { 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 @@ private void loadCapabilitiesCommon() { 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 void updateShaderSourceData(ShaderSource source) { } 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 void setFrameBuffer(FrameBuffer fb) { 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 void setTexture(int unit, Texture tex) throws TextureUnitException { 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 void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { 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) { diff --git a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java index 36b94137e8..8698bf421a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java @@ -332,6 +332,7 @@ public int getComponentSize() { 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 void read(JmeImporter im) throws IOException { 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; + } } diff --git a/jme3-core/src/main/java/com/jme3/system/AppSettings.java b/jme3-core/src/main/java/com/jme3/system/AppSettings.java index cdc1324ea9..f3fb5a158f 100644 --- a/jme3-core/src/main/java/com/jme3/system/AppSettings.java +++ b/jme3-core/src/main/java/com/jme3/system/AppSettings.java @@ -1299,7 +1299,9 @@ public String getOpenCLPlatformChooser() { * 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}.
* 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 boolean isGraphicsDebug() { * 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}.
* 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() diff --git a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java index 1b66f9bee7..3b18bce1a2 100644 --- a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java +++ b/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 boolean isSrgb() { 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 diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java index 7118338229..f9899fe8b3 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java @@ -102,4 +102,20 @@ public int glClientWaitSync(final Object sync, final int flags, final long timeo 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); + } } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index baa28243d0..84aa6e387d 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -237,6 +237,7 @@ private void initContext(boolean first) { } this.renderer = new GLRenderer(gl, glext, glfbo); + if (this.settings.isGraphicsDebug()) ((GLRenderer)this.renderer).setDebugEnabled(true); } this.renderer.initialize();