LearningGfxHal

Disclaimer: gfx-rs is a work in progress. This is intended to be up to date with the gfx-hal 0.1 release (TODO: Figure out actual release numbers for the crate as a whole). This is also not a tutorial on 3D programming. It assumes that the reader is familiar with OpenGL or a similar API.

Learning gfx-rs

So what is gfx-rs, and how does it work? The goal is to have a 3D graphics API that is portable across different 3D implementations, in a fairly low-level and efficient way. For instance, you could take the same code and run it on OpenGL, Vulkan and DirectX and it will do the same thing on all of them.

How does it do this? Basically by providing an abstraction that is a common superset of the behavior of all these API’s. All of these systems are, in the end, a way of managing resources, shoving data into the graphics card, defining shader programs that run on this data with the given resources, and then telling it to actually do stuff. gfx-rs tries to wrap all that up in a common API that’s applicable to any of these drawing backends.

Background

So gfx-rs has changed a fair bit since its inception. The fundamental idea of gfx-rs is to present a modern, cross-platform graphics API in Rust, offering maximum flexibility and performance on all major platforms: Metal, OpenGL, Vulkan, and DirectX. The 0.17 version of gfx-rs documented in [[LearningGfx]] did an okay job of this, but the developers became increasingly unsatisfied with the tradeoffs they had to make between safety and speed to make a Vulkan backend work well enough, and decided to change their strategy. This lead to the birth of the “low-level” gfx-ll branch, which eventually became the gfx-hal crate (“hal” meaning “hardware abstraction layer”).

gfx-hal is a lower-level, unsafe API that corresponds very closely to a Rust-ier Vulkan. The goal is minimum overhead in exchange for safety, allowing higher-level graphics engines (such as the nascent gfx-render project) to be built atop it that can choose how much overhead they are willing to live with. A Vulkan-y API was chosen because it was realized that implementing Vulkan efficiently in DirectX12 or Metal would not be terribly difficult, but making all three of them cooperate in the existing gfx-rs code was becoming increasingly unsatisfactory. It also allows possibilities like the Vulkan portability initiative, which intends to write a portable subset of the Vulkan API, with exposed C bindings, so that any Vulkan application can run on any platform using gfx-rs as the renderer.

OpenGL is a little bit of odd one out here, since it’s a very different sort of API than the other three and will probably need a thicker translation layer. However, it is still a first-class citizen: gfx-rs is intended to support web and mobile platforms, and for the forseeable future that means using OpenGL.

Parts of a gfx-rs program

Notes to be incorporated

A place for me to collect notes about things that need explaining in gfx-rs

  • It’s basically vulkan-based API now. The goal is to make a mostly-safe portability layer, so that people can write Vulkan code (or code that’s easy to port from Vulkan) and have it then run on gfx-rs, and that gfx-rs can run on many backends (including OpenGL, WebGL, etc). So its approach is now sort of “make your vulkan run anywhere”.
  • This is because people weren’t happy with the runtime costs of a completely safe portability layer; now the goal is to have it as safe as is feasible, but with zero-overhead trumping that priority. There will probably be safer rendering engines built atop it, the mythical gfx_render or whatever it’s called. Amethyst and ggez devs are both interested in making such a thing, but it doesn’t exist yet.
  • Lots of generics. Most of them based around Backend that collects lots of OTHER generics together.
  • Lots of types that are expressed both as an enum, and a trait+marker struct. This is to let you express a thing as both runtime and compile time constraints.
  • Resources are managed explicitly, not by RAII! Vulkan style, not Rust style.

Device initialization: Okay, you get an Instance from the backend. From an Instance you get a list of Adapter’s. Each Adapter contains a B::PhysicalDevice, which implements the PhysicalDevice trait. From a PhysicalDevice you can get a Gpu. A Gpu contains B::Device, which implements the Device trait. Whew! You can also get a Device straight from an Adapter::open_with()

Texels are elements of a texture, fragments are elements of a fragment shader, pixels are elements displayed on screen.