Tui-rs: flickering when drawing multiple widgets - rust

Good evening!
I'm trying to write a very simple terminal application that draws two textboxes on screen, accepting input on one and showing output on the other, using Rust and tui-rs. The first part works perfectly, but my problems arose when i tried to draw two blocks at the same time: for some reason, it only shows the second block (in order of drawing) and if i move my mouse, it flickers between the two in a weird way. My best guess is that this is due to my drawing implementation, which somehow "clears" the screen whenever it needs to draw something, but if that's the case, i couldn't find any doc on it, and i wouldn't know how to go about working around this. I've provided some code that should be enough to replicate the issue on a smaller scale.
#![allow(unused_imports)]
#![allow(unused_variables)]
use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io
use tui::{
backend::CrosstermBackend,
layout::Rect,
widgets::{Block, Borders},
Terminal,
};
struct FirstStruct {}
impl FirstStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("One")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
struct SecondStruct { }
impl SecondStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x + w, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("Two")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
fn main() -> io::Result<()>{
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let first = FirstStruct {};
let second = SecondStruct {};
let mut running = true;
while running {
if let Event::Key(key) = event::read()? {
running = false;
}
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
}
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
Ok(())
}
Does anybody know how i can fix this issue? Thanks in advance!!

Every time you call Terminal::draw(), you must draw everything that you want to be visible at once. Instead of passing Terminal to your own draw functions, pass the Frame that you get from Terminal::draw(). That is, replace
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
with
terminal.draw(|f| {
first.draw(f)?;
second.draw(f)?;
});
and change the signature of FirstStruct and SecondStruct to match.
Also, it would be more usual to, instead of computing the rectangle for each widget in the individual functions, decide at the top level (using Layout, perhaps) and pass Rects down to the drawing functions. That way, they can be positioned differently in different situations. What you have will work, but it's not as easy to change.
Layout code from the documentation's example, adjusted to your situation:
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(50),
Constraint::Percentage(50),
].as_ref()
)
.split(f.size());
first.draw(f, chunks[0])?;
second.draw(f, chunks[1])?;
});

Related

VST3 Sampler using NIH-Plug

I'm trying to make a NIH-Plug sample player. This wouldn't be a pitch shifting plugin, but per note a different sample. (note A1 -> A1.wav). Are there any resources, about this subject. I'm also trying to cache the samples, but it's not really succeeding.
Ultimately, I'm going to use include_dir instead of include_bytes.
Here's a PolyModSynth demo, which I'm using and trying to adapt to playing samples.
Thank you.
let C1 = include_bytes!("samples/c1.wav");
let CS1 = include_bytes!("samples/c#1.wav");
fn process(
&mut self,
buffer: &mut Buffer,
_aux: &mut AuxiliaryBuffers,
context: &mut impl ProcessContext<Self>
) -> ProcessStatus {
let mut next_event = context.next_event();
for (sample_id, wav_sample) in buffer.iter_samples().zip(wav_vec) {
// Act on the next MIDI event
while let Some(event) = next_event {
if event.timing() > (sample_id as u32) {
break;
}
// handle the MIDI event as needed
// ...
next_event = context.next_event();
}
// Set the sample value in the output buffer
for sample in channel_samples {
*sample = self.sample_value[sample_id];
}
}

How can I turn a Vec of trait objects into a tree structure?

I'm trying to implement the BVH algorithm in my Rust ray tracer, but I'm having trouble with lifetimes and ownership. I have a trait Hittable that a bunch of different things implement -- Sphere, Triangle, Mesh, etc. And so I have a Vec<Box<dyn Hittable>>, which I want to turn into a tree of this struct:
pub struct BvhNode {
bounding_box: BoundingBox,
left: Box<dyn Hittable>,
right: Box<dyn Hittable>,
}
And so I have this recursive algorithm that almost works, if not for lifetime issues. My function looks like
pub fn new(objects: Vec<Box<dyn Hittable>>, start: usize, end: usize, t0: f64, t1: f64) -> Self {
let r = util::rand();
let comp = if r < 1. / 3. {
util::box_x_compare
} else if r < 2. / 3. {
util::box_y_compare
} else {
util::box_z_compare
}; // which axis to compare along (random for now)
let num_obj = end - start;
let mut left: Box<dyn Hittable>;
let mut right: Box<dyn Hittable>;
if num_obj == 1 {
left = objects[start];
right = objects[start];
} else if num_obj == 2 {
if comp(&&objects[start], &&objects[start + 1]) != Ordering::Greater {
left = objects[start];
right = objects[start + 1];
} else {
left = objects[start + 1];
right = objects[start];
}
} else {
let mut slice: Vec<&Box<dyn Hittable>> = Vec::new();
for i in start..end { // make a copy to sort
slice.push(&objects[i]);
}
slice.sort_by(comp);
let mid = start + num_obj / 2;
let l = BvhNode::new(objects, start, mid, t0, t1);
let r = BvhNode::new(objects, mid, end, t0, t1);
left = Box::new(l.clone());
right = Box::new(r.clone());
}
let left_box = left.get_bounding_box(t0, t1);
let right_box = right.get_bounding_box(t0, t1);
if left_box.is_none() || right_box.is_none() {
println!("Error: No bounding box in Bvh Node");
panic!();
}
Self { left, right, bounding_box: BoundingBox::new(Point3::origin(), Point3::origin()) }
}
First, I ran into some issues with trying to "move out of a vec", which I can't do, so I tried to implement Clone on all of the types that implement Hittable. It works for almost all of them, but my Triangle struct
pub struct Triangle <'b> {
mat: &'b Box<dyn Material>,
bounding_box: Option<BoundingBox>,
p: Vec<Point3<f64>>,
n: Vec<Vector3<f64>>,
uv: Vec<Vector2<f64>>,
}
contains reference to the material of its associated mesh, which clone doesn't like. I could make a clone of the Material itself, but it could potentially be very large and I don't want to have thousands of copies of it for each triangle in a mesh.
I feel like there has to be a better way to design my system, like get rid of references in Triangle so it can easily be copied. Once this structure is created, I won't need the Vec anymore, so being able to move the objects to out of it inside of copying them would also work, but I don't see a way to do that either.
In case it helps, the full file is on GitHub here
I was able to solve this by changing the objects Vec to &mut Vec<Box<dyn Hittable>> and then using objects.remove() instead of indexing into it. I had to change the algorithm slightly to handle the new way, but it should work.

