1#![macro_use]
2
3use std::ffi::CStr;
4use std::time::{Duration, Instant};
5
6use gl;
7use gl::types::GLubyte;
8
9use log::{debug, info, error};
10
11pub fn elapsed(start_time: Instant) -> String {
12 let elapsed = start_time.elapsed();
13 format_duration(elapsed)
14}
15
16fn format_duration(duration: Duration) -> String {
17 let secs = duration.as_secs();
18 let nanos = duration.subsec_nanos();
19 let ms = f64::from(nanos) / 1_000_000.0;
20 if secs > 0 {
21 let secs = secs as f64 + ms / 1000.0;
22 format!("{:<4.*} s", 1, secs)
23 }
24 else {
25 let places =
26 if ms >= 20.0 { 0 }
27 else if ms >= 1.0 { 1 }
28 else {
29 let micros = f64::from(nanos) / 1000.0;
30 let places = if micros >= 10.0 { 0 } else { 2 };
31 return format!("{:>3.*} µs", places, micros)
32 };
33 format!("{:>3.*} ms", places, ms)
34 }
35}
36
37pub fn print_elapsed(message: &str, start_time: Instant) {
38 info!("{:<25}{}", message, elapsed(start_time));
39}
40
41pub struct FrameTimer {
42 message: String,
43 averaging_window: usize,
44 current_frame_start: Instant,
45 pub frame_times: Vec<Duration>,
46}
47
48impl FrameTimer {
51 pub fn new(message: &str, averaging_window: usize) -> FrameTimer {
52 FrameTimer {
53 message: message.to_owned(),
54 averaging_window,
55 current_frame_start: Instant::now(),
56 frame_times: Vec::with_capacity(averaging_window),
57 }
58 }
59
60 pub fn start(&mut self) {
61 self.current_frame_start = Instant::now();
62 }
63
64 pub fn end(&mut self) {
65 self.frame_times.push(self.current_frame_start.elapsed());
66 if self.frame_times.len() == self.averaging_window {
67 self.print_and_reset();
68 }
69 }
70
71 pub fn print_and_reset(&mut self) {
72 {
73 let avg = self.frame_times.iter().sum::<Duration>() / self.frame_times.len() as u32;
74 let min = self.frame_times.iter().min().unwrap();
75 let max = self.frame_times.iter().max().unwrap();
76 info!("{:<15}{} (min: {}, max: {})", self.message,
77 format_duration(avg), format_duration(*min), format_duration(*max));
78 }
79 self.frame_times.clear();
80 }
81}
82
83pub unsafe fn gl_check_error(file: &str, line: u32) -> u32 {
84 let mut error_code = gl::GetError();
85 while error_code != gl::NO_ERROR {
86 let error = match error_code {
87 gl::INVALID_ENUM => "INVALID_ENUM",
88 gl::INVALID_VALUE => "INVALID_VALUE",
89 gl::INVALID_OPERATION => "INVALID_OPERATION",
90 gl::STACK_OVERFLOW => "STACK_OVERFLOW",
91 gl::STACK_UNDERFLOW => "STACK_UNDERFLOW",
92 gl::OUT_OF_MEMORY => "OUT_OF_MEMORY",
93 gl::INVALID_FRAMEBUFFER_OPERATION => "INVALID_FRAMEBUFFER_OPERATION",
94 _ => "unknown GL error code"
95 };
96
97 error!("{} | {} ({})", error, file, line);
98
99 error_code = gl::GetError();
100 }
101 error_code
102}
103
104#[allow(unused_macros)]
105macro_rules! gl_check_error {
106 () => (
107 gl_check_error(file!(), line!())
108 )
109}
110
111pub unsafe fn print_context_info() {
114 debug!("Renderer : {}", gl_string(gl::GetString(gl::RENDERER)));
115 debug!("Vendor : {}", gl_string(gl::GetString(gl::VENDOR)));
116 debug!("Version : {}", gl_string(gl::GetString(gl::VERSION)));
117 debug!("GLSL : {}", gl_string(gl::GetString(gl::SHADING_LANGUAGE_VERSION)));
118
119 let mut val = 0;
120 gl::GetIntegerv(gl::CONTEXT_PROFILE_MASK, &mut val);
121 let val = val as gl::types::GLenum;
122 let profile = if (val & gl::CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0 {
123 "Compatibility"
124 } else if (val & gl::CONTEXT_CORE_PROFILE_BIT) != 0 {
125 "Core"
126 } else {
127 "None"
128 };
129 debug!("Profile : {}", profile);
130
131 let (debug, forward_compatible) = {
132 let mut val = 0;
133 gl::GetIntegerv(gl::CONTEXT_FLAGS, &mut val);
134 let val = val as gl::types::GLenum;
135 ((val & gl::CONTEXT_FLAG_DEBUG_BIT) != 0,
136 (val & gl::CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) != 0)
137 };
138 debug!("Context Flags: Debug: {}, Forward Compatible: {}", debug, forward_compatible);
139
140 let mut num_extensions = 0;
141 gl::GetIntegerv(gl::NUM_EXTENSIONS, &mut num_extensions);
142 let extensions: Vec<_> = (0 .. num_extensions).map(|num| {
143 gl_string(gl::GetStringi(gl::EXTENSIONS, num as gl::types::GLuint))
144 }).collect();
145 debug!("Extensions : {}", extensions.join(", "))
146}
147
148pub unsafe fn gl_string(raw_string: *const GLubyte) -> String {
149 if raw_string.is_null() { return "(NULL)".into() }
150 String::from_utf8(CStr::from_ptr(raw_string as *const _).to_bytes().to_vec())
151 .expect("gl_string: non-UTF8 string")
152}