_includes/
_layouts/
_pages/
_posts/
2010-04-24-staying-the-hell-out-of-insert-mode.md
4.2 KiB
2020-07-19-popol.md
9.1 KiB
2021-01-22-nakamoto.md
8.2 KiB
2024-01-31-segfault-the-mother-of-invention.md
6.2 KiB
assets/
css/
fonts/
images/
js/
.gitignore
62 B
Gemfile
68 B
Gemfile.lock
1.7 KiB
Makefile
198 B
README
192 B
_config.yml
278 B
avatar.png
44.3 KiB
cloudhead.gpg
892 B
favicon.png
179 B
index.md
79 B
publish
1.6 KiB
robots.txt
33 B
_posts/2024-01-31-segfault-the-mother-of-invention.md
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 |