Introduction and why I decided to write an OpenGL hello-triangle program the hard way: no sdl, no glfw, only Linux platform libraries.
- Part 0*
Grokking the wayland protocol and creating a window.
Initializing our OpenGL context and using EGL to connect it to our window.
I decided to start working on a small OpenGL project to evaluate zig as language to use for some project ideas I had, and it was my first time writing a graphical application without winit to abstract everything for me.
OpenGL only provides abstractions for doing computations on the GPU, it doesn't open windows for you or handle things like input, etc. For that one uses a windowing library. In the C/C++/zig world there are a couple of good options:
glfw — a small C library that for creating and managing windows, OpenGL and OpenGL ES contexts and Vulkan surfaces, enumerating monitors and video modes as well as handling inputs such as keyboard, mouse, joystick, clipboard and time.
sdl2 — Simple Direct Layer is a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.
Since I wanted the option to use Vulkan, it seemed natural to use glfw. I started getting into it but when it came time to start handling input, I was feeling kind of dissatisfied with the API. I wanted to know why things where callback based (I had gotten used to the winit approach). I thought this was a good opportunity to try using the Linux platform libraries directly instead:
wayland — A protocol for communicating between an application (client) and a compositor which combines all the individual application windows into the final image presented to the display.
EGL — interface between Khronos rendering APIs such as OpenGL ES or Open VG and the underlying native platform window system.
It turns out it was a lot harder than I thought, but I'm not convinced it ought to be. By this I don't mean to say that wayland is easy, and that using it with OpenGL is easy, but I think the lack of any good guides or material make it harder than it should be. There's no high level explanation aimed at a beginner, or at least there isn't one that also uses OpenGL. Almost every example I found just gives up and falls back to using glfw or sdl2, or worse is just an unannotated example that assumes you are already familiar with EGL.
All I really wanted in this search was to find a simple guide that explained:
- What is actually happening at a high level?
- Is there any context that can help me that I might not already know?
- What prerequisite steps would I need to take to even start (dependencies)?
- Does my application take on dependencies that the user needs to install?
- Step-by-step, what do I need to do in order to get a triangle on screen?
- How portable is it?
Since I haven't been able to find one, and since I am sure to forget these steps and need to refer back sometime, I decided to write one myself.
This guide will be written using zig, though it should be possible to follow along in C or Rust with enough effort. A lot of the information in this guide, however, is language agnostic.
I ultimately chose to use zig over Rust, which I am very partial to, in the hopes that it would push me to go deeper and lower level, and it has been very fruitful in this respect, especially due to many of the libraries involved being C libraries.
This series is targeted at people who already have some familiarity with OpenGL, but who are hoping to learn a bit more and go a bit deeper into actually creating a window on Linux using wayland and EGL.
It will be a long journey, so strap in!
Part 1 — grokking Wayland. Wayland is, at its core, a way for GUI applications to cooperate over a shared resource: the screen. Although there are wayland libraries, wayland is a protocol for inter-process communication. In other words, it is a generic way for different programs that aren't even written in the same programming language to communicate over showing images on the screen.
Part 2 — up and running with EGL. EGL is a library for initializing an OpenGL context and informing the operating system and windowing system which bit of memory on the GPU our application intends to show on screen. It is also supported on multiple platforms, which means it is a platform independent way to do so.