Architecture

Overview

All drawing commands are first recorded, then sorted for optimal execution, and finally dispatched to a backend for rendering.

Canvas -> Device -> Recorder -> Recording -> DrawPass -> Backend

                                                     +-----+-----+
        |
                                                 CpuBackend  GLBackend
                                                 (software)  (OpenGL 3.3+)

                                                            GpuContext
                                                       (backend-agnostic)

                                                            GpuImpl
                                                       (internal virtual)

                                                          GLGpuImpl
                                                     (GL texture cache)

Core Components

  • Canvas – User-facing drawing API with save/restore state stack and clip management

  • Device – Converts Canvas commands into compact recorded operations

  • Recording – Immutable command buffer with arena-allocated variable data

  • DrawPass – Sorts operations by clip group, type, and color to minimize state changes

  • Backend – Executes recorded operations on a specific rendering target

  • GpuContext – Backend-agnostic shared GPU resource context, internally dispatches to backend-specific GpuImpl

Backends

Backend

Status

Description

CpuBackend

Working

Software rasterization to a Pixmap buffer

GLBackend

Working

OpenGL 3.3+ rendering via FBO + GLSL shaders, vertex batching, scissor clipping, GPU snapshots

VulkanBackend

Planned

Vulkan rendering

Surface Compositing

Surfaces can be composited together using drawImage. Snapshots are backend-aware: CPU surfaces produce CPU-backed images, and GPU surfaces produce GPU-backed images (texture handle, no readback required). When a CPU image is drawn on a GPU surface, it is automatically uploaded and cached via GpuContext.

// Render to offscreen surfaces
auto layer1 = ink::Surface::MakeRaster(800, 600);
layer1->beginFrame();
layer1->canvas()->fillRect({0, 0, 800, 600}, {255, 0, 0, 128});
layer1->endFrame();
layer1->flush();

// Composite onto final surface
auto snapshot = layer1->makeSnapshot();
finalSurface->canvas()->drawImage(snapshot, 0, 0);

Project Structure

ink/
├── CMakeLists.txt
├── include/ink/
│   ├── ink.hpp              # Public umbrella header
│   ├── version.hpp          # Version info
│   ├── types.hpp            # Core types (Point, Rect, Color)
│   ├── pixmap.hpp           # Pixel buffer management
│   ├── pixel_data.hpp       # Non-owning pixel data descriptor
│   ├── image.hpp            # Immutable pixel snapshot
│   ├── canvas.hpp           # User-facing drawing API
│   ├── device.hpp           # Recording device
│   ├── recording.hpp        # Command recording and compact ops
│   ├── draw_pass.hpp        # Command sorting
│   ├── backend.hpp          # Abstract rendering backend
│   ├── cpu_backend.hpp      # CPU software rasterizer
│   ├── surface.hpp          # Top-level rendering target
│   ├── glyph_cache.hpp      # Font rasterization
│   └── gpu/
│       ├── gpu_context.hpp
│       └── gl/
│           ├── gl_context.hpp
│           └── gl_backend.hpp
├── src/
│   ├── *.cpp
│   └── gpu/
│       └── gl/
├── tests/
├── examples/
└── third_party/
    └── stb_truetype.h