Crate rafx_api

Source
Expand description

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]).

rafx-api 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. 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.

Additional high-level documentation is available in the github repo

§Main API Objects

  • RafxApi - Primary entry point to using the API. Use the new_* functions to initialize the desired backend.
  • RafxBuffer - Memory that can be accessed by the rendering API. It may reside in CPU or GPU memory.
  • RafxCommandBuffer - A list of commands recorded by the CPU and submitted to the GPU.
  • RafxCommandPool - A pool of command buffers. A command pool is necessary to create a command buffer.
  • RafxDescriptorSetArray - An array of descriptor sets. These are expected to be pooled and reused.
  • RafxDeviceContext - A cloneable, thread-safe handle used to create graphics resources.
  • RafxFence - A GPU -> CPU synchronization mechanism.
  • RafxPipeline - Represents a complete GPU configuration for executing work.
  • RafxQueue - A queue allows work to be submitted to the GPU
  • RafxRootSignature - Represents the full “layout” or “interface” of a shader (or set of shaders.)
  • RafxSampler - Configures how images will be sampled by the GPU
  • RafxSemaphore - A GPU -> GPU synchronization mechanism.
  • RafxShader - Represents one or more shader stages, producing an entire “program” to execute on the GPU
  • RafxShaderModule - Rrepresents loaded shader code that can be used to create a pipeline.
  • RafxSwapchain - A set of images that act as a “backbuffer” of a window.
  • RafxTexture - An image that can be used by the GPU.

§Usage Summary

In order to interact with a graphics API, construct a RafxApi. A different new_* function exists for each backend.

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.

let device_context = api.device_context();

Most objects are created via RafxDeviceContext. For example:

// (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.

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.

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.

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.

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!)

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:

    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
    let texture = device_context.create_texture(&RafxTextureDef {
        extents: RafxExtents3D {
            width: 512,
            height: 512,
            depth: 1,
        },
        format: RafxFormat::R8G8B8A8_UNORM,
        ..Default::default()
    })?;

Re-exports§

pub use raw_window_handle;
pub use extra::swapchain_helper::*;
pub use crate::extra::indirect::*;

Modules§

extra
recommended_formats

Structs§

