diff --git a/neothesia-cli/src/main.rs b/neothesia-cli/src/main.rs index ff1a87a9..fafbe4c1 100644 --- a/neothesia-cli/src/main.rs +++ b/neothesia-cli/src/main.rs @@ -114,6 +114,8 @@ impl Recorder { .update(&self.gpu.queue, time_without_lead_in(&self.playback)); self.keyboard.update(&self.gpu.queue, &mut self.text); + + self.text.update((self.width, self.height), &self.gpu); } fn render( @@ -123,10 +125,11 @@ impl Recorder { texture_desc: &wgpu::TextureDescriptor<'_>, output_buffer: &wgpu::Buffer, ) { - self.gpu.clear(view, self.config.background_color.into()); + let bg_color = self.config.background_color; + let bg_color = wgpu_jumpstart::Color::from(bg_color).into_linear_wgpu_color(); { - let mut render_pass = self + let mut rpass = self .gpu .encoder .begin_render_pass(&wgpu::RenderPassDescriptor { @@ -135,23 +138,18 @@ impl Recorder { view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Load, + load: wgpu::LoadOp::Clear(bg_color), store: true, }, })], depth_stencil_attachment: None, }); - self.waterfall - .render(&self.transform_uniform, &mut render_pass); - - self.keyboard - .render(&self.transform_uniform, &mut render_pass); + self.waterfall.render(&self.transform_uniform, &mut rpass); + self.keyboard.render(&self.transform_uniform, &mut rpass); + self.text.render(&mut rpass); } - self.text - .render((self.width, self.height), &mut self.gpu, view); - { let u32_size = std::mem::size_of::() as u32; diff --git a/neothesia-core/src/render/background_animation/mod.rs b/neothesia-core/src/render/background_animation/mod.rs index 95e5c47e..3b2c103c 100644 --- a/neothesia-core/src/render/background_animation/mod.rs +++ b/neothesia-core/src/render/background_animation/mod.rs @@ -12,7 +12,7 @@ pub struct BgPipeline { time_uniform: Uniform, } -impl<'a> BgPipeline { +impl BgPipeline { pub fn new(gpu: &Gpu) -> Self { let shader = gpu .device @@ -56,7 +56,7 @@ impl<'a> BgPipeline { } } - pub fn render(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + pub fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { render_pass.set_pipeline(&self.render_pipeline); render_pass.set_bind_group(0, &self.time_uniform.bind_group, &[]); diff --git a/neothesia-core/src/render/text/mod.rs b/neothesia-core/src/render/text/mod.rs index 70ce28f4..eedc8f86 100644 --- a/neothesia-core/src/render/text/mod.rs +++ b/neothesia-core/src/render/text/mod.rs @@ -117,7 +117,7 @@ impl TextRenderer { }); } - pub fn render(&mut self, logical_size: (u32, u32), gpu: &mut Gpu, view: &wgpu::TextureView) { + pub fn update(&mut self, logical_size: (u32, u32), gpu: &Gpu) { let elements = self.queue.iter().map(|area| glyphon::TextArea { buffer: &area.buffer, left: area.left, @@ -142,22 +142,10 @@ impl TextRenderer { ) .unwrap(); - // TODO: Use single pass, now that this is posible thanks to glyphon - let mut pass = gpu.encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("glyphon text"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - self.text_renderer.render(&self.atlas, &mut pass).unwrap(); - self.queue.clear(); } + + pub fn render<'rpass>(&'rpass mut self, render_pass: &mut wgpu::RenderPass<'rpass>) { + self.text_renderer.render(&self.atlas, render_pass).unwrap(); + } } diff --git a/neothesia/src/main.rs b/neothesia/src/main.rs index 1cade4ad..702af103 100644 --- a/neothesia/src/main.rs +++ b/neothesia/src/main.rs @@ -7,6 +7,9 @@ mod scene; mod target; mod utils; +use std::time::Duration; + +use iced_core::Renderer; use scene::{menu_scene, playing_scene, Scene}; use target::Target; use utils::window::WindowState; @@ -39,7 +42,6 @@ struct Neothesia { target: Target, surface: Surface, - last_time: std::time::Instant, fps_timer: fps_ticker::Fps, game_scene: Box, } @@ -55,7 +57,6 @@ impl Neothesia { Self { target, surface, - last_time: std::time::Instant::now(), fps_timer: Default::default(), game_scene: Box::new(game_scene), } @@ -114,6 +115,8 @@ impl Neothesia { fn neothesia_event(&mut self, event: NeothesiaEvent, control_flow: &mut ControlFlow) { match event { NeothesiaEvent::Play(midi_file) => { + self.target.iced_manager.renderer.clear(); + let to = playing_scene::PlayingScene::new(&self.target, midi_file); self.game_scene = Box::new(to); } @@ -131,16 +134,18 @@ impl Neothesia { } } - fn update(&mut self) { - self.fps_timer.tick(); - - let delta = self.last_time.elapsed(); - self.last_time = std::time::Instant::now(); + fn update(&mut self, delta: Duration) { + #[cfg(debug_assertions)] + { + self.fps_timer.tick(); + self.target.text_renderer.queue_fps(self.fps_timer.avg()); + } self.game_scene.update(&mut self.target, delta); - - #[cfg(debug_assertions)] - self.target.text_renderer.queue_fps(self.fps_timer.avg()); + self.target.text_renderer.update( + self.target.window_state.physical_size.into(), + &self.target.gpu, + ); } fn render(&mut self) { @@ -156,20 +161,48 @@ impl Neothesia { .texture .create_view(&wgpu::TextureViewDescriptor::default()); + { + let bg_color = self.target.config.background_color; + let bg_color = wgpu_jumpstart::Color::from(bg_color).into_linear_wgpu_color(); + let mut rpass = + self.target + .gpu + .encoder + .begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Main Neothesia Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(bg_color), + store: true, + }, + })], + + depth_stencil_attachment: None, + }); + + self.game_scene.render(&self.target.transform, &mut rpass); + self.target.text_renderer.render(&mut rpass); + } + self.target - .gpu - .clear(view, self.target.config.background_color.into()); - - self.game_scene.render(&mut self.target, view); - - self.target.text_renderer.render( - ( - self.target.window_state.physical_size.width, - self.target.window_state.physical_size.height, - ), - &mut self.target.gpu, - view, - ); + .iced_manager + .renderer + .with_primitives(|backend, primitive| { + if !primitive.is_empty() { + backend.present( + &self.target.gpu.device, + &self.target.gpu.queue, + &mut self.target.gpu.encoder, + None, + view, + primitive, + &self.target.iced_manager.viewport, + &self.target.iced_manager.debug.overlay(), + ); + } + }); self.target.gpu.submit(); frame.present(); @@ -188,6 +221,8 @@ fn main() { let mut app = Neothesia::new(target, surface); + let mut last_time = std::time::Instant::now(); + // Investigate: // https://github.com/gfx-rs/wgpu-rs/pull/306 @@ -197,18 +232,19 @@ fn main() { Event::UserEvent(event) => { app.neothesia_event(event, control_flow); } - Event::MainEventsCleared => { - app.game_scene.main_events_cleared(&mut app.target); - - app.update(); - app.target.window.request_redraw(); - } Event::WindowEvent { event, .. } => { app.window_event(&event, control_flow); } Event::RedrawRequested(_) => { + let delta = last_time.elapsed(); + last_time = std::time::Instant::now(); + + app.update(delta); app.render(); } + Event::RedrawEventsCleared => { + app.target.window.request_redraw(); + } _ => {} } }); diff --git a/neothesia/src/scene/menu_scene/mod.rs b/neothesia/src/scene/menu_scene/mod.rs index 97e42b94..3a8ce854 100644 --- a/neothesia/src/scene/menu_scene/mod.rs +++ b/neothesia/src/scene/menu_scene/mod.rs @@ -11,6 +11,7 @@ use iced_menu::AppUi; use iced_style::Theme; use neothesia_core::render::BgPipeline; +use wgpu_jumpstart::{TransformUniform, Uniform}; use winit::event::WindowEvent; use crate::{ @@ -55,43 +56,36 @@ impl Scene for MenuScene { fn update(&mut self, target: &mut Target, delta: Duration) { self.bg_pipeline.update_time(&mut target.gpu, delta); self.iced_state.queue_message(iced_menu::Message::Tick); + + self.futures + .retain_mut(|f| match f.as_mut().poll(&mut self.context) { + std::task::Poll::Ready(msg) => { + self.iced_state.queue_message(msg); + false + } + std::task::Poll::Pending => true, + }); + + if !self.iced_state.is_queue_empty() { + if let Some(command) = self.iced_state.update(target) { + for a in command.actions() { + match a { + iced_runtime::command::Action::Future(f) => { + self.futures.push(f); + } + _ => {} + } + } + } + } } - fn render(&mut self, target: &mut Target, view: &wgpu::TextureView) { - self.bg_pipeline - .render( - &mut target - .gpu - .encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, - })], - depth_stencil_attachment: None, - }), - ); - - target - .iced_manager - .renderer - .with_primitives(|backend, primitive| { - backend.present( - &target.gpu.device, - &target.gpu.queue, - &mut target.gpu.encoder, - None, - view, - primitive, - &target.iced_manager.viewport, - &target.iced_manager.debug.overlay(), - ) - }) + fn render<'pass>( + &'pass mut self, + _transform: &'pass Uniform, + rpass: &mut wgpu::RenderPass<'pass>, + ) { + self.bg_pipeline.render(rpass); } fn window_event(&mut self, target: &mut Target, event: &WindowEvent) { @@ -112,51 +106,5 @@ impl Scene for MenuScene { } } } - - // Well this feature was fun, but there is no way to detect user interaction with a - // scrollbar so this has to go for now - - // match &event { - // WindowEvent::MouseInput { - // state: ElementState::Pressed, - // button: MouseButton::Left, - // .. - // } => { - // if self.iced_state.mouse_interaction() == Interaction::Idle { - // target.window.drag_window().ok(); - // } - // } - // _ => {} - // } - } - - fn main_events_cleared(&mut self, target: &mut Target) { - if !self.iced_state.is_queue_empty() { - if let Some(command) = self.iced_state.update(target) { - for a in command.actions() { - match a { - iced_runtime::command::Action::Future(f) => { - self.futures.push(f); - } - _ => {} - } - } - } - } - - let context = &mut self.context; - let mut messages = Vec::new(); - - self.futures.retain_mut(|f| match f.as_mut().poll(context) { - std::task::Poll::Ready(msg) => { - messages.push(msg); - false - } - std::task::Poll::Pending => true, - }); - - for msg in messages { - self.iced_state.queue_message(msg); - } } } diff --git a/neothesia/src/scene/mod.rs b/neothesia/src/scene/mod.rs index 1e34aba2..97a7820a 100644 --- a/neothesia/src/scene/mod.rs +++ b/neothesia/src/scene/mod.rs @@ -4,13 +4,17 @@ pub mod playing_scene; use crate::target::Target; use midi_file::midly::MidiMessage; use std::time::Duration; +use wgpu_jumpstart::{TransformUniform, Uniform}; use winit::event::WindowEvent; pub trait Scene { fn resize(&mut self, _target: &mut Target) {} fn update(&mut self, target: &mut Target, delta: Duration); - fn render(&mut self, target: &mut Target, view: &wgpu::TextureView); + fn render<'pass>( + &'pass mut self, + transform: &'pass Uniform, + rpass: &mut wgpu::RenderPass<'pass>, + ); fn window_event(&mut self, _target: &mut Target, _event: &WindowEvent) {} fn midi_event(&mut self, _target: &mut Target, _channel: u8, _message: &MidiMessage) {} - fn main_events_cleared(&mut self, _target: &mut Target) {} } diff --git a/neothesia/src/scene/playing_scene/keyboard.rs b/neothesia/src/scene/playing_scene/keyboard.rs index a9dff6d6..b83de251 100644 --- a/neothesia/src/scene/playing_scene/keyboard.rs +++ b/neothesia/src/scene/playing_scene/keyboard.rs @@ -25,7 +25,7 @@ impl Keyboard { target.window_state.logical_size.height, ); - let mut renderer = KeyboardRenderer::new(&target.gpu, &target.transform_uniform, layout); + let mut renderer = KeyboardRenderer::new(&target.gpu, &target.transform, layout); renderer.position_on_bottom_of_parent(target.window_state.logical_size.height); Self { renderer } diff --git a/neothesia/src/scene/playing_scene/mod.rs b/neothesia/src/scene/playing_scene/mod.rs index c2a83e3f..84b530f5 100644 --- a/neothesia/src/scene/playing_scene/mod.rs +++ b/neothesia/src/scene/playing_scene/mod.rs @@ -1,7 +1,7 @@ use midi_file::{midly::MidiMessage, MidiFile}; use neothesia_core::render::{QuadInstance, QuadPipeline}; use std::time::Duration; -use wgpu_jumpstart::Color; +use wgpu_jumpstart::{Color, TransformUniform, Uniform}; use winit::event::{KeyboardInput, WindowEvent}; use super::Scene; @@ -39,7 +39,7 @@ impl PlayingScene { &target.gpu, &midi_file.tracks, &target.config, - &target.transform_uniform, + &target.transform, keyboard_layout.clone(), ); @@ -52,7 +52,7 @@ impl PlayingScene { notes, player, rewind_controler: RewindController::new(), - quad_pipeline: QuadPipeline::new(&target.gpu, &target.transform_uniform), + quad_pipeline: QuadPipeline::new(&target.gpu, &target.transform), toast_manager: ToastManager::default(), } @@ -101,31 +101,14 @@ impl Scene for PlayingScene { self.toast_manager.update(&mut target.text_renderer); } - fn render(&mut self, target: &mut Target, view: &wgpu::TextureView) { - let mut render_pass = target - .gpu - .encoder - .begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - self.notes - .render(&target.transform_uniform, &mut render_pass); - - self.keyboard - .render(&target.transform_uniform, &mut render_pass); - - self.quad_pipeline - .render(&target.transform_uniform, &mut render_pass) + fn render<'pass>( + &'pass mut self, + transform: &'pass Uniform, + rpass: &mut wgpu::RenderPass<'pass>, + ) { + self.notes.render(transform, rpass); + self.keyboard.render(transform, rpass); + self.quad_pipeline.render(transform, rpass) } fn window_event(&mut self, target: &mut Target, event: &WindowEvent) { diff --git a/neothesia/src/target.rs b/neothesia/src/target.rs index fda9b7d4..759ae9ea 100644 --- a/neothesia/src/target.rs +++ b/neothesia/src/target.rs @@ -19,7 +19,7 @@ pub struct Target { pub window_state: WindowState, pub gpu: Gpu, - pub transform_uniform: Uniform, + pub transform: Uniform, pub text_renderer: TextRenderer, @@ -82,7 +82,7 @@ impl Target { window_state, gpu, - transform_uniform, + transform: transform_uniform, text_renderer, @@ -95,12 +95,12 @@ impl Target { } pub fn resize(&mut self) { - self.transform_uniform.data.update( + self.transform.data.update( self.window_state.logical_size.width, self.window_state.logical_size.height, self.window_state.scale_factor as f32, ); - self.transform_uniform.update(&self.gpu.queue); + self.transform.update(&self.gpu.queue); self.iced_manager.resize( ( diff --git a/wgpu-jumpstart/src/color.rs b/wgpu-jumpstart/src/color.rs index fb468b7b..1ff96f59 100644 --- a/wgpu-jumpstart/src/color.rs +++ b/wgpu-jumpstart/src/color.rs @@ -54,6 +54,16 @@ impl Color { linear_component(self.b), ] } + + pub fn into_linear_wgpu_color(self) -> wgpu::Color { + let rgba = self.into_linear_rgba(); + wgpu::Color { + r: rgba[0] as f64, + g: rgba[1] as f64, + b: rgba[2] as f64, + a: rgba[3] as f64, + } + } } impl From<(u8, u8, u8)> for Color {