_posts/2024-01-31-segfault-the-mother-of-invention.md 6.2 KiB raw
1
---
2
title: "Segfault, the mother of invention"
3
date: 2024-01-31
4
type: post
5
layout: default
6
permalink: /drafts/segfault/
7
draft: true
8
---
9
10
There is no greater joy for a programmer than to build his own tools. A few
11
weeks ago, I embarked on a little coding experiment: I wanted to see if it was
12
possible to build a 2D/3D software suite with an embedded scripting language
13
more or less from scratch.
14
15
This all started when I wanted to try Blender, a popular open source 3D tool.
16
17
    $ blender
18
    Writing: /tmp/blender.crash.txt
19
    zsh: segmentation fault (core dumped)  blender
20
21
The last time I tried running Blender, which was some five years ago, it worked
22
fine. Turns out this is likely due to an LLVM dynamic library incompatibility,
23
as stated by someone on the Arch Linux bug tracker:
24
25
> So, OSL is linking LLVM15, and the driver is linking LLVM16. Something is
26
> breaking because two different versions of LLVM are being linked concurrently
27
> and calling into each other, presumably because symbol versioning is disabled
28
> or broken.
29
30
If you're wondering why Blender depends on LLVM, one of the most sophisticated
31
compiler toolchains ever created, with millions of lines of code, your guess is
32
as good as mine. But anyway, if your reaction to the above is not *wtf?*, you
33
have already given up on software.
34
35
Here's a useful principle:
36
37
> **Principle**: To assess the complexity of a piece of software, its entire
38
> dependency tree must be considered.
39
40
Naturally, dynamic linking makes a software's dependency tree a runtime
41
concern, ie. mostly unpredictable. Shucks.
42
43
I tend to have very limited patience dealing with code I didn't write,
44
especially when it doesn't work. To avoid that, I try to keep dependencies to a
45
bare minimum, and pick ones that mostly get out of the way. This has worked
46
well for me, probably because bugs are typically corelated with code size (yes,
47
this includes transitive dependencies). In other words, less code equals less
48
things that can go wrong. Every dependency you add has a benefit and a cost,
49
and we tend to overestimate the benefit and underestimate the cost. Picking
50
dependencies is all about this cost to value ratio.
51
52
    Ratio = Benefit (time saved) ÷ Cost (dependency burden)
53
54
Naturally, when I wasn't able to run Blender, I asked myself what it would take
55
to write my own. I've dabbled in graphics programming before and wrote a [pixel
56
editor][rx] a few years ago, what would it take to add a third dimension? This
57
is the question I wanted to answer, and the direction I wanted to explore. I
58
like to constrain myself to working mostly with the standard library of a
59
language and a very minimal set of dependencies; mostly for the code I don't
60
want to write. Things like C bindings and platform-specific code are usually in
61
that category.
62
63
Hence, when thinking about new projects, the question of dependencies is the
64
first one I ask, and after doing a bunch of research, I landed on these three,
65
somewhat unsurprisingly, for a 2D/3D graphics application:
66
67
    [dependencies]
68
    glfw = { version = "0.54.0" }
69
    glow = { version = "0.13.0" }
70
    log = { version = "0.4.20" }
71
72
These are the things I don't want to have to reinvent.
73
74
[glfw][glfw] is a rock solid OpenGL context creation and input handling library
75
I've been using without issue for over a decade. Compared to SDL, it's a tenth
76
of the size, while doing everything I need. It contains lots of
77
platform-specific code I'm not willing nor able to write.
78
79
I chose `glfw` over [winit][winit] because I'm not interested in running
80
software on mobile or web, and winit's API is a compromise made to support
81
these platforms. For instance, `winit` doesn't give you control over your event
82
loop. This is a deal breaker for me. It's common to lose power and flexibility
83
when using APIs that try to abstract over vastly different domains.
84
85
[glow][glow] is a set of GL bindings for Rust, optimized for portability. It's
86
a great library that doesn't try to do too much:
87
88
> GL on Whatever: a set of bindings to run GL anywhere (Open GL, OpenGL ES, and
89
> WebGL) and avoid target-specific code.
90
91
The `log` crate doesn't need much of an introduction. Since I avoid async rust,
92
there is no need for anything more advanced in terms of logging.
93
94
I chose to build directly on OpenGL 3.3, as opposed to using something like
95
[wgpu][wgpu] because OpenGL is already the hardware abstraction layer that I
96
need to write cross-platform code, and it's not going anywhere. All I would add
97
is a thin wrapper around it to make it more palatable.
98
99
What about the GUI? There are dozens of libraries in Rust to choose from, but
100
they either depend on something like GTK, have a massive dependency tree, or
101
are coupled with their graphics backend. I'm a big fan of [Raph Levien's][raph]
102
work, such as [druid][druid] and [xilem][xilem], but these are large, and still
103
unfinished projects that I'd rather not take on as dependencies. However, I
104
think a lot of [good ideas][archi] can be borrowed from them.
105
106
There are many other promising UI library projects for Rust, but none tick
107
all of the boxes for me, so I decided to build my own on top of `glow`.
108
The goal, as usual, is to write something maintainable and as close to
109
dependency-free as possible. If it ends up being useful to others, great,
110
but it will be primarily built based on my own requirements for this project.
111
112
So far, a few weeks into the project, I have a thin, ergonomic wrapper around
113
`glow` inspired by [sokol-gfx][sokol], a basic 3D renderer, a scripting
114
language, and the beginnings of a small [retained mode][retained] GUI library.
115
116
I'm discovering a lot of interesting problems along the way and having to make
117
all sorts of trade-offs and decisions to keep the code size small and
118
manageable for one person. So far, it feels like the goal of having a usable
119
2D/3D software stack for my own needs is achievable. I'll be posting updates
120
here as I make progress.
121
122
[raph]: https://raphlinus.github.io/
123
[archi]: https://raphlinus.github.io/rust/gui/2022/05/07/ui-architecture.html
124
[xilem]: https://xilem.dev/
125
[rx]: https://rx.cloudhead.io
126
[glfw]: https://www.glfw.org/
127
[glow]: https://crates.io/crates/glow/
128
[winit]: https://crates.io/crates/winit
129
[wgpu]: https://wgpu.rs
130
[druid]: https://docs.rs/druid/latest/druid/
131
[sokol]: https://floooh.github.io/2017/07/29/sokol-gfx-tour.html
132
[retained]: https://en.wikipedia.org/wiki/Retained_mode