Skip to content

glcanon: rewrite the G-code preview on OpenGL 3.3 core - #4293

Open
alex-pres wants to merge 1 commit into
LinuxCNC:masterfrom
alex-pres:pr-gremlin-opengl-rewrite
Open

glcanon: rewrite the G-code preview on OpenGL 3.3 core#4293
alex-pres wants to merge 1 commit into
LinuxCNC:masterfrom
alex-pres:pr-gremlin-opengl-rewrite

Conversation

@alex-pres

Copy link
Copy Markdown

Replaces the fixed-function preview shared by AXIS, the GTK screens and QtVCP with a shader/VBO renderer: a baked trajectory buffer, an offscreen ID-buffer pass for picking, and a glyph atlas for overlay text. The GL matrix stack, display lists, immediate mode and GL_SELECT are gone.

Replaces the fixed-function preview shared by AXIS, the GTK screens and
QtVCP with a shader/VBO renderer: a baked trajectory buffer, an offscreen
ID-buffer pass for picking, and a glyph atlas for overlay text. The GL
matrix stack, display lists, immediate mode and GL_SELECT are gone.
@alex-pres

alex-pres commented Jul 30, 2026

Copy link
Copy Markdown
Author

Why rewrite?

  • OpenGL immediate mode has been deprecated for 18 years (since OpenGL 3.0 in 2008).
  • This unblocks many UI decisions — for example, it now works with XWayland.
  • There is a solid performance win, although it's only visible for large ngc files (engraving or 3D reliefs).

Performance

  • The new code does not make many OpenGL function calls per trajectory point. Instead, it builds a numpy array
    of trajectory points, transmits it directly from system memory to GPU memory, and submits (once) a small
    program to the GPU with instructions on how to convert it from 3D to 2D (the "shader" program). The same for backplot points.
  • Additionally, operations on trajectory points are optimized with numpy batch handling.
  • Selection of g-code line by clicking point must be also faster and GPU powered with new code.

Code Refactoring

Code is also reviewed and refactored for eaiser maintenance .

  • Previous code handled scene rotation from many places by directly calling OpenGL methods. Now there is a dedicated matrix that handle this.
  • Program siumlation reported points and Backlog points are separated to objects to easily see logic.
  • Also added new concept of Scene that contains Part(s) like Axes, Backplot, DRO, Program, and so on. Each part is self-contained and can be disabled, replaced or extended from code.
  • Previous version of code maintained arrays of moves, feeds and so on right in Python. These lists may take significant memory. For example look at comparsion below: | gcode.parse | old: +575 MB vs new:+28 MB | for 18M ngc file. New code does not need these lists and makes all necessary calculations while collecting points.

Hardware support

This rewrite targets the OpenGL 3.3 standard, released in 2010 and widely supported.

In case of hardware problems, or if drivers don't support the necessary features, the env var
LIBGL_ALWAYS_SOFTWARE=1 can be set to enable software OpenGL handling via llvmpipe.

Verification

  • During development, I tried to get pixel-to-pixel comparisons as much as possible and fix any differences.

Known Problems

  • Touchscreen handling seems to be broken. I can fix it if this PR is generally going to be accepted.

Performance testing results

Benchmarked against stock master on fractal-1M.ngc (18.9 MB, 1,000,149 moves). Mac M4 ARM64, Docker + Xvfb + llvmpipe.

AXIS end-to-end (4 reps, real GUI, open_file_guts)

master rewrite
peak RSS 1674 MB 748 MB 2.24× lower
⤷ minus idle baseline (194 MB both) 1480 MB 554 MB 2.67× lower
steady redraw frame 0.32 s 0.12 s 2.7× faster
open_file_guts (open→drawn) 7.6 s (6.8–8.5) 7.1 s (7.1–7.4) a wash
load_preview alone 3.00 s 2.96 s a wash

Both screenshots confirm the file actually drew — this isn't one branch skipping work.

Phase harness (3 reps, EGL, isolates parse / extents / GPU build)

phase master rewrite
gcode.parse 1.66 s, +575 MB 2.76 s, +28 MB
calc_extents 0.86 s, +432 MB 0.00002 s, +0
GPU build 0.28 s (glNewList), +31 MB bake 0.027 s + upload 0.002 s
GL payload not queryable 24,003,600 B VBO (counted at glBufferData)
load total 2.82 s 2.80 s
peak RSS 1122 MB 276 MB
first click in preview +0.75 s, +276 MB (selection lists) none

What the numbers say

  • Memory is the headline. 2.2× lower peak in the real GUI, and the gap widens on first click: master compiles a second, per-line-named copy of the program into selection display lists (+276 MB); the rewrite's picker reads the buffers already uploaded.
  • Load time is unchanged, but for a non-obvious reason. The rewrite's raw parse is ~1.1 s slower — it transforms and fills arrays on the move path. That's fully paid back by calc_extents/unrotate_preview dropping from 0.86 s to zero, since the rewrite accumulates extents during the fill. If you want load time to actually drop, the parse hot path is where the remaining 1.1 s lives.
  • Frame time is 2.7× better and the GPU build is 10× cheaper (0.03 s vs 0.28 s).

Two caveats worth stating: llvmpipe puts display lists and VBOs in process RSS, so the RSS deltas are the only cross-branch-comparable "GPU memory" figure — a display list has no queryable size, so master gets no exact byte count. And I noticed that at the same view master's preview saturates to white where the rewrite renders mid-grey; that's a blend difference on densely overlapping geometry which I did not investigate.

@BsAtHome

Copy link
Copy Markdown
Contributor

Good work.

Functional touch screen support is rather important to keep working. Breaking a touch interface would probably make quite a few users pulling hairs and may even require baldness treatments when it persists.

Some cases that should function: running on RPi4, Rpi5 and running with remote X (also ssh tunneled)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants