query_gl.rs (8589B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use gleam::gl; 6 use std::mem; 7 use std::rc::Rc; 8 9 use crate::device::GpuFrameId; 10 use crate::profiler::GpuProfileTag; 11 12 #[derive(Copy, Clone, Debug)] 13 pub enum GpuDebugMethod { 14 None, 15 MarkerEXT, 16 KHR, 17 } 18 19 #[derive(Debug, Clone)] 20 pub struct GpuTimer { 21 pub tag: GpuProfileTag, 22 pub time_ns: u64, 23 } 24 25 #[derive(Debug, Clone)] 26 pub struct GpuSampler { 27 pub tag: GpuProfileTag, 28 pub count: u64, 29 } 30 31 pub struct QuerySet<T> { 32 set: Vec<gl::GLuint>, 33 data: Vec<T>, 34 pending: gl::GLuint, 35 } 36 37 impl<T> QuerySet<T> { 38 fn new() -> Self { 39 QuerySet { 40 set: Vec::new(), 41 data: Vec::new(), 42 pending: 0, 43 } 44 } 45 46 fn reset(&mut self) { 47 self.data.clear(); 48 self.pending = 0; 49 } 50 51 fn add(&mut self, value: T) -> Option<gl::GLuint> { 52 assert_eq!(self.pending, 0); 53 self.set.get(self.data.len()).cloned().map(|query_id| { 54 self.data.push(value); 55 self.pending = query_id; 56 query_id 57 }) 58 } 59 60 fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> { 61 let mut data = mem::replace(&mut self.data, Vec::new()); 62 for (value, &query) in data.iter_mut().zip(self.set.iter()) { 63 fun(value, query) 64 } 65 data 66 } 67 } 68 69 pub struct GpuFrameProfile { 70 gl: Rc<dyn gl::Gl>, 71 timers: QuerySet<GpuTimer>, 72 samplers: QuerySet<GpuSampler>, 73 frame_id: GpuFrameId, 74 inside_frame: bool, 75 debug_method: GpuDebugMethod, 76 } 77 78 impl GpuFrameProfile { 79 fn new(gl: Rc<dyn gl::Gl>, debug_method: GpuDebugMethod) -> Self { 80 GpuFrameProfile { 81 gl, 82 timers: QuerySet::new(), 83 samplers: QuerySet::new(), 84 frame_id: GpuFrameId::new(0), 85 inside_frame: false, 86 debug_method 87 } 88 } 89 90 fn enable_timers(&mut self, count: i32) { 91 self.timers.set = self.gl.gen_queries(count); 92 } 93 94 fn disable_timers(&mut self) { 95 if !self.timers.set.is_empty() { 96 self.gl.delete_queries(&self.timers.set); 97 } 98 self.timers.set = Vec::new(); 99 } 100 101 fn enable_samplers(&mut self, count: i32) { 102 self.samplers.set = self.gl.gen_queries(count); 103 } 104 105 fn disable_samplers(&mut self) { 106 if !self.samplers.set.is_empty() { 107 self.gl.delete_queries(&self.samplers.set); 108 } 109 self.samplers.set = Vec::new(); 110 } 111 112 fn begin_frame(&mut self, frame_id: GpuFrameId) { 113 self.frame_id = frame_id; 114 self.timers.reset(); 115 self.samplers.reset(); 116 self.inside_frame = true; 117 } 118 119 fn end_frame(&mut self) { 120 self.finish_timer(); 121 self.finish_sampler(); 122 self.inside_frame = false; 123 } 124 125 fn finish_timer(&mut self) { 126 debug_assert!(self.inside_frame); 127 if self.timers.pending != 0 { 128 self.gl.end_query(gl::TIME_ELAPSED); 129 self.timers.pending = 0; 130 } 131 } 132 133 fn finish_sampler(&mut self) { 134 debug_assert!(self.inside_frame); 135 if self.samplers.pending != 0 { 136 self.gl.end_query(gl::SAMPLES_PASSED); 137 self.samplers.pending = 0; 138 } 139 } 140 141 fn start_timer(&mut self, tag: GpuProfileTag) -> GpuTimeQuery { 142 self.finish_timer(); 143 144 let marker = GpuMarker::new(&self.gl, tag.label, self.debug_method); 145 146 if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) { 147 self.gl.begin_query(gl::TIME_ELAPSED, query); 148 } 149 150 GpuTimeQuery(marker) 151 } 152 153 fn start_sampler(&mut self, tag: GpuProfileTag) -> GpuSampleQuery { 154 self.finish_sampler(); 155 156 if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) { 157 self.gl.begin_query(gl::SAMPLES_PASSED, query); 158 } 159 160 GpuSampleQuery 161 } 162 163 fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer>, Vec<GpuSampler>) { 164 debug_assert!(!self.inside_frame); 165 let gl = &self.gl; 166 167 ( 168 self.frame_id, 169 self.timers.take(|timer, query| { 170 timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT) 171 }), 172 self.samplers.take(|sampler, query| { 173 sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT) 174 }), 175 ) 176 } 177 } 178 179 impl Drop for GpuFrameProfile { 180 fn drop(&mut self) { 181 self.disable_timers(); 182 self.disable_samplers(); 183 } 184 } 185 186 const NUM_PROFILE_FRAMES: usize = 4; 187 188 pub struct GpuProfiler { 189 gl: Rc<dyn gl::Gl>, 190 frames: [GpuFrameProfile; NUM_PROFILE_FRAMES], 191 next_frame: usize, 192 debug_method: GpuDebugMethod 193 } 194 195 impl GpuProfiler { 196 pub fn new(gl: Rc<dyn gl::Gl>, debug_method: GpuDebugMethod) -> Self { 197 let f = || GpuFrameProfile::new(Rc::clone(&gl), debug_method); 198 199 let frames = [f(), f(), f(), f()]; 200 GpuProfiler { 201 gl, 202 next_frame: 0, 203 frames, 204 debug_method 205 } 206 } 207 208 pub fn enable_timers(&mut self) { 209 const MAX_TIMERS_PER_FRAME: i32 = 256; 210 211 for frame in &mut self.frames { 212 frame.enable_timers(MAX_TIMERS_PER_FRAME); 213 } 214 } 215 216 pub fn disable_timers(&mut self) { 217 for frame in &mut self.frames { 218 frame.disable_timers(); 219 } 220 } 221 222 pub fn enable_samplers(&mut self) { 223 const MAX_SAMPLERS_PER_FRAME: i32 = 16; 224 if cfg!(target_os = "macos") { 225 warn!("Expect macOS driver bugs related to sample queries") 226 } 227 228 for frame in &mut self.frames { 229 frame.enable_samplers(MAX_SAMPLERS_PER_FRAME); 230 } 231 } 232 233 pub fn disable_samplers(&mut self) { 234 for frame in &mut self.frames { 235 frame.disable_samplers(); 236 } 237 } 238 239 pub fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer>, Vec<GpuSampler>) { 240 self.frames[self.next_frame].build_samples() 241 } 242 243 pub fn begin_frame(&mut self, frame_id: GpuFrameId) { 244 self.frames[self.next_frame].begin_frame(frame_id); 245 } 246 247 pub fn end_frame(&mut self) { 248 self.frames[self.next_frame].end_frame(); 249 self.next_frame = (self.next_frame + 1) % self.frames.len(); 250 } 251 252 pub fn start_timer(&mut self, tag: GpuProfileTag) -> GpuTimeQuery { 253 self.frames[self.next_frame].start_timer(tag) 254 } 255 256 pub fn start_sampler(&mut self, tag: GpuProfileTag) -> GpuSampleQuery { 257 self.frames[self.next_frame].start_sampler(tag) 258 } 259 260 pub fn finish_sampler(&mut self, _sampler: GpuSampleQuery) { 261 self.frames[self.next_frame].finish_sampler() 262 } 263 264 pub fn start_marker(&mut self, label: &str) -> GpuMarker { 265 GpuMarker::new(&self.gl, label, self.debug_method) 266 } 267 268 pub fn place_marker(&mut self, label: &str) { 269 GpuMarker::fire(&self.gl, label, self.debug_method) 270 } 271 } 272 273 #[must_use] 274 pub struct GpuMarker { 275 gl: Option<(Rc<dyn gl::Gl>, GpuDebugMethod)>, 276 } 277 278 impl GpuMarker { 279 fn new(gl: &Rc<dyn gl::Gl>, message: &str, debug_method: GpuDebugMethod) -> Self { 280 let gl = match debug_method { 281 GpuDebugMethod::KHR => { 282 gl.push_debug_group_khr(gl::DEBUG_SOURCE_APPLICATION, 0, message); 283 Some((Rc::clone(gl), debug_method)) 284 }, 285 GpuDebugMethod::MarkerEXT => { 286 gl.push_group_marker_ext(message); 287 Some((Rc::clone(gl), debug_method)) 288 }, 289 GpuDebugMethod::None => None, 290 }; 291 GpuMarker { gl } 292 } 293 294 fn fire(gl: &Rc<dyn gl::Gl>, message: &str, debug_method: GpuDebugMethod) { 295 match debug_method { 296 GpuDebugMethod::KHR => gl.debug_message_insert_khr(gl::DEBUG_SOURCE_APPLICATION, gl::DEBUG_TYPE_MARKER, 0, gl::DEBUG_SEVERITY_NOTIFICATION, message), 297 GpuDebugMethod::MarkerEXT => gl.insert_event_marker_ext(message), 298 GpuDebugMethod::None => {} 299 }; 300 } 301 } 302 303 impl Drop for GpuMarker { 304 fn drop(&mut self) { 305 if let Some((ref gl, debug_method)) = self.gl { 306 match debug_method { 307 GpuDebugMethod::KHR => gl.pop_debug_group_khr(), 308 GpuDebugMethod::MarkerEXT => gl.pop_group_marker_ext(), 309 GpuDebugMethod::None => {} 310 }; 311 } 312 } 313 } 314 315 #[must_use] 316 pub struct GpuTimeQuery(#[allow(dead_code)] GpuMarker); 317 #[must_use] 318 pub struct GpuSampleQuery;