rafx_api/lib.rs
1//! Rafx API is an `unsafe` graphics API abstraction layer designed specifically for games and tools
2//! for games. The goal is to achieve near-native performance with reduced complexity. It may be
3//! used directly, or indirectly through other crates in rafx (such as [rafx-resources] and
4//! [rafx-assets]).
5//!
6//! `rafx-api` is an **opinionated** API. It does not expose every possible operation a graphics API
7//! might provide. However, the wrapped API-specific objects are exposed in an easily accessible
8//! manner.
9//!
10//! The API does not track resource lifetimes or states (such as vulkan image layouts) or try to
11//! enforce safe usage at compile time or runtime. Safer abstractions are available in
12//! rafx-framework and rafx-assets.
13//!
14//! **Every API call is potentially unsafe.** However, the unsafe keyword is only placed on APIs
15//! that are particularly likely to cause undefined behavior if used incorrectly.
16//!
17//! The general shape of the API is inspired by
18//! [The Forge](https://github.com/ConfettiFX/The-Forge). It was chosen for its modern design,
19//! multiple working backends, open development model, and track record of shipped games. However,
20//! there are some changes in API design, feature set, and implementation details.
21//!
22//! [Additional high-level documentation is available in the github repo](https://github.com/aclysma/rafx/blob/master/docs/index.md)
23//!
24//! # Main API Objects
25//!
26//! * [RafxApi] - Primary entry point to using the API. Use the new_* functions to initialize the desired backend.
27//! * [RafxBuffer] - Memory that can be accessed by the rendering API. It may reside in CPU or GPU memory.
28//! * [RafxCommandBuffer] - A list of commands recorded by the CPU and submitted to the GPU.
29//! * [RafxCommandPool] - A pool of command buffers. A command pool is necessary to create a command buffer.
30//! * [RafxDescriptorSetArray] - An array of descriptor sets. These are expected to be pooled and reused.
31//! * [RafxDeviceContext] - A cloneable, thread-safe handle used to create graphics resources.
32//! * [RafxFence] - A GPU -> CPU synchronization mechanism.
33//! * [RafxPipeline] - Represents a complete GPU configuration for executing work.
34//! * [RafxQueue] - A queue allows work to be submitted to the GPU
35//! * [RafxRootSignature] - Represents the full "layout" or "interface" of a shader (or set of shaders.)
36//! * [RafxSampler] - Configures how images will be sampled by the GPU
37//! * [RafxSemaphore] - A GPU -> GPU synchronization mechanism.
38//! * [RafxShader] - Represents one or more shader stages, producing an entire "program" to execute on the GPU
39//! * [RafxShaderModule] - Rrepresents loaded shader code that can be used to create a pipeline.
40//! * [RafxSwapchain] - A set of images that act as a "backbuffer" of a window.
41//! * [RafxTexture] - An image that can be used by the GPU.
42//!
43//! # Usage Summary
44//!
45//! In order to interact with a graphics API, construct a `RafxApi`. A different new_* function
46//! exists for each backend.
47//!
48//! ```ignore
49//! let api = RafxApi::new_vulkan(...);
50//! ```
51//!
52//! After initialization, most interaction will be via `RafxDeviceContext` Call
53//! `RafxApi::device_context()` on the the api object to obtain a cloneable handle that can be
54//! used from multiple threads.
55//!
56//! ```ignore
57//! let device_context = api.device_context();
58//! ```
59//!
60//! Most objects are created via `RafxDeviceContext`. For example:
61//!
62//! ```ignore
63//! // (See examples for more detail here!)
64//! let texture = device_context.create_texture(...)?;
65//! let buffer = device_context.create_buffer(...)?;
66//! let shader_module = device_context.create_shader_module(...)?;
67//! ```
68//!
69//! In order to submit work to the GPU, a `RafxCommandBuffer` must be submitted to a `RafxQueue`.
70//! Most commonly, this needs to be a "Graphics" queue.
71//!
72//! Obtaining a `RafxQueue` is straightforward. Here we will get a "Graphics" queue. This queue type
73//! supports ALL operations (including compute) and is usually the correct one to use if you aren't
74//! sure.
75//!
76//! ```ignore
77//! let queue = device_context.create_queue(RafxQueueType::Graphics)?;
78//! ```
79//!
80//! A command buffer cannot be created directly. It must be allocated out of a pool.
81//!
82//! The command pool and all command buffers allocated from it share memory. The standard rust rules
83//! about mutability apply but are not enforced at compile time or runtime.
84//! * Do not modify two command buffers from the same pool concurrently
85//! * Do not allocate from a command pool while modifying one of its command buffers
86//! * Once a command buffer is submitted to the GPU, do not modify its pool, or any command buffers
87//! created from it, until the GPU completes its work.
88//!
89//! In general, do not modify textures, buffers, command buffers, or other GPU resources while a
90//! command buffer referencing them is submitted. Additionally, these resources must persist for
91//! the entire duration of the submitted workload.
92//!
93//! ```ignore
94//! let command_pool = queue.create_command_pool(&RafxCommandPoolDef {
95//! transient: true
96//! })?;
97//!
98//! let command_buffer = command_pool.create_command_buffer(&RafxCommandBufferDef {
99//! is_secondary: false,
100//! })?;
101//! ```
102//!
103//! Once a command buffer is obtained, write to it by calling "cmd" functions on it, For example,
104//! drawing primitives looks like this. Call begin() before writing to it, and end() after finished
105//! writing to it.
106//!
107//! ```ignore
108//! command_buffer.begin()?;
109//! // other setup...
110//! command_buffer.cmd_draw(3, 0)?;
111//! command_buffer.end()?;
112//! ```
113//!
114//! For the most part, no actual work is performed when calling these functions. We are just
115//! "scheduling" work to happen later when we give the command buffer to the GPU.
116//!
117//! After writing the command buffer, it must be submitted to the queue. The "scheduled" work
118//! described in the command buffer will happen asynchronously from the rest of the program.
119//!
120//! ```ignore
121//! queue.submit(
122//! &[&command_buffer],
123//! &[], // No semaphores or fences in this example
124//! &[],
125//! None
126//! )?;
127//! queue.wait_for_queue_idle()?;
128//! ```
129//!
130//! The command buffer, the command pool it was allocated from, all other command buffers allocated
131//! from that pool, and any other resources referenced by this command buffer cannot be dropped
132//! until the queued work is complete, and generally speaking must remain immutable.
133//!
134//! More fine-grained synchronization is available via RafxFence and RafxSemaphore but that will
135//! not be covered here.
136//!
137//! # Resource Barriers
138//!
139//! CPUs generally provide a single "coherent" view of memory, but this is not the case for GPUs.
140//! Resources can also be stored in many forms depending on how they are used. (The details of this
141//! are device-specific and outside the scope of these docs). Resources must be placed into an
142//! appropriate state to use them.
143//!
144//! Additionally modifying a resource (or transitioning its state) can result in memory hazards. A
145//! memory hazard is when reading/writing to memory occurs in an undefined order, resulting in
146//! undefined behavior.
147//!
148//! `Barriers` are used to transition resources into the correct state and to avoid these hazards.
149//! Here is an example where we take an image from the swapchain and prepare it for use.
150//! (We will also need a barrier after we modify it to transition it back to PRESENT!)
151//!
152//! ```ignore
153//! command_buffer.cmd_resource_barrier(
154//! &[], // no buffers to transition
155//! &[
156//! // Transition `texture` from PRESENT state to RENDER_TARGET state
157//! RafxTextureBarrier::state_transition(
158//! &texture,
159//! RafxResourceState::PRESENT,
160//! RafxResourceState::RENDER_TARGET,
161//! )
162//! ],
163//! )?;
164//! ```
165//!
166//! # "Definition" structs
167//!
168//! Many functions take a "def" parameter. For example, `RafxDeviceContext::create_texture()` takes
169//! a single `RafxTextureDef` parameter. Here is an example call:
170//!
171//! ```ignore
172//! let texture = device_context.create_texture(&RafxTextureDef {
173//! extents: RafxExtents3D {
174//! width: 512,
175//! height: 512,
176//! depth: 1,
177//! },
178//! array_length: 1,
179//! mip_count: 1,
180//! sample_count: RafxSampleCount::SampleCount1,
181//! format: RafxFormat::R8G8B8A8_UNORM,
182//! resource_type: RafxResourceType::TEXTURE,
183//! dimensions: RafxTextureDimensions::Dim2D,
184//! })?;
185//! ```
186//!
187//! There are advantages to this approach:
188//! * The code is easier to read - parameters are clearly labeled
189//! * Default values can be used
190//! * When new "parameters" are added, if Default is used, the code will still compile. This avoids
191//! boilerplate to implement the builder pattern
192//!
193//! ```ignore
194//! let texture = device_context.create_texture(&RafxTextureDef {
195//! extents: RafxExtents3D {
196//! width: 512,
197//! height: 512,
198//! depth: 1,
199//! },
200//! format: RafxFormat::R8G8B8A8_UNORM,
201//! ..Default::default()
202//! })?;
203//! ```
204//!
205//!
206
207//
208// Re-export upstream metal crates
209//
210#[cfg(feature = "rafx-metal")]
211pub use foreign_types_shared;
212#[cfg(feature = "rafx-metal")]
213pub use metal_rs;
214#[cfg(feature = "rafx-metal")]
215pub use objc;
216
217//
218// Re-export upstream vulkan crates
219//
220#[cfg(feature = "rafx-vulkan")]
221pub use ash;
222#[cfg(feature = "rafx-vulkan")]
223pub use gpu_allocator;
224
225//
226// Re-export upstream API-agnostic crates
227//
228pub use raw_window_handle;
229
230//
231// Backends
232//
233#[cfg(feature = "rafx-dx12")]
234pub use backends::dx12;
235#[cfg(feature = "rafx-dx12")]
236pub use backends::dx12::RafxApiDefDx12;
237
238#[cfg(feature = "rafx-metal")]
239pub use backends::metal;
240#[cfg(feature = "rafx-metal")]
241pub use backends::metal::RafxApiDefMetal;
242
243#[cfg(feature = "rafx-vulkan")]
244pub use backends::vulkan;
245#[cfg(feature = "rafx-vulkan")]
246pub use backends::vulkan::RafxApiDefVulkan;
247
248#[cfg(feature = "rafx-gles2")]
249pub use backends::gles2;
250#[cfg(feature = "rafx-gles2")]
251pub use backends::gles2::RafxApiDefGles2;
252
253#[cfg(feature = "rafx-gles3")]
254pub use backends::gles3;
255#[cfg(feature = "rafx-gles3")]
256pub use backends::gles3::RafxApiDefGles3;
257
258#[cfg(any(
259 feature = "rafx-empty",
260 not(any(
261 feature = "rafx-dx12",
262 feature = "rafx-metal",
263 feature = "rafx-vulkan",
264 feature = "rafx-gles2",
265 feature = "rafx-gles3"
266 ))
267))]
268pub use backends::empty;
269
270//
271// Public modules
272//
273pub mod extra;
274
275//
276// Internal Modules
277//
278mod backends;
279mod error;
280mod internal_shared;
281mod reflection;
282mod types;
283
284//
285// API-agnostic API modules
286//
287mod api;
288mod buffer;
289mod command_buffer;
290mod command_pool;
291mod descriptor_set_array;
292mod device_context;
293mod fence;
294mod pipeline;
295mod queue;
296mod root_signature;
297mod sampler;
298mod semaphore;
299mod shader;
300mod shader_module;
301mod swapchain;
302mod texture;
303
304//
305// Constants
306//
307
308/// The maximum descriptor set layout index allowed. Vulkan only guarantees up to 4 are available
309pub const MAX_DESCRIPTOR_SET_LAYOUTS: usize = 4;
310/// The maximum number of simultaneously attached render targets
311// In sync with RafxBlendStateTargets
312// Also coupled to d3d12 API D3D12_GRAPHICS_PIPELINE_STATE_DESC
313pub const MAX_RENDER_TARGET_ATTACHMENTS: usize = 8;
314// Vulkan guarantees up to 16
315pub const MAX_VERTEX_INPUT_BINDINGS: usize = 16;
316
317//
318// Exported public API
319//
320pub use api::*;
321pub use buffer::*;
322pub use command_buffer::*;
323pub use command_pool::*;
324pub use descriptor_set_array::*;
325pub use device_context::*;
326pub use error::*;
327pub use extra::swapchain_helper::*;
328pub use fence::*;
329pub use pipeline::*;
330pub use queue::*;
331pub use root_signature::*;
332pub use sampler::*;
333pub use semaphore::*;
334pub use shader::*;
335pub use shader_module::*;
336pub use swapchain::*;
337pub use texture::*;
338pub use types::*;