GLFW
Kevin| 6 min read| December 09, 2022| [Devlog] #OpenGL #Context #RendererSo far we only used winit
and raw-gl-context
for our window and context
creations. However, to be certain that our current abstraction works with
different context providers, we will try glfw
.
GLFW stands for Graphics Library Framework and is a lightweight solution to create a window and OpenGL/Vulkan context. In C++ it's one of the most common solutions in addition to SDL2.
Well, let's see how our abstraction works with this C-based library.
First of all, we need to follow the prerequisites as described on the crates.io page and actually install GLFW on our machine. I use Arch by the way, so I only had to call:
but your mileage may vary.
Then we add it to our cargo.toml as another dev-dependency, disabling the default-features again(unless you need wayland support):
#alternative window and context creation
= { = "0.*", = false }
Now, we can pretty much yoink the sample code, just how we did with winit. However, I added some window hints to make sure we get suitable context, and I also made it a debug context, to make sure we get (more) debug messages with our callback. Also, we don't need event handling, so I just checked for ESC to close the window and removed the event handler
Note Because I am using a tiling window manager, I make the window not resizeable. This has the effect that the window "pops out" of the tiles and is floating on top.
Instead of putting it into main, I rename our existing one to winit_main
and
this new one glfw_main
, so that we can toggle between them in our actual main.
use ;
If all went well, running it should open a little window.
Now we implement our GLContext trait over a newtype again. GLFW
is one of the
aforementioned dependencies that actually require mutability for swap_buffers
and get_proc_address
, so our foresight paid off!
;
Now we try to do the same with did before: We try to create our context after making the GLContext current.
let mut context = new?;
However, now we run into an issue: glfw gives us a window, which also acts as context. If we were to transfer ownership, we wouldn't be able to use the window anymore.
Rust throws at us:
error: borrow of moved value: `window`
-/examples/playground.rs:204:12
|
195 | let = glfw
| ---------- move occurs because `window` has type ` Window`, which does not implement the `Copy` trait
...
202 | let context = new?;
204 | while !window.should_close
Let's take a moment to appreciate the amazing error messages that Rust provides.
Well, it makes sense. We can't move ownership when we are using the object afterwards. There are two possible solutions.
We either do what Rust-programmers hate and use shared mutable ownership, also known as good ol' Rc<RefCell<T>>
, so our
window and context can co-exist, or we add another function to expose the
"inner context" somehow.
Note
Actually, GLFW allows us to create a RenderContext
from the Window, which we
could pass to our Context with no problems. Its intended use is for sharing the
context between threads, so it would be kinda a misuse. We would also need to
expose something else to load the function pointers. Exposing the "inner"
field seems to be the better alternative.
While this is probably one of the few cases where Rc<RefCell<T>>
is not that
bad, since the overhead is negligible(just a few calls per frame) and since we
are limited to the main thread anyway, and there should never be a borrow issue,
exposing the inner field seems to be valuable in general.
So let's just add this function to our Context:
and then adjust the calls in our main. While we are at it, let's add our OpenGL calls as well:
There are some ways to make it a bit more ergonomic, like abusing Deref
or
changing the GLContext trait, but it's literally just 2 calls, so we can marginally bear it without making anyone angry.
Now, let's get rid of dem pesky GL calls in our playground.