Why does a simple println!() or dbg!() call prevent a runtime error within my FFI environment?

Edit 1:
When I had written this post I shortened the original code for better presentation. This however also inadvertently introduced a code error in the external library call foo.
I edited the code to fix the rather obvious mistake regarding the handover of a Vec<T> as the community has already correctly pointed out.
Question
How and why does a println or dbg line of code change my runtime behaviour as illustrated? And of more importance: How can I fix my code, such that it works?
I am sincerely sorry for the long post, but I could not leave out any information as it may play a vital role in finding a solution.
Background information
Rust is running as a dynamic system library ([crate_type = "cdylib"]) within a Delphi program. The Delphi program runs within a single process, no other threads are spawned. This process calls Rust, handing over a data object holding a triangle mesh as well as some other parameters. When the line described below is processed, Delphi throws an Invalid_Floating_Point exception.
I have already tested that the handover is interpreted correctly, i.e. I am able to interpret the mesh object as a mesh as well as other parameters handed over.
Note that, if I run the code with a rust example with excately the same setup, the runtime error does not occur.
Rust Code
Call Stack
FFI function:
#[no_mangle]
pub unsafe extern "C" fn foo(
target: &mut *mut Supports,
mesh: *const Mesh
space: f32) -> i32
{
mesh = unsafe { &*mesh };
let rays: Vec<Ray> = generate_rays_from_mesh(&mesh, space);
if let Some(supports) = generate_target(&mesh, rays) {
*target = Box::into_raw(supports);
0
} else {
1
}
}
The mesh stores a BVH for faster processing. generate_target calls the BVH's get_items function. The function intends to find all triangles, which potentially intersect with the ray:
pub fn generate_target(mesh: &Mesh, rays: Vec<Ray>) -> Supports {
...
for ray in rays{
let triangles = mesh.bvh.get_items(ray);
}
}
The BVH tree redirects the get_items call to the root node of the BVH. As the root node is of type Node::Internal, Node being an enum with variants (Internal, Leaf), we dive into the implementation of struct Node. Note that get_items calls itself recursively until we find a leaf node. As the error occurs yet within the first call of Node get_items I omit the code for the leaf.
impl Node {
fn get_items(&self, ray: &Ray) -> Vec<u32> {
match self {
Node::Internal(internal) => {
if intersects_aabb(&ray, &internal.aabb) {
let (mut a, b) = rayon::join(
|| internal.lo.get_items(obj),
|| internal.hi.get_items(obj),
);
a.extend(b.into_iter());
a
} else {
Vec::new()
}
}
Node::Leaf(leaf) => {
...
}
}
}
}
As you can see, the line with the if-statement gets called before the function calls itself recursively. Within this if-statement we dive deeper into the calling stack, as the error occurs here:
#[inline]
pub fn intersects_aabb(ray: &Ray, aabb: &AABB3<f32>) -> Option<f32> {
let mut hit_val = 0.;
let mut max_val = None;
for i in 0..3 {
let s = ray.pos[i];
let d = ray.dir[i];
let min = aabb.min[i];
let max = aabb.max[i];
if relative_eq!(d, 0.0) {
if s < min || max < s {
return None;
}
} else {
let mut t2 = (max - s) / d;
let mut t1 = (min - s) / d;
if t1 > t2 {
mem::swap(&mut t1, &mut t2);
}
hit_val = hit_val.max(t1);
if let Some(m) = max_val {
max_val = ::nalgebra::partial_min(&m, &t2).cloned();
} else {
max_val = Some(t2);
}
if let Some(m) = max_val {
if hit_val > m {
return None;
}
}
}
}
Some(hit_val)
}
Error-throwing part
While debugging, I cannot go beyond the first line of the code shown below.
However, if I debug the values needed for the calculation, all values seem fine, d is not zero as would have already been catched by the if block above these lines.
...
let mut t2 = (max - s) / d;
let mut t1 = (min - s) / d;
...
Changing the function and its result
When I change the function as follows,
let mut t1 = (min - s) / d;
dbg!(&t1);
let mut t1 = (max - s) / d;
dbg!(&t2);
the DLL call does not produce a runtime error.

