Odin Book update + sale. Karl2D progress. Binding generator rewrite.
Now this was a productive month! Let’s talk about what happened.
We’ll start with news regarding my book Understanding the Odin Programming Language, then some updates on my 2D game library Karl2D and finally some future plans for my binding generator.
Book update: Strings chapter overhaul, and a sale!
One big thing that happened in the September release of Odin was native UTF-16 support using the string16
and cstring16
types. Odin doesn’t use these types a lot, since it focuses on UTF-8. The UTF-16 types are mainly for interfacing with Windows code (and any other library that needs UTF-16). Nevertheless, I did have a section in my book Understanding the Odin Programming Language where I showed how to interface with the Windows API. Now the code in that chapter no longer compiled, so the book needed to be updated!
Having a native UTF-16 type in the language made me treat UTF-16 a bit more rigorously. As I tried to rewrite those sections, I also noticed that my discussion around the interplay between Unicode and UTF-8 was a bit lacking.
So I started doing a major overhaul of the Strings chapter of the book! It needed more details on what Unicode actually is, and how UTF-8 is used to encode/decode Unicode. That way I could extend that discussion to UTF-16 in the later parts of the chapter.
I also added a section on how to manually decode UTF-8. That specific section is freely available on my blog and also as a YouTube video:
This update, including some other small fixes, took almost two weeks to complete. The updated Strings chapter (chapter 11) has several almost completely rewritten sections, as well as a few new sections.
The update is live, with release notes here.
I am running a sale of the book on store.zylinski.se — 25% off until October 9!
Karl2D progress
Since the last newsletter I’ve been doing good progress on my “Karl2D” library! It’s a 2D game creation library. The API is based on Raylib, but meant to be written in native Odin, using as few dependencies as possible.
It is not yet ready for use, but here follows some highlights.
Follow the development of the library on GitHub.
More efficient rendering
I ported the “bunnymark” Raylib demo.
When running the ported demo, I ran into some errors in how I handle batching. After fix that I could spawn around 160000 bunnies while staying at 60 fps. This is not meant to be a benchmark, as I haven’t really optimized anything yet. But it’s looking OK!
The port is available here (original here).
The batching works by building a vertex buffer that can be up to 1 megabyte large. If it hits the limit, then it does a draw call and then empties the buffer and continues drawing. It’s similar to what Raylib does. However, I also have the possibility to customize vertex inputs. In Raylib that is hard-coded. This way, you can add extra vertex inputs in the shader and set their value while drawing, without modifying the library.
Gamepad support
The basic gamepad support uses XInput. I’ll make sure to get PlayStation controllers to work as well. Later, when I add more platforms, I’ll use that platform’s native API to do gamepad support on it.
Basic text rendering (work in progress)
I use stb_truetype to generate bitmap fonts. Similar to how raylib does it. The offsets and line heights are still a bit wrong in this image, but I’ll get there. I am thinking if I perhaps should have some fancier way to process fonts, such as dynamically building bitmap atlases. We shall see!
stb_truetype is one of few non-OS / non-rendering APIs that I’ve used. We shall see if it stays in the long run, but it’s a simple enough library that I would be OK with the dependency.
People may wonder what the philosophy is here: Why are some libraries OK while others are not? For example, why am I not just using SDL_GPU for the rendering?
The answer lies on how heavily your project becomes dependent on the library. Something like stb_truetype is easy to swap out if the need arises. However, if I use SDL_GPU everywhere and don’t even make any graphics API abstractions, then I am heavily dependent on it. The day I want a platform where I can’t use SDL_GPU, then I’m in deep trouble.
So I only use libraries that I’m forced to use (OS APIs, rendering APIs) and libraries that I can easily swap out (stb_truetype).
Also, I ended up watching this informative and soothing video by Sebastian Lague. Perhaps trying to do some curve-based approach to text rendering is interesting, but as you can see in the video, it is a huge can of worms. We shall see!
Karl2D API documentation
Creating APIs that are easily overviewable isn’t one of Odin’s strengths. In C you use headers for that. But I don’t want any header-like things! But I also don’t want to force people to use a web-based documentation file.
What I’ve come up with instead is to use core:odin/parser
to parse the source files and create a karl2d.doc.odin
file that contains a good, programmer friendly overview (essentially stripping out the procedure bodies). The file is not meant to be compiled (it has #+build ignore
at the top). It’s there for documentation while at the same time being as close to real code as possible.
You can see the generated file here: https://github.com/karl-zylinski/karl2d/blob/master/karl2d.doc.odin
The source of the program that creates the file is available in the api_doc_builder
folder of the repository.
Binding generator rewrite
Recently, odin-c-bindgen got a big update: We switched from using the clang executable to directly using libclang. This was due to great help from the contributor Xandaron.
What this means is that we use libclang to analyze the C headers. And from the information we get out of libclang we can generate the bindings. This is more tidy and less error-prone than using the clang executable, ouputting JSON and then parsing that JSON.
Since all that happened, I’ve been putting off doing some maintenance work on the generator. The code has gotten a bit messy! So… I am going to use the great work that Xandaron did as a reference, and do a “bindgen 2.0” rewrite.
My idea is to have a clearer separation between different “stages” of generation:
Collect: Use libclang to build an intermediate representation of the C headers.
Process: Take any extra configuration options and use that to transform the collected representation. For example, this is where “bit setification” of enums would happen. The output of this stage is a “final representation”.
Output: Take the final representation and prettily output it as Odin files. An important aspect, in order to keep the messiness down, is to make sure that the outputter knows nothing about libclang. So the two stages before this one must prepare the data in an orderly fashion. One of the big issues with the old version was that clang things seeped into the output stage. This give me a hard hard time when trying to fix bugs, because there is clang-related things happening just about anywhere.
The one thing I like less about libclang vs parsing JSON from clang is that libclang uses “visitors” to iterate children. So for example, to get all the fields of a struct definition, you pass libclang a callback that it will call for each child of the struct.
This makes the code so much more messy. It’s must better to just iterate an array. So what I do in the rewrite is that I just use the visitor to make an array of children, return that array and then loop through it. The code then becomes much easier to read.
Join my Discord community
If you want to chat about my book, Karl2D or the binding generator, then why don’t you join my Discord community? It’s a friendly place where you can also talk about your projects, be it gamedev- or Odin-related.
Here’s the invite: https://discord.gg/4FsHgtBmFK
Until next time, happy programming!
/Karl