Crate rafx_api[][src]

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::*;

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)

RafxCmdCopyBufferToTextureParams

Parameters for copying a buffer to a texture

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

RafxExtents2D

A 2d size for windows, textures, etc.

RafxExtents3D

A 3d size for windows, textures, etc.

RafxGraphicsPipelineDef

Used to create a RafxPipeline for graphics operations

RafxImmutableSamplers

Describes an immutable sampler key/value pair

RafxIndexBufferBinding

An index buffer to be bound during a renderpass

RafxOffsetSize

Used when binding buffers to descriptor sets

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

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.

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.

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.

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.

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

Type Definitions

RafxResult