subagent transcript

session 18e25685 · workflow wf_23df5b1c
delegated task
You are an adversarial verifier for a FreeCAD-wasm code review. A reviewer claims: SEVERITY: medium CATEGORY: feature-gap FILE: src/Gui/WasmGLFixedFun
▸ Magik
You are an adversarial verifier for a FreeCAD-wasm code review. A reviewer claims: SEVERITY: medium CATEGORY: feature-gap FILE: src/Gui/WasmGLFixedFunc.cpp SUMMARY: SoText2 and all raster/bitmap text render nothing because glRasterPos*, glBitmap and glDrawPixels are no-ops, and the JS-side matrices are not read-backable. DETAIL/EVIDENCE: glRasterPos2f/3f/3fv, glBitmap, glDrawPixels are empty stubs (lines 477-481). SoText2 (dimension text, labels, coordinate/axis text, measurement annotations) draws via raster position + bitmap glyphs, so no on-screen text appears. Additionally glGetFloatv/glGetDoublev(GL_MODELVIEW_MATRIX/PROJECTION_MATRIX) are not intercepted (glGetDoublev stub returns 0 at line 465; glGetFloatv is not defined so emscripten returns nothing for these enums), so even a texture-based SoText2 that needs to project to screen cannot read back the matrices the emulator keeps in JS. FIX: implement a textured-glyph path and expose glGetFloatv(GL_MODELVIEW_MATRIX/GL_PROJECTION_MATRIX) from the JS stacks, or document text as unsupported. RECOMMENDATION: (none) Independently verify by reading the actual file(s) and running git/grep as needed (repo at /home/magik6k/lcad-wasm/freecad-port/FreeCAD, toolchain at /opt/toolchains, deploy at /home/magik6k/lcad-wasm/freecad-port/deploy-gui). Default to REFUTED if you cannot confirm the evidence. Adjust severity to what the evidence actually supports.
Fable
thinking
I should check the actual file to confirm what's being claimed.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Gui/WasmGLFixedFunc.cpp"}
tool result
1	/* SPDX-License-Identifier: LGPL-2.1-or-later
2	 *
3	 * WebAssembly build only. A compact fixed-function OpenGL 1.x emulation over
4	 * WebGL2/GLES3, so Coin3D (which renders FreeCAD geometry with legacy GL) can
5	 * draw into the WebGL2 context Qt hands us. Emscripten's LEGACY_GL_EMULATION
6	 * is WebGL1-only and conflicts with Qt's forced WebGL2, so we implement the
7	 * subset Coin actually exercises ourselves.
8	 *
9	 * Design: the legacy entry points are C functions here; the state and dr  …[truncated]
Fable
thinking
I'm checking the claim about those graphics functions being empty stubs—that's confirmed. Now I need to verify how SoText2 actually renders in Coin, since it traditionally uses glBitmap for text rendering, which would explain why no text appears on screen if those functions are stubbed out.

I should also confirm that glGetFloatv isn't defined anywhere in this WebAssembly port, and verify whether SoText2 in this codebase actually depends on glBitmap for rendering. Let me search the repository to check the Coin source and see how text rendering is implemented.
Fable
⚙ Bash {"command": "grep -rn \"glGetFloatv\" src/Gui/WasmGLFixedFunc.cpp; echo \"---grep whole tree for glGetFloatv defs in wasm---\"; grep -rln \"glGetFloatv\" src/Gui/ | head"}
Fable
⚙ Bash {"command": "grep -rln \"SoText2\" src/ | head -30"}
tool result
---grep whole tree for glGetFloatv defs in wasm---
src/Gui/View3DInventorViewer.cpp
tool result
src/3rdParty/coin/FAQ
src/3rdParty/coin/NEWS
src/3rdParty/coin/docs/ChangeLog.2002
src/3rdParty/coin/docs/ChangeLog.v3.1.0
src/3rdParty/coin/docs/ChangeLog.v4.0.1
src/3rdParty/coin/docs/ChangeLog.2006
src/3rdParty/coin/docs/ChangeLog.2003
src/3rdParty/coin/docs/announcement-2_4_3.txt
src/3rdParty/coin/docs/announcement-2_1_0.txt
src/3rdParty/coin/docs/ChangeLog.2000
src/3rdParty/coin/docs/ChangeLog.1999
src/3rdParty/coin/docs/ChangeLog.v3.0.0
src/3rdParty/coin/docs/ChangeLog.2005
src/3rdParty/co  …[truncated]
Fable
thinking
I need to check how SoText2 handles text rendering in GLRender—whether it's using glBitmap or textured glyphs for the coin source that's bundled.
Fable
⚙ Bash {"command": "grep -n \"glBitmap\\|glRasterPos\\|glDrawPixels\\|glPixelStorei\\|cc_glglue_glBitmap\\|drawPixels\\|glTexImage\\|SoGlyph\\|dumpGlyph\\|getBitmap\" src/3rdParty/coin/src/nodes/SoText2.cpp | head -40"}
Fable
⚙ Bash {"command": "grep -rn \"glGetFloatv\" src/Gui/View3DInventorViewer.cpp"}
tool result
ugrep: warning: src/3rdParty/coin/src/nodes/SoText2.cpp: No such file or directory
tool result
src/Gui/View3DInventorViewer.cpp:3322:    glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
Fable
⚙ Bash {"command": "find src/3rdParty/coin -name \"SoText2.cpp\" -o -name \"SoText2*.cpp\" 2>/dev/null; echo \"---\"; find src/3rdParty/coin/src -iname \"*text2*\"; echo \"---all text---\"; find src/3rdParty/coin/src -iname \"*text*\" | head"}
tool result
src/3rdParty/coin/src/shapenodes/SoText2.cpp
---
src/3rdParty/coin/src/shapenodes/SoText2.cpp
---all text---
src/3rdParty/coin/src/bundles/SoTextureCoordinateBundle.cpp
src/3rdParty/coin/src/caches/SoTextureCoordinateCache.cpp
src/3rdParty/coin/src/details/SoTextDetail.cpp
src/3rdParty/coin/src/misc/SoContextHandler.cpp
src/3rdParty/coin/src/elements/SoAnnoText3CharOrientElement.cpp
src/3rdParty/coin/src/elements/SoAnnoText3FontSizeHintElement.cpp
src/3rdParty/coin/src/elements/SoAnnoText3Render  …[truncated]
Fable
⚙ Bash {"command": "grep -n \"glBitmap\\|glRasterPos\\|glDrawPixels\\|glPixelStorei\\|cc_glglue_glBitmap\\|glPixelZoom\\|glTexImage\\|glGetFloatv\\|GL_MODELVIEW_MATRIX\\|GL_PROJECTION_MATRIX\\|glGetIntegerv\\|GL_VIEWPORT\\|dumpGlyph\\|renderString…
tool result
391:    glPixelStorei(GL_UNPACK_ALIGNMENT,1);
468:            glBitmap(ix,iy,0,0,0,0,(const GLubyte *)buffer);
540:      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
545:      glDrawPixels(bbsize[0], bbsize[1], GL_RGBA, GL_UNSIGNED_BYTE, (const GLubyte *)PRIVATE(this)->pixel_buffer);
553:    glPixelStorei(GL_UNPACK_ALIGNMENT,4);
983:  glRasterPos3f(rpx,rpy,z);
984:  if (offvp) { glBitmap(0, 0, 0, 0,offsetx,offsety, NULL); }
Fable
thinking
I'm checking the render code in those sections to trace how the entire rendering pipeline relies on glRasterPos3f, glBitmap, and glDrawPixels.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/shapenodes/SoText2.cpp", "offset": 360, "limit": 220}
tool result
360	    const SbViewportRegion & vp = SoViewportRegionElement::get(state);
361	    SbVec2s vpsize = vp.getViewportSizePixels();
362	
363	    projmatrix.multVecMatrix(nilpoint, nilpoint);
364	    nilpoint[0] = (nilpoint[0] + 1.0f) * 0.5f * vpsize[0];
365	    nilpoint[1] = (nilpoint[1] + 1.0f) * 0.5f * vpsize[1];
366	
367	    SbVec2s bbsize = PRIVATE(this)->bbox.getSize();
368	    const SbVec2s& bbmin = PRIVATE(this)->bbox.getMin();
369	    const SbVec2s& bbmax = PRIVATE(this)->bbox.getMax();
370	  …[truncated]
Fable
thinking
I'm tracing through SoText2's GLRender implementation to understand how it handles raster positioning—specifically looking at the setRasterPos3f function around line 983 to see how it manages the bitmap and pixel drawing operations.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/shapenodes/SoText2.cpp", "offset": 960, "limit": 30}
tool result
960	  box.makeEmpty();
961	
962	  box.extendBy(v0);
963	  box.extendBy(v1);
964	  box.extendBy(v2);
965	  box.extendBy(v3);
966	
967	  center = box.getCenter();
968	}
969	
970	// Sets the raster position for GL raster operations.
971	// Handles the special case where the x/y coordinates are negative
972	void
973	SoText2P::setRasterPos3f(GLfloat x, GLfloat y, GLfloat z)
974	{
975	  float rpx = x >= 0 ? x : 0;
976	  int offvp = x < 0 ? 1 : 0;
977	  float offsetx = x >= 0 ? 0 : x;
978	
979	  float   …[truncated]
Fable
thinking
So SoText2 relies entirely on three OpenGL functions that are stubbed out as no-ops in the WebAssembly implementation, meaning it wouldn't render anything. Now I need to verify whether FreeCAD actually uses SoText2 for the purposes mentioned—dimension text, labels, coordinate and axis text, measurement annotations—by searching the codebase.
Fable
⚙ Bash {"command": "grep -rln \"SoText2\" src/Gui src/Mod 2>/dev/null | grep -v 3rdParty | head -30"}
tool result
src/Gui/ArcEngine.h
src/Gui/InventorAll.h
src/Gui/SoFCColorGradient.cpp
src/Gui/SoTextLabel.h
src/Gui/SoTextLabel.cpp
src/Gui/SoFCColorLegend.cpp
src/Gui/ViewProviderAnnotation.h
src/Gui/View3DInventorViewer.h
src/Gui/ViewProviderPlacement.h
src/Gui/ViewProviderAnnotation.cpp
src/Gui/ViewProviderLine.cpp
src/Gui/Inventor/Draggers/SoPlanarDragger.cpp
src/Gui/Inventor/SoAxisCrossKit.h
src/Gui/Inventor/Draggers/SoTransformDragger.cpp
src/Gui/Inventor/SoFCBoundingBox.cpp
src/Gui/Inventor/SoAxisCross  …[truncated]
Fable
thinking
SoText2 is widely used throughout the codebase—in color legends, axis crosses, bounding boxes, draft dimensions, measurement views, and annotations. But there's a critical limitation: the OpenGL matrix queries for modelview and projection matrices aren't being intercepted, which means even if SoText2 were texture-based and needed to project coordinates to screen space, it couldn't access those matrices to do so.

Actually, looking at the GLRender code more carefully, it's pulling the projection matrix from SoProjectionMatrixElement in Coin's state rather than calling glGetFloatv directly. Let me trace where that matrix originates in the GLRender initialization.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/coin/src/shapenodes/SoText2.cpp", "offset": 330, "limit": 30}
tool result
330	// **************************************************************************
331	
332	// doc in super
333	void
334	SoText2::GLRender(SoGLRenderAction * action)
335	{
336	  if (!this->shouldGLRender(action)) return;
337	
338	  SoState * state = action->getState();
339	
340	  state->push();
341	  SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
342	
343	  PRIVATE(this)->lock();
344	  PRIVATE(this)->buildGlyphCache(state);
345	  SoCacheElement::addCacheDependency(state, PRIVATE(  …[truncated]
Fable
thinking
The reviewer's claim about texture-based SoText2 needing to read back matrices is actually speculative—the real implementation uses Coin's matrix elements directly rather than glGetFloatv, so that part of the argument loses some force. The reviewer framed it as a hypothetical scenario, but the actual evidence doesn't quite support it.

The core claim holds up though: glGetFloatv doesn't intercept MODELVIEW/PROJECTION matrices, and glGetDoublev returns 0. But that's not what actually breaks SoText2—it's the raster and bitmap operations being no-ops that cause the rendering failure. So the main conclusion (SoText2 renders nothing) is confirmed, even if the matrix-readback reasoning is secondar  …
Fable
⚙ Bash {"command": "grep -rln \"glBitmap\\|glRasterPos3f\\|glDrawPixels\" src/Gui src/App 2>/dev/null | grep -v 3rdParty; echo \"=== any other def of these symbols in wasm-specific files ===\"; grep -rn \"void glBitmap\\|void glRasterPos3f\\|void …
tool result
src/Gui/WasmGLFixedFunc.cpp
=== any other def of these symbols in wasm-specific files ===
src/Gui/View3DInventorViewer.cpp:3322:    glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
src/Gui/WasmGLFixedFunc.cpp:478:void glRasterPos3f(GLfloat,GLfloat,GLfloat){ }
src/Gui/WasmGLFixedFunc.cpp:479:void glRasterPos3fv(const GLfloat*){ }
src/Gui/WasmGLFixedFunc.cpp:480:void glBitmap(GLint,GLint,GLfloat,GLfloat,GLfloat,GLfloat,const GLubyte*){ }
src/Gui/WasmGLFixedFunc.cpp:481:void glDrawPixels(GLint,GLint,  …[truncated]
Fable
thinking
I've confirmed these symbols are no-ops in the wasm port and Text2 rendering depends entirely on them. Now I'm checking the glGetDoublev stub at line 465 and whether glGetFloatv handles MODELVIEW/PROJECTION matrices—I see it's called in View3DInventorViewer.cpp with GL_COLOR_CLEAR_VALUE, which should be straightforward.

Actually, glGetFloatv isn't defined in WasmGLFixedFunc.cpp at all—emscripten's real implementation handles it. But since emscripten doesn't know about the JS-side matrix stacks, querying GL_MODELVIEW_MATRIX or GL_PROJECTION_MATRIX would fail because those are legacy enums removed from WebGL2. This confirms the reviewer's point about matrix readback not being possible.

The m  …
Fable
I've verified all the key claims by reading the actual files. **Primary claim — CONFIRMED.** In `src/Gui/WasmGLFixedFunc.cpp`, `glRasterPos2f`/`glRasterPos3f`/`glRasterPos3fv` (lines 477-479), `glBitmap` (480), and `glDrawPixels` (481) are all empty no-op stubs, and these are the only definitions of those symbols in the FreeCAD-side tree (nothing else overrides them). Coin's `SoText2::GLRender` (`src/3rdParty/coin/src/shapenodes/SoText2.cpp`) depends entirely on this path: mono glyphs go through `SoText2P::setRasterPos3f` (line 973) → `glRasterPos3f` + `glBitmap` (983-984, 468), and anti-aliased glyphs are composited into a pixel buffer flushed via `glDrawPixels` (line 545). With all three being no-ops, SoText2 emits nothing to the framebuffer. `SoText2` is used broadly across FreeCAD Gui/Mod (color legend/`SoFCColorLegend`, `SoAxisCrossKit`, `SoFCBoundingBox` dims, Draft dimensions/labels/text, Measure view providers, annotations), so the "no on-screen text" impact is real and wide. **Secondary claim (matrix readback) — factually true but a weaker/partly-speculative point.** `glGetDoublev` returns 0 (line 465), and `glGetFloatv` is not defined here (emscripten's real one handles the `GL_COLOR_CLEAR_VALUE` call at View3DInventorViewer.cpp:3322, and would not know about the JS `__ff` matrix stacks). So `GL_MODELVIEW_MATRIX`/`GL_PROJECTION_MATRIX` indeed cannot be read back. However, this is *not* what breaks current SoText2: its GLRender reads matrices from Coin's own state elements (`SoModelMatrixElement`/`SoViewingMatrixElement`/`SoProjectionMatrixElement`, lines 357-359), not from `glGetFloatv`. The reviewer frames it as a blocker for a hypothetical "texture-based SoText2," which is accurate as stated but doesn't add to the actual current-code defect. **Severity — medium is appropriate.** The core defect (all raster/bitmap text invisible) is solidly evidenced and affects many features, but this is an explicitly limited fixed-function emulation shim (file header scopes it to "the FreeCAD viewport subset," with text among the listed remaining no-ops), so it's a known feature gap rather than a regression, consistent with the reviewer's own "document as unsupported" alternative.
Fable
⚙ StructuredOutput {"verdict": "CONFIRMED", "reason": "Verified in WasmGLFixedFunc.cpp: glRasterPos2f/3f/3fv (477-479), glBitmap (480), glDrawPixels (481) are all empty no-op stubs and the only definitions of those symbols. Coin's SoText2::GLRender (src/3rdPa…
tool result
Structured output provided successfully