Home

Awesome

Tephra

A modern C++17 graphics and compute library filling the gap between Vulkan and high-level APIs such as OpenGL.

License: MIT

Current version: v0.4.1

Links: User guide | API Documentation | Discussions

Build status: build status

About

Tephra aspires to provide a modern alternative to high-level graphics APIs like OpenGL and DirectX 11, while leveraging the benefits of the underlying Vulkan ecosystem. Its goal is to strike a good balance between ease-of-use, performance and relevance. To that end, Tephra provides:

Tephra is being used and partially developed by Bohemia Interactive Simulations.

Comparison to OpenGL / DirectX 11

One of the main differences when moving over from these older graphics APIs is the execution model. Much like in Vulkan, your draw calls don't take effect immediately in Tephra, but are instead recorded into either jobs or command lists that then get executed at a later time. There is no "immediate context". This allows for easy parallel recording and full control over the execution of workloads. Recording commands is usually done in two passes. A "job" first defines high-level commands such as:

The actual draw and dispatch commands then get recorded into the command lists of each pass after the job itself has been finalized. For convenience, a callback function can be optionally used to record a small command list in-place to help with code organization.

While Tephra handles most of the Vulkan-mandated synchronization automatically from the list of job commands, analyzing commands recorded into command lists would have unacceptable performance overhead. This could ordinarily be circumvented by manually specifying all the resource accesses of each render / compute pass, but for the majority of read-only accesses, the library offers the much more convenient "export" mechanism. Once an image or buffer is written to by a prior command or pass, it can be exported for all future accesses of a certain type, for example as a sampled texture. In effect, for a resource used inside shaders, this means that you generally only need to specify how it is going to be read in the future after each time you write into it. In most cases that only needs to be done once.

Another system inherited from Vulkan is its binding model. By default, resources get bound as descriptors in sets, rather than individually. You can think of a material's textures - the albedo map, normal map, roughness map, etc - as one descriptor set, through which all of its textures get bound to a compatible shader pipeline at the same time. Alternatively, you can use the "bindless" style of managing a global array of all of your textures inside a single giant descriptor set that you then index into inside your shaders. Tephra streamlines working with either method.

Comparison to Vulkan and other Vulkan abstractions

Starting from the initialization stage, Tephra already provides amenities for interacting with the varied world of Vulkan devices. Arbitrary number of queues can be used from each supported queue family, irrespective of the actual number exposed by Vulkan. Feature maps and format utilities further help handle hardware differences. Vulkan profile support is planned, making the process of choosing and relying upon a specific set of hardware features even easier. Overall, the initialization process is greatly simplified compared to raw Vulkan, akin to using the vk-bootstrap library.

Tephra leverages VMA for all of its resource allocations. On top of that, it allows efficient use of temporary resources within each job. Requested job-local resources can be aliased to the same memory location to reduce memory usage if their usage does not overlap. Growable ring buffers provide temporary staging buffers for easy uploading of data. The pools that all these reusable resources are allocated from are controllable and configurable. In general, the library tries to avoid allocations whenever possible, opting instead for pooling and reuse.

RAII is used to manage the lifetime of resources and other objects. Their destruction is delayed until the device is done using the objects, so they can be safely dropped even right after enqueuing a job. The idea of buffer and image views has been expanded upon and nearly all interactions with resources are done through these non-owning views. They can reference the entire resource, or just its part, and are relatively cheap to create on the fly.

Automatic synchronization is implemented between all job commands submitted to the same queue. The implementation tries to minimize the number of barriers without reordering the commands - the control of that is left in the hands of the user. All dependencies are resolved on a subresource level, including byte ranges for buffers and array layers / mip levels for images. Synchronization across different queues is handled with timeline semaphores and resource exports in a thread safe manner.

Many other Vulkan abstractions opt for render graphs to manage synchronization and resource aliasing. Tephra's approach works the same as a render graph that does not reorder passes, but has a smaller API footprint, does not force resource virtualization and is already familiar to users of last-gen graphics APIs. A render graph solution can be easily implemented on top of Tephra, if desired.

Descriptor sets differ from Vulkan's by being immutable. Changing them requires waiting until the device is done with any workload that uses it, which is infeasible in practice. Instead, Tephra recycles and reuses old descriptor sets to create new ones in the background. Besides these ordinary descriptor sets, a mutable descriptor set implementation is also provided. It can be useful for emulating the binding of individual resources, or to assist with a bindless resource model.

Tephra provides many other abstractions around Vulkan, such as pools, pipelines, swapchain and others to form an all-encompassing, high-level-ish graphics library. You generally do not need to use the Vulkan API directly, except when working with extensions that Tephra does not natively support, or when interacting with various device properties and features.

Feature list

The following features are already present:

The following features are planned and will likely be available in the future:

The following features are out of scope for the library and won't be included:

See the user guide for more detailed explanations and inline code examples of Tephra's features, or the examples folder for a runnable showcase.

Prerequisities

Building the documentation:

Contributing

Feel free to create issues, submit pull requests for non-trivial changes and participate in Discussions. Submitting examples, validation and tests is also very appreciated.