Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NPS & Polyphony stats #76

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
3 changes: 3 additions & 0 deletions src/gui/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct GuiWasabiWindow {
keyboard: GuiKeyboard,
midi_file: Option<MIDIFileUnion>,
fps: fps::Fps,
nps: stats::NpsCounter,

settings_win: SettingsWindow,
midi_picker: Option<Receiver<PathBuf>>,
Expand Down Expand Up @@ -73,6 +74,7 @@ impl GuiWasabiWindow {
keyboard: GuiKeyboard::new(),
midi_file: None,
fps: fps::Fps::new(),
nps: Default::default(),

settings_win,
midi_picker: None,
Expand Down Expand Up @@ -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);
}
});
Expand Down
16 changes: 6 additions & 10 deletions src/gui/window/fps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,28 @@ use std::{
};

pub struct Fps {
frames: VecDeque<Instant>,
current: Instant,
ticks: VecDeque<Instant>,
}

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()
}
}
1 change: 1 addition & 0 deletions src/gui/window/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub struct GuiRenderScene {

pub struct RenderResultData {
pub notes_rendered: u64,
pub polyphony: Option<u64>,
pub key_colors: Vec<Option<MIDIColor>>,
}

Expand Down
1 change: 1 addition & 0 deletions src/gui/window/scene/cake_system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ impl CakeRenderer {

RenderResultData {
notes_rendered: rendered_notes,
polyphony: None,
key_colors: colors,
}
}
Expand Down
114 changes: 65 additions & 49 deletions src/gui/window/scene/note_list_system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -108,6 +114,7 @@ impl NoteRenderer {
}

let mut notes_pushed = 0;
let mut polyphony = 0;

let mut cycle = 0;

Expand All @@ -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| {
MyBlackMIDIScore marked this conversation as resolved.
Show resolved Hide resolved
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::<usize>()
let temp = out_data.collect::<Vec<_>>();
ColumnReturnData {
polyphony: temp.iter().map(|d| d.polyphony).sum::<usize>(),
written_notes: temp.iter().map(|d| d.written_notes).sum::<usize>(),
}
});

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
Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/gui/window/settings/midi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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\
Expand Down
69 changes: 69 additions & 0 deletions src/gui/window/stats.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{collections::VecDeque, time::Instant};

use egui::{Context, Frame, Pos2};

use crate::{
Expand All @@ -11,6 +13,7 @@ pub struct GuiMidiStats {
time_passed: f64,
time_total: f64,
notes_on_screen: u64,
polyphony: Option<u64>,
voice_count: Option<u64>,
}

Expand All @@ -20,6 +23,7 @@ impl GuiMidiStats {
time_passed: 0.0,
time_total: 0.0,
notes_on_screen: 0,
polyphony: None,
voice_count: None,
}
}
Expand All @@ -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<u64>) {
self.polyphony = polyphony;
}
}

fn num_or_q(num: Option<impl ToString>) -> String {
Expand Down Expand Up @@ -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
}
}
12 changes: 10 additions & 2 deletions src/settings/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ pub enum Statistics {
VoiceCount = 2,
Rendered = 3,
NoteCount = 4,
Polyphony = 5,
Nps = 6,
}

impl Statistics {
Expand All @@ -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()
Expand All @@ -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)),
}
}
Expand Down
Loading
Loading