RafxApiDef
General configuration that all APIs will make best effort to respect
RafxBlendState
Affects the way the result of a pixel shader is blended with a value it will overwrite. Commonly used to enable “alpha-blending”.
RafxBlendStateRenderTarget
Configures blend state for a particular render target
RafxBlendStateTargets
Indicates what render targets are affected by a blend state
RafxBufferBarrier
A memory barrier for buffers. This is used to transition buffers between resource states and possibly from one queue to another
RafxBufferDef
Used to create a RafxBuffer
RafxBufferElementData
RafxCmdBlitParams
Parameters for blitting one image to another (vulkan backend only)
RafxCmdCopyBufferToBufferParams
Parameters for copying a buffer to a texture
RafxCmdCopyBufferToTextureParams
Parameters for copying a buffer to a texture
RafxCmdCopyTextureToTextureParams
RafxColorClearValue
A clear value for color attachments
RafxColorFlags
Flags for enabling/disabling color channels, used with RafxBlendState
RafxColorRenderTargetBinding
A color render target bound during a renderpass
RafxCommandBufferDef
Used to create a RafxCommandBuffer
RafxCommandPoolDef
Used to create a RafxCommandPool
RafxComputePipelineDef
Used to create a RafxPipeline for compute operations
RafxDepthState
Affects depth testing and stencil usage. Commonly used to enable “Z-buffering”.
RafxDepthStencilClearValue
A clear values for depth/stencil attachments. One or both values may be used depending on the format of the attached image
RafxDepthStencilRenderTargetBinding
A depth/stencil render target to be bound during a renderpass
RafxDescriptorElements
Specifies what value to assign to a descriptor set
RafxDescriptorIndex
A rafx-specific index that refers to a particular binding. Instead of doing name/binding lookups every frame, query the descriptor index during startup and use it instead. This is a more efficient way to address descriptors.
RafxDescriptorSetArrayDef
Used to create a RafxDescriptorSetArray
RafxDescriptorUpdate
Describes how to update a single descriptor
RafxDeviceInfo
Information about the device, mostly limits, requirements (like memory alignment), and flags to indicate whether certain features are supported
RafxDispatchIndirectCommand
RafxDrawIndexedIndirectCommand
RafxDrawIndirectCommand
RafxExtents2D
A 2d size for windows, textures, etc.
RafxExtents3D
A 3d size for windows, textures, etc.
RafxGlUniformMember
RafxGraphicsPipelineDef
Used to create a RafxPipeline for graphics operations
RafxHashedShaderPackage
A shader package and its hash. This allows storing the package with a pre-generated hash to file. The shader package is immutable to ensure the hash is never stale.
RafxImmutableSamplers
Describes an immutable sampler key/value pair
RafxIndexBufferBinding
An index buffer to be bound during a renderpass
RafxOffsetSize
Used in various APIs where we supply an offset/size pair
RafxPipelineReflection
Reflection data for a pipeline, created by merging shader stage reflection data
RafxRasterizerState
Affects rasterization, commonly used to enable backface culling or wireframe rendering
RafxReflectedDescriptorSetLayout
RafxReflectedDescriptorSetLayoutBinding
RafxReflectedEntryPoint
RafxReflectedVertexInput
RafxResourceState
The current state of a resource. When an operation is performed that references a resource, it must be in the correct state. Resources are moved between state using barriers.
RafxResourceType
Indicates how a resource will be used. In some cases, multiple flags are allowed.
RafxRootSignatureDef
Used to create a RafxRootSignature
RafxSamplerDef
Used to create a RafxSampler
RafxShaderModuleDef
Used to create a RafxShaderModule
RafxShaderPackage
Owns data necessary to create a shader module in (optionally) multiple APIs.
RafxShaderPackageHash
Provides a stable has for contents of a shader package
RafxShaderResource
A data source within a shader. Often a descriptor or push constant.
RafxShaderResourceBindingKey
Indicates where a resource is bound
RafxShaderStageDef
Describes a single stage within a shader
RafxShaderStageFlags
Indicates a particular stage of a shader, or set of stages in a shader. Similar to VkShaderStageFlagBits
RafxShaderStageReflection
Reflection data for a single shader stage
RafxSwapchainDef
Used to create a RafxSwapchain
RafxSwapchainImage
Represents an image owned by the swapchain
RafxTextureBarrier
A memory barrier for textures. This is used to transition textures between resource states and possibly from one queue to another.
RafxTextureDef
Used to create a RafxTexture
RafxVertexBufferBinding
A vertex buffer to be bound during a renderpass
RafxVertexLayout
Describes how vertex attributes are laid out within one or more buffers
RafxVertexLayoutAttribute
Describes an attribute within a RafxVertexLayout
RafxVertexLayoutBuffer
Describes a buffer that provides vertex attribute data (See RafxVertexLayout)

Enums§