How to add threading to a for loop in Rust?

I'm trying to write a raytracer in Rust. I'm having difficulty getting the for loops to run in parallel. I'm not sure where the problem is, but I can't seem to get anything on the screen. Is this the correct approach or am I completely heading in the wrong direction?
I've tried running the for loops without multi-threading and it does correctly produce output. I've also added loggers to the consumer loop and I'm getting the correct values as well. It just doesn't seem to update the window.
#[derive(Clone, Copy)]
pub struct Pixel {
pub x: usize,
pub y: usize,
pub color: Vec3,
}
let mut buffer : Vec<u32> = vec![0; WIDTH * HEIGHT];
let (tx, rx) = mpsc::channel()
for x in 0..HEIGHT {
let tx_t = tx.clone();
thread::spawn(move || {
for y in 0..WIDTH {
let mut color = cast_ray(x, y); // returns vec3
let pixel = Pixel { x: x, y: y, color: color };
tx_t.send(pixel).unwrap();
}
});
}
for received in rx {
buffer[received.x * WIDTH + received.y] = received.color.x << 16 | received.color.y << 8 | received.color.z;
}
while window.is_open() && !window.is_key_down(Key::Escape) {
window.update_with_buffer(&buffer).unwrap();
}
I'm expecting a few spheres or color to appear on the screen, but it's just black.

Is it possible to compile a Vulkano shader at runtime?

