An open API service indexing awesome lists of open source software.

https://github.com/nongio/layers

Layers is a rendering engine for animated user interfaces
https://github.com/nongio/layers

graphical-user-interface scene-graph skia ui

Last synced: 22 days ago
JSON representation

Layers is a rendering engine for animated user interfaces

Awesome Lists containing this project

README

          


Layers Engine Logo

## laye-rs engine
![CI Status](https://github.com/nongio/layers/actions/workflows/ci.yml/badge.svg)

laye-rs is a rendering engine for animated user interfaces, mainly designed in support of the Otto compositor [project](https://github.com/nongio/otto).

It uses a scene graph to render the nodes in retained mode, optmising the most common UI interpolations (opacity, 2d transformations, blending).
Nodes of the scene graph are graphical layers like text or simple shapes like rectangles but can also be external textures.

- Nodes have animatable properties that accepts changes and schedule them in the engine to be executed.
- Layers use a Command pattern to receive changes with a consistent api between immediate changes and animated changes.
- The rendering commands are optimised using displaylist.
- Node properties can be animated, hooks to different stages of the animation progress are exposed by the API.

## Read the docs
The api is getting documented, be aware that is also still in evolution.
[documentation](https://nongio.github.io/layers/docs/)

- Engine update pipeline & damage tracking: see `docs/` for a deep dive into frame stages and redraw rules.
- Layers Inspector: a browser-based debugger that shows the live scene graph with search and highlights (`docs/layers_inspector.md`).
- **Web UI**: `http://localhost:8000/client/index.html`
- **HTTP API**: The debugger server exposes a REST endpoint to fetch the scene graph:
- `GET /scene` — returns the full scene tree as JSON
- `GET /scene/{node_id}` — returns only the subtree rooted at the specified node
- Port defaults to `8000`, configurable via `LAYERS_DEBUGGER_PORT` env var

## Rendering
At the moment the components are rendered using Skia on different backends. This enables some drawing optimisation: the draw calls can be cached using a DisplayList. A Skia PictureRecorder is generate on a separate thread and then replayed on the main thread when needed.

### Scene
The scene tree is stored in a memory arena using IndexTree, which allow fast read/write and thread safe parallel iterations.

### Colors
Colors are stored in OK lab color space to enable smooth and uniform looking transitions between them.
more about Oklab in Björn Ottosson [blog](https://bottosson.github.io/posts/oklab/)

### Layout
The layout is done using Taffy, every layer supports Flex, Block and Grid layout.

# Build the library
At the moment the project requires to setup skia-safe configuration variables before building. See `Cargo.toml`. Once configured the library can be built using cargo.
The C header will be generated in the `target` folder.
The project requires nightly rust to use the negative_impl feature.
```
cargo build
```

### Run the damage test suite
The integration tests in `tests/damage.rs` cover the damage tracking pipeline end-to-end.
```
cargo test --test damage
```

## Build the rust example
The rust example is setup as a different workspace.
```
cargo build -p hello-rust
```
Likewise to run the rust example:
```
cargo run -p hello-rust
```

## Build the C example
The C example is setup with meson. It requires linux to be built and run because of the dependency with Wayland.
To build, it first needs to configure meson:
```
meson build/
```
and then run ninja:
```
ninja -c build
```
the executable will be in the `build/` folder.

## Usage

## Usage: Setup a basic scene with a root layer
```rust
use layers::prelude::*;
let engine = Engine::create(800.0, 600.0);
let layer = engine.new_layer();
let engine = Engine::create(1024.0, 768.0);
let root_layer = engine.new_layer();
root_layer.set_position(Point { x: 0.0, y: 0.0 });
root_layer.set_background_color(
PaintColor::Solid {
color: Color::new_rgba255(180, 180, 180, 255),
}
);
root_layer.set_border_corner_radius(10.0, None);
root_layer.set_layout_style(taffy::Style {
position: taffy::Position::Absolute,
display: taffy::Display::Flex,
flex_direction: taffy::FlexDirection::Column,
justify_content: Some(taffy::JustifyContent::Center),
align_items: Some(taffy::AlignItems::Center),
..Default::default()
});
engine.add_layer(root_layer.clone());
```

## Usage: Tick the engine to update layout and animations
```rust
use layers::prelude::*;
let engine = Engine::create(800.0, 600.0);
// setup the scene...
engine.update(0.016);
```

## Usage: View builder
```rust
use layers::prelude::*;

// view rendering function
pub fn render_main_view(state: &bool, view: &View) -> LayerTree {
let mut position = 0.0;
if *state {
position = 100.0;
}
let view = view.clone();

LayerTreeBuilder::default()
.key("main_view")
.position(
Point {
x: position,
y: position,
}
)
.size(
Size {
width: taffy::Dimension::Length(50.0),
height: taffy::Dimension::Length(50.0),
}
)
.build()
.unwrap()
}

let engine = Engine::create(1000.0, 1000.0);
// create a new layer and add it to the scene
let layer = engine.new_layer();
engine.add_layer(layer.clone());

// define a new view with a boolean state
let initial = false;
let mut view = View::new("test_view", initial, render_one_child_view);
// assign the layer to the view, the rendered tree will be added to the layer
view.mount_layer(layer);
// layout the scene and update animations
engine.update(0.016);
// render the scene to terminal
debug_scene(engine.scene(), engine.scene_root().unwrap());
```

## More examples
Please check the example folder:
https://github.com/nongio/layers/tree/main/examples