gfx-rs nuts and bolts

gfx-rs is a project bringing efficient cross-platform graphics to rust. This blog supposedly hosts the major milestones, concepts, and recaps of the project.


5 Year Anniversary

12 Jun 2019

gfx-rs project started with a simple idea: separate the API-specific logic of interaction with the graphic driver from a Rust application. That idea was brewing in the heads of @kvark and @bjz precisely 5 years ago, when they realized the common goal and kicked off the project. The Rust game dev community at the time consisted of a few prominent projects (like kiss3d, claymore, and q3) driven by individuals. They used gl-rs for rendering with no strong separation between higher levels, built as mostly monolithic systems. Because GL was known to keep the CPU occupied on the owning thread, we wanted to provide a separate thread dedicated to talking to the GPU. We had a lot to learn, some great contributors to meet, and hoped to eventually make Rust ecosystem a better place.

Our team (or rather, project community) grew quickly, and so did the feature scope of the project. Not only did we want to provide a graphics API, we also wanted to make it safe by removing the context from user actions. Instead of relying on the last texture bound, we required the user to provide the whole batch context with all data that was needed by the GPU. This ensured that redundant state changes could be avoided entirely. This lead us to exploration of macros and eventually the concept of gfx pipeline. The threading model started with 2 implicitly created threads (“device thread” and “render thread”), moving to one (just “device thread”), and eventually zero with the introduction of command buffers. Users were able to make a lot of shiny things with our API:

hematite some game zone of control

Growing a safe and opaque API also allowed us to explore targeting other graphics backends beyond OpenGL. We launched initiatives of bringing D3D11, Metal, WebGL, and eventually started looking at Vulkan. At the time, more and more users started to become interested in gfx-rs or port their project to it. The Rust ecosystem noticed some shortcomings of the gfx-rs API, but needed something decent to use. As we grew a better understanding of how the GPU hardware worked, what low-level APIs were, and why they were introduced, we realized that merely providing a Vulkan backend would not allow for the performance that low-level APIs are made for. We anticipated some things right: device abstraction, command buffers, but we missed some others: render passes, resource lifetimes, pools. At this time of conceptual crisis we were lucky to meet @msiglreith who started driving the project into the low level territory with unhuman persistence.

Eventually, the old gfx-rs was moved out into the pre-ll branch (for “pre low-level”), and we re-focused the effort on the low-level branch, which became the new master. These were the dark times for the project and community: times of confusion, failed expectations, lack of visible progress, and constant re-learning. The ecosystem, however, didn’t have a proper replacement for gfx-rs: glium was losing activity, vulkano was young and non-portable to macs and some windows machines, and luminance was GL only. Even when we reached certain level of polish with the low-level implementation, which worked in a basic form on Vulkan, D3D12, and Metal, its API was too unstable and complex for serious applications…

After long discussions about the API concepts to expose, we decided to just follow Vulkan verbatim, trusting the Khronos working group to make the informed design choices. We started a Vulkan Portability implementation based on gfx-rs, reworked the Metal and D3D12 backends, and eventually became ready to run production applications like Dota2 and Dolphin Emulator. This was a great success, an experimental confirmation that mapping from Vulkan is feasible. By the end of 2018 we managed to release hal-0.1 on crates.io, as an invitation for the Rust community to look at it seriously.

gfx-hal structure

There was still a missing elephant in the room: what API can we recommend for Rust developers? gfx-hal was obviously hard and only accessible to experts. A lot of attempts to build higher level abstractions failed under the complexity of the task. Eventually though, a sole effort of omni-viral saw the light of success, and today his Rendy project is a state of art “build your own engine” kit on top of gfx-hal. In the meantime, another interesting opportunity appeared on the horison: WebGPU was shaping up to be a simple, portable, yet modern and safe graphics/compute API, and we wanted to implement it. The safety guarantee seemed like a perfect match for a Rust API, and so did the level of abstraction chosen by the W3C working group, which we (gfx-rs community) were participating in. The idea was to develop a Rust library that exposed this safe API to both the native users and Gecko internals as an implementation detail, and that’s how wgpu-rs emerged.

Today, I’m happy to declare the dark ages of Rust graphics abstraction to be over, the renaissance is here! We are seeing wgpu-rs approaching 0.3 release and maturing quickly, with more and more users looking at experimenting with it. It’s easy to prototype an application with wgpu-rs and get instant access to Vulkan, D3D12, D3D11, Metal, and even OpenGL (to an extent) behind a simple unified API. We are seeing Amethyst moving to Rendy, and other successful engines (like ggez) and frameworks considering a similar move, aiming for an equally wide reach to API backends while paying less for the abstraction.

rendy-pbr

People change, ideas change, goals emerge, but gfx-rs community survives, and it’s as strong as ever. We have probably the largest GPU expert base in Rust ecosystem, and are always happy to share that expertise by helping out on gitter or any other chat platform. We are looking forward to the next 5 years ahead of us, excited to see what incredible things our users (and us) are able to build with Rust.