Skip to content
dream-soft-render

Library active

dream-soft-render

A CPU rasterizer for 2D triangle meshes and egui frames. It writes premultiplied RGBA8 into a buffer you own, and the same draw calls give the same bytes on every platform.

cargo add dream-soft-render
A dark egui settings form for importing Morrowind.ini, with a floating window titled Choose encoding over it: radio buttons, a slider, a progress bar at 42 percent, and a soft shadow.
The window golden scene: 640x480, drawn by dream-soft-render.
Stable
1.0.0 2026-09-28
Package
Rust crate
License
MIT OR Apache-2.0

Overview

A handheld running PortMaster has a framebuffer, a small ARM core, and no GPU a program can count on. An egui interface still has to be drawn. dream-soft-render draws it on the CPU: rectangles, textured rectangles and triangle meshes go in, and premultiplied RGBA8 comes out, in a buffer you own. Copying it to /dev/fb0, writing a PNG or hashing it in a test is up to you.

There is no window, no swapchain and no driver. There is also no randomness: the same draw calls produce the same bytes on x86-64 and on AArch64, whose NEON code is held to the portable code by tests. It was pulled out of dream-ini, whose PortMaster build draws its whole GUI through it; the header image is one of the egui scenes its golden tests pin, drawn by this crate.

Draw calls in, bytes out

Both ways in rasterize into one surface, through the same recognizers and the same blending, so identical geometry gives identical bytes.

  1. source

    Draw calls

    Frame::clear, fill_rect, textured_rect and mesh, each rasterized the moment you make it.

  2. tool

    The rasterizer

    Rectangles, one-color fans and triangles with one texel draw as spans. Everything else is covered pixel by pixel. NEON on AArch64, the same bytes either way.

  3. result

    SoftwareSurface

    width x height x 4 bytes of premultiplied RGBA8, top row first. Yours to copy to a framebuffer, a file or a hash.

input

An egui UI

With the egui feature, render_egui runs your UI closure, applies egui's texture uploads and tessellates at one pixel per point.

service

Textures

Premultiplied RGBA8, created and freed through the renderer. egui's font atlas lives in the same store, under the same 8 MiB budget.

How the pieces relate
  • Draw calls The rasterizer
  • The rasterizer SoftwareSurface
  • An egui UI render_egui The rasterizer
  • Textures sampled The rasterizer

What it does#

  • Same bytes everywhere. Output is a function of the draw calls. Golden hashes of whole frames pass on x86-64 and on AArch64, NEON included.
  • Draws now. Each call rasterizes the moment you make it, in call order. No command list, no deferred state, and no clear you did not ask for.
  • Refuses bad input. An index past the vertices, a NaN coordinate, pixel data of the wrong length or a freed texture is an error, not a quietly wrong frame.
  • Hosts egui, if you want. With the egui feature, render_egui runs a UI closure into the same surface, and skips frames that came out identical to the last.
  • Knows what it is drawing. Rectangles, glyph quads, one-color fans and single-texel triangles draw as spans. Only the rest pays for per-pixel triangle work.
  • Depends on nothing. Without the egui feature the crate has no dependencies, and no platform code beyond its NEON kernels.
use dream_soft_render::{ClipRect, Color, Error, Rect, SoftwareRenderer};

fn main() -> Result<(), Error> {
    let mut renderer = SoftwareRenderer::default();
    let mut frame = renderer.begin_frame(320, 240)?;
    frame.clear(Color::from_rgb(17, 20, 28));
    frame.fill_rect(
        Rect::from_min_size([8.0, 8.0], [304.0, 40.0]),
        Color::from_rgba_unmultiplied(80, 160, 255, 192),
        ClipRect::ALL,
    )?;

    // 320 * 240 * 4 bytes, top row first.
    let rgba: &[u8] = &frame.surface().pixels;
    assert_eq!(&rgba[..4], &[17, 20, 28, 255]);
    Ok(())
}

Documentation#

This site is the crate’s documentation, its API reference included.

  • Start here: add the crate, draw a frame and look at it.
  • The rules: coordinates, coverage, color, blending and sampling, exactly.
  • Hosting egui: render_egui, textures, unchanged frames and feathering.
  • Rust API: every type and method, with what it does and how it fails.

Screenshots

2 items

Install

Rust crate · 1.0.0

Add it with Cargofrom crates.io, 1.0.0 today

From your project's directory:

cargo add dream-soft-render

Or by hand, in Cargo.toml:

[dependencies]
dream-soft-render = "1.0.0"

Cargo reads a bare version as "this or any compatible newer release" and records the exact one in Cargo.lock.

From the repositorythe default branch, ahead of the latest release
[dependencies]
dream-soft-render = { git = "https://github.com/DreamWeave-MP/dream-soft-render" }

Cargo pins the commit it fetched in Cargo.lock; cargo update -p dream-soft-render moves it forward.

  1. 1.0.0 stable 2026-09-28

    First release: the software rasterizer from dream-ini’s PortMaster build, with a drawing API of its own and egui as an option.

    Added

    • SoftwareRenderer and Frame: clear, fill_rect, textured_rect and mesh, rasterized immediately into a premultiplied RGBA8 surface.
    • create_texture, update_texture and free_texture, with handles issued process-wide and never reused.
    • Color, Vertex, Rect, ClipRect and TextureId, and one Error type for every rejected call.
    • The egui feature: render_egui, RenderFrame, RenderOutcome, and skipping frames whose output did not change.
    • NEON kernels on AArch64 for translucent spans, textured and glyph rows, gradient rows and coverage scans, held byte for byte to the portable code.
    • The draw and frame benchmarks, and golden hashes of four whole frames.

    Known issues

    • A surface wider or taller than 65535 pixels is accepted and draws wrong past pixel 65535. Refused on the default branch.
    • A texture wider or taller than 65536 texels is accepted and samples the wrong texels. Refused on the default branch.
    • In render_egui, a frame after one whose texture uploads failed part way can be skipped as unchanged and keep the old texels. Fixed on the default branch.
    • In render_egui, textures egui releases in a frame that fails stay in the store and count against the budget. Fixed on the default branch.
    • In render_egui, a mesh with an index past its vertices, or a trailing partial triangle, is skipped instead of failing the frame. Fixed on the default branch.
    dream-soft-render 1.0.0 133.60 kB · crates.io
    View on crates.io
    Verify · sha256 77cfb5e928da…d5147326
    File
    dream-soft-render-1.0.0.crate
    Size
    136806 bytes
    SHA-256
    77cfb5e928da15f9baaea82bd581e2ff20a4ba8f6aee2baaaee5ae4bd5147326

    The same digest as the crates.io index's checksum, which Cargo checks on every download.

Credits