wallet: migrate widgets to src/ui/

This commit is contained in:
darkfi
2024-06-20 11:27:45 +02:00
parent 9565b6a390
commit 751037d44f
10 changed files with 643 additions and 513 deletions

View File

@@ -12,7 +12,9 @@ use crate::{
error::{Error, Result},
expr::{Op, SExprMachine, SExprVal},
gfx::Rectangle,
gfx2::{self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
gfx2::{
self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex,
},
prop::{
Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr,
PropertySubType, PropertyType, PropertyUint32,
@@ -22,12 +24,12 @@ use crate::{
MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo,
SceneNodeType,
},
ui::{
eval_rect, get_parent_rect, read_rect, DrawUpdate, Mesh, OnModify, RenderLayer,
RenderLayerPtr, Stoppable, Window, WindowPtr,
},
};
trait Stoppable {
async fn stop(&self);
}
pub type AsyncRuntimePtr = Arc<AsyncRuntime>;
pub struct AsyncRuntime {
@@ -299,501 +301,3 @@ impl App {
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
struct OnModify<T> {
ex: Arc<smol::Executor<'static>>,
node_name: String,
node_id: SceneNodeId,
me: Weak<T>,
tasks: Vec<smol::Task<()>>,
}
impl<T: Send + Sync + 'static> OnModify<T> {
fn new(
ex: Arc<smol::Executor<'static>>,
node_name: String,
node_id: SceneNodeId,
me: Weak<T>,
) -> Self {
Self { ex, node_name, node_id, me, tasks: vec![] }
}
fn when_change<F>(&mut self, prop: PropertyPtr, f: impl Fn(Arc<T>) -> F + Send + 'static)
where
F: std::future::Future<Output = ()> + Send + 'static,
{
let node_name = self.node_name.clone();
let node_id = self.node_id;
let on_modify_sub = prop.subscribe_modify();
let prop_name = prop.name.clone();
let me = self.me.clone();
let task = self.ex.spawn(async move {
loop {
let _ = on_modify_sub.receive().await;
debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name);
let Some(self_) = me.upgrade() else {
// Should not happen
panic!(
"'{}':{}/'{}' self destroyed before modify_task was stopped!",
node_name, node_id, prop_name
);
};
debug!(target: "app", "property modified");
f(self_).await;
}
});
self.tasks.push(task);
}
}
fn eval_rect(rect: PropertyPtr, parent_rect: &Rectangle<f32>) -> Result<()> {
if rect.array_len != 4 {
return Err(Error::PropertyWrongLen)
}
for i in 0..4 {
if !rect.is_expr(i)? {
continue
}
let expr = rect.get_expr(i).unwrap();
let machine = SExprMachine {
globals: vec![
("w".to_string(), SExprVal::Float32(parent_rect.w)),
("h".to_string(), SExprVal::Float32(parent_rect.h)),
],
stmts: &expr,
};
let v = machine.call()?.as_f32()?;
rect.set_cache_f32(i, v).unwrap();
}
Ok(())
}
fn read_rect(rect_prop: PropertyPtr) -> Result<Rectangle<f32>> {
if rect_prop.array_len != 4 {
return Err(Error::PropertyWrongLen)
}
let mut rect = [0.; 4];
for i in 0..4 {
if rect_prop.is_expr(i)? {
rect[i] = rect_prop.get_cached(i)?.as_f32()?;
} else {
rect[i] = rect_prop.get_f32(i)?;
}
}
Ok(Rectangle::from_array(rect))
}
fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option<Rectangle<f32>> {
// read our parent
if node.parents.is_empty() {
info!("RenderLayer {:?} has no parents so skipping", node);
return None
}
if node.parents.len() != 1 {
error!("RenderLayer {:?} has too many parents so skipping", node);
return None
}
let parent_id = node.parents[0].id;
let parent_node = sg.get_node(parent_id).unwrap();
let parent_rect = match parent_node.typ {
SceneNodeType::Window => {
let Some(screen_size_prop) = parent_node.get_property("screen_size") else {
error!(
"RenderLayer {:?} parent node {:?} missing screen_size property",
node, parent_node
);
return None
};
let screen_width = screen_size_prop.get_f32(0).unwrap();
let screen_height = screen_size_prop.get_f32(1).unwrap();
let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height };
parent_rect
}
SceneNodeType::RenderLayer => {
// get their rect property
let Some(parent_rect) = parent_node.get_property("rect") else {
error!(
"RenderLayer {:?} parent node {:?} missing rect property",
node, parent_node
);
return None
};
// read parent's rect
let Ok(parent_rect) = read_rect(parent_rect) else {
error!(
"RenderLayer {:?} parent node {:?} malformed rect property",
node, parent_node
);
return None
};
parent_rect
}
_ => {
error!(
"RenderLayer {:?} parent node {:?} wrong type {:?}",
node, parent_node, parent_node.typ
);
return None
}
};
Some(parent_rect)
}
struct DrawUpdate {
key: u64,
draw_calls: Vec<(u64, DrawCall)>,
}
pub type WindowPtr = Arc<Window>;
pub struct Window {
node_id: SceneNodeId,
tasks: Vec<smol::Task<()>>,
screen_size_prop: PropertyPtr,
render_api: RenderApiPtr,
}
impl Window {
pub async fn new(
sg: SceneGraphPtr2,
node_id: SceneNodeId,
ex: Arc<smol::Executor<'static>>,
render_api: RenderApiPtr,
event_pub: GraphicsEventPublisherPtr,
) -> Pimpl {
debug!(target: "app", "Window::new()");
let scene_graph = sg.lock().await;
let node = scene_graph.get_node(node_id).unwrap();
let node_name = node.name.clone();
let screen_size_prop = node.get_property("screen_size").unwrap();
let scale_prop = node.get_property("scale").unwrap();
drop(scene_graph);
let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
// Start a task monitoring for window resize events
// which updates screen_size
let ev_sub = event_pub.subscribe_resize();
let screen_size_prop2 = screen_size_prop.clone();
let me2 = me.clone();
let sg2 = sg.clone();
let resize_task = ex.spawn(async move {
loop {
let Ok((w, h)) = ev_sub.receive().await else {
debug!(target: "app", "Event relayer closed");
break
};
debug!(target: "app", "Window resized ({w}, {h})");
// Now update the properties
screen_size_prop2.set_f32(0, w);
screen_size_prop2.set_f32(1, h);
let Some(self_) = me2.upgrade() else {
// Should not happen
panic!("self destroyed before modify_task was stopped!");
};
let sg = sg2.lock().await;
self_.draw(&sg).await;
}
});
let sg2 = sg.clone();
let redraw_fn = move |self_: Arc<Self>| {
let sg = sg2.clone();
async move {
let sg = sg.lock().await;
self_.draw(&sg).await;
}
};
let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
on_modify.when_change(scale_prop, redraw_fn);
let mut tasks = on_modify.tasks;
tasks.push(resize_task);
Self { node_id, tasks, screen_size_prop, render_api }
});
Pimpl::Window(self_)
}
async fn draw(&self, sg: &SceneGraph) {
debug!(target: "app", "Window::draw()");
// SceneGraph should remain locked for the entire draw
let self_node = sg.get_node(self.node_id).unwrap();
let screen_width = self.screen_size_prop.get_f32(0).unwrap();
let screen_height = self.screen_size_prop.get_f32(1).unwrap();
let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height };
let mut draw_calls = vec![];
let mut child_calls = vec![];
for child_inf in self_node.get_children2() {
let node = sg.get_node(child_inf.id).unwrap();
debug!(target: "app", "Window::draw() calling draw() for node '{}':{}", node.name, node.id);
let dcs = match &node.pimpl {
Pimpl::RenderLayer(layer) => layer.draw(sg, &parent_rect).await,
_ => {
error!(target: "app", "unhandled pimpl type");
continue
}
};
let Some(mut draw_update) = dcs else { continue };
draw_calls.append(&mut draw_update.draw_calls);
child_calls.push(draw_update.key);
}
let root_dc = DrawCall { instrs: vec![], dcs: child_calls };
draw_calls.push((0, root_dc));
//debug!(" => {:?}", draw_calls);
self.render_api.replace_draw_calls(draw_calls).await;
debug!("Window::draw() - replaced draw call");
}
}
// Nodes should be stopped before being removed
impl Stoppable for Window {
async fn stop(&self) {}
}
pub type RenderLayerPtr = Arc<RenderLayer>;
pub struct RenderLayer {
sg: SceneGraphPtr2,
node_id: SceneNodeId,
tasks: Vec<smol::Task<()>>,
render_api: RenderApiPtr,
dc_key: u64,
is_visible: PropertyBool,
rect: PropertyPtr,
parent_rect: Mutex<Rectangle<f32>>,
}
impl RenderLayer {
pub async fn new(
sg_ptr: SceneGraphPtr2,
node_id: SceneNodeId,
ex: Arc<smol::Executor<'static>>,
render_api: RenderApiPtr,
) -> Pimpl {
let sg = sg_ptr.lock().await;
let node = sg.get_node(node_id).unwrap();
let node_name = node.name.clone();
let is_visible =
PropertyBool::wrap(node, "is_visible", 0).expect("RenderLayer::is_visible");
let rect = node.get_property("rect").expect("RenderLayer::rect");
drop(sg);
// Monitor for changes to screen_size or scale properties
// If so then trigger draw
let rect_sub = rect.subscribe_modify();
let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
on_modify.when_change(rect.clone(), Self::redraw);
Self {
sg: sg_ptr,
node_id,
tasks: on_modify.tasks,
render_api,
dc_key: OsRng.gen(),
is_visible,
rect,
parent_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }),
}
});
Pimpl::RenderLayer(self_)
}
async fn redraw(self: Arc<Self>) {
let sg = self.sg.lock().await;
let node = sg.get_node(self.node_id).unwrap();
let Some(parent_rect) = get_parent_rect(&sg, node) else {
return;
};
let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
error!("RenderLayer {:?} failed to draw", node);
return;
};
self.render_api.replace_draw_calls(draw_update.draw_calls).await;
debug!("replace draw calls done");
}
#[async_recursion]
pub async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle<f32>) -> Option<DrawUpdate> {
debug!(target: "app", "RenderLayer::draw()");
let node = sg.get_node(self.node_id).unwrap();
if !self.is_visible.get() {
debug!(target: "app", "invisible layer node '{}':{}", node.name, node.id);
return None
}
if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
panic!("Node {:?} bad rect property: {}", node, err);
}
let Ok(mut rect) = read_rect(self.rect.clone()) else {
panic!("Node {:?} bad rect property", node);
};
rect.x += parent_rect.x;
rect.y += parent_rect.x;
if !parent_rect.includes(&rect) {
error!(
target: "app",
"layer '{}':{} rect {:?} is not inside parent {:?}",
node.name, node.id, rect, parent_rect
);
return None
}
debug!(target: "app", "Parent rect: {:?}", parent_rect);
debug!(target: "app", "Viewport rect: {:?}", rect);
// Apply viewport
let mut draw_calls = vec![];
let mut child_calls = vec![];
for child_inf in node.get_children2() {
let node = sg.get_node(child_inf.id).unwrap();
let dcs = match &node.pimpl {
Pimpl::RenderLayer(layer) => layer.draw(&sg, &rect).await,
Pimpl::Mesh(mesh) => mesh.draw(&sg, &rect),
_ => {
error!(target: "app", "unhandled pimpl type");
continue
}
};
let Some(mut draw_update) = dcs else { continue };
draw_calls.append(&mut draw_update.draw_calls);
child_calls.push(draw_update.key);
}
let dc = DrawCall { instrs: vec![DrawInstruction::ApplyViewport(rect)], dcs: child_calls };
draw_calls.push((self.dc_key, dc));
Some(DrawUpdate { key: self.dc_key, draw_calls })
}
}
impl Stoppable for RenderLayer {
async fn stop(&self) {}
}
pub struct Mesh {
render_api: RenderApiPtr,
vertex_buffer: miniquad::BufferId,
index_buffer: miniquad::BufferId,
// Texture
num_elements: i32,
dc_key: u64,
node_id: SceneNodeId,
rect: PropertyPtr,
}
impl Mesh {
pub async fn new(
sg: SceneGraphPtr2,
node_id: SceneNodeId,
render_api: RenderApiPtr,
verts: Vec<Vertex>,
indices: Vec<u16>,
) -> Pimpl {
let num_elements = indices.len() as i32;
let vertex_buffer = render_api.new_vertex_buffer(verts).await.unwrap();
let index_buffer = render_api.new_index_buffer(indices).await.unwrap();
let mut sg = sg.lock().await;
let node = sg.get_node_mut(node_id).unwrap();
let rect = node.get_property("rect").expect("RenderLayer::rect");
Pimpl::Mesh(Self {
render_api,
vertex_buffer,
index_buffer,
num_elements,
dc_key: OsRng.gen(),
node_id,
rect,
})
}
pub fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle<f32>) -> Option<DrawUpdate> {
debug!(target: "app", "Mesh::draw()");
// Only used for debug messages
let node = sg.get_node(self.node_id).unwrap();
let mesh = DrawMesh {
vertex_buffer: self.vertex_buffer,
index_buffer: self.index_buffer,
texture: None,
num_elements: self.num_elements,
};
if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
panic!("Node {:?} bad rect property: {}", node, err);
}
let Ok(mut rect) = read_rect(self.rect.clone()) else {
panic!("Node {:?} bad rect property", node);
};
rect.x += parent_rect.x;
rect.y += parent_rect.x;
let off_x = rect.x / parent_rect.w;
let off_y = rect.y / parent_rect.h;
let scale_x = rect.w / parent_rect.w;
let scale_y = rect.h / parent_rect.h;
let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
Some(DrawUpdate {
key: self.dc_key,
draw_calls: vec![(
self.dc_key,
DrawCall {
instrs: vec![DrawInstruction::ApplyMatrix(model), DrawInstruction::Draw(mesh)],
dcs: vec![],
},
)],
})
}
}
impl Stoppable for Mesh {
async fn stop(&self) {
// TODO: Delete own draw call
// Free buffers
// Should this be in drop?
self.render_api.delete_buffer(self.vertex_buffer);
self.render_api.delete_buffer(self.index_buffer);
}
}

