Hey guys, I've been tinkering with FigDraw. It's a split out of the opengl rendering engine in Figuro, which itself was a modification of the one in Fidget.
The main goal is to support using SDFs for drawing the 2D GUI primitives. It still supports the texture atlas and images. It's gone pretty well thanks to @treeform's very handy Shady library.
My next step is to do some bench-marking and memory usage tests. A big limit in Figuro was the memory usage of the rendering engine. Even using 9-patch for rectangles, corners, shadows, etc, the texture atlas could grow pretty large. 9-patches on shadows introduce annoying artifacts as well. SDFs seem like they should be much more memory efficient and fast so far.
The 2D render library is designed to support run on it's own thread with arc/orc/atomicArc. The core Fig render nodes are value objects passed via a few seqs through a ring buffer channel. It supports z-index layering and multiple roots. Though that stuff needs documenting.

Made it to version 0.2.0 with some big performance improvements!
The default rendering mode now uses SDFs with pure texture mode as a backup. SDF wins with FPS increase with the number of fig nodes rendered. I'm now running 60+ FPS with 6000 elements where the texture based approach runs at ~30 FPS and crashes quite a lot. I'm running on both AMD embedded GPU and a Macbook Pro M3.
The bigger win however is memory usage! It's essentially flat regardless of the number of fig nodes. In particular with shadows and corners with changing sizes where a texture atlas based approach has to re-render the shadows and corners constantly. This meant Figuro and Fidget both need lots of GPU memory to run with fancy styling.
Using SDFs for main 2D GUI elements saves memory for images and text glyphs and also looks better (e.g. trying to use 9-patch with shadows plagued me with 1-pixel artifacts).
Here's the FPS for the demo shown below. It's fully dynamic with all most of the shadows and node corners dynamically changing sizes. Note that making 6001 fig elements only takes ~2ms!
fps: 66.7533297876409 | elems: 6001 | makeRenderTree avg(ms): 2.044776119402985 | renderFrame avg(ms): 4.014925373134329

There's still some work to do. SDFs don't seem to be working with OpenGL ES mode, but that could be machine specific.
Note: It really is fun vibe coding with OpenAI/Codex and Nim. Just ask it about obscure OpenGL stuff and point it in the right direction.
BTW, my goal is to get it working and well tested as a simple and stable back-end for any 2D GUIs for the Nim ecosystem. Maybe also adding Metal and Vulkan shaders as well.
I've removed the windowing dependencies so it's just a single renderFrame call now. Though FigDraw currently creates and manages the OpenGL context. That could probably change. Also I had to drop Shady as it broke on my macbook.
I also want to add a C API to FigDraw so other languages can use it. Honestly I'm a bit underwhelmed with the 2D GUI renderers out there. Aside from Rive which is complicated to compile and to use, but can handle real vector graphics.
I also want to add a C API to FigDraw so other languages can use it.
Meh, C programmers don't use non-C-stuff anyway, what's the point.
Why not? It’d be like 30 lines of code. Probably won’t get any C devs, but hey it’d be proof of what’s possible in a small GPU based 2D GUI renderer. It’d make it easy to make a small dynlib too.
Who knows, maybe it’d get included in some benchmark somewhere.

Similar to before but prints the current FPS in the corner (this is with -d:debug):

Anything other core features to add?
I'm unsure about doing SDF fonts. Looks like Valve used 64x64 textures for their fonts, however 2D UI's don't go from 12pt to 48pt fonts dynamically. That said I'm unsure it'd be more memory efficient if most of the time the fonts are statically sized.
Though it'd be interesting to convert arbitrary vector scenes to Valve style SDF distance masks that could then be rendered quickly at many sizes. That'd let it compete with ThorVG, Skia, etc. Setup Pixie to handle vector apis, and then rasterize it and store it as a precomputed sign field. Tempting..