1
1
Fork 0
mirror of https://github.com/griffi-gh/hUI.git synced 2025-04-11 02:07:21 -05:00

wip hui-painter refactor

This commit is contained in:
griffi-gh 2025-03-03 16:37:30 +01:00
parent 6ce17d0625
commit 5515cbf84b
10 changed files with 184 additions and 15 deletions
hui-painter-wip
hui-shared/src/rect

View file

@ -22,3 +22,4 @@ rect_packer = "0.2" # TODO: use sth else like `crunch` instead?
hashbrown = "0.15"
nohash-hasher = "0.2"
fontdue = "0.9"
rustc-hash = "2.0"

View file

@ -1,3 +1,5 @@
use std::hash::Hash;
use glam::Vec2;
use crate::{paint::buffer::PaintBuffer, PainterInstance};
@ -29,14 +31,16 @@ pub trait PaintCommand {
/// Do not allocate new textures or cache glyphs here, use `pre_paint` instead!\
/// (Doing this WILL lead to atlas corruption flicker for a single frame if it's forced to resize!)
fn paint(&self, ctx: &mut PainterInstance, into: &mut PaintBuffer);
}
pub trait Measurable: PaintCommand {
/// Hash of the parameters that affect command's appearance
///
/// Must be unique for each possilbe combination of parameters
fn cache_hash(&self) -> u64;
fn size(&self, ctx: &PainterInstance) -> Vec2;
}
// TODO move paint_root to PaintCommand instead of separate trait?
pub trait PaintRoot: PaintCommand {
/// Paint the root command, calling `pre_paint` before painting
///

View file

@ -1,3 +1,5 @@
use std::hash::Hasher;
use crate::PainterInstance;
use super::PaintCommand;
@ -42,4 +44,20 @@ impl PaintCommand for PaintList {
command.paint(ctx, into);
}
}
fn cache_hash(&self) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
for command in self.commands.iter() {
hasher.write_u64(command.cache_hash());
}
hasher.finish()
}
fn size(&self, ctx: &PainterInstance) -> glam::Vec2 {
let mut size = glam::Vec2::ZERO;
for command in &self.commands {
size = size.max(command.size(ctx));
}
size
}
}

View file

@ -1,4 +1,4 @@
use std::num::NonZeroU16;
use std::{hash::Hasher, num::NonZeroU16};
use glam::{vec2, Vec2};
use hui_shared::{color, rect::{Corners, FillColor}};
use crate::{
@ -7,7 +7,8 @@ use crate::{
command::PaintCommand,
},
texture::TextureHandle,
PainterInstance
util::{hash_vec2, hash_vec3, hash_vec4},
PainterInstance,
};
/// Calculate the number of points based on the maximum corner radius
@ -271,7 +272,21 @@ impl PaintCommand for PaintRectangle {
]);
}
unimplemented!("Border radius is not supported yet");
// unimplemented!("Border radius is not supported yet");
}
}
fn size(&self, ctx: &PainterInstance) -> Vec2 {
self.size
}
fn cache_hash(&self) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
hash_vec2(&mut hasher, self.size);
for corner in self.color.corners() {
hash_vec4(&mut hasher, corner);
}
hasher.finish()
}
}

View file

@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, hash::{Hash, Hasher}};
use fontdue::layout::{CoordinateSystem, Layout};
use glam::{vec2, Vec2};
use crate::{
@ -8,7 +8,6 @@ use crate::{
}, text::FontHandle, PainterInstance
};
use super::Measurable;
// TODO align, multichunk etc
@ -75,11 +74,11 @@ impl PaintCommand for PaintText {
// let glyph_raster = ctx.fonts().render_glyph(atlas, font, config);
// }
todo!()
}
}
// todo!()
// TODO text rendering
}
impl Measurable for PaintText {
fn size(&self, ctx: &PainterInstance) -> Vec2 {
let font_array = self.build_font_array(ctx);
let layout = self.build_layout(&font_array);
@ -94,4 +93,12 @@ impl Measurable for PaintText {
vec2(width, height)
}
fn cache_hash(&self) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
self.text.font.hash(&mut hasher);
hasher.write_u32(self.text.size.to_bits());
hasher.write(self.text.text.as_bytes());
hasher.finish()
}
}

View file

