perf counter macros
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/rust-engine-proc/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
|
||||
@@ -20,6 +20,7 @@ gilrs = "0.7"
|
||||
gltf = "0.15"
|
||||
glyph_brush = "0.7"
|
||||
winapi = "0.3"
|
||||
rust-engine-proc = { path = "rust-engine-proc" }
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
13
rust-engine-proc/Cargo.toml
Normal file
13
rust-engine-proc/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "rust-engine-proc"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
quote = "1.0"
|
||||
28
rust-engine-proc/src/lib.rs
Normal file
28
rust-engine-proc/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{AttributeArgs, ItemFn, Lit, Meta, parse_macro_input, parse_quote};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn perf(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attrs = parse_macro_input!(attr as AttributeArgs);
|
||||
let name = match &attrs[0] {
|
||||
syn::NestedMeta::Lit(Lit::Str(l)) => l.value(),
|
||||
_ => panic!("Invalid attribute"),
|
||||
};
|
||||
let counter_type = match &attrs[1] {
|
||||
syn::NestedMeta::Meta(Meta::Path(p)) => p,
|
||||
_ => panic!("Invalid attribute"),
|
||||
};
|
||||
let mut function = parse_macro_input!(item as ItemFn);
|
||||
function.block.stmts.insert(0, parse_quote! {
|
||||
let __perf_start = std::time::Instant::now();
|
||||
});
|
||||
function.block.stmts.push(parse_quote! {
|
||||
unsafe {
|
||||
#counter_type::write_perf(#name, __perf_start.elapsed().as_micros());
|
||||
}
|
||||
});
|
||||
quote!(#function).into()
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
use cgmath::{Vector4, vec4};
|
||||
|
||||
use crate::{input::InputState, text::{create_text_object, update_text}, vulkan::{MeshHandle, PERF_COUNTER_SIZE, TextVertex, Vertex, VulkanRenderer, gameobject::{GameObject, GameObjectHandle, Updatable}, mesh::{CPUMesh, CPUVertexList}}};
|
||||
use crate::{input::InputState, text::{create_text_object, update_text}, vulkan::{MeshHandle, TextVertex, Vertex, VulkanRenderer, gameobject::{GameObject, GameObjectHandle, Updatable}, mesh::{CPUMesh, CPUVertexList}}};
|
||||
|
||||
use super::{GameState, TestGame};
|
||||
|
||||
pub struct FpsCounter {
|
||||
pub game_object: GameObjectHandle
|
||||
pub game_object: GameObjectHandle,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl FpsCounter {
|
||||
pub fn new(game: &mut TestGame, renderer: &mut VulkanRenderer) -> FpsCounter {
|
||||
let text_mesh = create_text_object(&mut game.game_state.brush, renderer, "", 30.);
|
||||
FpsCounter { game_object: game.add_game_object(renderer, text_mesh) }
|
||||
FpsCounter { game_object: game.add_game_object(renderer, text_mesh), text: String::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +23,17 @@ impl Updatable for FpsCounter {
|
||||
go.visible = !go.visible;
|
||||
}
|
||||
|
||||
let update_duration = renderer.game_data.update_perf_counters.iter().sum::<u128>() / PERF_COUNTER_SIZE as u128;
|
||||
let render_duration = renderer.game_data.render_perf_counters.iter().sum::<u128>() / PERF_COUNTER_SIZE as u128;
|
||||
let other_duration = renderer.game_data.other_perf_counters.iter().sum::<u128>() / PERF_COUNTER_SIZE as u128;
|
||||
let other_variance = renderer.game_data.other_perf_counters.iter().fold(0, |acc, b| acc + (*b as i128 - other_duration as i128) * (*b as i128 - other_duration as i128)) / PERF_COUNTER_SIZE as i128;
|
||||
let text = format!("Update: {:.0}ms\nRender: {:.0}ms\nTotal: {:.0}ms (±{:.1})\nDelta: {:.0}ms", update_duration as f64 / 1000., render_duration as f64 / 1000., other_duration as f64 / 1000., f64::sqrt(other_variance as f64) / 1000., _delta_time * 1000.);
|
||||
update_text(self.game_object, &text, 30., renderer, &mut game_state.brush, game_objects);
|
||||
self.text.clear();
|
||||
unsafe {
|
||||
if let Some(pcs) = &crate::perf::PERF_COUNTERS {
|
||||
for (name, pc) in pcs {
|
||||
let mean = pc.mean();
|
||||
let stddev = pc.stddev(mean as i128);
|
||||
self.text.push_str(&format!("{}: {:.1}ms (±{:.1})\n", name, (mean as f64) / 1000., stddev / 1000.));
|
||||
}
|
||||
}
|
||||
}
|
||||
update_text(self.game_object, &self.text, 30., renderer, &mut game_state.brush, game_objects);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Instant;
|
||||
use cgmath::{Deg, Euler, Quaternion, vec3};
|
||||
use glyph_brush::GlyphBrush;
|
||||
use rust_engine_proc::perf;
|
||||
use winit::event::Event;
|
||||
|
||||
use level::{load_level, save_level};
|
||||
@@ -56,9 +57,8 @@ impl Game for TestGame {
|
||||
self.input.on_window_event(event);
|
||||
}
|
||||
|
||||
#[perf("update", crate::perf::PerformanceCounter)]
|
||||
fn update(self: &mut Self, renderer: &mut VulkanRenderer) {
|
||||
let precise_start = Instant::now();
|
||||
|
||||
// Input and timing
|
||||
self.input.frame_start();
|
||||
let time = (renderer.game_data.start_time.elapsed().unwrap().as_micros() as f64 / 1000000.0) as f32;
|
||||
@@ -126,9 +126,6 @@ impl Game for TestGame {
|
||||
_dummy1: [0; 4],
|
||||
_dummy2: [0; 4],
|
||||
};
|
||||
|
||||
let precise_duration = precise_start.elapsed().as_micros();
|
||||
renderer.game_data.update_perf_counters[renderer.game_data.performance_counter_index] = precise_duration;
|
||||
}
|
||||
|
||||
fn get_ubo(&self) -> &vs::ty::ObjectUniformData {
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::iter::FromIterator;
|
||||
use cgmath::{InnerSpace, Vector2, Zero, vec2};
|
||||
use gilrs;
|
||||
use gilrs::{EventType, Gilrs};
|
||||
use rust_engine_proc::perf;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use toml;
|
||||
use winapi::shared::minwindef::UINT;
|
||||
@@ -233,6 +234,7 @@ impl InputState {
|
||||
return state;
|
||||
}
|
||||
|
||||
#[perf("input_events", crate::perf::PerformanceCounter)]
|
||||
pub fn on_window_event(self: &mut Self, event: &Event<()>) {
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input, .. }, .. } => {
|
||||
|
||||
@@ -13,6 +13,7 @@ mod game;
|
||||
mod tests;
|
||||
mod text;
|
||||
mod util;
|
||||
mod perf;
|
||||
|
||||
fn main() {
|
||||
let log_config = LogConfig::from_file("config/log.toml");
|
||||
|
||||
64
src/perf.rs
Normal file
64
src/perf.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub const PERF_COUNTER_SIZE: usize = 100;
|
||||
pub static mut PERF_COUNTERS: Option<HashMap<String, PerformanceCounter>> = None;
|
||||
|
||||
pub struct PerformanceCounter {
|
||||
pub values: [u128; PERF_COUNTER_SIZE],
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl PerformanceCounter {
|
||||
pub fn mean(&self) -> u128 {
|
||||
self.values.iter().sum::<u128>() / PERF_COUNTER_SIZE as u128
|
||||
}
|
||||
|
||||
pub fn stddev(&self, mean: i128) -> f64 {
|
||||
f64::sqrt(self.values.iter().fold(0, |acc, b| acc + (*b as i128 - mean) * (*b as i128 - mean)) as f64 / PERF_COUNTER_SIZE as f64)
|
||||
}
|
||||
|
||||
pub fn init_perf() {
|
||||
unsafe {
|
||||
if PERF_COUNTERS.is_none() { PERF_COUNTERS = Some(HashMap::new()); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_perf(name: &str, value: u128) {
|
||||
unsafe {
|
||||
if let Some(pcs) = &mut PERF_COUNTERS {
|
||||
if let Some(pc) = pcs.get_mut(name) {
|
||||
pc.values[pc.index] = value;
|
||||
} else {
|
||||
let mut pc = PerformanceCounter { values: [0; PERF_COUNTER_SIZE], index: 0 };
|
||||
pc.values[0] = value;
|
||||
pcs.insert(name.to_string(), pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _get_perf(name: &str) -> u128 {
|
||||
unsafe {
|
||||
if let Some(pcs) = &mut PERF_COUNTERS {
|
||||
if let Some(pc) = pcs.get_mut(name) {
|
||||
pc.mean()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perf_next_frame(name: &str) {
|
||||
unsafe {
|
||||
if let Some(pcs) = &mut PERF_COUNTERS {
|
||||
if let Some(pc) = pcs.get_mut(name) {
|
||||
pc.index = (pc.index + 1) % PERF_COUNTER_SIZE;
|
||||
pc.values[pc.index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use cgmath::{Matrix4, SquareMatrix};
|
||||
use dds::get_block_size;
|
||||
use rust_engine_proc::perf;
|
||||
use vulkano::device::physical::PhysicalDevice;
|
||||
use vulkano::pipeline::viewport::Viewport;
|
||||
use vulkano::render_pass::{FramebufferAbstract, RenderPass};
|
||||
@@ -29,6 +30,7 @@ use pipelines::vs;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use crate::config::RenderConfig;
|
||||
use crate::perf::PerformanceCounter;
|
||||
use crate::vulkan::gameobject::GameObject;
|
||||
|
||||
use self::mesh::CPUVertexList;
|
||||
@@ -44,8 +46,6 @@ const VALIDATION_LAYERS: &[&str] = &[
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
];
|
||||
|
||||
pub const PERF_COUNTER_SIZE: usize = 1000;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
@@ -114,11 +114,6 @@ pub struct GameData {
|
||||
pub meshes: Vec<Mesh<Vertex>>,
|
||||
pub meshes_text: Vec<Mesh<TextVertex>>,
|
||||
pub textures: Vec<Texture>,
|
||||
pub update_perf_counters: [u128; PERF_COUNTER_SIZE],
|
||||
pub render_perf_counters: [u128; PERF_COUNTER_SIZE],
|
||||
pub other_perf_counters: [u128; PERF_COUNTER_SIZE],
|
||||
pub other_perf_instant: Instant,
|
||||
pub performance_counter_index: usize,
|
||||
}
|
||||
|
||||
pub struct VulkanRenderer {
|
||||
@@ -149,11 +144,6 @@ impl VulkanRenderer {
|
||||
meshes: vec![],
|
||||
meshes_text: vec![],
|
||||
textures: vec![],
|
||||
update_perf_counters: [0; PERF_COUNTER_SIZE],
|
||||
render_perf_counters: [0; PERF_COUNTER_SIZE],
|
||||
other_perf_counters: [0; PERF_COUNTER_SIZE],
|
||||
performance_counter_index: 0,
|
||||
other_perf_instant: Instant::now(),
|
||||
};
|
||||
|
||||
// Create basic vulkan instance with layers and info
|
||||
@@ -342,9 +332,8 @@ impl VulkanRenderer {
|
||||
Arc::new(builder.build().unwrap())
|
||||
}
|
||||
|
||||
#[perf("renderer", crate::perf::PerformanceCounter)]
|
||||
pub fn render_loop(self: &mut Self, new_ubo: &vs::ty::ObjectUniformData, game_objects: &Vec<GameObject>) {
|
||||
let precise_start = Instant::now();
|
||||
|
||||
// cleanup previous frame
|
||||
self.previous_frame_end.as_mut().unwrap().cleanup_finished();
|
||||
|
||||
@@ -438,8 +427,6 @@ impl VulkanRenderer {
|
||||
self.previous_frame_end = Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
||||
}
|
||||
};
|
||||
|
||||
self.game_data.render_perf_counters[self.game_data.performance_counter_index] = precise_start.elapsed().as_micros();
|
||||
}
|
||||
|
||||
pub fn upload_mesh(self: &mut Self, mesh: CPUMesh, original_path: Option<String>) -> usize {
|
||||
@@ -676,6 +663,8 @@ impl VulkanRenderer {
|
||||
}
|
||||
|
||||
pub fn start_event_loop(mut renderer: VulkanRenderer, mut game: Box<dyn Game>, event_loop: EventLoop<()>) {
|
||||
PerformanceCounter::init_perf();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
game.on_window_event(&event);
|
||||
|
||||
@@ -691,12 +680,12 @@ pub fn start_event_loop(mut renderer: VulkanRenderer, mut game: Box<dyn Game>, e
|
||||
renderer.recreate_swapchain = true;
|
||||
},
|
||||
Event::RedrawRequested(..) => {
|
||||
PerformanceCounter::perf_next_frame("renderer");
|
||||
renderer.render_loop(game.get_ubo(), &game.get_game_objects());
|
||||
renderer.game_data.other_perf_counters[renderer.game_data.performance_counter_index] = renderer.game_data.other_perf_instant.elapsed().as_micros();
|
||||
renderer.game_data.other_perf_instant = Instant::now();
|
||||
renderer.game_data.performance_counter_index = (renderer.game_data.performance_counter_index + 1) % PERF_COUNTER_SIZE;
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
PerformanceCounter::perf_next_frame("update");
|
||||
PerformanceCounter::perf_next_frame("input_events");
|
||||
game.update(&mut renderer);
|
||||
renderer.surface.window().request_redraw();
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user