etagere

A fast, dependency-light Rust library for dynamic 2D texture atlas allocation using the shelf packing algorithm.

Library
Cargo
v0.3.0
164stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
32/100Needs Attention
Development Activity8
Maintenance0
Community40
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture74
Code Quality78
Innovation62
Learning Curve65

Etagere is a Rust crate that implements dynamic 2D texture atlas allocation using the shelf-packing algorithm, the same general technique used by projects like Mozilla’s WebRender to batch GPU draw calls efficiently. It ships two allocator implementations: AtlasAllocator, which tracks each item individually and works to minimize fragmentation, and BucketedAtlasAllocator, which groups items into buckets for faster allocation and deallocation at some cost to packing efficiency — so callers can pick the trade-off that fits their workload, whether that’s dynamic glyph atlases or GPU texture batching.

The crate is no_std-compatible by default (with an opt-in std feature enabling SVG debugging output), supports optional serde serialization, alignment constraints, vertical shelf orientation, and multi-column layouts, and ships a small companion CLI tool for interactively experimenting with the packing algorithms.

What You Get

  • AtlasAllocator - a per-item shelf-packing allocator that tracks each allocation individually and works to minimize fragmentation as items are added and removed.
  • BucketedAtlasAllocator - a bucketed variant that groups items and only reclaims space once an entire bucket empties, trading some packing efficiency for faster allocate/deallocate performance on large item counts.
  • Configurable AllocatorOptions - alignment constraints, vertical vs. horizontal shelf orientation, and multi-column splitting to tune packing behavior for a given workload.
  • no_std support - the core crate builds without the standard library, with an opt-in std feature for SVG dump/debug output.
  • Optional serde serialization - atlas state and IDs can be serialized via the serialization feature for persistence or cross-process use.
  • A companion CLI (etagere-cmd) - an interactive command-line tool in the cli/ workspace member for experimenting with and visualizing the packing algorithms.

Common Use Cases

  • Dynamic glyph atlases - packing rasterized font glyphs into a shared GPU texture as text is rendered, reclaiming space as glyphs fall out of use.
  • GPU draw-call batching - combining many small textures into one atlas so a renderer can issue fewer draw calls, as used by rendering engines like WebRender.
  • Game engine sprite/texture packing - allocating space for sprites or UI textures at runtime rather than pre-baking a fixed atlas layout.
  • FFI-driven graphics pipelines - the ffi feature exposes a C-compatible API so non-Rust renderers can drive the same allocator.

Under The Hood

Architecture Etagere is organized around two parallel allocator implementations that share a common shape but differ in bookkeeping strategy. AtlasAllocator (src/allocator.rs) maintains a list of shelves, each holding a row of allocated rectangles, and walks/merges free space on deallocation to control fragmentation; BucketedAtlasAllocator (src/bucketed.rs) instead groups items into fixed-size buckets within shelves and defers reclaiming a shelf’s space until every item in its bucket has been freed, with limited support for coalescing adjacent empty shelves. Both allocators expose the same AllocId/Allocation/Rectangle vocabulary defined in lib.rs, so callers can swap one for the other without changing call sites. An optional ffi module (src/ffi.rs) wraps the core API behind a #[repr(C)]-friendly surface for non-Rust consumers, and a small cli/ workspace crate built on clap and ron provides an interactive front end for exercising both allocators. Because there’s no external state beyond the allocator struct itself, changing the shelf/bucket data structures is the main lever for altering packing behavior — there’s no plugin or trait-based extension point.

Tech Stack The crate targets Rust 2018 edition and is no_std by default via #![cfg_attr(not(feature = "std"), no_std)], pulling in alloc directly rather than assuming a full standard library. Its only required dependency is euclid (geometry primitives for points/sizes/boxes), with svg_fmt gated behind the std feature for SVG debug dumps and serde gated behind a serialization feature. The workspace also contains a cli member depending on clap 2.x and ron for a standalone command-line tool, and a fuzz/ directory for fuzz-testing the allocators. There’s no async runtime, database, or web framework involved — this is a pure computational library meant to be embedded in graphics/rendering codebases.

Code Quality Both core modules carry substantial inline #[test] suites (a dozen-plus tests each in allocator.rs and bucketed.rs) covering basic allocation/deallocation, option handling, shelf coalescing, and edge cases, and CI (.github/workflows) runs cargo build and cargo test --all on both stable and nightly Rust. A fuzz/ crate is present for additional confidence beyond unit tests. The public API relies on Rust’s type system (newtype IDs, Option-returning fallible allocation) rather than panics for the common failure path, though some internal bookkeeping still uses unwrap/expect where invariants are assumed to hold. There’s no separate linter/formatter config beyond standard cargo fmt/clippy conventions implied by the ecosystem.

What Makes It Unique Rather than a general-purpose bin-packing library, Etagere is narrowly focused on the dynamic case — supporting deallocation, not just one-shot packing — which is the harder and less commonly solved variant of the shelf-packing problem. Offering two allocator strategies with the same API lets consumers benchmark fragmentation-minimizing behavior against bucketed throughput for their specific workload without switching libraries, and its no_std, FFI-ready design makes it usable from constrained or non-Rust graphics pipelines, which is a deliberate design choice most atlas-packing crates don’t make.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search