subagent transcript
session 18e25685 · workflow wf_23df5b1c
delegated task
Deeply review the fixed-function-GL-over-WebGL2 emulator at /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp (535 lines). It e
▸ Magik
Deeply review the fixed-function-GL-over-WebGL2 emulator at /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp (535 lines). It emulates OpenGL 1.x fixed-function (matrix stacks, materials/lighting, glBegin/glEnd immediate mode, client vertex arrays, indexed draws) over WebGL2 for Coin3D. Assess: correctness of the matrix math and shader, completeness vs what Coin's SoGLRenderAction actually needs (faces via SoBrepFaceSet, edges, SoText2, markers, transparency), the glDrawArrays/glDrawElements FF-mode gating (must pass Qt's own draws through untouched), the fcWasmResolveGL resolver table and fcWasmSyncGLContext context binding. Identify what is missing or wrong for a box to actually render with orbit/pan/zoom. Report concrete findings + a gap list for the viewport.
Fable
thinking
Looking at the file now.
Fable
I'll review the emulator file thoroughly.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp"}
Fable
thinking
I'm working through several key concerns about this implementation: the matrix math conventions, shader correctness, whether it covers everything Coin's SoGLRenderAction needs, the gating of glDrawArrays and glDrawElements for fixed-function mode, the fcWasmResolveGL resolver table, and how the context binding works in fcWasmSyncGLContext. Starting with the matrix math—I need to verify that the column-major convention is being applied correctly in the multiplication operations.
Checking the matrix operations more carefully. The mul function correctly implements column-major matrix multiplication where the result computes a*b properly. The MVP is correctly computed as projection times model- …
Fable
Let me check how the resolver and context sync are wired, and whether the quad/missing-symbol concerns are real.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui 2>/dev/null && grep -rn \"fcWasmResolveGL\\|fcWasmSyncGLContext\\|WasmGLFixedFunc\" . 2>/dev/null | head -50; echo \"---GREP ROOT---\"; grep -rln \"fcWasmResolveGL\\|fcWas…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== glClearDepth / glDepthRange desktop-double refs in Coin/src ===\"; grep -rn \"glClearDepth\\b\\|glDepthRange\\b\" --include=*.cpp --include=*.c --include=*…
Fable
thinking
I'm checking how SoVertexArrayIndexer handles GL_QUADS specifically around line 146-147.
Fable
Let me verify the SoVertexArrayIndexer quad handling and how the resolver is wired into Coin's glue.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== SoVertexArrayIndexer quad->triangle conversion ===\"; sed -n '140,175p;225,275p' src/3rdParty/coin/src/rendering/SoVertexArrayIndexer.cpp; echo \"=== how f…
Fable
thinking
I'm seeing that SoVertexArrayIndexer passes GL_QUADS directly to glDrawElements, which triggers an INVALID_ENUM error in WebGL2 since quads aren't supported there — this prevents anything from rendering for quad-based face sets. I'm checking whether FreeCAD's geometry classes like SoBrepFaceSet handle this by triangulating internally, or if this is a broader compatibility issue.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; sed -n '600,660p' src/3rdParty/coin/src/glue/gl.cpp"}
Fable
thinking
The resolver order matters here—emscripten_GetProcAddress handles the standard WebGL2 and core GL functions that emscripten knows about, while fcWasmResolveGL picks up the legacy ones. But there's a problem: when emscripten_GetProcAddress resolves something like glDrawArrays, it returns emscripten's own implementation, not our overridden version, so Coin would be calling the wrong function.
This is the critical issue—cc_glglue_glDrawElements uses a function pointer that gets resolved through emscripten_GetProcAddress, which returns emscripten's real WebGL2 glDrawElements without any fixed-function emulation. So when Coin's VBO rendering path calls glDrawElements through that pointer, it's h …
Fable
Critical wiring question: does Coin's VBO render path call `glDrawElements` directly (our override) or through an `emscripten_GetProcAddress`-resolved pointer (real WebGL, bypassing emulation)?
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== cc_glglue_glDrawElements definition ===\"; grep -n \"cc_glglue_glDrawElements\\|glDrawElements\\b\" src/3rdParty/coin/src/glue/gl.cpp | head; echo; echo \"…
Fable
thinking
I've found the critical issue—the PROC macro is resolving to Emscripten's actual WebGL2 functions through cc_glglue_getprocaddress, which means glDrawArrays and glDrawElements are being bound to the real WebGL implementations rather than the mocked versions.
Since glVertexPointer and glEnableClientState don't exist in WebGL2, Emscripten's lookup fails and falls back to our overrides, but glDrawArrays and glDrawElements succeed in Emscripten's table, so they bypass our fixed-function emulation entirely. This means the vertex array rendering path skips ff_setup_and_draw completely—it calls the real WebGL functions with no valid shader program bound, which is why nothing renders. The real fix …
Fable
I have enough to assess this thoroughly. Let me verify one more thing about the quad-index path and confirm the depth-symbol situation compiles.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== is glClearDepth/glDepthRange (double) defined or stubbed anywhere in-tree? ===\"; grep -rn \"void glClearDepth\\b\\|void glDepthRange\\b\\|glClearDepth(GLc…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; find src -iname \"SoBrepFaceSet.cpp\" 2>/dev/null; echo \"---\"; grep -rln \"SoBrepFaceSet\" src/Mod 2>/dev/null | head; echo \"=== primitive used by SoBrepFaceSet ==…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; f=src/Mod/Part/Gui/SoBrepFaceSet.cpp; grep -n \"glDrawElements\\|glDrawArrays\\|glBegin\\|GL_TRIANGLES\\|GL_QUADS\\|glVertexPointer\\|glEnableClientState\\|glNormalPo…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; f=src/Mod/Part/Gui/SoBrepFaceSet.cpp; grep -n \"glDraw\\|glBegin\\|GL_TRI\\|GL_QUAD\\|Pointer\\|ClientState\\|renderShape\\|glEnable\\|glVertex\" \"$f\" | head -40; e…
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; f=src/Mod/Part/Gui/SoBrepFaceSet.cpp; grep -n \"gl[A-Z]\\|GL_\" \"$f\" | head -40"}
Fable
thinking
I'm checking how SoBrepFaceSet renders its geometry, likely through render helpers that call glDrawElements internally, so I need to look at the relevant code section around lines 745-775.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; f=src/Mod/Part/Gui/SoBrepFaceSet.cpp; sed -n '700,780p' \"$f\""}
Fable
thinking
Looking at how SoBrepFaceSet renders through its inherited GLRender method, it triangulates faces and sends them to the GPU via either the VBO path through cc_glglue_glDrawElements or immediate mode rendering, both using GL_TRIANGLES which avoids any quad handling issues for FreeCAD Part faces. Now I need to determine which rendering path is actually being used.
The choice between VBO and immediate mode depends on whether SoGLVBOElement enables vertex buffer objects — on initial frames or smaller meshes, Coin typically falls back to immediate-mode sogl_render with direct glBegin/glVertex calls where our emulation works, but the VBO path bypasses our override entirely. Either way, the critic …
Fable
I now have a complete, evidence-backed picture. Compiling findings.
Fable
⚙ StructuredOutput {"findings": [{"severity": "critical", "category": "render-path-bypass", "summary": "Coin's primary vertex-array/VBO draw path resolves glDrawArrays/glDrawElements to emscripten's real WebGL2 functions, bypassing the fixed-function emulatio…
Fable
thinking
I need to remove the recommendation_line field I added since it's not part of the allowed schema, and merge that information into the recommendation and detail fields instead.
Fable
⚙ StructuredOutput {"findings": [{"severity": "critical", "category": "render-path-bypass", "summary": "Coin's primary vertex-array/VBO draw path resolves glDrawArrays/glDrawElements to emscripten's real WebGL2 functions, bypassing the fixed-function emulatio…