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