Skip to content

Commit

Permalink
Merge pull request #430 from CryZe/render-layers
Browse files Browse the repository at this point in the history
Introduce Layers in the Renderer
  • Loading branch information
CryZe authored Jun 9, 2021
2 parents 17e36f7 + f680105 commit d043746
Show file tree
Hide file tree
Showing 58 changed files with 1,774 additions and 936 deletions.
14 changes: 8 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- NetBSD x86_64

# Solaris
- Solaris x86_64
# - Solaris x86_64
# weird error
# https://travis-ci.org/LiveSplit/livesplit-core/jobs/327011754
# - env: TARGET=sparcv9-sun-solaris
Expand Down Expand Up @@ -533,11 +533,13 @@ jobs:
tests: skip

# Solaris
- label: Solaris x86_64
target: x86_64-sun-solaris
os: ubuntu-latest
tests: skip
dylib: skip
# - label: Solaris x86_64
# target: x86_64-sun-solaris
# os: ubuntu-latest
# tests: skip
# dylib: skip
# FIXME: Solaris stopped working. core isn't available:
# https://github.com/LiveSplit/livesplit-core/runs/2777745289?check_suite_focus=true

# Testing other channels
- label: Windows Beta
Expand Down
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ serde_json = { version = "1.0.8", optional = true }
utf-8 = { version = "0.7.4", optional = true }

# Rendering
ahash = { version = "0.7.0", default-features = false, optional = true }
euclid = { version = "0.22.1", default-features = false, optional = true }
rustybuzz = { version = "0.3.0", optional = true }
ttf-parser = { version = "0.12.0", optional = true }
Expand All @@ -62,7 +63,7 @@ ttf-parser = { version = "0.12.0", optional = true }
font-kit = { version = "0.10.0", optional = true }

# Software Rendering
tiny-skia = { version = "0.4.2", optional = true }
tiny-skia = { version = "0.5.1", optional = true }

# Networking
splits-io-api = { version = "0.2.0", optional = true }
Expand All @@ -84,7 +85,7 @@ doesnt-have-atomics = []
std = ["byteorder", "chrono/std", "chrono/clock", "image", "indexmap", "livesplit-hotkey/std", "parking_lot", "quick-xml", "serde_json", "serde/std", "snafu/std", "utf-8"]
more-image-formats = ["image/webp", "image/pnm", "image/ico", "image/jpeg", "image/tiff", "image/tga", "image/bmp", "image/hdr"]
image-shrinking = ["std", "bytemuck", "more-image-formats"]
rendering = ["std", "more-image-formats", "euclid", "ttf-parser", "rustybuzz", "bytemuck/derive"]
rendering = ["std", "more-image-formats", "euclid", "ttf-parser", "rustybuzz", "bytemuck/derive", "ahash"]
font-loading = ["std", "rendering", "font-kit"]
software-rendering = ["rendering", "tiny-skia"]
wasm-web = ["std", "web-sys", "chrono/wasmbind", "livesplit-hotkey/wasm-web", "parking_lot/wasm-bindgen"]
Expand All @@ -108,15 +109,15 @@ name = "balanced_pb"
harness = false

[[bench]]
name = "dummy_rendering"
name = "layout_state"
harness = false

[[bench]]
name = "layout_state"
name = "parsing"
harness = false

[[bench]]
name = "parsing"
name = "scene_management"
harness = false

