SpaiR / imgui-java

JNI based binding for Dear ImGui
MIT License
547 stars 90 forks source link

Calls to getCmdListVtxBufferData() and getCmdListIdxBufferData() overwrite eachother #198

Closed factrick45 closed 8 months ago

factrick45 commented 10 months ago

Version

1.86.10

What happened?

I've been attempting to write a renderer backend using OpenGL 2 and LWJGL2, using this as a reference. However, these artifacts appear and the backgrounds of windows don't show, making large sections of the window unreadable depending on its position.

1

When disabling scissor tests, it seems to be caused by some of the vertices being put into the top left corner of the screen?

2

Some changes were made from the C++ version to account for API changes (no user callback), and the apparent lack of need to reset GL state after rendering, as it has no effect on the rest of the program. I don't believe these are what causing the bug and instead my misunderstanding of either LWJGL or these bindings.

Particularly, the data provided by getCmdListVtxBufferData() seems to contain junk floats like 9.18355E-41.

Reproduction

Source code for backend ```java import java.nio.ByteBuffer; import imgui.ImDrawData; import imgui.ImGui; import imgui.flag.ImGuiBackendFlags; import imgui.type.ImInt; import static org.lwjgl.opengl.GL11.*; public class ImGuiImplGl2 { private int FontTexture = -1; public void init() { var io = ImGui.getIO(); io.setBackendRendererName("imgui_java_impl_opengl2"); } public void newFrame() { if (FontTexture != -1) return; // generate font texture var io = ImGui.getIO(); var width = new ImInt(); var height = new ImInt(); ByteBuffer image = io.getFonts().getTexDataAsRGBA32(width, height); int tex = glGenTextures(); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width.get(), height.get(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image ); io.getFonts().setTexID(tex); } private void setupRenderState(int fbWidth, int fbHeight) { glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); glEnable(GL_SCISSOR_TEST); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glShadeModel(GL_SMOOTH); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glViewport(0, 0, fbWidth, fbHeight); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, fbWidth, fbHeight, 0, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); } public void renderDrawData(ImDrawData drawData) { int fbWidth = (int)(drawData.getDisplaySizeX() * drawData.getFramebufferScaleX()); int fbHeight = (int)(drawData.getDisplaySizeY() * drawData.getFramebufferScaleY()); setupRenderState( fbWidth, fbHeight ); float clipoffx = drawData.getDisplayPosX(); float clipoffy = drawData.getDisplayPosY(); float clipscalex = drawData.getFramebufferScaleX(); float clipscaley = drawData.getFramebufferScaleY(); for (int i = 0; i < drawData.getCmdListsCount(); i++) { ByteBuffer vtxbuffer = drawData.getCmdListVtxBufferData(i); ByteBuffer idxbuffer = drawData.getCmdListIdxBufferData(i); int stride = ImDrawData.sizeOfImDrawVert(); glVertexPointer(2, GL_FLOAT, stride, vtxbuffer.position(0)); glTexCoordPointer(2, GL_FLOAT, stride, vtxbuffer.position(8)); glColorPointer(4, GL_UNSIGNED_BYTE, stride, vtxbuffer.position(16)); for (int j = 0; j < drawData.getCmdListCmdBufferSize(i); j++) { var cr = drawData.getCmdListCmdBufferClipRect(i, j); float c1x = (cr.x - clipoffx) * clipscalex; float c1y = (cr.y - clipoffy) * clipscaley; float c2x = (cr.z - clipoffx) * clipscalex; float c2y = (cr.w - clipoffy) * clipscaley; if (c2x <= c1x || c2y <= c1y) continue; glScissor( (int)c1x, (int)(fbHeight - c2y), (int)(c2x - c1x), (int)(c2y - c1y) ); int tex = drawData.getCmdListCmdBufferTextureId(i, j); int elemc = drawData.getCmdListCmdBufferElemCount(i, j); int idxoff = ImDrawData.sizeOfImDrawIdx() * drawData.getCmdListCmdBufferIdxOffset(i, j); int type = ImDrawData.sizeOfImDrawIdx() == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; glBindTexture(GL_TEXTURE_2D, tex); glDrawElements( GL_TRIANGLES, elemc, type, idxbuffer.position(idxoff) ); } } glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } } ```

Relevant log output

No response

factrick45 commented 10 months ago

I have found the cause of this issue using apitrace. getCmdListVtxBufferData() and getCmdListIdxBufferData() are overwriting each other. So, when the former is called first, then the latter, the first 136 components of vertex data will actually be the indices. When I swap these two lines of code, I get this instead: a

factrick45 commented 10 months ago

Using de indexed buffers instead, everything works fine.

PF94 commented 10 months ago

@factrick45 do you plan on publishing this LWJGL2 port?

(i apologize if this is completely irrelevant to the issue)

factrick45 commented 9 months ago

@PF94 Probably not as a maven package, but I can upload the code under MIT.

ghost commented 9 months ago

@factrick45

Hi. I think many people would be grateful, especially me, if you shared your implementation LWJGL2+OpenGL2 =)

factrick45 commented 9 months ago

I've uploaded the classes here. You should be able to paste them into your project and use them similarly to the LWJGL3 versions. You just need to call the onMouse and onKey methods when your program polls inputs.

https://github.com/factrick45/imgui-lwjgl2

iocmet commented 9 months ago

Hi, it looks like you used it for minecraft, where you renders it? when you not in world game limits loop to 30 calls per second so imgui is "laggy"

factrick45 commented 8 months ago

The issue can be completely mitigated by copying the ByteBuffer before making the second call, but this is still confusing and seems to be different behavior from C++.