I've been using Vulkano in order to get some simple 3D graphics going on. Generally, I like to write my GLSL shaders in text and restart my program, or even changing shaders while the program is running. The examples given in Vulkano appear to use a macro to convert the GLSL to some form of SPIR-V based shader with Rust functions attached, but the GLSL is actually compiled into the binary (even when using a path to a file).
I've managed to get the crate shaderc to build my SPIR-V on the fly:
let mut f = File::open("src/grafx/vert.glsl")
.expect("Can't find file src/bin/runtime-shader/vert.glsl
This example needs to be run from the root of the example crate.");
let mut source = String::new();
f.read_to_string(&mut source);
//let source = "#version 310 es\n void EP() {}";
let mut compiler = shaderc::Compiler::new().unwrap();
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some("main"));
let binary_result = compiler.compile_into_spirv(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert_eq!(Some(&0x07230203), binary_result.as_binary().first());
let text_result = compiler.compile_into_spirv_assembly(
&source, shaderc::ShaderKind::Vertex,
"shader.glsl", "main", Some(&options)).unwrap();
assert!(text_result.as_text().starts_with("; SPIR-V\n"));
//println!("Compiled Vertex Shader: {}", text_result.as_text());
let vert_spirv = {
unsafe { ShaderModule::new(device.clone(), binary_result.as_binary_u8()) }.unwrap()
};
vert_spirv
So far, so good, we have a ShaderModule which seems to be the first step. However, we we actually need is a GraphicsEntryPoint which we can then put into our GraphicsPipeline. Apparently, GraphicsPipeline is where we string together our shaders, triangles and depth maps and all that lovely stuff.
Trouble is, I've no idea what is going on with the code that performs this feat:
pub fn shade_vertex <'a, S> (vert_spirv: &'a Arc<ShaderModule>) ->
GraphicsEntryPoint<'a, S, VertInput, VertOutput, VertLayout> {
let tn = unsafe {
vert_spirv.graphics_entry_point(
CStr::from_bytes_with_nul_unchecked(b"main\0"),
VertInput,
VertOutput,
VertLayout(ShaderStages { vertex: true, ..ShaderStages::none() }),
GraphicsShaderType::Vertex
)
};
tn
}
Specifically, what is VertInput and VertOutput? I've copied them from the example.
This is the closest example I could find that deals with loading Shaders on the fly. It looks like Input and Output are looking for entry points into the SPIR-V or something but I've no idea what to do with that. I'm hoping there is a function somewhere in the existing macro that will just take care of this for me. I've gotten this far but I seem a little stuck.
Has anyone else tried loading shaders at runtime?
I'm using wgpu, I've made my device, render_pipeline multithreaded like this:
let rx = Arc::new(Mutex::new(rx));
let window = Arc::new(Mutex::new(window));
let fs = Arc::new(Mutex::new(fs));
let fs_module = Arc::new(Mutex::new(fs_module));
let render_pipeline = Arc::new(Mutex::new(render_pipeline));
let device = Arc::new(Mutex::new(device));
used notify to listen to change events:
notify = "4.0.15"
use notify::{RecommendedWatcher, Watcher, RecursiveMode};
//mainxx
let (tx, rx) = mpsc::channel();
let mut watcher: RecommendedWatcher =
Watcher::new(tx, Duration::from_millis(500)).unwrap();
log::info!("Starting watcher on {:?}", *FRAG_SHADER_PATH);
watcher.watch((*FRAG_SHADER_PATH).clone(), RecursiveMode::NonRecursive).unwrap();
Then spawn a thread that listens to changes:
thread::spawn(move || {
log::info!("Shader watcher thread spawned");
loop {
if let Ok(notify::DebouncedEvent::Write(..)) = rx.lock().unwrap().recv() {
log::info!("Write event in fragment shader");
window.lock().unwrap().set_title("Loading shader.frag...");
*fs.lock().unwrap() = load_fs().unwrap();
*fs_module.lock().unwrap() = load_fs_module(Arc::clone(&device), &Arc::clone(&fs).lock().unwrap());
*render_pipeline.lock().unwrap() = create_render_pipeline_multithreaded(Arc::clone(&device), Arc::clone(&fs_module));
render.lock().unwrap().deref_mut()();
window.lock().unwrap().set_title(TITLE);
};
}
});
where load_fs is a closure that uses glsl_to_spirv:
let load_fs = move || -> Result<Vec<u32>, std::io::Error> {
log::info!("Loading fragment shader");
let mut buffer = String::new();
let mut f = File::open(&*FRAG_SHADER_PATH)?;
f.read_to_string(&mut buffer)?;
// Load fragment shader
wgpu::read_spirv(
glsl_to_spirv::compile(
&buffer,
glsl_to_spirv::ShaderType::Fragment
).expect("Compilation failed")
)
};
There is an updated example for this in the vulkano repository.
I followed that and the example for shaderc-rs to get to this:
fn compile_to_spirv(src: &str, kind: shaderc::ShaderKind, entry_point_name: &str) -> Vec<u32> {
let mut f = File::open(src).unwrap_or_else(|_| panic!("Could not open file {}", src));
let mut glsl = String::new();
f.read_to_string(&mut glsl)
.unwrap_or_else(|_| panic!("Could not read file {} to string", src));
let compiler = shaderc::Compiler::new().unwrap();
let mut options = shaderc::CompileOptions::new().unwrap();
options.add_macro_definition("EP", Some(entry_point_name));
compiler
.compile_into_spirv(&glsl, kind, src, entry_point_name, Some(&options))
.expect("Could not compile glsl shader to spriv")
.as_binary()
.to_vec()
}
let vs = {
unsafe {
ShaderModule::from_words(
device.clone(),
&compile_to_spirv(
"shaders/triangle/vs.glsl",
shaderc::ShaderKind::Vertex,
"main",
),
)
}
.unwrap()
};
After this, vs can be used as in the example.

Resources