damage-rects 0.1.0

Accumulate, coalesce, and emit dirty rectangles for partial GPU redraw
Documentation
  • Coverage
  • 100%
    26 out of 26 items documented1 out of 22 items with examples
  • Size
  • Source code size: 86.79 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.97 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 37s Average build duration of successful builds.
  • all releases: 40s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • doracawl

damage-rects

Crates.io docs.rs License MSRV Downloads

English | 简体中文 | 日本語

Accumulate dirty rectangles and emit a merged redraw region for partial GPU canvas updates. Zero dependencies, no GPU coupling, no viewport assumptions — pairs with any 2D renderer (Skia, wgpu, softbuffer, raw Metal).

Also known as / if you're searching for: damage tracking, dirty rectangle tracking, partial redraw, invalidation region, repaint region, region coalescing, editor/terminal incremental redraw.

Why

Every interactive GPU-rendered app has a choice: redraw the whole frame every tick, or track which regions changed and redraw only those. The first is simple but wastes power; the second is what editors, design tools, terminals, and dashboards actually do.

GUI frameworks (gpui, iced, slint) bake damage tracking into their internals and never expose it as a reusable crate. This is that crate.

API

use damage_rects::{DamageRect, DamageTracker};

let mut tracker = DamageTracker::new();

// during state updates
tracker.add(DamageRect::new(10.0, 20.0, 100.0, 30.0)); // a line changed
tracker.add(DamageRect::new(50.0, 30.0, 80.0, 40.0));  // cursor moved

// at frame time
if tracker.is_full() {
    // something invalidated the whole viewport — redraw everything
} else if let Some(region) = tracker.merged() {
    // render(region)
}
tracker.clear();

Two orthogonal states:

  • full-damage flag (mark_full) — set when something invalidates the whole viewport. merged() then returns None and is_full() returns true. Individual rects are ignored.
  • pending rects (add) — merged via merged() into one bounding rect, or exposed via rects() if you want to implement a custom coalescing strategy (conservative / aggressive / threshold).

Threshold strategy

When dirty area is a large fraction of total, it's cheaper to redraw the whole viewport than many small rects. area_upper_bound() gives a cheap (overcounts overlap) sum of rect areas:

if tracker.area_upper_bound() > viewport_area * 0.5 {
    // give up on partial — render everything
    tracker.mark_full();
}

Demo

cargo run --example visualize -p damage-rects

Interactive window:

  • click to add a dirty rect
  • Space to render the merged region (green outline, then clear)
  • F to toggle full-damage flag (yellow tint)
  • C to clear

Install

[dependencies]
damage-rects = "0.1"

Coordinate system

DamageRect { x, y, width, height } in f32. Half-open at right/bottom (contains_point returns false on right/bottom edges). Coordinate orientation is up to the caller — the library only uses arithmetic.

License

MIT — see LICENSE.