Skip to content

Commit

Permalink
Implement Layout Backgrounds (#774)
Browse files Browse the repository at this point in the history
This implements background for the layouts. This is a lot more involved
than what it would seem. Part of it is a huge refactoring that changes
all layout states to be truly absolute. Previously they only included
image changes whenever an image changes. We did this because we didn't
want to serialize all the image data on every single frame. The new
approach is to have a cache for the images and only serialize the image
IDs. The image IDs are SHA-256 hashes, which allow deduplicating the
images as well.
  • Loading branch information
CryZe authored Feb 19, 2024
1 parent 351068a commit f957c93
Show file tree
Hide file tree
Showing 89 changed files with 2,365 additions and 2,129 deletions.
26 changes: 15 additions & 11 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,15 @@ jobs:
- label: Linux i586
target: i586-unknown-linux-gnu
auto_splitting: skip
# FIXME: rustls currently does not support i586.
networking: skip

- label: Linux i586 musl
target: i586-unknown-linux-musl
auto_splitting: skip
dylib: skip
# FIXME: rustls currently does not support i586.
networking: skip

- label: Linux i686
target: i686-unknown-linux-gnu
Expand Down Expand Up @@ -544,10 +548,10 @@ jobs:

steps:
- name: Checkout Commit
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: hecrj/setup-rust-action@v2
with:
rust-version: ${{ matrix.toolchain || 'stable' }}

Expand All @@ -557,7 +561,7 @@ jobs:

- name: Download cross
if: matrix.cross == '' && matrix.no_std == ''
uses: robinraju/release-downloader@v1.7
uses: robinraju/release-downloader@v1.9
with:
repository: "cross-rs/cross"
latest: true
Expand Down Expand Up @@ -619,10 +623,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Commit
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: hecrj/setup-rust-action@v2

- name: Generate bindings
run: |
Expand All @@ -635,10 +639,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Commit
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: hecrj/setup-rust-action@v2
with:
components: clippy

Expand All @@ -650,10 +654,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Commit
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: hecrj/setup-rust-action@v2
with:
components: rustfmt

Expand All @@ -667,10 +671,10 @@ jobs:
CRITERION_TOKEN: ${{ secrets.CRITERION_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Rust
uses: hecrj/setup-rust-action@v1
uses: hecrj/setup-rust-action@v2

- name: Run benchmarks
run: |
Expand Down
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.13.0"
authors = ["Christopher Serr <[email protected]>"]
documentation = "https://docs.rs/livesplit-core/"
repository = "https://github.com/LiveSplit/livesplit-core"
license = "Apache-2.0/MIT"
license = "MIT OR Apache-2.0"
description = "livesplit-core is a library that provides a lot of functionality for creating a speedrun timer."
readme = "README.md"
keywords = ["speedrun", "timer", "livesplit", "gaming"]
Expand Down Expand Up @@ -47,14 +47,16 @@ libm = "0.2.1"
livesplit-hotkey = { path = "crates/livesplit-hotkey", version = "0.7.0", default-features = false }
livesplit-title-abbreviations = { path = "crates/livesplit-title-abbreviations", version = "0.3.0" }
memchr = { version = "2.3.4", default-features = false }
simdutf8 = { version = "0.1.4", default-features = false, features = [
simdutf8 = { git = "https://github.com/CryZe/simdutf8", branch = "wasm-ub-panic", default-features = false, features = [
"aarch64_neon",
] }
serde = { version = "1.0.186", default-features = false, features = ["alloc"] }
serde_derive = { version = "1.0.186", default_features = false }
serde_json = { version = "1.0.60", default-features = false, features = [
"alloc",
] }
sha2 = { version = "0.10.8", default-features = false }
slab = { version = "0.4.9", default-features = false }
smallstr = { version = "0.3.0", default-features = false }
snafu = { version = "0.8.0", default-features = false }
unicase = "2.6.0"
Expand Down Expand Up @@ -135,6 +137,8 @@ std = [
"cosmic-text?/std",
"serde_json/std",
"serde/std",
"sha2/std",
"slab/std",
"simdutf8/std",
"snafu/std",
"time/local-offset",
Expand Down
30 changes: 15 additions & 15 deletions benches/layout_state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use criterion::{criterion_group, criterion_main, Criterion};
use livesplit_core::{run::parser::livesplit, Layout, Run, Segment, Timer};
use livesplit_core::{run::parser::livesplit, settings::ImageCache, Layout, Run, Segment, Timer};
use std::fs;

criterion_main!(benches);
Expand All @@ -11,7 +11,7 @@ criterion_group!(
reuse_artificial
);

fn artificial() -> (Timer, Layout) {
fn artificial() -> (Timer, Layout, ImageCache) {
let mut run = Run::new();
run.set_game_name("Game");
run.set_category_name("Category");
Expand All @@ -20,51 +20,51 @@ fn artificial() -> (Timer, Layout) {
let mut timer = Timer::new(run).unwrap();
timer.start();

(timer, Layout::default_layout())
(timer, Layout::default_layout(), ImageCache::new())
}

fn real() -> (Timer, Layout) {
fn real() -> (Timer, Layout, ImageCache) {
let buf = fs::read_to_string("tests/run_files/Celeste - Any% (1.2.1.5).lss").unwrap();
let run = livesplit::parse(&buf).unwrap();

let mut timer = Timer::new(run).unwrap();
timer.start();

(timer, Layout::default_layout())
(timer, Layout::default_layout(), ImageCache::new())
}

fn no_reuse_real(c: &mut Criterion) {
let (timer, mut layout) = real();
let (timer, mut layout, mut image_cache) = real();

c.bench_function("No Reuse (Real)", move |b| {
b.iter(|| layout.state(&timer.snapshot()))
b.iter(|| layout.state(&mut image_cache, &timer.snapshot()))
});
}

fn reuse_real(c: &mut Criterion) {
let (timer, mut layout) = real();
let (timer, mut layout, mut image_cache) = real();

let mut state = layout.state(&timer.snapshot());
let mut state = layout.state(&mut image_cache, &timer.snapshot());

c.bench_function("Reuse (Real)", move |b| {
b.iter(|| layout.update_state(&mut state, &timer.snapshot()))
b.iter(|| layout.update_state(&mut state, &mut image_cache, &timer.snapshot()))
});
}

fn no_reuse_artificial(c: &mut Criterion) {
let (timer, mut layout) = artificial();
let (timer, mut layout, mut image_cache) = artificial();

c.bench_function("No Reuse (Artificial)", move |b| {
b.iter(|| layout.state(&timer.snapshot()))
b.iter(|| layout.state(&mut image_cache, &timer.snapshot()))
});
}

fn reuse_artificial(c: &mut Criterion) {
let (timer, mut layout) = artificial();
let (timer, mut layout, mut image_cache) = artificial();

let mut state = layout.state(&timer.snapshot());
let mut state = layout.state(&mut image_cache, &timer.snapshot());

c.bench_function("Reuse (Artificial)", move |b| {
b.iter(|| layout.update_state(&mut state, &timer.snapshot()))
b.iter(|| layout.update_state(&mut state, &mut image_cache, &timer.snapshot()))
});
}
13 changes: 7 additions & 6 deletions benches/scene_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cfg_if::cfg_if! {
PathBuilder, ResourceAllocator, SceneManager, Label, FontKind, SharedOwnership,
},
run::parser::livesplit,
settings::Font,
settings::{Font, ImageCache},
Run, Segment, TimeSpan, Timer, TimingMethod,
};
use std::fs;
Expand Down Expand Up @@ -81,23 +81,25 @@ cfg_if::cfg_if! {
run.set_attempt_count(1337);
let mut timer = Timer::new(run).unwrap();
let mut layout = Layout::default_layout();
let mut image_cache = ImageCache::new();

start_run(&mut timer);
make_progress_run_with_splits_opt(&mut timer, &[Some(5.0), None, Some(10.0)]);

let state = layout.state(&timer.snapshot());
let state = layout.state(&mut image_cache, &timer.snapshot());

let mut manager = SceneManager::new(Dummy);

c.bench_function("Scene Management (Default)", move |b| {
b.iter(|| manager.update_scene(Dummy, (300.0, 500.0), &state))
b.iter(|| manager.update_scene(Dummy, (300.0, 500.0), &state, &image_cache))
});
}

fn subsplits_layout(c: &mut Criterion) {
let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
let mut timer = Timer::new(run).unwrap();
let mut layout = lsl("tests/layout_files/subsplits.lsl");
let mut image_cache = ImageCache::new();

start_run(&mut timer);
make_progress_run_with_splits_opt(
Expand All @@ -106,13 +108,12 @@ cfg_if::cfg_if! {
);

let snapshot = timer.snapshot();
let mut state = layout.state(&snapshot);
layout.update_state(&mut state, &snapshot);
let state = layout.state(&mut image_cache, &snapshot);

let mut manager = SceneManager::new(Dummy);

c.bench_function("Scene Management (Subsplits Layout)", move |b| {
b.iter(|| manager.update_scene(Dummy, (300.0, 800.0), &state))
b.iter(|| manager.update_scene(Dummy, (300.0, 800.0), &state, &image_cache))
});
}

Expand Down
23 changes: 7 additions & 16 deletions benches/software_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ cfg_if::cfg_if! {
layout::{self, Layout},
rendering::software::Renderer,
run::parser::livesplit,
settings::ImageCache,
Run, Segment, TimeSpan, Timer, TimingMethod,
},
std::fs,
Expand All @@ -21,43 +22,33 @@ cfg_if::cfg_if! {
run.set_attempt_count(1337);
let mut timer = Timer::new(run).unwrap();
let mut layout = Layout::default_layout();
let mut image_cache = ImageCache::new();

start_run(&mut timer);
make_progress_run_with_splits_opt(&mut timer, &[Some(5.0), None, Some(10.0)]);

let snapshot = timer.snapshot();
let mut state = layout.state(&snapshot);
let state = layout.state(&mut image_cache, &timer.snapshot());
let mut renderer = Renderer::new();

// Do a single frame beforehand as otherwise the layout state will
// keep saying that the icons changed.
renderer.render(&state, [300, 500]);
layout.update_state(&mut state, &snapshot);

c.bench_function("Software Rendering (Default)", move |b| {
b.iter(|| renderer.render(&state, [300, 500]))
b.iter(|| renderer.render(&state, &image_cache, [300, 500]))
});
}

fn subsplits_layout(c: &mut Criterion) {
let run = lss("tests/run_files/Celeste - Any% (1.2.1.5).lss");
let mut timer = Timer::new(run).unwrap();
let mut layout = lsl("tests/layout_files/subsplits.lsl");
let mut image_cache = ImageCache::new();

start_run(&mut timer);
make_progress_run_with_splits_opt(&mut timer, &[Some(10.0), None, Some(20.0), Some(55.0)]);

let snapshot = timer.snapshot();
let mut state = layout.state(&snapshot);
let state = layout.state(&mut image_cache, &timer.snapshot());
let mut renderer = Renderer::new();

// Do a single frame beforehand as otherwise the layout state will
// keep saying that the icons changed.
renderer.render(&state, [300, 800]);
layout.update_state(&mut state, &snapshot);

c.bench_function("Software Rendering (Subsplits Layout)", move |b| {
b.iter(|| renderer.render(&state, [300, 800]))
b.iter(|| renderer.render(&state, &image_cache, [300, 800]))
});
}

Expand Down
2 changes: 1 addition & 1 deletion capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ crate-type = ["staticlib", "cdylib"]
livesplit-core = { path = "..", default-features = false, features = ["std"] }
serde_json = { version = "1.0.8", default-features = false }
time = { version = "0.3.4", default-features = false, features = ["formatting"] }
simdutf8 = { version = "0.1.4", default-features = false }
simdutf8 = { git = "https://github.com/CryZe/simdutf8", branch = "wasm-ub-panic", default-features = false }

[features]
default = ["image-shrinking"]
Expand Down
14 changes: 0 additions & 14 deletions capi/bind_gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ mod python;
mod ruby;
mod swift;
mod typescript;
mod wasm;
mod wasm_bindgen;

use clap::Parser;
Expand Down Expand Up @@ -310,19 +309,6 @@ fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
}
path.pop();

path.push("wasm");
create_dir_all(&path)?;
{
path.push("livesplit_core.js");
wasm::write(BufWriter::new(File::create(&path)?), classes, false)?;
path.pop();

path.push("livesplit_core.ts");
wasm::write(BufWriter::new(File::create(&path)?), classes, true)?;
path.pop();
}
path.pop();

path.push("wasm_bindgen");
create_dir_all(&path)?;
{
Expand Down
Loading

0 comments on commit f957c93

Please sign in to comment.