RafxAddressMode
Affects image sampling, particularly for UV coordinates outside the [0, 1] range. Similar to VkSamplerAddressMode
RafxApi
Primary entry point to using the API. Use the new_* functions to initialize the desired backend.
RafxApiType
RafxBarrierQueueTransition
Determines if a barrier is transferring a resource from one queue to another.
RafxBlendFactor
Affects blending. Similar to VkBlendFactor
RafxBlendOp
Affects blending. Similar to VkBlendOp
RafxBuffer
Memory that can be accessed by the rendering API. It may reside in CPU or GPU memory.
RafxColorType
The color space an image data is in. The correct color space often varies between texture types (like normal maps vs. albedo maps).
RafxCommandBuffer
A list of commands recorded by the CPU and submitted to the GPU.
RafxCommandPool
A pool of command buffers. A command pool is necessary to create a command buffer.
RafxCompareOp
Affects depth testing and sampling. Similar to VkCompareOp
RafxCullMode
Determines if we cull polygons that are front-facing or back-facing. Facing direction is determined by RafxFrontFace, sometimes called “winding order”. Similar to VkCullModeFlags
RafxDescriptorKey
Selects a particular descriptor in a descriptor set
RafxDescriptorSetArray
An array of descriptor sets. These are expected to be pooled and reused.
RafxDescriptorSetHandle
A lightweight handle to a specific descriptor set in a RafxDescriptorSetArray.
RafxDeviceContext
A cloneable, thread-safe handle used to create graphics resources.
RafxError
Generic error that contains all the different kinds of errors that may occur when using the API
RafxFence
A GPU -> CPU synchronization mechanism.
RafxFenceStatus
Indicates the current state of a fence.
RafxFillMode
Whether to fill in polygons or not. Similar to VkPolygonMode
RafxFilterType
Filtering method when sampling. Similar to VkFilter
RafxFormat
Describes the encoding of an image or buffer.
RafxFrontFace
Determines what winding order is considerered the front face of a polygon. Similar to VkFrontFace
RafxImmutableSamplerKey
Indicates which immutable sampler is being set
RafxIndexType
The size of index buffer elements
RafxLoadOp
Determines if the contents of an image attachment in a renderpass begins with its previous contents, a clear value, or undefined data. Similar to VkAttachmentLoadOp
RafxMemoryUsage
Indicates how the memory will be accessed and affects where in memory it needs to be allocated.
RafxMipMapMode
Similar to VkSamplerMipmapMode
RafxPipeline
Represents a complete GPU configuration for executing work.
RafxPipelineType
Indicates the type of pipeline, roughly corresponds with RafxQueueType
RafxPresentSuccessResult
Indicates the result of presenting a swapchain image
RafxPrimitiveTopology
How to intepret vertex data into a form of geometry. Similar to VkPrimitiveTopology
RafxQueue
A queue allows work to be submitted to the GPU
RafxQueueType
Used to indicate which type of queue to use. Some operations require certain types of queues.
RafxRootSignature
Represents the full “layout” or “interface” of a shader (or set of shaders.)
RafxSampleCount
Number of MSAA samples to use. 1xMSAA and 4xMSAA are most broadly supported
RafxSampler
Configures how images will be sampled by the GPU
RafxSemaphore
A GPU -> GPU synchronization mechanism.
RafxShader
Represents one or more shader stages, producing an entire “program” to execute on the GPU
RafxShaderModule
Rrepresents loaded shader code that can be used to create a pipeline.
RafxShaderPackageDx12
Metal-specific shader package. Can be used to create a RafxShaderModuleDef, which in turn is used to initialize a shader module GPU object
RafxShaderPackageGles2
GL ES 2.0-specific shader package. Can be used to create a RafxShaderModuleDef, which in turn is used to initialize a shader module GPU object
RafxShaderPackageGles3
GL ES 3.0-specific shader package. Can be used to create a RafxShaderModuleDef, which in turn is used to initialize a shader module GPU object
RafxShaderPackageMetal
Metal-specific shader package. Can be used to create a RafxShaderModuleDef, which in turn is used to initialize a shader module GPU object
RafxShaderPackageVulkan
Vulkan-specific shader package. Can be used to create a RafxShaderModuleDef, which in turn is used to initialize a shader module GPU object
RafxStencilOp
Similar to VkStencilOp
RafxStoreOp
Determines if the contents of an image attachment in a rander pass will store the resulting state for use after the render pass
RafxSwapchain
A set of images that act as a “backbuffer” of a window.
RafxSwapchainColorSpace
RafxTexture
An image that can be used by the GPU.
RafxTextureBindType
Used when binding a texture to select between different ways to bind the texture
RafxTextureDimensions
Determines how many dimensions the texture will have.
RafxValidationMode
Controls if validation is enabled or not. The requirements/behaviors of validation is API-specific.
RafxVertexAttributeRate
Affects how quickly vertex attributes are consumed from buffers, similar to VkVertexInputRate

Constants§

ALL_SHADER_STAGE_FLAGS
Contains all the individual stages
MAX_DESCRIPTOR_SET_LAYOUTS
The maximum descriptor set layout index allowed. Vulkan only guarantees up to 4 are available
MAX_RENDER_TARGET_ATTACHMENTS
The maximum number of simultaneously attached render targets
MAX_VERTEX_INPUT_BINDINGS
RAFX_VALID_API_TYPES

Type Aliases§

RafxResult