Buffer & Commands
Every frame, your view function receives a RenderBuffer2D and populates it with commands. This page covers all command types and how to use them.
The buffer lifecycle
// Your view function signature:
val view : GameContext -> 'Model -> RenderBuffer2D -> unit
The buffer is pre-cleared by the renderer each frame. Do not call Clear() yourself. Just add commands:
let myView (ctx: GameContext) (model: Model) (buffer: RenderBuffer2D) =
let bg = Rectangle(0f, 0f, 800f, 600f)
let player = SpriteState.create(tex, Rectangle(100f, 100f, 32f, 32f), Rectangle(0f, 0f, 32f, 32f))
buffer
|> Draw.fillRect (0<RenderLayer>, Color.SkyBlue) bg
|> Draw.sprite player
|> Draw.drop
The |> Draw.drop at the end silences the unused-value warning. It does nothing.
Two ways to build commands
Draw DSL (pipe-friendly)
Use the Draw module for everyday rendering. Every Draw.* function takes styling parameters first, geometry parameters second, and the buffer last — enabling partial application:
// Partial application: bind styling once
let redFill = Draw.fillRect (10<RenderLayer>, Color.Red)
let blueOutline = Draw.rectOutline (10<RenderLayer>, Color.Blue, 2f)
buffer
|> redFill groundRect
|> blueOutline groundRect
|> Draw.text (TextState.create(font, "Score: 100", Vector2(10f, 10f)))
Command2D factories (command-first)
Use Command2D.* when you need to store a command or build one without a buffer:
let dest = Rectangle(0f, 0f, 64f, 64f)
let source = Rectangle(0f, 0f, 64f, 64f)
let mySprite = Command2D.sprite (SpriteState.create(tex, dest, source))
// Later, add to buffer:
buffer.Add(mySprite)
Command reference
All commands live in two modules in Mibo.Elmish.Graphics2D:
Category |
Draw DSL function |
What it draws |
|---|---|---|
Sprite |
|
Textured sprite via |
Text |
|
Text via |
Rect |
|
Filled rectangle |
|
Rectangle outline |
|
|
Rounded rectangle |
|
|
Rounded outline |
|
|
Vertical gradient rect |
|
|
Horizontal gradient rect |
|
|
4-corner gradient rect |
|
Circle |
|
Filled circle |
|
Circle outline |
|
|
Pie slice |
|
|
Gradient circle |
|
|
Ring / arc |
|
|
Ring outline |
|
|
Filled ellipse |
|
|
Ellipse outline |
|
Line |
|
1px line |
|
Thick line |
|
|
Connected line segments |
|
|
Quadratic bezier |
|
Triangle |
|
Filled triangle |
|
Triangle fan |
|
|
Triangle strip |
|
Polygon |
|
Regular polygon |
|
Polygon outline |
|
Camera |
|
Start camera transform |
|
End camera transform |
|
Shader |
|
Start shader mode |
|
End shader mode |
|
Target |
|
Render to texture |
|
End render-to-texture |
|
State |
|
Set blend mode |
|
Enable scissor rect |
|
|
Disable scissor |
|
|
Set line thickness |
|
|
Set viewport |
|
Clear |
|
Clear background |
Immediate |
|
Escape hatch (see Custom Commands) |
Per-command styling
All Draw functions group styling parameters first. This lets you bind them once:
let hudText text = Draw.text (TextState.create(font, text, Vector2(10f, 10f)))
// Reuse with different data:
buffer
|> hudText "HP: 100"
|> hudText "Score: 5000"
State commands (blend, scissor, viewport)
State commands affect subsequent draws within the same layer range:
buffer
|> Draw.setBlend 0<RenderLayer> BlendMode.Additive
|> Draw.fillCircle (10<RenderLayer>, Color.Red) (center, 20f)
|> Draw.setBlend 0<RenderLayer> BlendMode.Alpha
Blend mode, scissor rect, line width, and viewport are reset at the start of each frame.
Text and sprite state types
Sprite and text use state records. Use the create builders for quick setup:
// Sprite: texture + destination + source rect
let sprite = SpriteState.create(tex, Rectangle(100f, 100f, 32f, 32f), Rectangle(0f, 0f, 32f, 32f))
// Sprite with custom color
let redSprite = { sprite with Color = Color.Red; Layer = 10<RenderLayer> }
// Text: font + string + position
let scoreText = TextState.create(font, "Score: 100", Vector2(10f, 10f))
// Text with custom size
let bigText = { scoreText with FontSize = 24f; Color = Color.Yellow; Layer = 100<RenderLayer> }
Cameras
Wrap world-space content between Draw.beginCamera and Draw.endCamera:
let camera = Camera2D.create (Vector2(400f, 300f)) 1.0f viewportSize
let hudLabel = TextState.create(font, "HUD", Vector2(10f, 10f))
buffer
|> Draw.beginCamera 0<RenderLayer> camera
|> Draw.fillCircle (10<RenderLayer>, Color.Red) (worldPos, 20f)
|> Draw.endCamera 1000<RenderLayer>
// After endCamera, draws are in screen space:
|> Draw.text hudLabel
See Camera for details.
Mibo.Raylib