GPU Renderer¶
The GPU renderer (SDL3Renderer) is Brine2D's rendering backend, built on the SDL3 GPU API. It provides hardware-accelerated 2D rendering with automatic sprite batching, render targets, scissor clipping, and blend modes.
Architecture¶
graph TB
A[Your Game] --> B[IRenderer / IDrawContext]
B --> C[SDL3Renderer]
C --> D[SpriteBatcher]
C --> E[SDL3StateManager]
C --> F[SDL3RenderTargetManager]
C --> G[SDL3TextRenderer]
C --> H[SDL3 GPU API]
H --> I[GPU Backend]
I --> J1[Vulkan]
I --> J2[Metal]
I --> J3[D3D12]
J1 --> K[GPU Hardware]
J2 --> K
J3 --> K
style B fill:#2d5016,stroke:#4ec9b0,stroke-width:2px,color:#fff
style C fill:#4a2d4a,stroke:#c586c0,stroke-width:2px,color:#fff
style H fill:#4a3d1f,stroke:#ce9178,stroke-width:2px,color:#fff
style I fill:#1e3a5f,stroke:#569cd6,stroke-width:2px,color:#fff
Key components:
| Component | Responsibility |
|---|---|
| SpriteBatcher | Sorts and batches draw calls by texture, blend mode, and layer |
| SDL3StateManager | Tracks GPU state (scissor, blend, pipeline) to minimize redundant calls |
| SDL3RenderTargetManager | Manages render targets and the push/pop stack |
| SDL3TextRenderer | Font atlas management and text layout |
Rendering flow:
- Your game calls
IRenderer/IDrawContextmethods SDL3Renderertranslates to GPU commands- SDL3 GPU API manages the backend (Vulkan / Metal / D3D12)
- Commands execute on GPU hardware
- Results are presented to the screen
Configuration¶
builder.Configure(options =>
{
options.Window.Title = "My Game";
options.Window.Width = 1280;
options.Window.Height = 720;
options.Rendering.VSync = true;
options.Rendering.PreferredGPUDriver = GPUDriver.Auto; // Default
});
Render Targets¶
Render targets allow off-screen rendering - draw to a texture, then use that texture in subsequent draws.
Creating a Render Target¶
CreateRenderTarget is synchronous and returns an IRenderTarget:
IRenderTarget minimap = Renderer.CreateRenderTarget(256, 256);
Warning
CreateRenderTarget throws NotSupportedException in headless mode (no GPU available).
Set/Get Pattern¶
For simple cases, set the render target directly:
// Render to target
Renderer.SetRenderTarget(minimap);
RenderMinimapView();
// Render back to screen
Renderer.SetRenderTarget(null);
Renderer.DrawTexture(minimap.Texture, 10, 10);
Push/Pop Pattern (Recommended)¶
For nested render-to-texture operations, use the stack-based API:
// Draw scene to off-screen target
Renderer.PushRenderTarget(offscreen);
RenderScene();
Renderer.PopRenderTarget();
// Draw the result to screen
Renderer.DrawTexture(offscreen.Texture, 0, 0);
Push/Pop is safer for nested operations - each Pop restores the previous target automatically:
Renderer.PushRenderTarget(outerTarget);
RenderBackground();
Renderer.PushRenderTarget(innerTarget);
RenderOverlay();
Renderer.PopRenderTarget(); // Restores outerTarget
RenderForeground();
Renderer.PopRenderTarget(); // Restores screen (null)
Tip
Unmatched push/pop calls are caught at frame boundaries with diagnostic warnings.
Render Target Lifecycle¶
- Render targets implement
IDisposable- dispose them when no longer needed - Render targets are fixed-size and do not resize with the window
- The internal post-processing render targets resize automatically
private IRenderTarget? _offscreen;
protected override async Task OnLoadAsync(CancellationToken ct, IProgress<float>? progress = null)
{
_offscreen = Renderer.CreateRenderTarget(Renderer.Width, Renderer.Height);
}
protected override void OnExit()
{
_offscreen?.Dispose();
_offscreen = null;
}
Scissor Rects¶
Scissor rectangles clip all rendering to a specific screen-space region. Anything drawn outside the scissor rect is discarded.
Basic Usage¶
// Clip to a 200×200 region
Renderer.SetScissorRect(new Rectangle(10, 10, 200, 200));
Renderer.DrawTexture(largeTexture, 0, 0); // Only the part inside the rect is visible
// Disable clipping
Renderer.SetScissorRect(null);
Push/Pop for Nested Clipping¶
When rendering UI hierarchies, child elements should be clipped to parent bounds. PushScissorRect automatically intersects with the current scissor rect:
// Outer panel
Renderer.PushScissorRect(panelBounds);
RenderPanel();
// Inner scroll view - clipped to both panel AND scroll bounds
Renderer.PushScissorRect(scrollViewBounds);
RenderScrollContent();
Renderer.PopScissorRect();
Renderer.PopScissorRect();
Note
Scissor rects are in screen coordinates and are not affected by camera transforms. Negative dimensions throw ArgumentException. The rect is clamped to the current render target (or viewport) bounds.
Blend Modes¶
Control how pixels combine when drawing on top of existing content:
Renderer.SetBlendMode(BlendMode.Additive);
Renderer.DrawTexture(_glowEffect, position);
Renderer.SetBlendMode(BlendMode.Alpha); // Restore default
Available Modes¶
| Mode | Formula | Use Case |
|---|---|---|
BlendMode.Alpha |
Src × SrcA + Dst × (1 − SrcA) |
Default - sprites, UI |
BlendMode.Additive |
Src + Dst |
Fire, explosions, lights |
BlendMode.Multiply |
Src × Dst |
Shadows, darkening |
BlendMode.None |
Src |
Opaque objects |
The default blend mode is BlendMode.Alpha and resets each frame.
Render Layers¶
Render layers control draw ordering when sprites are submitted through the SpriteBatcher:
Renderer.SetRenderLayer(0); // Background
Renderer.DrawTexture(_background, 0, 0);
Renderer.SetRenderLayer(128); // Default layer - game objects
Renderer.DrawTexture(_player, _playerPos);
Renderer.SetRenderLayer(255); // Foreground / UI
Renderer.DrawText("Score: 1000", 10, 10, Color.White);
Note
Direct draw calls are rendered in submission order regardless of the active layer. Layers affect ordering only through the sprite batching system.
The default render layer is 128 and resets each frame.
Text Rendering¶
Basic Text¶
Renderer.DrawText("Hello, Brine2D!", 10, 10, Color.White);
Advanced Text with Options¶
Use TextRenderOptions for rich formatting:
Renderer.DrawText("Styled Text", 100, 100, new TextRenderOptions
{
Color = Color.Gold,
FontSize = 24f,
MaxWidth = 400f, // Word wrap at 400px
HorizontalAlign = TextAlignment.Center,
LineSpacing = 1.5f,
ShadowOffset = new Vector2(2, 2),
ShadowColor = new Color(0, 0, 0, 128),
OutlineThickness = 1f,
OutlineColor = Color.Black
});
Measuring Text¶
Measure text dimensions for layout calculations:
Vector2 size = Renderer.MeasureText("Hello!", fontSize: 24f);
float width = size.X;
float height = size.Y;
// With full options
Vector2 wrappedSize = Renderer.MeasureText("Long text...", new TextRenderOptions
{
FontSize = 16f,
MaxWidth = 200f
});
Custom Fonts¶
IFont customFont = await _assets.GetOrLoadFontAsync("assets/fonts/pixel.ttf", 16, ct);
Renderer.SetDefaultFont(customFont);
TextRenderOptions Reference¶
| Property | Default | Description |
|---|---|---|
Color |
White |
Default text color |
Font |
null |
Font (null = renderer default) |
FontSize |
16f |
Base size in points |
MaxWidth |
null |
Word wrap width (null = no wrap) |
MaxHeight |
null |
Vertical constraint (null = none) |
HorizontalAlign |
Left |
Left, Center, Right |
VerticalAlign |
Top |
Top, Middle, Bottom |
LineSpacing |
1.2f |
Line spacing multiplier |
ParseMarkup |
false |
Parse BBCode-style markup tags |
MarkupParser |
null |
Custom markup parser |
ShadowOffset |
null |
Shadow offset (null = no shadow) |
ShadowColor |
(0,0,0,128) |
Shadow color |
OutlineThickness |
0f |
Outline width in pixels (0 = none) |
OutlineColor |
Black |
Outline color |
Platform-Specific Notes¶
Windows¶
- GPU Backend: Direct3D 12 (preferred) or Vulkan
- Requirements: Windows 10+, modern GPU, updated drivers
Linux¶
- GPU Backend: Vulkan
- Requirements: Vulkan-capable GPU, Mesa 20.0+ or proprietary drivers
macOS¶
- GPU Backend: Metal
- Requirements: macOS 10.14+ (all modern Macs support Metal)
Performance Tips¶
- Batch by texture - draw all sprites using the same texture together
- Use texture atlasing - fewer texture switches means better batching
- Minimize render target switches - each switch flushes the current batch
- Use scissor rects for UI clipping instead of manual bounds checking
- Press F3 to toggle the performance overlay (FPS, draw calls, memory)
Summary¶
| Feature | Details |
|---|---|
| Backend | SDL3 GPU API (Vulkan / Metal / D3D12) |
| Batching | Automatic sprite batching |
| Render Targets | Synchronous creation, push/pop stack |
| Scissor Rects | Nested clipping with auto-intersection |
| Blend Modes | Alpha, Additive, Multiply, None |
| Render Layers | 0–255 for draw ordering |
| Text | Basic + rich options (wrap, align, shadow, outline) |
| VSync | Enabled by default |
Next Steps¶
- Rendering Architecture - Interfaces, headless mode, driver selection
- Sprites & Textures - Loading and drawing images
- Drawing Primitives - Lines, rectangles, circles
- Post-Processing - Off-screen rendering and effects
- Cameras - Camera movement and zoom
- Performance Optimization - Optimization techniques