[[bench]]
Expand Down
26 changes: 12 additions & 14 deletions benches/dummy_rendering.rs → benches/scene_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ cfg_if::cfg_if! {
use criterion::{criterion_group, criterion_main, Criterion};
use livesplit_core::{
layout::{self, Layout},
rendering::{Backend, FillShader, PathBuilder, Renderer, Rgba, Transform},
rendering::{PathBuilder, ResourceAllocator, SceneManager},
run::parser::livesplit,
Run, Segment, TimeSpan, Timer, TimingMethod,
};
Expand All @@ -25,20 +25,15 @@ cfg_if::cfg_if! {
fn finish(self, _: &mut Dummy) -> Self::Path {}
}

impl Backend for Dummy {
impl ResourceAllocator for Dummy {
type PathBuilder = Dummy;
type Path = ();
type Image = ();

fn path_builder(&mut self) -> Self::PathBuilder {
Dummy
}
fn render_fill_path(&mut self, _: &Self::Path, _: FillShader, _: Transform) {}
fn render_stroke_path(&mut self, _: &Self::Path, _: f32, _: Rgba, _: Transform) {}
fn render_image(&mut self, _: &Self::Image, _: &Self::Path, _: Transform) {}
fn free_path(&mut self, _: Self::Path) {}
fn create_image(&mut self, _: u32, _: u32, _: &[u8]) -> Self::Image {}
fn free_image(&mut self, _: Self::Image) {}
}

fn default(c: &mut Criterion) {
Expand All @@ -54,10 +49,10 @@ cfg_if::cfg_if! {

let state = layout.state(&timer.snapshot());

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

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

Expand All @@ -67,16 +62,19 @@ cfg_if::cfg_if! {
let mut layout = lsl("tests/layout_files/subsplits.lsl");

start_run(&mut timer);
make_progress_run_with_splits_opt(&mut timer, &[Some(10.0), None, Some(20.0), Some(55.0)]);
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);
layout.update_state(&mut state, &snapshot);

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

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

Expand Down
6 changes: 3 additions & 3 deletions benches/software_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cfg_if::cfg_if! {
criterion::{criterion_group, criterion_main, Criterion},
livesplit_core::{
layout::{self, Layout},
rendering::software::SoftwareRenderer,
rendering::software::Renderer,
run::parser::livesplit,
Run, Segment, TimeSpan, Timer, TimingMethod,
},
Expand All @@ -27,7 +27,7 @@ cfg_if::cfg_if! {

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

c.bench_function("Software Rendering (Default)", move |b| {
b.iter(|| renderer.render(&state, [300, 500]))
Expand All @@ -44,7 +44,7 @@ cfg_if::cfg_if! {

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

c.bench_function("Software Rendering (Subsplits Layout)", move |b| {
b.iter(|| renderer.render(&state, [300, 800]))
Expand Down
18 changes: 16 additions & 2 deletions capi/bind_gen/src/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type ComponentStateJson =
/**
* Colors can be used to describe what color to use for visualizing backgrounds,
* texts, lines and various other elements that are being shown. They are stored
* as RGBA colors with float point numbers ranging from 0.0 to 1.0 per channel.
* as RGBA colors with floating point numbers ranging from 0.0 to 1.0 per channel.
*/
export type Color = number[];

Expand Down Expand Up @@ -198,6 +198,11 @@ export interface TimerComponentStateJson {
bottom_color: Color,
/** The height of the timer. */
height: number,
/**
* This value indicates whether the timer is currently frequently being
* updated. This can be used for rendering optimizations.
*/
updates_frequently: boolean,
}

/** The state object describes the information to visualize for this component. */
Expand Down Expand Up @@ -348,6 +353,11 @@ export interface SplitColumnState {
semantic_color: SemanticColor,
/** The visual color of the value. */
visual_color: Color,
/**
* This value indicates whether the column is currently frequently being
* updated. This can be used for rendering optimizations.
*/
updates_frequently: boolean,
}

/**
Expand Down Expand Up @@ -383,6 +393,11 @@ export interface KeyValueComponentStateJson {
* two separate rows.
*/
display_two_rows: boolean,
/**
* This value indicates whether the value is currently frequently being
* updated. This can be used for rendering optimizations.
*/
updates_frequently: boolean,
}

/**
Expand Down Expand Up @@ -598,7 +613,6 @@ export type SettingsDescriptionValueJson =
{ Int: number } |
{ String: string } |
{ OptionalString: string | null } |
{ Float: number } |
{ Accuracy: AccuracyJson } |
{ DigitsFormat: DigitsFormatJson } |
{ OptionalTimingMethod: TimingMethodJson | null } |
Expand Down
34 changes: 29 additions & 5 deletions capi/src/layout_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
//! state objects that can be visualized by any kind of User Interface.
use super::{output_vec, Json};
use crate::component::OwnedComponent;
use crate::layout::OwnedLayout;
use crate::layout_editor_state::OwnedLayoutEditorState;
use crate::setting_value::OwnedSettingValue;
use livesplit_core::{LayoutEditor, Timer};
use crate::{
component::OwnedComponent, layout::OwnedLayout, layout_editor_state::OwnedLayoutEditorState,
setting_value::OwnedSettingValue,
};
use livesplit_core::{layout::LayoutState, LayoutEditor, Timer};

/// type
pub type OwnedLayoutEditor = Box<LayoutEditor>;
Expand Down Expand Up @@ -58,6 +58,30 @@ pub extern "C" fn LayoutEditor_layout_state_as_json(
})
}

/// Updates the layout's state based on the timer provided.
#[no_mangle]
pub extern "C" fn LayoutEditor_update_layout_state(
this: &mut LayoutEditor,
state: &mut LayoutState,
timer: &Timer,
) {
this.update_layout_state(state, &timer.snapshot())
}

/// Updates the layout's state based on the timer provided and encodes it as
/// JSON.
#[no_mangle]
pub extern "C" fn LayoutEditor_update_layout_state_as_json(
this: &mut LayoutEditor,
state: &mut LayoutState,
timer: &Timer,
) -> Json {
this.update_layout_state(state, &timer.snapshot());
output_vec(|o| {
state.write_json(o).unwrap();
})
}

/// Selects the component with the given index in order to modify its
/// settings. Only a single component is selected at any given time. You may
/// not provide an invalid index.
Expand Down
6 changes: 0 additions & 6 deletions capi/src/setting_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@ pub extern "C" fn SettingValue_from_optional_empty_string() -> OwnedSettingValue
Box::new(None::<String>.into())
}

/// Creates a new setting value from a floating point number.
#[no_mangle]
pub extern "C" fn SettingValue_from_float(value: f64) -> OwnedSettingValue {
Box::new(value.into())
}

/// Creates a new setting value from an accuracy name. If it doesn't match a
/// known accuracy, <NULL> is returned.
#[no_mangle]
Expand Down
22 changes: 13 additions & 9 deletions capi/src/software_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use livesplit_core::layout::LayoutState;

#[cfg(feature = "software-rendering")]
use livesplit_core::rendering::software::BorrowedSoftwareRenderer as SoftwareRenderer;
use livesplit_core::rendering::software::BorrowedRenderer as SoftwareRenderer;

#[cfg(not(feature = "software-rendering"))]
/// dummy
Expand All @@ -15,7 +15,7 @@ impl SoftwareRenderer {
panic!("The software renderer is not compiled in.")
}

fn render(&mut self, _: &LayoutState, _: &mut [u8], _: [u32; 2], _: u32) {}
fn render(&mut self, _: &LayoutState, _: &mut [u8], _: [u32; 2], _: u32, _: bool) {}
}

/// type
Expand All @@ -33,13 +33,15 @@ pub extern "C" fn SoftwareRenderer_drop(this: OwnedSoftwareRenderer) {
drop(this);
}

/// Renders the layout state provided into the image buffer provided. The
/// image has to be an array of RGBA8 encoded pixels (red, green, blue,
/// alpha with each channel being an u8). Some frameworks may over allocate
/// an image's dimensions. So an image with dimensions 100x50 may be over
/// allocated as 128x64. In that case you provide the real dimensions of
/// 100 and 50 as the width and height, but a stride of 128 pixels as that
/// correlates with the real width of the underlying buffer.
/// Renders the layout state provided into the image buffer provided. The image
/// has to be an array of RGBA8 encoded pixels (red, green, blue, alpha with
/// each channel being an u8). Some frameworks may over allocate an image's
/// dimensions. So an image with dimensions 100x50 may be over allocated as
/// 128x64. In that case you provide the real dimensions of 100x50 as the width
/// and height, but a stride of 128 pixels as that correlates with the real
/// width of the underlying buffer. By default the renderer will try not to
/// redraw parts of the image that haven't changed. You can force a redraw in
/// case the image provided or its contents have changed.
#[no_mangle]
pub unsafe extern "C" fn SoftwareRenderer_render(
this: &mut SoftwareRenderer,
Expand All @@ -48,11 +50,13 @@ pub unsafe extern "C" fn SoftwareRenderer_render(
width: u32,
height: u32,
stride: u32,
force_redraw: bool,
) {
this.render(
layout_state,
std::slice::from_raw_parts_mut(data, stride as usize * height as usize * 4),
[width, height],
stride,
force_redraw,
);
}
18 changes: 13 additions & 5 deletions src/analysis/current_pace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{analysis, timing::Snapshot, TimeSpan, TimerPhase};
/// Calculates the current pace of the active attempt based on the comparison
/// provided. If there's no active attempt, the final time of the comparison is
/// returned instead.
pub fn calculate(timer: &Snapshot<'_>, comparison: &str) -> Option<TimeSpan> {
pub fn calculate(timer: &Snapshot<'_>, comparison: &str) -> (Option<TimeSpan>, bool) {
let timing_method = timer.current_timing_method();
let last_segment = timer.run().segments().last().unwrap();

Expand All @@ -21,20 +21,28 @@ pub fn calculate(timer: &Snapshot<'_>, comparison: &str) -> Option<TimeSpan> {
)
.unwrap_or_default();

let mut is_live = false;

catch! {
let live_delta = timer.current_time()[timing_method]?
- timer.current_split().unwrap().comparison(comparison)[timing_method]?;

if live_delta > delta {
delta = live_delta;
is_live = true;
}
};

catch! {
let value = catch! {
last_segment.comparison(comparison)[timing_method]? + delta
}
};

(
value,
is_live && timer.current_phase().is_running() && value.is_some(),
)
}
TimerPhase::Ended => last_segment.split_time()[timing_method],
TimerPhase::NotRunning => last_segment.comparison(comparison)[timing_method],
TimerPhase::Ended => (last_segment.split_time()[timing_method], false),
TimerPhase::NotRunning => (last_segment.comparison(comparison)[timing_method], false),
}
}
Loading

0 comments on commit d043746

Please sign in to comment.