diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 2f2d4587..ef096759 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -2,7 +2,7 @@ use std::{default::Default, time::Duration}; use neothesia_core::{ config::Config, - render::{KeyboardRenderer, TextRenderer, WaterfallRenderer}, + render::{KeyboardRenderer, QuadPipeline, TextRenderer, WaterfallRenderer}, }; use wgpu_jumpstart::{wgpu, Gpu, TransformUniform, Uniform}; @@ -12,6 +12,7 @@ struct Recorder { playback: midi_file::PlaybackState, + quad_pipeline: QuadPipeline, keyboard: KeyboardRenderer, waterfall: WaterfallRenderer, text: TextRenderer, @@ -70,14 +71,12 @@ impl Recorder { wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ); + let quad_pipeline = QuadPipeline::new(&gpu, &transform_uniform); + let keyboard_layout = get_layout(width as f32, height as f32); - let mut keyboard = KeyboardRenderer::new( - &gpu, - &transform_uniform, - keyboard_layout.clone(), - config.vertical_guidelines, - ); + let mut keyboard = + KeyboardRenderer::new(keyboard_layout.clone(), config.vertical_guidelines); keyboard.position_on_bottom_of_parent(height as f32); @@ -101,6 +100,7 @@ impl Recorder { playback, + quad_pipeline, keyboard, waterfall, text, @@ -118,7 +118,10 @@ impl Recorder { self.waterfall .update(&self.gpu.queue, time_without_lead_in(&self.playback)); - self.keyboard.update(&self.gpu.queue, &mut self.text); + self.quad_pipeline.clear(); + self.keyboard + .update(&mut self.quad_pipeline, &mut self.text); + self.quad_pipeline.prepare(&self.gpu.queue); self.text.update((self.width, self.height), &self.gpu); } @@ -151,7 +154,8 @@ impl Recorder { }); self.waterfall.render(&self.transform_uniform, &mut rpass); - self.keyboard.render(&self.transform_uniform, &mut rpass); + self.quad_pipeline + .render(&self.transform_uniform, &mut rpass); self.text.render(&mut rpass); } @@ -296,7 +300,7 @@ fn file_midi_events( key.pressed_by_file_off(); } - keyboard.queue_reupload(); + keyboard.invalidate_cache(); } } } diff --git a/neothesia-core/src/render/keyboard/mod.rs b/neothesia-core/src/render/keyboard/mod.rs index 021348fb..e336db11 100644 --- a/neothesia-core/src/render/keyboard/mod.rs +++ b/neothesia-core/src/render/keyboard/mod.rs @@ -1,14 +1,12 @@ use crate::{ render::{QuadInstance, QuadPipeline}, utils::Point, - TransformUniform, Uniform, }; use piano_math::range::KeyboardRange; mod key_state; pub use key_state::KeyState; -use wgpu_jumpstart::Gpu; use super::TextRenderer; @@ -17,37 +15,30 @@ pub struct KeyboardRenderer { key_states: Vec, - quad_pipeline: QuadPipeline, - should_reupload: bool, - layout: piano_math::KeyboardLayout, vertical_guidelines: bool, + + cache: Vec, } impl KeyboardRenderer { - pub fn new( - gpu: &Gpu, - transform_uniform: &Uniform, - layout: piano_math::KeyboardLayout, - vertical_guidelines: bool, - ) -> Self { - let quad_pipeline = QuadPipeline::new(gpu, transform_uniform); + pub fn new(layout: piano_math::KeyboardLayout, vertical_guidelines: bool) -> Self { let key_states: Vec = layout .range .iter() .map(|id| KeyState::new(id.is_black())) .collect(); + let cache = Vec::with_capacity(key_states.len() + 1); + Self { pos: Default::default(), key_states, - quad_pipeline, - should_reupload: false, - layout, vertical_guidelines, + cache, } } @@ -55,7 +46,7 @@ impl KeyboardRenderer { for key in self.key_states.iter_mut() { key.pressed_by_file_off(); } - self.queue_reupload(); + self.invalidate_cache(); } pub fn range(&self) -> &KeyboardRange { @@ -79,7 +70,7 @@ impl KeyboardRenderer { pub fn set_pos(&mut self, pos: Point) { self.pos = pos; - self.queue_reupload(); + self.invalidate_cache(); } pub fn layout(&self) -> &piano_math::KeyboardLayout { @@ -88,76 +79,77 @@ impl KeyboardRenderer { pub fn set_layout(&mut self, layout: piano_math::KeyboardLayout) { self.layout = layout; - self.queue_reupload(); + self.invalidate_cache(); } - pub fn queue_reupload(&mut self) { - self.should_reupload = true; + pub fn invalidate_cache(&mut self) { + self.cache.clear(); } /// Reupload instances to GPU - fn reupload(&mut self, queue: &wgpu::Queue) { - self.quad_pipeline.with_instances_mut(queue, |instances| { - instances.clear(); - - // black_background - instances.push(QuadInstance { - position: self.pos.into(), - size: [self.layout.width, self.layout.height], - color: [0.0, 0.0, 0.0, 1.0], - ..Default::default() - }); + fn reupload(&mut self) { + let instances = &mut self.cache; + + // black_background + instances.push(QuadInstance { + position: self.pos.into(), + size: [self.layout.width, self.layout.height], + color: [0.0, 0.0, 0.0, 1.0], + ..Default::default() + }); - for key in self - .layout - .keys - .iter() - .filter(|key| key.kind().is_neutral()) - { - let id = key.id(); - let color = self.key_states[id].color(); - - if self.vertical_guidelines { - // Horizontal guides - // TODO: Does not really fit in keyboard renderer - if key.note_id() == 0 || key.note_id() == 5 { - let x = self.pos.x + key.x(); - let y = 0.0; - - let w = 1.0; - let h = f32::MAX; - - let color = if key.note_id() == 0 { - [0.2, 0.2, 0.2, 1.0] - } else { - [0.05, 0.05, 0.05, 1.0] - }; - - instances.push(QuadInstance { - position: [x, y], - size: [w, h], - color, - border_radius: [0.0, 0.0, 0.0, 0.0], - }); - } + for key in self + .layout + .keys + .iter() + .filter(|key| key.kind().is_neutral()) + { + let id = key.id(); + let color = self.key_states[id].color(); + + if self.vertical_guidelines { + // Horizontal guides + // TODO: Does not really fit in keyboard renderer + if key.note_id() == 0 || key.note_id() == 5 { + let x = self.pos.x + key.x(); + let y = 0.0; + + let w = 1.0; + let h = f32::MAX; + + let color = if key.note_id() == 0 { + [0.2, 0.2, 0.2, 1.0] + } else { + [0.05, 0.05, 0.05, 1.0] + }; + + instances.push(QuadInstance { + position: [x, y], + size: [w, h], + color, + border_radius: [0.0, 0.0, 0.0, 0.0], + }); } - - instances.push(key_state::to_quad(key, color, self.pos)); } - for key in self.layout.keys.iter().filter(|key| key.kind().is_sharp()) { - let id = key.id(); - let color = self.key_states[id].color(); + instances.push(key_state::to_quad(key, color, self.pos)); + } - instances.push(key_state::to_quad(key, color, self.pos)); - } - }); - self.should_reupload = false; + for key in self.layout.keys.iter().filter(|key| key.kind().is_sharp()) { + let id = key.id(); + let color = self.key_states[id].color(); + + instances.push(key_state::to_quad(key, color, self.pos)); + } } - pub fn update(&mut self, queue: &wgpu::Queue, text: &mut TextRenderer) { - if self.should_reupload { - self.reupload(queue); + pub fn update(&mut self, quads: &mut QuadPipeline, text: &mut TextRenderer) { + if self.cache.is_empty() { + self.reupload(); + } + + for quad in self.cache.iter() { + quads.instances().push(*quad); } for (id, key) in self @@ -203,12 +195,4 @@ impl KeyboardRenderer { }); } } - - pub fn render<'rpass>( - &'rpass mut self, - transform_uniform: &'rpass Uniform, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - self.quad_pipeline.render(transform_uniform, render_pass); - } } diff --git a/neothesia-core/src/render/quad/mod.rs b/neothesia-core/src/render/quad/mod.rs index f2fc05b4..372a5dc5 100644 --- a/neothesia-core/src/render/quad/mod.rs +++ b/neothesia-core/src/render/quad/mod.rs @@ -68,6 +68,18 @@ impl<'a> QuadPipeline { render_pass.draw_indexed(0..self.quad.indices_len, 0, 0..self.instances.len()); } + pub fn clear(&mut self) { + self.instances.data.clear(); + } + + pub fn instances(&mut self) -> &mut Vec { + &mut self.instances.data + } + + pub fn prepare(&self, queue: &wgpu::Queue) { + self.instances.update(queue); + } + pub fn update_instance_buffer(&mut self, queue: &wgpu::Queue, instances: Vec) { self.instances.data = instances; self.instances.update(queue); diff --git a/neothesia/src/scene/playing_scene/keyboard.rs b/neothesia/src/scene/playing_scene/keyboard.rs index 7b574f7c..b807092b 100644 --- a/neothesia/src/scene/playing_scene/keyboard.rs +++ b/neothesia/src/scene/playing_scene/keyboard.rs @@ -1,7 +1,6 @@ use midi_file::midly::MidiMessage; -use neothesia_core::render::TextRenderer; +use neothesia_core::render::{QuadPipeline, TextRenderer}; use piano_math::KeyboardRange; -use wgpu_jumpstart::{TransformUniform, Uniform}; use crate::{config::Config, render::KeyboardRenderer, target::Target}; @@ -25,12 +24,7 @@ impl Keyboard { target.window_state.logical_size.height, ); - let mut renderer = KeyboardRenderer::new( - &target.gpu, - &target.transform, - layout, - target.config.vertical_guidelines, - ); + let mut renderer = KeyboardRenderer::new(layout, target.config.vertical_guidelines); renderer.position_on_bottom_of_parent(target.window_state.logical_size.height); Self { renderer } @@ -62,22 +56,14 @@ impl Keyboard { self.position_on_bottom_of_parent(target.window_state.logical_size.height); } - pub fn update(&mut self, queue: &wgpu::Queue, brush: &mut TextRenderer) { - self.renderer.update(queue, brush) + pub fn update(&mut self, quads: &mut QuadPipeline, brush: &mut TextRenderer) { + self.renderer.update(quads, brush) } pub fn reset_notes(&mut self) { self.renderer.reset_notes() } - pub fn render<'rpass>( - &'rpass mut self, - transform_uniform: &'rpass Uniform, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - self.renderer.render(transform_uniform, render_pass) - } - pub fn user_midi_event(&mut self, message: &MidiMessage) { let range_start = self.range().start() as usize; @@ -92,7 +78,7 @@ impl Keyboard { let key = &mut self.renderer.key_states_mut()[id]; key.set_pressed_by_user(is_on); - self.renderer.queue_reupload(); + self.renderer.invalidate_cache(); } } @@ -117,7 +103,7 @@ impl Keyboard { key.pressed_by_file_off(); } - self.renderer.queue_reupload(); + self.renderer.invalidate_cache(); } } } diff --git a/neothesia/src/scene/playing_scene/mod.rs b/neothesia/src/scene/playing_scene/mod.rs index 84b530f5..a6e1705f 100644 --- a/neothesia/src/scene/playing_scene/mod.rs +++ b/neothesia/src/scene/playing_scene/mod.rs @@ -5,7 +5,9 @@ use wgpu_jumpstart::{Color, TransformUniform, Uniform}; use winit::event::{KeyboardInput, WindowEvent}; use super::Scene; -use crate::{render::WaterfallRenderer, target::Target, NeothesiaEvent}; +use crate::{ + render::WaterfallRenderer, target::Target, utils::window::WindowState, NeothesiaEvent, +}; mod keyboard; use keyboard::Keyboard; @@ -58,17 +60,14 @@ impl PlayingScene { } } - fn update_progresbar(&mut self, target: &mut Target) { - let size_x = target.window_state.logical_size.width * self.player.percentage(); - self.quad_pipeline.update_instance_buffer( - &target.gpu.queue, - vec![QuadInstance { - position: [0.0, 0.0], - size: [size_x, 5.0], - color: Color::from_rgba8(56, 145, 255, 1.0).into_linear_rgba(), - ..Default::default() - }], - ); + fn update_progresbar(&mut self, window_state: &WindowState) { + let size_x = window_state.logical_size.width * self.player.percentage(); + self.quad_pipeline.instances().push(QuadInstance { + position: [0.0, 0.0], + size: [size_x, 5.0], + color: Color::from_rgba8(56, 145, 255, 1.0).into_linear_rgba(), + ..Default::default() + }); } } @@ -89,16 +88,21 @@ impl Scene for PlayingScene { self.keyboard.file_midi_events(&target.config, &midi_events); } - self.update_progresbar(target); + self.toast_manager.update(&mut target.text_renderer); self.notes.update( &target.gpu.queue, self.player.time_without_lead_in() + target.config.playback_offset, ); + self.quad_pipeline.clear(); + self.keyboard - .update(&target.gpu.queue, &mut target.text_renderer); - self.toast_manager.update(&mut target.text_renderer); + .update(&mut self.quad_pipeline, &mut target.text_renderer); + + self.update_progresbar(&target.window_state); + + self.quad_pipeline.prepare(&target.gpu.queue); } fn render<'pass>( @@ -107,7 +111,6 @@ impl Scene for PlayingScene { rpass: &mut wgpu::RenderPass<'pass>, ) { self.notes.render(transform, rpass); - self.keyboard.render(transform, rpass); self.quad_pipeline.render(transform, rpass) }