@ -1,3 +1,5 @@
use std::hash::Hasher;
use crate::{
PainterInstance,
paint::{
@ -40,4 +42,18 @@ impl<T: PaintCommand + 'static> PaintCommand for PaintTransform<T> {
vtx.position += offset;
}
}
fn cache_hash(&self) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
hasher.write_u64(self.child.cache_hash());
self.transform.to_cols_array().iter().for_each(|v| {
hasher.write_u32(v.to_bits())
});
hasher.finish()
}
fn size(&self, ctx: &PainterInstance) -> glam::Vec2 {
// TODO take transform into account
self.child.size(ctx)
}
}

View file

@ -3,7 +3,7 @@ use nohash_hasher::BuildNoHashHasher;
pub(crate) type FontId = u16;
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct FontHandle(pub(crate) FontId);
pub(crate) struct FontRepr {

View file

@ -76,7 +76,7 @@ type TextureId = u32;
///
/// Can be cheaply copied and passed around.\
/// The handle is only valid for the texture atlas it was created from.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub struct TextureHandle {
pub(crate) id: TextureId,
pub(crate) size: UVec2,

View file

@ -0,0 +1,22 @@
use std::hash::Hasher;
#[inline]
pub(crate) fn hash_vec2(hasher: &mut impl Hasher, vec: glam::Vec2) {
hasher.write_u32(vec.x.to_bits());
hasher.write_u32(vec.y.to_bits());
}
#[inline]
pub(crate) fn hash_vec3(hasher: &mut impl Hasher, vec: glam::Vec3) {
hasher.write_u32(vec.x.to_bits());
hasher.write_u32(vec.y.to_bits());
hasher.write_u32(vec.z.to_bits());
}
#[inline]
pub(crate) fn hash_vec4(hasher: &mut impl Hasher, vec: glam::Vec4) {
hasher.write_u32(vec.x.to_bits());
hasher.write_u32(vec.y.to_bits());
hasher.write_u32(vec.z.to_bits());
hasher.write_u32(vec.w.to_bits());
}

View file

@ -1,5 +1,7 @@
use std::mem::ManuallyDrop;
/// Represents 4 corners of a rectangular shape.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct Corners<T> {
pub top_left: T,
pub top_right: T,
@ -7,6 +9,32 @@ pub struct Corners<T> {
pub bottom_right: T,
}
impl<T> Corners<T> {
#[inline]
pub fn to_array(self) -> [T; 4] {
[self.top_left, self.top_right, self.bottom_left, self.bottom_right]
}
#[inline]
pub fn as_array(&self) -> [&T; 4] {
[
&self.top_left,
&self.top_right,
&self.bottom_left,
&self.bottom_right,
]
}
pub fn as_array_mut(&mut self) -> [&mut T; 4] {
[
&mut self.top_left,
&mut self.top_right,
&mut self.bottom_left,
&mut self.bottom_right,
]
}
}
impl<T: Clone> Corners<T> {
#[inline]
pub fn all(value: T) -> Self {
@ -83,3 +111,61 @@ impl<T> From<(T, T, T, T)> for Corners<T> {
}
}
}
impl<T> IntoIterator for Corners<T> {
type Item = T;
type IntoIter = std::array::IntoIter<Self::Item, 4>;
fn into_iter(self) -> Self::IntoIter {
self.to_array().into_iter()
}
}
impl<'a, T> IntoIterator for &'a Corners<T> {
type Item = &'a T;
type IntoIter = std::array::IntoIter<Self::Item, 4>;
fn into_iter(self) -> Self::IntoIter {
self.as_array().into_iter()
}
}
impl<'a, T> IntoIterator for &'a mut Corners<T> {
type Item = &'a mut T;
type IntoIter = std::array::IntoIter<Self::Item, 4>;
fn into_iter(self) -> Self::IntoIter {
self.as_array_mut().into_iter()
}
}
// over-engineered :p
// struct CornersIter<T> {
// values: [ManuallyDrop<T>; 4],
// curr: u8,
// }
// impl<T> Iterator for CornersIter<T> {
// type Item = T;
// fn next(&mut self) -> Option<Self::Item> {
// if self.curr >= 4 {
// return None
// }
// let result = unsafe {
// ManuallyDrop::take(&mut self.values[self.curr as usize])
// };
// self.curr += 1;
// Some(result)
// }
// }
// impl<T> Drop for CornersIter<T> {
// fn drop(&mut self) {
// for i in self.curr..4 {
// unsafe {
// ManuallyDrop::drop(&mut self.values[i as usize]);
// }
// }
// }
// }