From 5d93d3b1f53421f6ea79f83eed65d00d9b3fe413 Mon Sep 17 00:00:00 2001 From: arimu1 <19286898+arimu1@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:54:04 +0700 Subject: [PATCH] fix(lwjgl3): ensure GL device objects before renderDrawData Since 1.87, ImGuiImplGl3 creates the shader/program/VBO and font texture lazily in newFrame() rather than in init(), matching upstream. Call sites that only call init() + renderDrawData() (the pre-1.87 pattern used by many jME / custom loops) then hit a native NULL dereference inside glDrawElementsBaseVertex. Share the lazy create path with renderDrawData so those upgrades no longer crash. Prefer calling newFrame() every frame; this is a safety net for the upgrade path reported in #361. Fixes #361 --- .../src/main/java/imgui/gl3/ImGuiImplGl3.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java index 7a135a84..ba5c7fa8 100644 --- a/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java +++ b/imgui-lwjgl3/src/main/java/imgui/gl3/ImGuiImplGl3.java @@ -242,6 +242,10 @@ public boolean init() { * Method to do an initialization of the {@link ImGuiImplGl3} state. * It SHOULD be called before calling of the {@link ImGuiImplGl3#renderDrawData(ImDrawData)} method. *

+ * GL device objects (shader, buffers, font texture) are not created here; they are + * created lazily on the first {@link #newFrame()} or {@link #renderDrawData(ImDrawData)} call + * (behavior since 1.87, matching upstream). Prefer calling {@link #newFrame()} every frame. + *

* Method takes an argument, which should be a valid GLSL string with the version to use. *

      * ----------------------------------------
@@ -371,7 +375,25 @@ public void shutdown() {
         data = null;
     }
 
+    /**
+     * Prepare per-frame GL resources.
+     * 

+ * Since Dear ImGui 1.87 / imgui-java 1.87, shader program, buffers and the font + * texture are created lazily here (not in {@link #init(String)}). Call this every + * frame before {@link ImGui#newFrame()}, or at least once after {@code init} and + * before the first {@link #renderDrawData(ImDrawData)}. + */ public void newFrame() { + ensureDeviceObjects(); + } + + /** + * Create shader/program/VBO and font texture if they are not yet available. + * Shared by {@link #newFrame()} and {@link #renderDrawData(ImDrawData)} so that + * integrators that only call {@code init} + {@code renderDrawData} (the pre-1.87 + * pattern) do not hit a native NULL dereference in {@code glDrawElementsBaseVertex}. + */ + protected void ensureDeviceObjects() { if (data.shaderHandle == 0) { createDeviceObjects(); } @@ -470,6 +492,12 @@ public void renderDrawData(final ImDrawData drawData) { return; } + // Device objects used to be created in init() (imgui-java <= 1.86). Since 1.87 they are + // created lazily in newFrame() to match upstream imgui_impl_opengl3. Ensure them here as + // well so call sites that skip newFrame() (common after upgrades; see #361) do not crash + // with a native NULL pointer inside glDrawElementsBaseVertex. + ensureDeviceObjects(); + // In C++: iterates draw_data->Textures and calls ImGui_ImplOpenGL3_UpdateTexture for each non-OK status. // In Java: ImTextureData is not exposed in imgui-binding (follow-up); we keep the legacy createFontsTexture // path triggered from newFrame(), so dynamic atlas updates are not honored here yet.