▶ YouTube Summaries

← All summaries

Porting Sean Barrett's iprof from OpenGL to raylib on Linux

2026-10-05 Mon ⏱ 2 hr 35 min tsodingdaily

Tsoding takes iprof — Sean Barrett's 2003 Win32 interactive profiler — and ports it to Linux. He also removes its hard dependency on OpenGL so that rendering goes through user-supplied callbacks, as in Jonathan Blow's Jai module inspired by it. After a long hunt for a crash, he gets the profiler running on raylib, with live, navigable per-zone timings.

Motivation

  • iprof only needs a tiny set of graphics primitives: text, text width, rectangles and lines.
  • Text output and input are already callbacks; only rectangles and lines are hard-wired to OpenGL 1.1 immediate mode.
  • The Jai version abstracts all of this into a config struct of draw callbacks (draw text, text width, rectangle, line, with colors). The goal is the same in C, so any backend — raw GL, SDL or raylib — can plug in.
  • References: iprof (silverspaceship.com/src/iprof), RAD Debugger, the result at github.com/tsoding/iprof-nogl.

Getting it to compile on Linux

  • Add a prof_linux.h / prof_linux.c platform layer, mirroring the Win32 one:
  • define Prof_Int64 via stdint.h;
  • provide a timestamp through the __rdtsc intrinsic (needs clang);
  • provide a seconds timer through clock_gettime(CLOCK_MONOTONIC), which replaces QueryPerformanceCounter.
  • Old-code friction:
  • case-insensitive Windows include names;
  • non-standard strdup that leaks every frame for a "SpeedStep" warning;
  • unused-variable and missing-field-initializer warnings;
  • abs() applied to a float.
  • Multiple-definition linker errors came from a timestamp helper defined in a header. Marking it static makes each translation unit keep its own copy.
  • He puts the pristine source under git before refactoring, so the diff shows exactly what changed.

Refactoring out OpenGL

  • The graph routine:
  • the glBegin/glColor/glVertex sequence becomes a draw_line(x0, y0, x1, y1, r, g, b, a) callback;
  • colors change from 0–255 ubyte to 0–1 floats;
  • a "previous point" variable turns the line-strip into individual segments.
  • A callback-of-a-callback mess is flattened. The graph location becomes a global, and the graph function is called directly with draw_line.
  • draw_rectangle becomes a callback too, and text and rectangle callbacks now take an RGBA color instead of relying on the implicit current GL color.
  • An Emacs regexp query-replace rewrites the many inline color tuples into separate assignments.
  • GL state management — texture enable/disable, face culling, blending — is simply deleted; that's the caller's business.
  • Aside: the "current color" in GL is implicit global state that glVertex reads, which is why glColor works outside glBegin/glEnd. Immediate mode is so pleasant that people keep reimplementing it on top of retained-mode APIs.

Wiring it to raylib

  • The raylib callbacks map almost one-to-one:
  • DrawText and MeasureText for text;
  • DrawRectangle with ColorFromNormalized for rectangles;
  • DrawLine for lines.
  • Text first rendered off-screen. The fix was to use a positive line spacing, because y grows downward in raylib.
  • Usage: bracket the frame with Prof_Begin(frame) / Prof_End, call Prof_update(record) once per frame, then call the draw function.

The crash hunt

  • With recording on, Prof_update segfaulted on the second frame.
  • He built and tried the RAD Debugger on Linux — good first impression, fast build, smooth onboarding — and also used gf2. The fault was an unmapped pointer in the zone-history update.
  • Chat suspected stray semicolons after the Prof_Begin macros. That was wrong.
  • The real cause was Prof_MAX_PROFILING_ZONES, which the old code base defines inconsistently between files. Defining it explicitly fixed the crash.
  • Lesson: the docs explain instrumentation at length but never show one complete main-loop example. He plans to publish a raylib example with the port.

Result

  • Live zone timings render in raylib, with highlight animation.
  • Navigation works once keys are mapped to iprof's actions (move cursor, select). Input was already abstracted; only lines and rectangles were missing, and that was the last step to OpenGL independence.
  • He couldn't get a hierarchical display working despite trying the report modes.
  • Asides:
  • Working with code older than your programming career feels like executable archaeology.
  • DuckDuckGo search quality is poor.
  • grep.app and OpenGrok-style indexes are invaluable for finding real-world usage.
  • On LLMs: they might make migrations like Python 2 → 3 much less painful.