Backend-agnostic GPU command buffer and driver contracts (extracted from kagura)
┌─────────────────────────────────────────────────────────┐
│ your renderer │
│ builds DrawTrianglesCommand values │
└────────────────┬────────────────────────────────────────┘
│ enqueue_draw_triangles
┌────────────────▼────────────────────────────────────────┐
│ CommandQueue (SimpleCommandQueue or your own) │
│ merges adjacent compatible draws │
└────────────────┬────────────────────────────────────────┘
│ flush_commands
┌────────────────▼────────────────────────────────────────┐
│ GraphicsDriver (your backend impl) │
│ WebGPU / wgpu-native / WebGL2 / offscreen / null │
└─────────────────────────────────────────────────────────┘test "draw two triangles into a null driver" {
// 1. Pick a driver. `create_null_graphics` returns a StubGraphicsDriver
// that counts begin/end/draw calls but doesn't talk to a GPU.
let driver = @gfx.create_null_graphics(640, 480)
driver.initialize()
// 2. Allocate a render target image and a shader from the driver.
let dst = driver.new_image(640, 480)
let shader = driver.new_shader("// dummy WGSL")
// 3. Build a draw command. Layout is type-erased: vertex_data is a
// flat Array[Double], indices are Array[Int].
let region = @gfx.new_dst_region(0, 0, 640, 480, 6)
let command = @gfx.new_draw_triangles_command(
dst,
shader,
[region],
0,
1, // pipeline_id (opaque)
0, // uniform_hash
@gfx.BlendMode::Alpha,
[
0.0, 0.0, 0.0, 0.0,
1.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0,
],
[0, 1, 2, 1, 3, 2],
[], // no source textures
[], // no uniforms
)
// 4. Inspect the command summary (useful for batching / debug).
let dispatch = command.build_dispatch()
let _ = dispatch.checksum() // stable Int fingerprint
// 5. Enqueue and flush through the driver. Two identical commands
// will be merged by SimpleCommandQueue into one backend draw.
let queue = @gfx.new_simple_command_queue()
queue.enqueue_draw_triangles(command)
queue.enqueue_draw_triangles(command)
@gfx.flush_commands(driver, queue, false)
}struct MyDriver { /* device, queue, swapchain, ... */ }
impl @gfx.GraphicsDriver for MyDriver with initialize(self) {
// bind a device / swapchain
}
impl @gfx.GraphicsDriver for MyDriver with begin(self, pass) {
// open a render pass; `pass.clear_color` / `pass.clear_enabled`
}
impl @gfx.GraphicsDriver for MyDriver with end(self, present) {
// close the pass; swap buffers if `present`
}
impl @gfx.GraphicsDriver for MyDriver with resize(self, w, h) { ... }
impl @gfx.GraphicsDriver for MyDriver with new_image(self, w, h) {
// allocate a texture and return a handle that wraps your backend id
@gfx.new_image_handle(/* backend id */ 1, w, h)
}
impl @gfx.GraphicsDriver for MyDriver with new_shader(self, src) { ... }
impl @gfx.GraphicsDriver for MyDriver with draw_triangles(self, cmd) {
// upload cmd.vertex_data / cmd.indices (or reuse a cached payload
// when cmd.resource_cache_key != 0) and issue one draw call per
// cmd.dst_regions entry, respecting cmd.blend, cmd.uniform_dwords, ...
}
impl @gfx.GraphicsDriver for MyDriver with read_pixels(self, x, y, w, h) {
// None if not supported; otherwise an RGBA8 Array[Int]
}| Tier | Meaning | Examples |
|---|---|---|
| Stable input (pub(all) struct/enum) | You build these as struct literals | Color, DrawTrianglesCommand, DstRegion, RenderPassDesc, blend / filter / uniform enums, ShaderCompileRequest, BuiltinShaderKey, ... |
| Stable output (pub struct) | gfx hands these to you; read-only fields, no struct-literal construction | StubGraphicsDriver, Basic{ShaderFrontend,UniformCanonicalizer,BuiltinShaderSourceRepo}, BuiltinShaderCacheStats, GraphicsResizeStats, FramebufferSnapshot, PixelDiffResult, SimpleCommandQueue, {Native,Web}GraphicsHooks |
| Backend-return (pub(all) struct) | You build these only from inside a trait impl that returns them | ImageHandle, ShaderHandle, PipelineHandle, ShaderIR, ShaderSourceHash, PackedUniforms, SurfaceToken |
| Open trait (pub(open) trait) | You implement these in your backend / canonicalizer / etc. | GraphicsDriver, CommandQueue, ShaderFrontend, UniformCanonicalizer, BuiltinShaderSourceRepo, SurfaceProvider, GraphicsBackendFactory |
src/
handle.mbt ImageHandle / ShaderHandle / PipelineHandle / FilterMode
blend.mbt BlendFactor / Operation / Equation / Mode + conversions
contracts.mbt DstRegion / Color / RenderPassDesc / DrawTrianglesCommand /
DrawCommandDispatch / dispatch_checksum
driver.mbt GraphicsDriver trait + FramebufferSnapshot / PixelDiffResult
queue.mbt CommandQueue trait + SimpleCommandQueue + merge logic
surface.mbt SurfaceKind / SurfaceToken / SurfaceProvider + factories
backend_contracts.mbt StubGraphicsDriver / GraphicsBackendKind / hooks registry
backend_native_hooks_stub.mbt
backend_web_hooks_stub.mbt
shader_contracts.mbt ShaderIR / ShaderFrontend / Uniform plumbing{
"deps": {
"mizchi/gfx": { "path": "../gfx-mbt" }
}
}import { "mizchi/gfx" @gfx }pub(open) trait BuiltinShaderSourceRepo {
fn shader_source(Self, key : BuiltinShaderKey) -> String
fn shader_source_ex(Self, key : BuiltinShaderKeyEx) -> String
}pub(open) trait CommandQueue {
fn enqueue_draw_triangles(Self, command : DrawTrianglesCommand) -> Unit
fn flush(Self) -> Array[DrawTrianglesCommand]
}pub(open) trait GraphicsBackendFactory {
fn create(Self, kind : GraphicsBackendKind, surface : SurfaceToken, options : GraphicsBackendOptions) -> Unit raise
}pub(open) trait GraphicsDriver {
fn initialize(Self) -> Unit raise
fn begin(Self, pass : RenderPassDesc) -> Unit raise
fn end(Self, present : Bool) -> Unit raise
fn resize(Self, width : Int, height : Int) -> Unit raise
fn new_image(Self, width : Int, height : Int) -> ImageHandle raise
fn new_shader(Self, source : String) -> ShaderHandle raise
fn draw_triangles(Self, command : DrawTrianglesCommand) -> Unit raise
fn read_pixels(Self, x : Int, y : Int, width : Int, height : Int) -> Array[Int]? raise
}driver.begin(pass)
for command in commands {
driver.draw_triangles(command)
}
driver.end(present)pub(open) trait ShaderFrontend {
fn compile_ir(Self, request : ShaderCompileRequest) -> ShaderIR raise
fn calc_source_hash(Self, request : ShaderCompileRequest) -> ShaderSourceHash raise
}pub(open) trait UniformCanonicalizer {
fn append_user_uniforms(Self, layout : UniformLayout, uniforms : Array[NamedUniform]) -> PackedUniforms raise
fn prepend_preserved_uniforms(Self, uniforms : PackedUniforms, context : PreservedUniformContext) -> PackedUniforms
fn filter_unused_uniforms(Self, ir : ShaderIR, layout : UniformLayout, uniforms : PackedUniforms) -> PackedUniforms
}pub struct BasicBuiltinShaderSourceRepo {
cache : Array[BuiltinShaderSourceCacheEntry]
cache_ex : Array[BuiltinShaderSourceCacheEntryEx]
tick : Int
max_cache_entries : Int
hit_count : Int
miss_count : Int
} derive(Debug)fn BasicBuiltinShaderSourceRepo::cache_stats(self : BasicBuiltinShaderSourceRepo) -> BuiltinShaderCacheStatsimpl ShaderFrontend for BasicShaderFrontendfn calc_source_hash(_self : BasicShaderFrontend, request : ShaderCompileRequest) -> ShaderSourceHash raisefn append_user_uniforms(_self : BasicUniformCanonicalizer, layout : UniformLayout, uniforms : Array[NamedUniform]) -> PackedUniforms raisefn filter_unused_uniforms(_self : BasicUniformCanonicalizer, ir : ShaderIR, layout : UniformLayout, uniforms : PackedUniforms) -> PackedUniformsfn prepend_preserved_uniforms(_self : BasicUniformCanonicalizer, uniforms : PackedUniforms, context : PreservedUniformContext) -> PackedUniformspub(all) struct BlendEquation {
src_factor_rgb : BlendFactor
dst_factor_rgb : BlendFactor
op_rgb : BlendOperation
src_factor_alpha : BlendFactor
dst_factor_alpha : BlendFactor
op_alpha : BlendOperation
} derive(Debug)impl Show for BlendEquationpub(all) enum BlendFactor {
Zero
One
SrcAlpha
OneMinusSrcAlpha
DstAlpha
OneMinusDstAlpha
SrcColor
OneMinusSrcColor
DstColor
OneMinusDstColor
} derive(Debug)impl Show for BlendFactorpub(all) enum BuiltinShaderAddress {
Unsafe
ClampToZero
ClampToEdge
Repeat
MirrorRepeat
} derive(Debug)pub(all) struct BuiltinShaderKey {
filter : BuiltinShaderFilter
address : BuiltinShaderAddress
use_color_m : Bool
} derive(Debug)impl Show for BuiltinShaderKeyimpl Show for BuiltinShaderKeyExpub(all) struct DrawCommandDispatch {
draw_calls : Int
pipeline_id : Int
uniform_hash : Int
blend_mode : Int
dst_image_id : Int
shader_id : Int
index_offset : Int
region_count : Int
total_index_count : Int
vertex_float_count : Int
index_count : Int
src_image_count : Int
uniform_dword_count : Int
} derive(Debug)impl Show for DrawCommandDispatchpub(all) struct DrawTrianglesCommand {
dst : ImageHandle
shader : ShaderHandle
dst_regions : Array[DstRegion]
index_offset : Int
pipeline_id : Int
uniform_hash : Int
blend : BlendMode
vertex_data : Array[Double]
indices : Array[Int]
src_image_ids : Array[Int]
uniform_dwords : Array[Int]
instance_count : Int
resource_cache_key : Int
vertex_stride_hint : Int
} derive(Debug)pub(all) struct DstRegion {
x : Int
y : Int
width : Int
height : Int
index_count : Int
} derive(Debug)impl Show for FilterModeimpl Show for FramebufferSnapshotfn FramebufferSnapshot::compare_with(self : FramebufferSnapshot, other : FramebufferSnapshot, threshold : Int) -> PixelDiffResultfn FramebufferSnapshot::from_pixels(x : Int, y : Int, width : Int, height : Int, pixels : Array[Int]) -> FramebufferSnapshotpub(all) struct GraphicsBackendOptions {
enable_validation : Bool
prefer_low_power : Bool
enable_vsync : Bool
} derive(Debug)impl Show for GraphicsBackendOptionspub struct GraphicsResizeStats {
resize_count : Int
suppressed_count : Int
current_width : Int
current_height : Int
last_resize_duration_ms : Double
total_resize_duration_ms : Double
} derive(Debug)impl Show for GraphicsResizeStatsimpl Show for ImageHandlepub struct NativeGraphicsHooks {
try_initialize : (Int, Int) -> Bool
on_begin : (Bool, RenderPassDesc) -> Unit
on_end : (Bool, Bool) -> Unit
on_draw : (Bool, DrawTrianglesCommand) -> Unit
on_resize : (Bool, Int, Int) -> Unit
on_read_pixels : (Bool, Int, Int, Int, Int) -> Array[Int]?
on_new_image : (Bool, Int, Int, Int) -> Unit
}impl Show for PackedUniformsimpl Show for PipelineHandlepub struct PixelDiffResult {
total_pixels : Int
diff_pixels : Int
max_channel_diff : Int
} derive(Debug)impl Show for PixelDiffResultimpl Show for PreservedUniformContextimpl Show for RenderPassDescpub(all) struct SamplerSpec {
filter : BuiltinShaderFilter
address_u : BuiltinShaderAddress
address_v : BuiltinShaderAddress
} derive(Debug)impl Show for SamplerSpecpub(all) struct ShaderCompileRequest {
source : String
unit_hint : ShaderUnit?
src_image_count : Int
entrypoints : ShaderEntrypoints
debug_name : String
} derive(Debug)impl Show for ShaderCompileRequestimpl Show for ShaderHandlepub(all) struct ShaderIR {
source : String
unit : ShaderUnit
noperspective : Bool
src_image_count : Int
entrypoints : ShaderEntrypoints
debug_name : String
source_hash : ShaderSourceHash
} derive(Debug)#alias(NativeGraphicsDriver)
#alias(WebGlGraphicsDriver)
#alias(WebGpuGraphicsDriver)
#alias(NullGraphicsDriver)
pub struct StubGraphicsDriver {
backend : GraphicsBackendKind
width : Int
height : Int
initialized : Bool
native_active : Bool
web_active : Bool
next_id : Int
begin_count : Int
end_count : Int
draw_count : Int
resize_count : Int
resize_suppressed_count : Int
last_resize_duration_ms : Double
total_resize_duration_ms : Double
} derive(Debug)impl GraphicsDriver for StubGraphicsDriverfn read_pixels(self : StubGraphicsDriver, x : Int, y : Int, width : Int, height : Int) -> Array[Int]? raisepub(all) enum SurfaceKind {
MetalLayer
WebGpuCanvasContext
WebGlCanvasContext
OffscreenBuffer
} derive(Debug)impl Show for SurfaceKindpub(all) struct SurfaceToken {
kind : SurfaceKind
opaque_id : Int
width : Int
height : Int
device_scale_factor : Double
} derive(Debug)impl Show for SurfaceTokenimpl Show for UniformLayoutpub struct WebGraphicsHooks {
try_initialize : (GraphicsBackendKind, Int, Int) -> Bool
on_begin : (Bool, GraphicsBackendKind, RenderPassDesc) -> Unit
on_end : (Bool, GraphicsBackendKind, Bool) -> Unit
on_draw : (Bool, GraphicsBackendKind, DrawTrianglesCommand) -> Unit
on_resize : (Bool, GraphicsBackendKind, Int, Int) -> Unit
on_read_pixels : (Bool, GraphicsBackendKind, Int, Int, Int, Int) -> Array[Int]?
}fn[T : UniformCanonicalizer] build_canonical_uniforms(canonicalizer : T, layout : UniformLayout, user_uniforms : Array[NamedUniform], context : PreservedUniformContext, ir : ShaderIR) -> PackedUniforms raisefn create_framebuffer_snapshot(driver : &GraphicsDriver, x : Int, y : Int, width : Int, height : Int) -> FramebufferSnapshot? raisefn create_webgl_graphics(surface : SurfaceToken, options : GraphicsBackendOptions) -> StubGraphicsDriverfn create_webgl_surface_token(opaque_id : Int, width : Int, height : Int, device_scale_factor : Double) -> SurfaceTokenfn create_webgpu_graphics(surface : SurfaceToken, options : GraphicsBackendOptions) -> StubGraphicsDriverfn create_webgpu_surface_token(opaque_id : Int, width : Int, height : Int, device_scale_factor : Double) -> SurfaceTokenfn create_wgpu_native_graphics(surface : SurfaceToken, options : GraphicsBackendOptions) -> StubGraphicsDriverfn default_builtin_shader_source() -> Stringfn double_to_f32_bits(v : Double) -> Intfn f32_bits_to_double(bits : Int) -> Doublefn[T : GraphicsDriver, Q : CommandQueue] flush_commands(driver : T, queue : Q, present : Bool, clear_color? : Color) -> Unit raisefn new_draw_triangles_command(dst : ImageHandle, shader : ShaderHandle, dst_regions : Array[DstRegion], index_offset : Int, pipeline_id : Int, uniform_hash : Int, blend : BlendMode, vertex_data : Array[Double], indices : Array[Int], src_image_ids : Array[Int], uniform_dwords : Array[Int], instance_count? : Int, resource_cache_key? : Int, vertex_stride_hint? : Int) -> DrawTrianglesCommandfn new_native_graphics_hooks(try_initialize : (Int, Int) -> Bool, on_begin : (Bool, RenderPassDesc) -> Unit, on_end : (Bool, Bool) -> Unit, on_draw : (Bool, DrawTrianglesCommand) -> Unit, on_resize : (Bool, Int, Int) -> Unit) -> NativeGraphicsHooksfn new_native_graphics_hooks_full(try_initialize : (Int, Int) -> Bool, on_begin : (Bool, RenderPassDesc) -> Unit, on_end : (Bool, Bool) -> Unit, on_draw : (Bool, DrawTrianglesCommand) -> Unit, on_resize : (Bool, Int, Int) -> Unit, on_read_pixels : (Bool, Int, Int, Int, Int) -> Array[Int]?, on_new_image : (Bool, Int, Int, Int) -> Unit) -> NativeGraphicsHooksfn new_render_pass_desc(clear_color : Color, clear_enabled : Bool, present? : Bool) -> RenderPassDescfn new_web_graphics_hooks(try_initialize : (GraphicsBackendKind, Int, Int) -> Bool, on_begin : (Bool, GraphicsBackendKind, RenderPassDesc) -> Unit, on_end : (Bool, GraphicsBackendKind, Bool) -> Unit, on_draw : (Bool, GraphicsBackendKind, DrawTrianglesCommand) -> Unit, on_resize : (Bool, GraphicsBackendKind, Int, Int) -> Unit) -> WebGraphicsHooksfn new_web_graphics_hooks_full(try_initialize : (GraphicsBackendKind, Int, Int) -> Bool, on_begin : (Bool, GraphicsBackendKind, RenderPassDesc) -> Unit, on_end : (Bool, GraphicsBackendKind, Bool) -> Unit, on_draw : (Bool, GraphicsBackendKind, DrawTrianglesCommand) -> Unit, on_resize : (Bool, GraphicsBackendKind, Int, Int) -> Unit, on_read_pixels : (Bool, GraphicsBackendKind, Int, Int, Int, Int) -> Array[Int]?) -> WebGraphicsHooksfn parse_kage_noperspective_directive(source : String) -> BoolBackend-agnostic GPU command buffer and driver contracts (extracted from kagura)