diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index 71ba1dd..34f0bf1 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -12,7 +12,6 @@ struct Recorder { playback: midi_file::PlaybackState, - bg_quad_pipeline: QuadPipeline, quad_pipeline: QuadPipeline, keyboard: KeyboardRenderer, waterfall: WaterfallRenderer, @@ -76,8 +75,9 @@ impl Recorder { wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ); - let bg_quad_pipeline = QuadPipeline::new(&gpu, &transform_uniform); - let quad_pipeline = QuadPipeline::new(&gpu, &transform_uniform); + let mut quad_pipeline = QuadPipeline::new(&gpu, &transform_uniform); + quad_pipeline.init_layer(&gpu, 30); // BG + quad_pipeline.init_layer(&gpu, 150); // FG let keyboard_layout = get_layout( width as f32, @@ -117,7 +117,6 @@ impl Recorder { playback, - bg_quad_pipeline, quad_pipeline, keyboard, waterfall, @@ -136,20 +135,21 @@ impl Recorder { let time = time_without_lead_in(&self.playback); - self.bg_quad_pipeline.clear(); + self.quad_pipeline.clear(); + self.guidelines.update( - &mut self.bg_quad_pipeline, + &mut self.quad_pipeline, + 0, self.config.animation_speed, time, ); - self.bg_quad_pipeline.prepare(&self.gpu.queue); self.waterfall.update(&self.gpu.queue, time); - self.quad_pipeline.clear(); self.keyboard - .update(&mut self.quad_pipeline, &mut self.text); - self.quad_pipeline.prepare(&self.gpu.queue); + .update(&mut self.quad_pipeline, 1, &mut self.text); + self.quad_pipeline + .prepare(&self.gpu.device, &self.gpu.queue); self.text.update((self.width, self.height), &self.gpu); } @@ -183,11 +183,11 @@ impl Recorder { occlusion_query_set: None, }); - self.bg_quad_pipeline - .render(&self.transform_uniform, &mut rpass); + self.quad_pipeline + .render(0, &self.transform_uniform, &mut rpass); self.waterfall.render(&self.transform_uniform, &mut rpass); self.quad_pipeline - .render(&self.transform_uniform, &mut rpass); + .render(1, &self.transform_uniform, &mut rpass); self.text.render(&mut rpass); } diff --git a/neothesia-core/src/render/guidelines.rs b/neothesia-core/src/render/guidelines.rs index 551248e..c140d1d 100644 --- a/neothesia-core/src/render/guidelines.rs +++ b/neothesia-core/src/render/guidelines.rs @@ -80,6 +80,7 @@ impl GuidelineRenderer { fn update_horizontal_guidelines( &mut self, quads: &mut QuadPipeline, + layer: usize, animation_speed: f32, time: f32, ) { @@ -98,7 +99,7 @@ impl GuidelineRenderer { break; } - quads.instances().push(QuadInstance { + quads.instances(layer).push(QuadInstance { position: [x, y], size: [w, h], color: [0.05, 0.05, 0.05, 1.0], @@ -107,17 +108,23 @@ impl GuidelineRenderer { } } - pub fn update(&mut self, quads: &mut QuadPipeline, animation_speed: f32, time: f32) { + pub fn update( + &mut self, + quads: &mut QuadPipeline, + layer: usize, + animation_speed: f32, + time: f32, + ) { if self.cache.is_empty() { self.reupload(); } if self.horizontal_guidelines { - self.update_horizontal_guidelines(quads, animation_speed, time); + self.update_horizontal_guidelines(quads, layer, animation_speed, time); } for quad in self.cache.iter() { - quads.instances().push(*quad); + quads.instances(layer).push(*quad); } } } diff --git a/neothesia-core/src/render/keyboard/mod.rs b/neothesia-core/src/render/keyboard/mod.rs index 161eb8e..992eafe 100644 --- a/neothesia-core/src/render/keyboard/mod.rs +++ b/neothesia-core/src/render/keyboard/mod.rs @@ -116,13 +116,13 @@ impl KeyboardRenderer { } } - pub fn update(&mut self, quads: &mut QuadPipeline, text: &mut TextRenderer) { + pub fn update(&mut self, quads: &mut QuadPipeline, layer: usize, text: &mut TextRenderer) { if self.cache.is_empty() { self.reupload(); } for quad in self.cache.iter() { - quads.instances().push(*quad); + quads.instances(layer).push(*quad); } let range_start = self.layout.range.start() as usize; diff --git a/neothesia-core/src/render/quad/mod.rs b/neothesia-core/src/render/quad/mod.rs index 45a4814..5a62359 100644 --- a/neothesia-core/src/render/quad/mod.rs +++ b/neothesia-core/src/render/quad/mod.rs @@ -6,7 +6,7 @@ use wgpu_jumpstart::{wgpu, Gpu, Instances, Shape, TransformUniform, Uniform}; pub struct QuadPipeline { render_pipeline: wgpu::RenderPipeline, quad: Shape, - instances: Instances, + instances: Vec>, } impl<'a> QuadPipeline { @@ -44,58 +44,77 @@ impl<'a> QuadPipeline { }); let quad = Shape::new_quad(&gpu.device); - let instances = Instances::new(&gpu.device, 100_000); Self { render_pipeline, quad, - instances, + instances: Vec::new(), } } + pub fn init_layer(&mut self, gpu: &Gpu, size: usize) { + self.instances.push(Instances::new(&gpu.device, size)); + } + pub fn render( &'a self, + batch_id: usize, transform_uniform: &'a Uniform, render_pass: &mut wgpu::RenderPass<'a>, ) { + let instances = &self.instances[batch_id]; render_pass.set_pipeline(&self.render_pipeline); render_pass.set_bind_group(0, &transform_uniform.bind_group, &[]); render_pass.set_vertex_buffer(0, self.quad.vertex_buffer.slice(..)); - render_pass.set_vertex_buffer(1, self.instances.buffer.slice(..)); + render_pass.set_vertex_buffer(1, instances.buffer.slice(..)); render_pass.set_index_buffer(self.quad.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.draw_indexed(0..self.quad.indices_len, 0, 0..self.instances.len()); + render_pass.draw_indexed(0..self.quad.indices_len, 0, 0..instances.len()); } pub fn clear(&mut self) { - self.instances.data.clear(); + for instances in self.instances.iter_mut() { + instances.data.clear(); + } } - pub fn instances(&mut self) -> &mut Vec { - &mut self.instances.data + pub fn instances(&mut self, batch_id: usize) -> &mut Vec { + &mut self.instances[batch_id].data } - pub fn push(&mut self, quad: QuadInstance) { - self.instances.data.push(quad) + pub fn push(&mut self, batch_id: usize, quad: QuadInstance) { + self.instances[batch_id].data.push(quad) } - pub fn prepare(&self, queue: &wgpu::Queue) { - self.instances.update(queue); + pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + for instances in self.instances.iter_mut() { + instances.update(device, queue); + } } - pub fn update_instance_buffer(&mut self, queue: &wgpu::Queue, instances: Vec) { - self.instances.data = instances; - self.instances.update(queue); + pub fn update_instance_buffer( + &mut self, + batch_id: usize, + device: &wgpu::Device, + queue: &wgpu::Queue, + instances: Vec, + ) { + let batch = &mut self.instances[batch_id]; + batch.data = instances; + batch.update(device, queue); } pub fn with_instances_mut)>( &mut self, + batch_id: usize, + device: &wgpu::Device, queue: &wgpu::Queue, cb: F, ) { - cb(&mut self.instances.data); - self.instances.update(queue); + let batch = &mut self.instances[batch_id]; + cb(&mut batch.data); + batch.update(device, queue); } } diff --git a/neothesia-core/src/render/waterfall/mod.rs b/neothesia-core/src/render/waterfall/mod.rs index fba20a4..a46643a 100644 --- a/neothesia-core/src/render/waterfall/mod.rs +++ b/neothesia-core/src/render/waterfall/mod.rs @@ -39,7 +39,7 @@ impl WaterfallRenderer { notes .notes_pipeline .set_speed(&gpu.queue, config.animation_speed); - notes.resize(&gpu.queue, config, layout); + notes.resize(&gpu.device, &gpu.queue, config, layout); notes } @@ -49,6 +49,7 @@ impl WaterfallRenderer { pub fn resize( &mut self, + device: &wgpu::Device, queue: &wgpu::Queue, config: &Config, layout: piano_math::KeyboardLayout, @@ -96,7 +97,7 @@ impl WaterfallRenderer { ); } - self.notes_pipeline.prepare(queue); + self.notes_pipeline.prepare(device, queue); } pub fn update(&mut self, queue: &wgpu::Queue, time: f32) { diff --git a/neothesia-core/src/render/waterfall/pipeline/mod.rs b/neothesia-core/src/render/waterfall/pipeline/mod.rs index de40f60..995ca76 100644 --- a/neothesia-core/src/render/waterfall/pipeline/mod.rs +++ b/neothesia-core/src/render/waterfall/pipeline/mod.rs @@ -99,8 +99,8 @@ impl<'a> WaterfallPipeline { &mut self.instances.data } - pub fn prepare(&self, queue: &wgpu::Queue) { - self.instances.update(queue); + pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + self.instances.update(device, queue); } pub fn speed(&self) -> f32 { diff --git a/neothesia/src/main.rs b/neothesia/src/main.rs index 7cca6b9..ab9475e 100644 --- a/neothesia/src/main.rs +++ b/neothesia/src/main.rs @@ -307,8 +307,10 @@ impl ApplicationHandler for NeothesiaBootstrap { } fn main() { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("neothesia=info")) - .init(); + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or("warn, wgpu_hal=error, oxisynth=error"), + ) + .init(); let event_loop: EventLoop = EventLoop::with_user_event().build().unwrap(); let proxy = event_loop.create_proxy(); diff --git a/neothesia/src/scene/playing_scene/keyboard.rs b/neothesia/src/scene/playing_scene/keyboard.rs index 8900a0e..7d50ec7 100644 --- a/neothesia/src/scene/playing_scene/keyboard.rs +++ b/neothesia/src/scene/playing_scene/keyboard.rs @@ -72,8 +72,8 @@ impl Keyboard { self.position_on_bottom_of_parent(ctx.window_state.logical_size.height); } - pub fn update(&mut self, quads: &mut QuadPipeline, brush: &mut TextRenderer) { - self.renderer.update(quads, brush) + pub fn update(&mut self, quads: &mut QuadPipeline, layer: usize, brush: &mut TextRenderer) { + self.renderer.update(quads, layer, brush) } pub fn reset_notes(&mut self) { diff --git a/neothesia/src/scene/playing_scene/mod.rs b/neothesia/src/scene/playing_scene/mod.rs index 1e52816..ab0613c 100644 --- a/neothesia/src/scene/playing_scene/mod.rs +++ b/neothesia/src/scene/playing_scene/mod.rs @@ -30,6 +30,9 @@ mod top_bar; const EVENT_CAPTURED: bool = true; const EVENT_IGNORED: bool = false; +const LAYER_BG: usize = 0; +const LAYER_FG: usize = 1; + pub struct PlayingScene { keyboard: Keyboard, waterfall: WaterfallRenderer, @@ -37,8 +40,7 @@ pub struct PlayingScene { player: MidiPlayer, rewind_controller: RewindController, - bg_quad_pipeline: QuadPipeline, - fg_quad_pipeline: QuadPipeline, + quad_pipeline: QuadPipeline, toast_manager: ToastManager, top_bar: TopBar, @@ -82,6 +84,10 @@ impl PlayingScene { ); waterfall.update(&ctx.gpu.queue, player.time_without_lead_in()); + let mut quad_pipeline = QuadPipeline::new(&ctx.gpu, &ctx.transform); + quad_pipeline.init_layer(&ctx.gpu, 50); // BG + quad_pipeline.init_layer(&ctx.gpu, 150); // FG + Self { keyboard, guidelines, @@ -89,8 +95,7 @@ impl PlayingScene { waterfall, player, rewind_controller: RewindController::new(), - bg_quad_pipeline: QuadPipeline::new(&ctx.gpu, &ctx.transform), - fg_quad_pipeline: QuadPipeline::new(&ctx.gpu, &ctx.transform), + quad_pipeline, toast_manager: ToastManager::default(), top_bar: TopBar::new(), } @@ -117,30 +122,36 @@ impl PlayingScene { self.guidelines.set_layout(self.keyboard.layout().clone()); self.guidelines.set_pos(*self.keyboard.pos()); - self.waterfall - .resize(&ctx.gpu.queue, &ctx.config, self.keyboard.layout().clone()); + self.waterfall.resize( + &ctx.gpu.device, + &ctx.gpu.queue, + &ctx.config, + self.keyboard.layout().clone(), + ); } } impl Scene for PlayingScene { fn update(&mut self, ctx: &mut Context, delta: Duration) { - self.bg_quad_pipeline.clear(); - self.fg_quad_pipeline.clear(); + self.quad_pipeline.clear(); self.rewind_controller.update(&mut self.player, ctx); self.toast_manager.update(&mut ctx.text_renderer); let time = self.update_midi_player(ctx, delta); self.waterfall.update(&ctx.gpu.queue, time); - self.guidelines - .update(&mut self.bg_quad_pipeline, ctx.config.animation_speed, time); + self.guidelines.update( + &mut self.quad_pipeline, + LAYER_BG, + ctx.config.animation_speed, + time, + ); self.keyboard - .update(&mut self.fg_quad_pipeline, &mut ctx.text_renderer); + .update(&mut self.quad_pipeline, LAYER_FG, &mut ctx.text_renderer); TopBar::update(self, &ctx.window_state, &mut ctx.text_renderer); - self.bg_quad_pipeline.prepare(&ctx.gpu.queue); - self.fg_quad_pipeline.prepare(&ctx.gpu.queue); + self.quad_pipeline.prepare(&ctx.gpu.device, &ctx.gpu.queue); if self.player.is_finished() { ctx.proxy.send_event(NeothesiaEvent::MainMenu).ok(); @@ -152,9 +163,9 @@ impl Scene for PlayingScene { transform: &'pass Uniform, rpass: &mut wgpu::RenderPass<'pass>, ) { - self.bg_quad_pipeline.render(transform, rpass); + self.quad_pipeline.render(LAYER_BG, transform, rpass); self.waterfall.render(transform, rpass); - self.fg_quad_pipeline.render(transform, rpass); + self.quad_pipeline.render(LAYER_FG, transform, rpass); } fn window_event(&mut self, ctx: &mut Context, event: &WindowEvent) { diff --git a/neothesia/src/scene/playing_scene/top_bar.rs b/neothesia/src/scene/playing_scene/top_bar.rs index 8d0058b..4d3e39d 100644 --- a/neothesia/src/scene/playing_scene/top_bar.rs +++ b/neothesia/src/scene/playing_scene/top_bar.rs @@ -14,7 +14,7 @@ use crate::{context::Context, utils::window::WindowState, NeothesiaEvent}; use super::{ animation::Animation, rewind_controller::RewindController, PlayingScene, EVENT_CAPTURED, - EVENT_IGNORED, + EVENT_IGNORED, LAYER_FG, }; mod button; @@ -247,7 +247,7 @@ impl TopBar { pub fn update(scene: &mut PlayingScene, window_state: &WindowState, text: &mut TextRenderer) { let PlayingScene { top_bar, - fg_quad_pipeline, + quad_pipeline, ref player, ref rewind_controller, .. @@ -268,7 +268,7 @@ impl TopBar { if !top_bar.is_expanded { let progress_x = top_bar.bbox.w() * player.percentage(); draw_rect( - fg_quad_pipeline, + quad_pipeline, &Bbox::new([0.0, 0.0], [progress_x, 5.0]), &BLUE, ); @@ -282,7 +282,7 @@ impl TopBar { top_bar.bbox.pos.y = -h + (bar_animation * h); - draw_rect(fg_quad_pipeline, &top_bar.bbox, &BAR_BG); + draw_rect(quad_pipeline, &top_bar.bbox, &BAR_BG); for f in [ update_proggress_bar, @@ -298,7 +298,7 @@ impl TopBar { fn update_proggress_bar(scene: &mut PlayingScene, _text: &mut TextRenderer) { let PlayingScene { top_bar, - fg_quad_pipeline, + quad_pipeline, player, .. } = scene; @@ -308,11 +308,7 @@ fn update_proggress_bar(scene: &mut PlayingScene, _text: &mut TextRenderer) { let w = top_bar.bbox.w(); let progress_x = w * player.percentage(); - draw_rect( - fg_quad_pipeline, - &Bbox::new([0.0, y], [progress_x, h]), - &BLUE, - ); + draw_rect(quad_pipeline, &Bbox::new([0.0, y], [progress_x, h]), &BLUE); for m in player.song().file.measures.iter() { let length = player.length().as_secs_f32(); @@ -327,14 +323,14 @@ fn update_proggress_bar(scene: &mut PlayingScene, _text: &mut TextRenderer) { DARK_MEASURE }; - draw_rect(fg_quad_pipeline, &Bbox::new([x, y], [1.0, h]), &color); + draw_rect(quad_pipeline, &Bbox::new([x, y], [1.0, h]), &color); } } fn update_buttons(scene: &mut PlayingScene, text: &mut TextRenderer) { let PlayingScene { top_bar, - fg_quad_pipeline, + quad_pipeline, .. } = scene; @@ -346,7 +342,7 @@ fn update_buttons(scene: &mut PlayingScene, text: &mut TextRenderer) { .set_pos((0.0, y)) .set_hovered(matches!(top_bar.hovered, Element::BackButton)) .set_icon(left_arrow_icon()) - .draw(fg_quad_pipeline, text); + .draw(quad_pipeline, text); let mut x = w; @@ -360,7 +356,7 @@ fn update_buttons(scene: &mut PlayingScene, text: &mut TextRenderer) { } else { gear_icon() }) - .draw(fg_quad_pipeline, text); + .draw(quad_pipeline, text); x -= 30.0; top_bar @@ -368,7 +364,7 @@ fn update_buttons(scene: &mut PlayingScene, text: &mut TextRenderer) { .set_pos((x, y)) .set_hovered(matches!(top_bar.hovered, Element::RepeatButton)) .set_icon(repeat_icon()) - .draw(fg_quad_pipeline, text); + .draw(quad_pipeline, text); x -= 30.0; top_bar @@ -380,13 +376,13 @@ fn update_buttons(scene: &mut PlayingScene, text: &mut TextRenderer) { } else { pause_icon() }) - .draw(fg_quad_pipeline, text); + .draw(quad_pipeline, text); } fn update_looper(scene: &mut PlayingScene, _text: &mut TextRenderer) { let PlayingScene { top_bar, - fg_quad_pipeline, + quad_pipeline, ref player, .. } = scene; @@ -424,15 +420,15 @@ fn update_looper(scene: &mut PlayingScene, _text: &mut TextRenderer) { let mut bg_box = top_bar.loop_start_tick; bg_box.size.w = length; - draw_rect(fg_quad_pipeline, &bg_box, &color); - draw_rect(fg_quad_pipeline, &top_bar.loop_start_tick, &start_color); - draw_rect(fg_quad_pipeline, &top_bar.loop_end_tick, &end_color); + draw_rect(quad_pipeline, &bg_box, &color); + draw_rect(quad_pipeline, &top_bar.loop_start_tick, &start_color); + draw_rect(quad_pipeline, &top_bar.loop_end_tick, &end_color); } fn update_settings_card(scene: &mut PlayingScene, _text: &mut TextRenderer) { let PlayingScene { top_bar, - fg_quad_pipeline, + quad_pipeline, .. } = scene; @@ -446,20 +442,26 @@ fn update_settings_card(scene: &mut PlayingScene, _text: &mut TextRenderer) { let card_w = 300.0; let card_x = card_w - (settings_animation * card_w); - fg_quad_pipeline.push(QuadInstance { - position: [card_x + w - card_w, y + h + 1.0], - size: [card_w, 100.0], - color: BAR_BG.into_linear_rgba(), - border_radius: [10.0, 0.0, 10.0, 0.0], - }); + quad_pipeline.push( + LAYER_FG, + QuadInstance { + position: [card_x + w - card_w, y + h + 1.0], + size: [card_w, 100.0], + color: BAR_BG.into_linear_rgba(), + border_radius: [10.0, 0.0, 10.0, 0.0], + }, + ); } } fn draw_rect(quad_pipeline: &mut QuadPipeline, bbox: &Bbox, color: &Color) { - quad_pipeline.push(QuadInstance { - position: bbox.pos.into(), - size: bbox.size.into(), - color: color.into_linear_rgba(), - ..Default::default() - }); + quad_pipeline.push( + LAYER_FG, + QuadInstance { + position: bbox.pos.into(), + size: bbox.size.into(), + color: color.into_linear_rgba(), + ..Default::default() + }, + ); } diff --git a/neothesia/src/scene/playing_scene/top_bar/button.rs b/neothesia/src/scene/playing_scene/top_bar/button.rs index ab14792..db7a210 100644 --- a/neothesia/src/scene/playing_scene/top_bar/button.rs +++ b/neothesia/src/scene/playing_scene/top_bar/button.rs @@ -4,6 +4,8 @@ use neothesia_core::{ Color, }; +use crate::scene::playing_scene::LAYER_FG; + pub struct Button { bbox: Bbox, is_hovered: bool, @@ -80,12 +82,15 @@ impl Button { } .into_linear_rgba(); - quad_pipeline.push(QuadInstance { - position: self.bbox.pos.into(), - size: self.bbox.size.into(), - color, - border_radius: [5.0; 4], - }); + quad_pipeline.push( + LAYER_FG, + QuadInstance { + position: self.bbox.pos.into(), + size: self.bbox.size.into(), + color, + border_radius: [5.0; 4], + }, + ); let icon_size = 20.0; text.queue_icon( diff --git a/wgpu-jumpstart/src/instances.rs b/wgpu-jumpstart/src/instances.rs index 2ccc0df..7cfea4a 100644 --- a/wgpu-jumpstart/src/instances.rs +++ b/wgpu-jumpstart/src/instances.rs @@ -6,27 +6,40 @@ where { pub data: Vec, pub buffer: wgpu::Buffer, + capacity: usize, } impl Instances where I: Pod, { - pub fn new(device: &wgpu::Device, max_size: usize) -> Self { + pub fn new(device: &wgpu::Device, size_hint: usize) -> Self { + Self { + data: Vec::new(), + buffer: Self::create_buffer(device, size_hint), + capacity: size_hint, + } + } + + fn create_buffer(device: &wgpu::Device, len: usize) -> wgpu::Buffer { let instance_size = std::mem::size_of::(); - let buffer = device.create_buffer(&wgpu::BufferDescriptor { + device.create_buffer(&wgpu::BufferDescriptor { label: None, - size: (instance_size * max_size) as u64, + size: (instance_size * len) as u64, usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, - }); - - Self { - data: Vec::new(), - buffer, - } + }) } - pub fn update(&self, queue: &wgpu::Queue) { + pub fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + if self.capacity < self.data.len() { + log::warn!( + "Dynamically growing instances buffer from {} to {}", + self.capacity, + self.data.len() + ); + self.buffer = Self::create_buffer(device, self.data.len()); + self.capacity = self.data.len(); + } queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&self.data)); }