performane optimisations

This commit is contained in:
2025-07-03 23:26:10 +02:00
parent e260a38028
commit c2f25e1a89
6 changed files with 129 additions and 120 deletions
Generated
+1
View File
@@ -341,6 +341,7 @@ name = "osgui"
version = "0.0.1"
dependencies = [
"minifb",
"once_cell",
"rusttype",
]
+1
View File
@@ -10,5 +10,6 @@ name = "osgui"
[dependencies]
minifb = "0.28.0"
once_cell = "1.21.3"
rusttype = "0.9.3"
+4 -17
View File
@@ -1,16 +1,11 @@
use std::sync::Arc;
use crate::{
component,
extensions::Extension,
style::{Position, Transform},
widget::Widget,
};
use crate::{component, extensions::Extension, style::Transform, widget::Widget};
pub struct VelocityExtension;
impl VelocityExtension {
fn apply_velocity(ticks: usize, velocity: i32, x: &mut usize) {
fn apply_velocity(ticks: usize, velocity: i32, x: &mut i32) {
if velocity.abs() != 0 && ticks as i32 % (1000 / velocity.abs()) == 0 {
if velocity > 0 {
*x += 1;
@@ -25,16 +20,8 @@ impl VelocityExtension {
fn apply_velocity_xy(ticks: usize, widget: &Arc<Widget>) {
if let Some(velocity) = widget.get::<Velocity>() {
if let Some(mut t) = widget.get::<Transform>() {
match &mut t.x {
Position::Const(x) => Self::apply_velocity(ticks, velocity.0, x),
_ => {}
}
match &mut t.y {
Position::Const(y) => Self::apply_velocity(ticks, velocity.1, y),
_ => {}
}
Self::apply_velocity(ticks, velocity.0, &mut t.mx);
Self::apply_velocity(ticks, velocity.1, &mut t.my);
widget.set_component(t);
}
}
+24 -11
View File
@@ -1,6 +1,5 @@
use std::sync::{Arc, Mutex};
use minifb::Window;
use std::sync::{Arc, Mutex};
use crate::{
extensions::Extension,
@@ -24,8 +23,8 @@ pub struct Screen {
}
impl Screen {
pub fn new(win: Window) -> Screen {
Screen {
pub fn new(win: Window) -> Self {
Self {
window: win,
widgets: Vec::new(),
extensions: Vec::new(),
@@ -42,7 +41,7 @@ impl Screen {
}
pub fn run(&mut self) -> std::io::Result<()> {
for elem in &mut self.widgets {
for elem in &self.widgets {
elem.component(Transform::new());
}
@@ -50,17 +49,30 @@ impl Screen {
ext.lock().unwrap().init(&self.widgets);
}
let (w, h) = self.window.get_size();
let mut scope = RenderScope::new(w, h);
let mut last_fps_check = std::time::Instant::now();
let mut frame_count = 0;
while self.window.is_open() {
self.render();
std::thread::sleep(std::time::Duration::from_millis(28));
self.render(&mut scope);
frame_count += 1;
// Print FPS every second
if last_fps_check.elapsed() >= std::time::Duration::from_secs(1) {
println!("{}", frame_count);
frame_count = 0;
last_fps_check = std::time::Instant::now();
}
}
Ok(())
}
fn render(&mut self) {
fn render(&mut self, scope: &mut RenderScope) {
let (w, h) = self.window.get_size();
let mut scope = RenderScope::new(w, h);
scope.resize_if_needed(w, h);
for ext in &self.extensions {
ext.lock()
@@ -74,7 +86,7 @@ impl Screen {
scope.set_transform(&t);
}
elem.0.lock().unwrap().render(&mut scope);
elem.0.lock().unwrap().render(scope);
if let Some(t) = elem.get() {
scope.set_transform(&t);
@@ -90,7 +102,8 @@ impl Screen {
}
self.window
.update_with_buffer(&scope.get_buffer1d(), w, h)
.update_with_buffer(scope.get_buffer1d(), w, h)
.unwrap();
scope.clear_buffer();
}
}
+7 -2
View File
@@ -1,7 +1,10 @@
use minifb::{Window, WindowOptions};
use osgui::{
elements::div::{Div, DivStyle},
extensions::mouse::{MouseExtension, OnClick},
extensions::{
mouse::{MouseExtension, OnClick},
velocity::{Velocity, VelocityExtension},
},
style::Transform,
Screen,
};
@@ -20,6 +23,7 @@ fn main() {
let mut app = Screen::new(window);
app.extension(MouseExtension::new());
app.extension(VelocityExtension);
milestones(&mut app);
@@ -49,6 +53,7 @@ fn milestones(app: &mut Screen) {
osgui::style::Position::Center,
osgui::style::Position::Const(145 + ((194 + 30) * i)),
))
.component(OnClick(|_| println!("Clicked!")));
.component(OnClick(|_| println!("Clicked!")))
.component(Velocity(50, 0));
}
}
+91 -89
View File
@@ -1,3 +1,4 @@
use once_cell::sync::Lazy;
use rusttype::{Font, Scale};
use crate::{
@@ -6,6 +7,7 @@ use crate::{
};
const FONT: &[u8] = include_bytes!("../assets/DejaVuSans.ttf");
static FONT_OBJ: Lazy<Font<'static>> = Lazy::new(|| Font::try_from_bytes(FONT).unwrap());
#[derive(Debug, Clone)]
pub enum RenderMethod {
@@ -22,13 +24,13 @@ pub struct RenderScope {
parent_width: usize,
parent_height: usize,
max_size: (usize, usize),
buffer: Vec<Vec<u32>>,
buffer: Vec<u32>,
}
impl RenderScope {
pub fn new(w: usize, h: usize) -> Self {
Self {
buffer: vec![vec![0; w]; h],
buffer: vec![0; w * h],
render_stack: Vec::new(),
parent_width: w,
parent_height: h,
@@ -43,8 +45,7 @@ impl RenderScope {
}
self.render_stack
.push(RenderMethod::Text(text.to_string(), x, y, scale, color));
let font = Font::try_from_bytes(FONT).unwrap();
let (w, h) = utils::measure_text(&font, &text, Scale::uniform(scale));
let (w, h) = utils::measure_text(&FONT_OBJ, text, Scale::uniform(scale));
self.transform.width = self.transform.width.max(w as usize);
self.transform.height = self.transform.height.max(h as usize);
self.update_size();
@@ -97,123 +98,115 @@ impl RenderScope {
// After draw
impl RenderScope {
pub fn draw(&mut self) {
let stride = self.parent_width;
let mut tmp = std::mem::take(&mut self.buffer);
self.draw_buf(&mut tmp);
self.draw_buf(&mut tmp, stride);
self.buffer = tmp;
}
pub fn draw_buf(&self, buf: &mut Vec<Vec<u32>>) {
pub fn draw_buf(&mut self, buf: &mut [u32], stride: usize) {
let offset_x = self.transform.x;
let offset_y = self.transform.y;
for m in &self.render_stack {
for m in &mut self.render_stack {
match m {
&RenderMethod::Rectangle(px, py, width, height, color) => {
for y in offset_y + py..(offset_y + py + height).min(self.parent_height) {
for x in offset_x + px..(offset_x + px + width).min(self.parent_width) {
buf[y][x] = color;
}
/* ───────────── Rectangle ───────────── */
RenderMethod::Rectangle(px, py, width, height, color) => {
let xs = offset_x + *px;
let xe = (xs + *width).min(self.parent_width);
let ys = offset_y + *py;
let ye = (ys + *height).min(self.parent_height);
for y in ys..ye {
let start = y * stride + xs;
let end = start + (xe - xs);
buf[start..end].fill(*color);
}
}
&RenderMethod::RoundedRectangle(px, py, width, height, radius, color) => {
let radius = radius as i32;
let w = width as i32;
let h = height as i32;
let start_x = offset_x as i32 + px as i32;
let start_y = offset_y as i32 + py as i32;
/* ─────────── Rounded Rectangle ─────────── */
RenderMethod::RoundedRectangle(px, py, width, height, radius, color) => {
let r = *radius as i32;
let ww = *width as i32;
let hh = *height as i32;
let sx = (offset_x + *px) as i32;
let sy = (offset_y + *py) as i32;
for y in 0..h {
for x in 0..w {
let dx = x;
let dy = y;
for y in 0..hh {
for x in 0..ww {
// corners ⇒ circle test
let in_tl = x < r && y < r;
let in_tr = x >= ww - r && y < r;
let in_bl = x < r && y >= hh - r;
let in_br = x >= ww - r && y >= hh - r;
let in_top_left = dx < radius && dy < radius;
let in_top_right = dx >= w - radius && dy < radius;
let in_bottom_left = dx < radius && dy >= h - radius;
let in_bottom_right = dx >= w - radius && dy >= h - radius;
let dist = |cx: i32, cy: i32| -> bool {
let dx = dx - cx;
let dy = dy - cy;
dx * dx + dy * dy <= radius * radius
let ok = if in_tl {
(x - r + 1).pow(2) + (y - r + 1).pow(2) <= r.pow(2)
} else if in_tr {
(x - (ww - r)).pow(2) + (y - r + 1).pow(2) <= r.pow(2)
} else if in_bl {
(x - r + 1).pow(2) + (y - (hh - r)).pow(2) <= r.pow(2)
} else if in_br {
(x - (ww - r)).pow(2) + (y - (hh - r)).pow(2) <= r.pow(2)
} else {
true
};
let draw = match (
in_top_left,
in_top_right,
in_bottom_left,
in_bottom_right,
) {
(true, _, _, _) => dist(radius - 1, radius - 1),
(_, true, _, _) => dist(w - radius, radius - 1),
(_, _, true, _) => dist(radius - 1, h - radius),
(_, _, _, true) => dist(w - radius, h - radius),
_ => true, // middle or edges outside corner
};
if draw {
let global_x = start_x + x;
let global_y = start_y + y;
if global_x >= 0
&& global_x < self.parent_width as i32
&& global_y >= 0
&& global_y < self.parent_height as i32
if ok {
let gx = sx + x;
let gy = sy + y;
if gx >= 0
&& gx < self.parent_width as i32
&& gy >= 0
&& gy < self.parent_height as i32
{
buf[global_y as usize][global_x as usize] = color;
buf[(gy as usize) * stride + gx as usize] = *color;
}
}
}
}
}
RenderMethod::Text(text, px, py, scale, base_color) => {
let font = Font::try_from_bytes(FONT).unwrap();
/* ────────────────── Text ───────────────── */
RenderMethod::Text(text, px, py, scale, base) => {
let scale = Scale::uniform(*scale);
let v_metrics = font.v_metrics(scale);
let ascent = FONT_OBJ.v_metrics(scale).ascent;
let glyphs: Vec<_> = font
.layout(
let r0 = ((*base >> 16) & 0xFF) as f32;
let g0 = ((*base >> 8) & 0xFF) as f32;
let b0 = (*base & 0xFF) as f32;
for glyph in FONT_OBJ.layout(
text,
scale,
rusttype::point(
(offset_x + px) as f32,
(offset_y + py) as f32 + v_metrics.ascent,
),
)
.collect();
let r_base = ((base_color >> 16) & 0xFF) as f32;
let g_base = ((base_color >> 8) & 0xFF) as f32;
let b_base = (base_color & 0xFF) as f32;
for glyph in glyphs {
rusttype::point((offset_x + *px) as f32, (offset_y + *py) as f32 + ascent),
) {
if let Some(bb) = glyph.pixel_bounding_box() {
glyph.draw(|gx, gy, v| {
if v < 0.5 {
return;
}
let x = (gx as i32 + bb.min.x + *px as i32) as usize;
let y = (gy as i32 + bb.min.y + *py as i32) as usize;
let x = gx as i32 + bb.min.x;
let y = gy as i32 + bb.min.y;
if let Some(row) = buf.get_mut(y) {
if let Some(col) = row.get_mut(x) {
let r = (r_base * v) as u32;
let g = (g_base * v) as u32;
let b = (b_base * v) as u32;
*col = (r << 16) | (g << 8) | b;
}
let idx = (y as usize) * stride + x as usize;
if let Some(p) = buf.get_mut(idx) {
let r = (r0 * v) as u32;
let g = (g0 * v) as u32;
let b = (b0 * v) as u32;
*p = (r << 16) | (g << 8) | b;
}
});
}
}
}
/* ───────────────── Merge ───────────────── */
RenderMethod::Merge(scope) => {
let mut scope = scope.clone();
scope.transform.x += self.transform.x;
scope.transform.y += self.transform.y;
scope.draw_buf(buf);
scope.draw_buf(buf, stride);
}
}
}
@@ -223,6 +216,14 @@ impl RenderScope {
// Update or variable functions
impl RenderScope {
pub fn resize_if_needed(&mut self, w: usize, h: usize) {
if self.parent_width != w || self.parent_height != h {
self.parent_width = w;
self.parent_height = h;
self.buffer.resize(w * h, 0);
}
}
pub fn set_transform(&mut self, transform: &Transform) {
transform.use_dimensions(&mut self.transform);
transform.use_position(self.parent_width, self.parent_height, &mut self.transform);
@@ -270,18 +271,19 @@ impl RenderScope {
(self.parent_width, self.parent_height)
}
pub fn get_buffer1d(&self) -> Vec<u32> {
self.buffer
.iter()
.flat_map(|row| row.iter().copied())
.collect()
pub fn get_buffer1d(&self) -> &Vec<u32> {
&self.buffer
}
pub fn get_buffer(&self) -> Vec<Vec<u32>> {
self.buffer.clone()
pub fn get_buffer(&self) -> Vec<&[u32]> {
self.buffer.chunks(self.parent_width).collect()
}
pub fn get_buffer_mut(&mut self) -> &mut Vec<Vec<u32>> {
&mut self.buffer
pub fn get_buffer_mut(&mut self) -> Vec<&mut [u32]> {
self.buffer.chunks_mut(self.parent_width).collect()
}
pub fn clear_buffer(&mut self) {
self.buffer.fill(0);
}
}