Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Context & Texture

Context

Entry point for all GPU operations. Holds the Metal device, command queue, and compiled shader library.

#![allow(unused)]
fn main() {
use vx_vision::Context;

let ctx = Context::new()?;
}

Texture creation

#![allow(unused)]
fn main() {
// From pixel data (ShaderRead)
let gray  = ctx.texture_gray8(&pixels, w, h)?;
let float = ctx.texture_r32float(&data, w, h)?;
let color = ctx.texture_rgba8(&pixels, w, h)?;

// Empty output (ShaderWrite)
let out = ctx.texture_output_gray8(w, h)?;
let out = ctx.texture_output_r32float(w, h)?;
let out = ctx.texture_output_rgba8(w, h)?;

// Pipeline intermediates (ShaderRead | ShaderWrite)
let tmp = ctx.texture_intermediate_gray8(w, h)?;
let tmp = ctx.texture_intermediate_r32float(w, h)?;
}

Use output_* when a texture is only written to by a kernel. Use intermediate_* when a texture is written by one kernel and read by the next in a pipeline chain.

Texture

Wraps a Metal texture with tracked dimensions and format.

Readback

#![allow(unused)]
fn main() {
let pixels: Vec<u8>  = tex.read_gray8();      // R8Unorm
let data:   Vec<f32> = tex.read_r32float();    // R32Float
let pixels: Vec<u8>  = tex.read_rgba8();       // RGBA8Unorm (4 bytes/pixel)
}

Call readback only after the GPU command buffer has completed. Reading while the GPU is still writing produces undefined results.

Properties

#![allow(unused)]
fn main() {
let w = tex.width();       // u32
let h = tex.height();      // u32
let f = tex.format();      // TextureFormat enum
}

External textures

For AVFoundation or Core Video integration, wrap an existing Metal texture without copying:

#![allow(unused)]
fn main() {
use vx_vision::{Texture, TextureFormat};

let tex = Texture::from_metal_texture(metal_tex, w, h, TextureFormat::RGBA8Unorm);
}

Pipeline

Batches multiple kernel dispatches into a single Metal command buffer.

#![allow(unused)]
fn main() {
use vx_vision::Pipeline;

let pipe = Pipeline::begin(&ctx)?;
let cmd = pipe.cmd_buf();

let _s1 = blur.encode(&ctx, cmd, &input, &temp, &cfg)?;
sobel.encode(&ctx, cmd, &temp)?;

let _retained = pipe.commit_and_wait();
}

Intermediate textures and encoded state must outlive the command buffer. The commit_and_wait() return value holds retained textures.

For CPU/GPU overlap:

#![allow(unused)]
fn main() {
let mut pipe = Pipeline::begin(&ctx)?;
blur.encode(&ctx, pipe.cmd_buf(), &input, &output, &cfg)?;
pipe.commit();       // non-blocking
// ... CPU work ...
pipe.wait();         // block until GPU done
}

TexturePool

Recycles GPU textures by (width, height, format) to avoid repeated allocation.

#![allow(unused)]
fn main() {
use vx_vision::TexturePool;

let mut pool = TexturePool::new();
let tex = pool.acquire_gray8(&ctx, 1920, 1080)?;
// ... use tex ...
pool.release(tex);

// Second acquire reuses the cached texture
let tex = pool.acquire_gray8(&ctx, 1920, 1080)?;
}

All pool textures have ShaderRead | ShaderWrite usage flags.

#![allow(unused)]
fn main() {
let pool = TexturePool::with_capacity(4);   // max 4 per bucket
pool.hit_rate();                             // cache efficiency
pool.cached_count();                         // total cached
pool.clear();                                // free all
}

Error handling

All fallible operations return Result<T, vx_vision::Error>. Error variants:

VariantMeaning
DeviceNotFoundNo Metal GPU available
ShaderMissing(String)Named shader function not in metallib
PipelineCompile(String)Metal failed to compile a pipeline
BufferAlloc { bytes }GPU buffer allocation failed
TextureSizeMismatchTexture dimensions don’t match
InvalidConfig(String)Parameter out of range
Gpu(String)Runtime GPU error