View File

@@ -232,7 +232,7 @@ fn create_chatview(sg: &mut SceneGraph, name: &str, layer_node_id: SceneNodeId)
let mut arg_data = vec![];
node_id.encode(&mut arg_data).unwrap();
//let (tx, rx) = mpsc::sync_channel::<Result<Vec<u8>>>(0);
let response_fn = Box::new(move |result| {
let response_fn = Box::new(move |_result| {
//tx.send(result).unwrap();
});
let win_node = sg.lookup_node_mut("/window").unwrap();

View File

@@ -590,7 +590,7 @@ impl EditBox {
"Delete" => {
let cursor_pos = self.cursor_pos.get();
let text = if !self.selected.is_null(0).unwrap() {
let _text = if !self.selected.is_null(0).unwrap() {
self.delete_highlighted();
} else {
let glyphs = &*self.glyphs.lock().unwrap();
@@ -618,7 +618,7 @@ impl EditBox {
"Backspace" => {
let cursor_pos = self.cursor_pos.get();
let text = if !self.selected.is_null(0).unwrap() {
let _text = if !self.selected.is_null(0).unwrap() {
self.delete_highlighted();
} else {
if cursor_pos == 0 {

View File

@@ -230,10 +230,7 @@ pub struct GraphicsEventPublisher {
impl GraphicsEventPublisher {
pub fn new() -> Arc<Self> {
Arc::new(Self {
key_down: Publisher::new(),
resize: Publisher::new(),
})
Arc::new(Self { key_down: Publisher::new(), resize: Publisher::new() })
}
fn notify_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) {

View File

@@ -1,6 +1,10 @@
#![feature(deadline_api)]
#![feature(str_split_whitespace_remainder)]
// Use these to incrementally fix warnings with cargo fix
//#![allow(warnings, unused)]
//#![deny(unused_imports)]
use async_lock::Mutex;
use futures::{stream::FuturesUnordered, StreamExt};
use std::{
@@ -31,6 +35,7 @@ mod res;
mod scene;
mod shader;
mod text;
mod ui;
use crate::{
error::{Error, Result},

View File

@@ -16,6 +16,7 @@ use crate::{
app, chatview, editbox,
error::{Error, Result},
prop::{Property, PropertyPtr, PropertyType},
ui,
};
pub type SceneNodeId = u32;
@@ -637,9 +638,9 @@ pub enum Pimpl {
Null,
EditBox(editbox::EditBoxPtr),
ChatView(chatview::ChatViewPtr),
Window(app::WindowPtr),
RenderLayer(app::RenderLayerPtr),
Mesh(app::Mesh),
Window(ui::WindowPtr),
RenderLayer(ui::RenderLayerPtr),
Mesh(ui::Mesh),
}
impl std::fmt::Debug for SceneNode {

View File

@@ -0,0 +1,163 @@
use async_lock::Mutex;
use async_recursion::async_recursion;
use futures::{stream::FuturesUnordered, StreamExt};
use rand::{rngs::OsRng, Rng};
use std::{
sync::{mpsc, Arc, Weak},
thread,
};
use crate::{
chatapp,
error::{Error, Result},
expr::{Op, SExprMachine, SExprVal},
gfx::Rectangle,
gfx2::{
self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex,
},
prop::{
Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr,
PropertySubType, PropertyType, PropertyUint32,
},
pubsub::PublisherPtr,
scene::{
MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo,
SceneNodeType,
},
};
use super::{eval_rect, get_parent_rect, read_rect, DrawUpdate, OnModify, Stoppable};
pub type RenderLayerPtr = Arc<RenderLayer>;
pub struct RenderLayer {
sg: SceneGraphPtr2,
node_id: SceneNodeId,
tasks: Vec<smol::Task<()>>,
render_api: RenderApiPtr,
dc_key: u64,
is_visible: PropertyBool,
rect: PropertyPtr,
parent_rect: Mutex<Rectangle<f32>>,
}
impl RenderLayer {
pub async fn new(
sg_ptr: SceneGraphPtr2,
node_id: SceneNodeId,
ex: Arc<smol::Executor<'static>>,
render_api: RenderApiPtr,
) -> Pimpl {
let sg = sg_ptr.lock().await;
let node = sg.get_node(node_id).unwrap();
let node_name = node.name.clone();
let is_visible =
PropertyBool::wrap(node, "is_visible", 0).expect("RenderLayer::is_visible");
let rect = node.get_property("rect").expect("RenderLayer::rect");
drop(sg);
// Monitor for changes to screen_size or scale properties
// If so then trigger draw
let rect_sub = rect.subscribe_modify();
let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
on_modify.when_change(rect.clone(), Self::redraw);
Self {
sg: sg_ptr,
node_id,
tasks: on_modify.tasks,
render_api,
dc_key: OsRng.gen(),
is_visible,
rect,
parent_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }),
}
});
Pimpl::RenderLayer(self_)
}
async fn redraw(self: Arc<Self>) {
let sg = self.sg.lock().await;
let node = sg.get_node(self.node_id).unwrap();
let Some(parent_rect) = get_parent_rect(&sg, node) else {
return;
};
let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
error!("RenderLayer {:?} failed to draw", node);
return;
};
self.render_api.replace_draw_calls(draw_update.draw_calls).await;
debug!("replace draw calls done");
}
#[async_recursion]
pub async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle<f32>) -> Option<DrawUpdate> {
debug!(target: "app", "RenderLayer::draw()");
let node = sg.get_node(self.node_id).unwrap();
if !self.is_visible.get() {
debug!(target: "app", "invisible layer node '{}':{}", node.name, node.id);
return None
}
if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
panic!("Node {:?} bad rect property: {}", node, err);
}
let Ok(mut rect) = read_rect(self.rect.clone()) else {
panic!("Node {:?} bad rect property", node);
};
rect.x += parent_rect.x;
rect.y += parent_rect.x;
if !parent_rect.includes(&rect) {
error!(
target: "app",
"layer '{}':{} rect {:?} is not inside parent {:?}",
node.name, node.id, rect, parent_rect
);
return None
}
debug!(target: "app", "Parent rect: {:?}", parent_rect);
debug!(target: "app", "Viewport rect: {:?}", rect);
// Apply viewport
let mut draw_calls = vec![];
let mut child_calls = vec![];
for child_inf in node.get_children2() {
let node = sg.get_node(child_inf.id).unwrap();
let dcs = match &node.pimpl {
Pimpl::RenderLayer(layer) => layer.draw(&sg, &rect).await,
Pimpl::Mesh(mesh) => mesh.draw(&sg, &rect),
_ => {
error!(target: "app", "unhandled pimpl type");
continue
}
};
let Some(mut draw_update) = dcs else { continue };
draw_calls.append(&mut draw_update.draw_calls);
child_calls.push(draw_update.key);
}
let dc = DrawCall { instrs: vec![DrawInstruction::ApplyViewport(rect)], dcs: child_calls };
draw_calls.push((self.dc_key, dc));
Some(DrawUpdate { key: self.dc_key, draw_calls })
}
}
impl Stoppable for RenderLayer {
async fn stop(&self) {}
}

View File

@@ -0,0 +1,123 @@
use async_lock::Mutex;
use async_recursion::async_recursion;
use futures::{stream::FuturesUnordered, StreamExt};
use rand::{rngs::OsRng, Rng};
use std::{
sync::{mpsc, Arc, Weak},
thread,
};
use crate::{
chatapp,
error::{Error, Result},
expr::{Op, SExprMachine, SExprVal},
gfx::Rectangle,
gfx2::{
self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex,
},
prop::{
Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr,
PropertySubType, PropertyType, PropertyUint32,
},
pubsub::PublisherPtr,
scene::{
MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo,
SceneNodeType,
},
};
use super::{eval_rect, read_rect, DrawUpdate, Stoppable};
pub struct Mesh {
render_api: RenderApiPtr,
vertex_buffer: miniquad::BufferId,
index_buffer: miniquad::BufferId,
// Texture
num_elements: i32,
dc_key: u64,
node_id: SceneNodeId,
rect: PropertyPtr,
}
impl Mesh {
pub async fn new(
sg: SceneGraphPtr2,
node_id: SceneNodeId,
render_api: RenderApiPtr,
verts: Vec<Vertex>,
indices: Vec<u16>,
) -> Pimpl {
let num_elements = indices.len() as i32;
let vertex_buffer = render_api.new_vertex_buffer(verts).await.unwrap();
let index_buffer = render_api.new_index_buffer(indices).await.unwrap();
let mut sg = sg.lock().await;
let node = sg.get_node_mut(node_id).unwrap();
let rect = node.get_property("rect").expect("RenderLayer::rect");
Pimpl::Mesh(Self {
render_api,
vertex_buffer,
index_buffer,
num_elements,
dc_key: OsRng.gen(),
node_id,
rect,
})
}
pub fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle<f32>) -> Option<DrawUpdate> {
debug!(target: "app", "Mesh::draw()");
// Only used for debug messages
let node = sg.get_node(self.node_id).unwrap();
let mesh = DrawMesh {
vertex_buffer: self.vertex_buffer,
index_buffer: self.index_buffer,
texture: None,
num_elements: self.num_elements,
};
if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
panic!("Node {:?} bad rect property: {}", node, err);
}
let Ok(mut rect) = read_rect(self.rect.clone()) else {
panic!("Node {:?} bad rect property", node);
};
rect.x += parent_rect.x;
rect.y += parent_rect.x;
let off_x = rect.x / parent_rect.w;
let off_y = rect.y / parent_rect.h;
let scale_x = rect.w / parent_rect.w;
let scale_y = rect.h / parent_rect.h;
let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
Some(DrawUpdate {
key: self.dc_key,
draw_calls: vec![(
self.dc_key,
DrawCall {
instrs: vec![DrawInstruction::ApplyMatrix(model), DrawInstruction::Draw(mesh)],
dcs: vec![],
},
)],
})
}
}
impl Stoppable for Mesh {
async fn stop(&self) {
// TODO: Delete own draw call
// Free buffers
// Should this be in drop?
self.render_api.delete_buffer(self.vertex_buffer);
self.render_api.delete_buffer(self.index_buffer);
}
}

View File

@@ -0,0 +1,190 @@
use async_lock::Mutex;
use async_recursion::async_recursion;
use futures::{stream::FuturesUnordered, StreamExt};
use rand::{rngs::OsRng, Rng};
use std::{
sync::{mpsc, Arc, Weak},
thread,
};
use crate::{
chatapp,
error::{Error, Result},
expr::{Op, SExprMachine, SExprVal},
gfx::Rectangle,
gfx2::{
self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex,
},
prop::{
Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr,
PropertySubType, PropertyType, PropertyUint32,
},
pubsub::PublisherPtr,
scene::{
MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo,
SceneNodeType,
},
};
mod mesh;
pub use mesh::Mesh;
mod layer;
pub use layer::{RenderLayer, RenderLayerPtr};
mod win;
pub use win::{Window, WindowPtr};
pub trait Stoppable {
async fn stop(&self);
}
pub struct DrawUpdate {
pub key: u64,
pub draw_calls: Vec<(u64, DrawCall)>,
}
pub struct OnModify<T> {
ex: Arc<smol::Executor<'static>>,
node_name: String,
node_id: SceneNodeId,
me: Weak<T>,
pub tasks: Vec<smol::Task<()>>,
}
impl<T: Send + Sync + 'static> OnModify<T> {
pub fn new(
ex: Arc<smol::Executor<'static>>,
node_name: String,
node_id: SceneNodeId,
me: Weak<T>,
) -> Self {
Self { ex, node_name, node_id, me, tasks: vec![] }
}
pub fn when_change<F>(&mut self, prop: PropertyPtr, f: impl Fn(Arc<T>) -> F + Send + 'static)
where
F: std::future::Future<Output = ()> + Send + 'static,
{
let node_name = self.node_name.clone();
let node_id = self.node_id;
let on_modify_sub = prop.subscribe_modify();
let prop_name = prop.name.clone();
let me = self.me.clone();
let task = self.ex.spawn(async move {
loop {
let _ = on_modify_sub.receive().await;
debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name);
let Some(self_) = me.upgrade() else {
// Should not happen
panic!(
"'{}':{}/'{}' self destroyed before modify_task was stopped!",
node_name, node_id, prop_name
);
};
debug!(target: "app", "property modified");
f(self_).await;
}
});
self.tasks.push(task);
}
}
pub fn eval_rect(rect: PropertyPtr, parent_rect: &Rectangle<f32>) -> Result<()> {
if rect.array_len != 4 {
return Err(Error::PropertyWrongLen)
}
for i in 0..4 {
if !rect.is_expr(i)? {
continue
}
let expr = rect.get_expr(i).unwrap();
let machine = SExprMachine {
globals: vec![
("w".to_string(), SExprVal::Float32(parent_rect.w)),
("h".to_string(), SExprVal::Float32(parent_rect.h)),
],
stmts: &expr,
};
let v = machine.call()?.as_f32()?;
rect.set_cache_f32(i, v).unwrap();
}
Ok(())
}
pub fn read_rect(rect_prop: PropertyPtr) -> Result<Rectangle<f32>> {
if rect_prop.array_len != 4 {
return Err(Error::PropertyWrongLen)
}
let mut rect = [0.; 4];
for i in 0..4 {
if rect_prop.is_expr(i)? {
rect[i] = rect_prop.get_cached(i)?.as_f32()?;
} else {
rect[i] = rect_prop.get_f32(i)?;
}
}
Ok(Rectangle::from_array(rect))
}
pub fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option<Rectangle<f32>> {
// read our parent
if node.parents.is_empty() {
info!("RenderLayer {:?} has no parents so skipping", node);
return None
}
if node.parents.len() != 1 {
error!("RenderLayer {:?} has too many parents so skipping", node);
return None
}
let parent_id = node.parents[0].id;
let parent_node = sg.get_node(parent_id).unwrap();
let parent_rect = match parent_node.typ {
SceneNodeType::Window => {
let Some(screen_size_prop) = parent_node.get_property("screen_size") else {
error!(
"RenderLayer {:?} parent node {:?} missing screen_size property",
node, parent_node
);
return None
};
let screen_width = screen_size_prop.get_f32(0).unwrap();
let screen_height = screen_size_prop.get_f32(1).unwrap();
let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height };
parent_rect
}
SceneNodeType::RenderLayer => {
// get their rect property
let Some(parent_rect) = parent_node.get_property("rect") else {
error!(
"RenderLayer {:?} parent node {:?} missing rect property",
node, parent_node
);
return None
};
// read parent's rect
let Ok(parent_rect) = read_rect(parent_rect) else {
error!(
"RenderLayer {:?} parent node {:?} malformed rect property",
node, parent_node
);
return None
};
parent_rect
}
_ => {
error!(
"RenderLayer {:?} parent node {:?} wrong type {:?}",
node, parent_node, parent_node.typ
);
return None
}
};
Some(parent_rect)
}

View File

@@ -0,0 +1,147 @@
use async_lock::Mutex;
use async_recursion::async_recursion;
use futures::{stream::FuturesUnordered, StreamExt};
use rand::{rngs::OsRng, Rng};
use std::{
sync::{mpsc, Arc, Weak},
thread,
};
use crate::{
chatapp,
error::{Error, Result},
expr::{Op, SExprMachine, SExprVal},
gfx::Rectangle,
gfx2::{
self, DrawCall, DrawInstruction, DrawMesh, GraphicsEventPublisherPtr, RenderApiPtr, Vertex,
},
prop::{
Property, PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyStr,
PropertySubType, PropertyType, PropertyUint32,
},
pubsub::PublisherPtr,
scene::{
MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNode, SceneNodeId, SceneNodeInfo,
SceneNodeType,
},
};
use super::{eval_rect, read_rect, DrawUpdate, OnModify, Stoppable};
pub type WindowPtr = Arc<Window>;
pub struct Window {
node_id: SceneNodeId,
tasks: Vec<smol::Task<()>>,
screen_size_prop: PropertyPtr,
render_api: RenderApiPtr,
}
impl Window {
pub async fn new(
sg: SceneGraphPtr2,
node_id: SceneNodeId,
ex: Arc<smol::Executor<'static>>,
render_api: RenderApiPtr,
event_pub: GraphicsEventPublisherPtr,
) -> Pimpl {
debug!(target: "app", "Window::new()");
let scene_graph = sg.lock().await;
let node = scene_graph.get_node(node_id).unwrap();
let node_name = node.name.clone();
let screen_size_prop = node.get_property("screen_size").unwrap();
let scale_prop = node.get_property("scale").unwrap();
drop(scene_graph);
let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
// Start a task monitoring for window resize events
// which updates screen_size
let ev_sub = event_pub.subscribe_resize();
let screen_size_prop2 = screen_size_prop.clone();
let me2 = me.clone();
let sg2 = sg.clone();
let resize_task = ex.spawn(async move {
loop {
let Ok((w, h)) = ev_sub.receive().await else {
debug!(target: "app", "Event relayer closed");
break
};
debug!(target: "app", "Window resized ({w}, {h})");
// Now update the properties
screen_size_prop2.set_f32(0, w);
screen_size_prop2.set_f32(1, h);
let Some(self_) = me2.upgrade() else {
// Should not happen
panic!("self destroyed before modify_task was stopped!");
};
let sg = sg2.lock().await;
self_.draw(&sg).await;
}
});
let sg2 = sg.clone();
let redraw_fn = move |self_: Arc<Self>| {
let sg = sg2.clone();
async move {
let sg = sg.lock().await;
self_.draw(&sg).await;
}
};
let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
on_modify.when_change(scale_prop, redraw_fn);
let mut tasks = on_modify.tasks;
tasks.push(resize_task);
Self { node_id, tasks, screen_size_prop, render_api }
});
Pimpl::Window(self_)
}
pub async fn draw(&self, sg: &SceneGraph) {
debug!(target: "app", "Window::draw()");
// SceneGraph should remain locked for the entire draw
let self_node = sg.get_node(self.node_id).unwrap();
let screen_width = self.screen_size_prop.get_f32(0).unwrap();
let screen_height = self.screen_size_prop.get_f32(1).unwrap();
let parent_rect = Rectangle { x: 0., y: 0., w: screen_width, h: screen_height };
let mut draw_calls = vec![];
let mut child_calls = vec![];
for child_inf in self_node.get_children2() {
let node = sg.get_node(child_inf.id).unwrap();
debug!(target: "app", "Window::draw() calling draw() for node '{}':{}", node.name, node.id);
let dcs = match &node.pimpl {
Pimpl::RenderLayer(layer) => layer.draw(sg, &parent_rect).await,
_ => {
error!(target: "app", "unhandled pimpl type");
continue
}
};
let Some(mut draw_update) = dcs else { continue };
draw_calls.append(&mut draw_update.draw_calls);
child_calls.push(draw_update.key);
}
let root_dc = DrawCall { instrs: vec![], dcs: child_calls };
draw_calls.push((0, root_dc));
//debug!(" => {:?}", draw_calls);
self.render_api.replace_draw_calls(draw_calls).await;
debug!("Window::draw() - replaced draw call");
}
}
// Nodes should be stopped before being removed
impl Stoppable for Window {
async fn stop(&self) {}
}