diff --git a/build.rs b/build.rs index d6f8beb..c57ef28 100644 --- a/build.rs +++ b/build.rs @@ -57,5 +57,5 @@ fn main() { icon_dir.write(File::create(icon_path).unwrap()).unwrap(); #[cfg(not(windows))] - println!("cargo:rerun-if-changed=logo.svg") + println!("cargo:rerun-if-changed=assets/logo.svg") } diff --git a/src/gui/window.rs b/src/gui/window.rs index 2eee04f..30dc9b7 100644 --- a/src/gui/window.rs +++ b/src/gui/window.rs @@ -41,6 +41,7 @@ pub struct GuiWasabiWindow { keyboard: GuiKeyboard, midi_file: Option, fps: fps::Fps, + nps: stats::NpsCounter, settings_win: SettingsWindow, midi_picker: Option>, @@ -73,6 +74,7 @@ impl GuiWasabiWindow { keyboard: GuiKeyboard::new(), midi_file: None, fps: fps::Fps::new(), + nps: Default::default(), settings_win, midi_picker: None, @@ -307,6 +309,7 @@ impl GuiWasabiWindow { settings.scene.note_speed, ); stats.set_rendered_note_count(result.notes_rendered); + stats.set_polyphony(result.polyphony); render_result_data = Some(result); } }); diff --git a/src/gui/window/fps.rs b/src/gui/window/fps.rs index 022fc38..d36356b 100644 --- a/src/gui/window/fps.rs +++ b/src/gui/window/fps.rs @@ -4,32 +4,28 @@ use std::{ }; pub struct Fps { - frames: VecDeque, - current: Instant, + ticks: VecDeque, } impl Fps { pub fn new() -> Self { Self { - frames: VecDeque::new(), - current: Instant::now(), + ticks: VecDeque::new(), } } pub fn update(&mut self) { - self.frames.push_back(Instant::now()); - while let Some(front) = self.frames.front() { + self.ticks.push_back(Instant::now()); + while let Some(front) = self.ticks.front() { if front.elapsed() > Duration::from_secs(1) { - self.frames.pop_front(); + self.ticks.pop_front(); } else { break; } } - - self.current = Instant::now(); } pub fn get_fps(&self) -> usize { - self.frames.len() + self.ticks.len() } } diff --git a/src/gui/window/scene.rs b/src/gui/window/scene.rs index 9a35c4a..cc8a28f 100644 --- a/src/gui/window/scene.rs +++ b/src/gui/window/scene.rs @@ -55,6 +55,7 @@ pub struct GuiRenderScene { pub struct RenderResultData { pub notes_rendered: u64, + pub polyphony: Option, pub key_colors: Vec>, } diff --git a/src/gui/window/scene/cake_system/mod.rs b/src/gui/window/scene/cake_system/mod.rs index 3cbe861..d135db4 100644 --- a/src/gui/window/scene/cake_system/mod.rs +++ b/src/gui/window/scene/cake_system/mod.rs @@ -469,6 +469,7 @@ impl CakeRenderer { RenderResultData { notes_rendered: rendered_notes, + polyphony: None, key_colors: colors, } } diff --git a/src/gui/window/scene/note_list_system/mod.rs b/src/gui/window/scene/note_list_system/mod.rs index 3dec5f8..ddddc59 100644 --- a/src/gui/window/scene/note_list_system/mod.rs +++ b/src/gui/window/scene/note_list_system/mod.rs @@ -15,6 +15,12 @@ use self::notes_render_pass::{NotePassStatus, NoteRenderPass, NoteVertex}; use super::RenderResultData; +#[derive(Default)] +struct ColumnReturnData { + polyphony: usize, + written_notes: usize, +} + pub struct NoteRenderer { render_pass: NoteRenderPass, thrad_pool: rayon::ThreadPool, @@ -108,6 +114,7 @@ impl NoteRenderer { } let mut notes_pushed = 0; + let mut polyphony = 0; let mut cycle = 0; @@ -120,71 +127,79 @@ impl NoteRenderer { let buffer_writer = UnsafeSyncCell::new(buffer.write().unwrap()); // A system to write multiple note columns into 1 large allocated array in parallel - let written_notes = self.thrad_pool.install(|| { + let column_data = self.thrad_pool.install(|| { // For each note column, write it into the buffer - let written_notes_per_key = - columns_view_info.par_iter_mut().rev().map(|column| { - if column.remaining == 0 { - return 0; - } - - let offset = - (column.offset as i64 - notes_pushed as i64).max(0) as usize; - - if offset >= buffer_length { - return 0; - } - - let remaining_buffer_space = buffer_length - offset; - let iter_length = column.remaining; - - let allowed_to_write = if iter_length > remaining_buffer_space { - remaining_buffer_space - } else { - iter_length - }; - - unsafe { - let buffer = buffer_writer.get_mut(); - - for i in 0..allowed_to_write { - let next_note = column.iter.next(); - if let Some(note) = next_note { - buffer[i + offset] = NoteVertex::new( - note.start, - note.len, - column.key, - note.color.as_u32(), - column.border_width as u32, - ); - - if note.start <= 0.0 - && column.color.is_none() - && note.start + note.len > 0.0 - { + let out_data = columns_view_info.par_iter_mut().rev().map(|column| { + if column.remaining == 0 { + return Default::default(); + } + + let offset = (column.offset as i64 - notes_pushed as i64).max(0) as usize; + + if offset >= buffer_length { + return Default::default(); + } + + let remaining_buffer_space = buffer_length - offset; + let iter_length = column.remaining; + + let allowed_to_write = if iter_length > remaining_buffer_space { + remaining_buffer_space + } else { + iter_length + }; + + let mut poly = 0; + + unsafe { + let buffer = buffer_writer.get_mut(); + + for i in 0..allowed_to_write { + let next_note = column.iter.next(); + if let Some(note) = next_note { + buffer[i + offset] = NoteVertex::new( + note.start, + note.len, + column.key, + note.color.as_u32(), + column.border_width as u32, + ); + + if note.start <= 0.0 && note.start + note.len > 0.0 { + poly += 1; + if column.color.is_none() { column.color = Some(note.color); } - } else { - panic!("Invalid iterator length"); } + } else { + panic!("Invalid iterator length"); } } + } - column.remaining -= allowed_to_write; + column.remaining -= allowed_to_write; - allowed_to_write - }); + ColumnReturnData { + polyphony: poly, + written_notes: allowed_to_write, + } + }); - written_notes_per_key.sum::() + let temp = out_data.collect::>(); + ColumnReturnData { + polyphony: temp.iter().map(|d| d.polyphony).sum::(), + written_notes: temp.iter().map(|d| d.written_notes).sum::(), + } }); - notes_pushed += written_notes; + polyphony += column_data.polyphony; + notes_pushed += column_data.written_notes; cycle += 1; if notes_pushed >= total_notes { NotePassStatus::Finished { - remaining: written_notes as u32, + remaining: column_data.written_notes as u32, } } else { NotePassStatus::HasMoreNotes @@ -196,6 +211,7 @@ impl NoteRenderer { RenderResultData { notes_rendered: notes_pushed as u64, + polyphony: Some(polyphony as u64), key_colors: columns_view_info .iter() .map(|column| column.color) diff --git a/src/gui/window/settings/midi.rs b/src/gui/window/settings/midi.rs index 67eef09..cb24290 100644 --- a/src/gui/window/settings/midi.rs +++ b/src/gui/window/settings/midi.rs @@ -33,7 +33,8 @@ impl SettingsWindow { - Cake\n\ \0 The most efficient loading and displaying algorithm.\n\ \0 The notes will be stored in binary trees and will be\n\ - \0 displayed dynamically.\n\ + \0 displayed dynamically. This mode does not support\n\ + \0 polyphony statistics.\n\ - Standard (RAM)\n\ \0 The MIDI will be loaded in the RAM and all the notes\n\ \0 will be rendered normally by the GPU.\n\ diff --git a/src/gui/window/stats.rs b/src/gui/window/stats.rs index 2467d60..26a6c29 100644 --- a/src/gui/window/stats.rs +++ b/src/gui/window/stats.rs @@ -1,3 +1,5 @@ +use std::{collections::VecDeque, time::Instant}; + use egui::{Context, Frame, Pos2}; use crate::{ @@ -11,6 +13,7 @@ pub struct GuiMidiStats { time_passed: f64, time_total: f64, notes_on_screen: u64, + polyphony: Option, voice_count: Option, } @@ -20,6 +23,7 @@ impl GuiMidiStats { time_passed: 0.0, time_total: 0.0, notes_on_screen: 0, + polyphony: None, voice_count: None, } } @@ -31,6 +35,10 @@ impl GuiMidiStats { pub fn set_rendered_note_count(&mut self, notes: u64) { self.notes_on_screen = notes; } + + pub fn set_polyphony(&mut self, polyphony: Option) { + self.polyphony = polyphony; + } } fn num_or_q(num: Option) -> String { @@ -163,8 +171,69 @@ impl GuiWasabiWindow { )); }); } + Statistics::Nps => { + ui.horizontal(|ui| { + ui.monospace("NPS:"); + ui.with_layout( + egui::Layout::right_to_left(egui::Align::Center), + |ui| { + self.nps.tick(note_stats.passed_notes.unwrap_or(0) as i64); + ui.monospace(format!("{}", self.nps.read())); + }, + ); + }); + } + Statistics::Polyphony => { + if let Some(poly) = stats.polyphony { + ui.horizontal(|ui| { + ui.monospace("Polyphony:"); + ui.with_layout( + egui::Layout::right_to_left(egui::Align::Center), + |ui| { + ui.monospace(format!("{}", poly)); + }, + ); + }); + } + } }; } }); } } + +#[derive(Default)] +pub struct NpsCounter { + ticks: VecDeque<(Instant, i64)>, +} + +impl NpsCounter { + const NPS_WINDOW: f64 = 0.5; + + pub fn tick(&mut self, passed: i64) { + self.ticks.push_back((Instant::now(), passed)); + while let Some((front_time, _passed)) = self.ticks.front() { + if front_time.elapsed().as_secs_f64() > Self::NPS_WINDOW { + self.ticks.pop_front(); + } else { + break; + } + } + } + + pub fn read(&self) -> u32 { + let old = if let Some((_time, front_passed)) = self.ticks.front() { + *front_passed as f64 + } else { + 0.0 + }; + + let last = if let Some((_time, back_passed)) = self.ticks.back() { + *back_passed as f64 + } else { + 0.0 + }; + + ((last - old).max(0.0) / Self::NPS_WINDOW).round() as u32 + } +} diff --git a/src/settings/enums.rs b/src/settings/enums.rs index b795581..8f7c956 100644 --- a/src/settings/enums.rs +++ b/src/settings/enums.rs @@ -89,6 +89,8 @@ pub enum Statistics { VoiceCount = 2, Rendered = 3, NoteCount = 4, + Polyphony = 5, + Nps = 6, } impl Statistics { @@ -100,15 +102,19 @@ impl Statistics { Statistics::VoiceCount => "Voice Count", Statistics::Rendered => "Rendered", Statistics::NoteCount => "Note Count", + Statistics::Polyphony => "Polyphony", + Statistics::Nps => "NPS", } } pub fn iter() -> Iter<'static, Statistics> { - static STATISTICS: [Statistics; 5] = [ + static STATISTICS: [Statistics; 7] = [ Statistics::Time, Statistics::Fps, - Statistics::VoiceCount, Statistics::Rendered, + Statistics::Nps, + Statistics::Polyphony, + Statistics::VoiceCount, Statistics::NoteCount, ]; STATISTICS.iter() @@ -125,6 +131,8 @@ impl FromStr for Statistics { "voicecount" => Ok(Statistics::VoiceCount), "rendered" => Ok(Statistics::Rendered), "notecount" => Ok(Statistics::NoteCount), + "polyphony" => Ok(Statistics::Polyphony), + "nps" => Ok(Statistics::Nps), s => Err(format!("{} was not expected.", s)), } } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 1fda028..58ec7a7 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -198,8 +198,13 @@ impl WasabiSettings { } else if let Ok(config) = fs::read_to_string(&config_path) { if config.starts_with(Self::VERSION_TEXT) { let offset = Self::VERSION_TEXT.len(); - match serde_json::from_str(&config[offset..]) { - Ok(config) => return Ok(config), + match serde_json::from_str::(&config[offset..]) { + Ok(mut config) => { + if config.scene.statistics.order.len() != Statistics::iter().len() { + config.scene.statistics.order = StatisticsSettings::default().order; + } + return Ok(config); + } Err(e) => err = WasabiError::SettingsError(e.to_string()), } } else if config.starts_with("# DON'T EDIT THIS LINE; Version: 1") {