Reinit
Hi again. This is Robbie. For most of a decade, I used to go by Rylee. I do math, I make music, I write code, I’m learning Japanese. I like graphic design, writing, cooking, some other stuff too. Lately I’ve been discovering that running and the gym are actually fun and good things, which would have blown my sixteen-year-old self’s mind.
This is a blog. Let’s go.
How to Draw a Triangle in Three Years
I am finally owning up to thinking that it would be fun to make a game. So I’m working on that, instead of, like, making a Lua environment ostensibly for music or wrapping Cocoa for easy access from Zig or any number of ways one could aim at making a game and miss perhaps deliberately.
Part of the discomfort that might cause one to miss, I think, is that making a game relies pretty fundamentally on other people’s code. Paraphrasing Carl Sagan, if you want to make a DAW from scratch, you must first invent the Zig programming language. Which is to say, software has the same problem as mathematics, and alas it is taking me about as long to recognize and resolve that problem.
Like in mathematics, in programming you can go all the way to the big bang if you’d like. You could write assembly. Or an assembler. Or an operating system. Or a C compiler. Or your own programming language. You could even write your own instruction set. Peeking one layer deeper into the abstraction is always at least theoretically possible.
For me, this leads directly to not-invented-here syndrome. Do not pass “go”, do not collect 10,000 hours of making-things time. If I don’t know how it works, how can I use it?
Unfortunately, being not-Andrew Kelley, I fear that if I attempt the same sort of from-first-principles exploration, I won’t even have demos of a game to show for it. (In fact, you don’t see Andrew Kelley reinventing Linux, for example, so picking on him obliquely is obviously unfair.)
Part of resolving the problem for me with math was intensifying the need to actually do something. I told my advisor I wanted to graduate in a year, and then did what I needed to do to do that: get out of my head and prove a theorem.
Another part of it, and this is sort of what I’m leaning on with programming, is the realization that “understanding,” here, is not an objective quantity that can be possessed seperate from experience using the thing. Putting the pieces together is an important part of graduating from armchair enthusiast to expert.
So, here is how to draw a triangle.
The First Year
In the first year or so, you should not know that you want to draw a triangle. You should be interested primarily in learning a programming language sufficiently powerful to let you shoot yourself in the foot (that is to say, link against C). I’m resisting giving Rust my customary noogie here.
As a result of your programming efforts, maybe you’ve discovered the edges of an ability to draw pixels to the user’s screen.
By the way, you should be running on macOS. After all, we’re using Vulkan, so anything else would be easy mode.
The Second Year
In the second year, you should learn about graphics pipelines. Enough to follow the Vulkan tutorial, but not enough to, like, read their “Getting Started” documentation. Since you’re coding along, you should be able to verify that your program type-checks but then doesn’t work at all. Ideally it stalls out at the “have GLFW load Vulkan” step, so that you can believe that your code works, but that something is just broken about GLFW or Vulkan on macOS, but not really solve it.
Then you should learn about Metal. It’s actually pretty helpful to do, since Metal and Vulkan agree to an embarrassing (for Apple? for not cooperating? for Khronos? for not working harder to get them onboard? answering these questions is above your pay grade) degree about what a graphics card does and how to talk to it. Maybe you can draw a triangle with Metal!
The Third Year
In the third year, you should ideally burn out for most of it. Give everything a break and reconnect with what’s actually interesting to you and what you are doing in order to please other people.
Really find yourself, tho. Like, learn jj. Delete and remake your website. Switch to fish. Lose track of the little notes apps and things that you used to keep yourself on track and then find them months later as a sad Tigger, a sorry Tigger, an “Oh Rabbit, am I glad to see you,” Tigger.
Then, clear-eyed and rarin to go, here are the steps.
Download Zig master
This can be done by clicking, or by being a script kiddie. In the interests of embarrassing myself, here’s a script-kiddie approach.
mkdir dwnld && cd dwnld
curl https://ziglang.org/download/index.json -o zig_idx.json
cat zig_idx.json | jq '"url = \(.master.["aarch64-macos"].tarball)"' -r | curl -K- -o zig.tar.xz
cat zig_idx.json | jq '"\(.master.["aarch64-macos"].shasum) zig.tar.xz"' -r | shasum -a 256 -c -
These commands download the JSON blob from the zig website into a new directory named dwnld, then grab and download the master tarball for aarch64-macos and check its sha256 checksum by using jq to parse the JSON. You should probably not continue if the final command above doesn’t tell you that the sum was OK, but probably you shouldn’t even use this approach in the first place.
mkdir zig && cd zig
tar -xf ../zig.tar.xz --strip-components=1
cd ..
rm -rf ~/bin/doc ~/bin/lib
mv zig/* ~/bin
xattr -d com.apple.quarantine ~/bin/zig
cd ..
rm -rf dwnld
The first pair of commands extracts the tarball into a new directory named zig. Status quo is that the tarball packages everything under a folder named something like zig-macos-aarch64-[VERSION-NAME-CRUFT], which is fine as far as it goes, but not very conducive to script kiddie powers, hence the --strip-components=1 flag on the tar command. As you can see, I put the zig compiler in ~/bin, which I had to add to my PATH. The xattr line avoids the “macOS won’t let you run this program bc you downloaded it” dance.
Install GLFW and Vulkan
For GLFW, you can just do
brew install glfw
Probably a similar invocation could work for Vulkan, but you might as well go to their website and download and install the SDK.
Get started with Vulkan-Zig
Go ahead and grab the repo
jj git clone git@github.com:Snektron/vulkan-zig
cd vulkan-zig
The repo should build fine with
zig build --build-file examples/build.zig
but probably it does not, complaining that GLFW cannot be found. The fix is pretty easy:
--- a/examples/build.zig
+++ b/examples/build.zig
@@ -22,7 +22,7 @@
.use_llvm = true,
});
b.installArtifact(triangle_exe);
- triangle_exe.linkSystemLibrary("glfw");
+ triangle_exe.linkSystemLibrary("glfw3");
const registry_path: std.Build.LazyPath = if (maybe_override_registry) |override_registry|
.{ .cwd_relative = override_registry }
After this, the repo should build, but it should fail when you try to run it, complaining that Vulkan cannot be found. To fix this, you need to make it so that dlopen can find Vulkan. This is much less scary than it sounds: for a development build it suffices to add a path to your DYLD_LIBRARY_PATH variable. For me, since DYLD_LIBRARY_PATH is empty, that looks like this:
set -gx DYLD_LIBRARY_PATH "/Users/robbie/VulkanSDK/1.4.328.1/macOS/lib"
Then you can run the program and see a beautiful Vulkan triangle! It only took you three years! Maybe the rest will be much quicker ;)