glium/draw_parameters/
query.rs

1use crate::backend::Facade;
2use crate::context::Context;
3use crate::context::CommandContext;
4use crate::ContextExt;
5use crate::DrawError;
6use crate::ToGlEnum;
7use crate::GlObject;
8use crate::QueryExt;
9
10use std::cell::Cell;
11use std::fmt;
12use std::rc::Rc;
13use std::error::Error;
14
15use crate::buffer::Buffer;
16use crate::buffer::BufferSlice;
17use crate::BufferExt;
18use crate::BufferSliceExt;
19
20use crate::gl;
21use crate::version::Api;
22use crate::version::Version;
23
24pub struct RawQuery {
25    context: Rc<Context>,
26    id: gl::types::GLuint,
27    ty: QueryType,
28
29    // true means that this query has already been used or is being used to get data
30    // this is important to know because we want to avoid erasing data
31    has_been_used: Cell<bool>,
32}
33
34pub enum QueryType {
35    SamplesPassed,
36    AnySamplesPassed,
37    AnySamplesPassedConservative,
38    TimeElapsed,
39    Timestamp,
40    PrimitivesGenerated,
41    TransformFeedbackPrimitivesWritten,
42}
43
44impl ToGlEnum for QueryType {
45    #[inline]
46    fn to_glenum(&self) -> gl::types::GLenum {
47        match *self {
48            QueryType::SamplesPassed => gl::SAMPLES_PASSED,
49            QueryType::AnySamplesPassed => gl::ANY_SAMPLES_PASSED,
50            QueryType::AnySamplesPassedConservative => gl::ANY_SAMPLES_PASSED_CONSERVATIVE,
51            QueryType::TimeElapsed => gl::TIME_ELAPSED,
52            QueryType::Timestamp => gl::TIMESTAMP,
53            QueryType::PrimitivesGenerated => gl::PRIMITIVES_GENERATED,
54            QueryType::TransformFeedbackPrimitivesWritten => {
55                gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
56            },
57        }
58    }
59}
60
61/// Error that can happen when creating a query object.
62#[derive(Copy, Clone, Debug)]
63pub enum QueryCreationError {
64    /// The given query type is not supported.
65    NotSupported,
66}
67
68impl fmt::Display for QueryCreationError {
69    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
70        use self::QueryCreationError::*;
71        let desc = match *self {
72            NotSupported => "The given query type is not supported",
73        };
74        fmt.write_str(desc)
75    }
76}
77
78impl Error for QueryCreationError {}
79
80/// Error that can happen when writing the value of a query to a buffer.
81#[derive(Copy, Clone, Debug)]
82pub enum ToBufferError {
83    /// Writing the result to a buffer is not supported.
84    NotSupported,
85}
86
87impl fmt::Display for ToBufferError {
88    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
89        use self::ToBufferError::*;
90        let desc = match *self {
91            NotSupported => "Writing the result to a buffer is not supported",
92        };
93        fmt.write_str(desc)
94    }
95}
96
97impl Error for ToBufferError {}
98
99impl RawQuery {
100    /// Builds a new query. Returns `None` if the backend doesn't support this type.
101    pub fn new<F: ?Sized>(facade: &F, ty: QueryType) -> Result<RawQuery, QueryCreationError>
102                  where F: Facade
103    {
104        let context = facade.get_context().clone();
105        let ctxt = facade.get_context().make_current();
106
107        // FIXME: handle Timestamp separately
108
109        let id = unsafe {
110            let mut id = 0;
111
112            if ctxt.version >= &Version(Api::Gl, 3, 3) {
113                match ty {
114                    QueryType::AnySamplesPassed | QueryType::SamplesPassed |
115                    QueryType::PrimitivesGenerated | QueryType::TimeElapsed |
116                    QueryType::TransformFeedbackPrimitivesWritten => (),
117                    QueryType::AnySamplesPassedConservative if
118                            ctxt.extensions.gl_arb_es3_compatibility ||
119                            ctxt.version >= &Version(Api:: Gl, 4, 3) => (),
120                    _ => return Err(QueryCreationError::NotSupported)
121                };
122
123                if ctxt.version >= &Version(Api:: Gl, 4, 5) ||
124                   ctxt.extensions.gl_arb_direct_state_access
125                {
126                    ctxt.gl.CreateQueries(ty.to_glenum(), 1, &mut id);
127                } else {
128                    ctxt.gl.GenQueries(1, &mut id);
129                }
130
131            } else if ctxt.version >= &Version(Api::Gl, 3, 0) {
132                match ty {
133                    QueryType::SamplesPassed | QueryType::PrimitivesGenerated |
134                    QueryType::TransformFeedbackPrimitivesWritten => (),
135                    QueryType::AnySamplesPassed if ctxt.extensions.gl_arb_occlusion_query2 => (),
136                    QueryType::AnySamplesPassedConservative if ctxt.extensions.gl_arb_es3_compatibility => (),
137                    QueryType::TimeElapsed if ctxt.extensions.gl_arb_timer_query => (),
138
139                    _ => return Err(QueryCreationError::NotSupported)
140                };
141
142                ctxt.gl.GenQueries(1, &mut id);
143
144            } else if ctxt.version >= &Version(Api::Gl, 1, 5) || ctxt.extensions.gl_arb_occlusion_query {
145                match ty {
146                    QueryType::SamplesPassed => (),
147                    QueryType::AnySamplesPassed if ctxt.extensions.gl_arb_occlusion_query2 => (),
148                    QueryType::AnySamplesPassedConservative if ctxt.extensions.gl_arb_es3_compatibility => (),
149                    QueryType::PrimitivesGenerated if ctxt.extensions.gl_ext_transform_feedback => (),
150                    QueryType::TransformFeedbackPrimitivesWritten if ctxt.extensions.gl_ext_transform_feedback => (),
151                    QueryType::TimeElapsed if ctxt.extensions.gl_arb_timer_query => (),
152                    _ => return Err(QueryCreationError::NotSupported)
153                };
154
155                if ctxt.version >= &Version(Api::Gl, 1, 5) {
156                    ctxt.gl.GenQueries(1, &mut id);
157                } else if ctxt.extensions.gl_arb_occlusion_query {
158                    ctxt.gl.GenQueriesARB(1, &mut id);
159                } else {
160                    unreachable!();
161                }
162
163            } else if ctxt.version >= &Version(Api::GlEs, 3, 0) {
164                match ty {
165                    QueryType::AnySamplesPassed | QueryType::AnySamplesPassedConservative |
166                    QueryType::TransformFeedbackPrimitivesWritten => (),
167                    _ => return Err(QueryCreationError::NotSupported)
168                };
169
170                ctxt.gl.GenQueries(1, &mut id);
171
172            } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
173                match ty {
174                    QueryType::AnySamplesPassed | QueryType::AnySamplesPassedConservative => (),
175                    _ => return Err(QueryCreationError::NotSupported)
176                };
177
178                ctxt.gl.GenQueriesEXT(1, &mut id);
179
180            } else {
181                return Err(QueryCreationError::NotSupported);
182            }
183
184            id
185        };
186
187        Ok(RawQuery {
188            context,
189            id,
190            ty,
191            has_been_used: Cell::new(false),
192        })
193    }
194
195    /// Queries the counter to see if the result is already available.
196    pub fn is_ready(&self) -> bool {
197        let mut ctxt = self.context.make_current();
198        self.deactivate(&mut ctxt);
199
200        if !self.has_been_used.get() {
201            return false;
202        }
203
204        Buffer::<u8>::unbind_query(&mut ctxt);
205
206        unsafe {
207            let mut value = 0;
208
209            if ctxt.version >= &Version(Api::Gl, 1, 5) ||
210               ctxt.version >= &Version(Api::GlEs, 3, 0)
211            {
212                ctxt.gl.GetQueryObjectuiv(self.id, gl::QUERY_RESULT_AVAILABLE, &mut value);
213
214            } else if ctxt.extensions.gl_arb_occlusion_query {
215                ctxt.gl.GetQueryObjectuivARB(self.id, gl::QUERY_RESULT_AVAILABLE, &mut value);
216
217            } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
218                ctxt.gl.GetQueryObjectuivEXT(self.id, gl::QUERY_RESULT_AVAILABLE, &mut value);
219
220            } else {
221                // if we reach here, user shouldn't have been able to create a query in the
222                // first place
223                unreachable!();
224            }
225
226            value != 0
227        }
228    }
229
230    /// Returns the value of the query. Blocks until it is available.
231    ///
232    /// This function doesn't block if `is_ready` returns true.
233    pub fn get_u32(&self) -> u32 {
234        let mut ctxt = self.context.make_current();
235        self.deactivate(&mut ctxt);
236
237        if !self.has_been_used.get() {
238            return 0;
239        }
240
241        Buffer::<u8>::unbind_query(&mut ctxt);
242
243        unsafe {
244            let mut value = 0;
245            self.raw_get_u32(&mut ctxt, &mut value);
246            value
247        }
248    }
249
250    /// Writes the value of the query to a buffer.
251    pub fn write_u32_to_buffer(&self, target: BufferSlice<'_, u32>) -> Result<(), ToBufferError> {
252        let mut ctxt = self.context.make_current();
253
254        if !(ctxt.version >= &Version(Api::Gl, 4, 4) || ctxt.extensions.gl_arb_query_buffer_object ||
255             ctxt.extensions.gl_amd_query_buffer_object)
256        {
257            return Err(ToBufferError::NotSupported);
258        }
259
260        self.deactivate(&mut ctxt);
261
262        if !self.has_been_used.get() {
263            panic!();
264        }
265
266        assert!(target.get_offset_bytes() % 4 == 0);
267
268        target.prepare_and_bind_for_query(&mut ctxt);
269        unsafe { self.raw_get_u32(&mut ctxt, target.get_offset_bytes() as *mut _); }
270
271        if let Some(fence) = target.add_fence() {
272            fence.insert(&mut ctxt);
273        }
274
275        Ok(())
276    }
277
278    unsafe fn raw_get_u32(&self, ctxt: &mut CommandContext<'_>, target: *mut gl::types::GLuint) {
279        if ctxt.version >= &Version(Api::Gl, 1, 5) || ctxt.version >= &Version(Api::GlEs, 3, 0) {
280            ctxt.gl.GetQueryObjectuiv(self.id, gl::QUERY_RESULT, target);
281
282        } else if ctxt.extensions.gl_arb_occlusion_query {
283            ctxt.gl.GetQueryObjectuivARB(self.id, gl::QUERY_RESULT, target);
284
285        } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
286            ctxt.gl.GetQueryObjectuivEXT(self.id, gl::QUERY_RESULT, target);
287
288        } else {
289            // if we reach here, user shouldn't have been able to create a query in the
290            // first place
291            unreachable!();
292        }
293    }
294
295    /// Returns the value of the query. Blocks until it is available.
296    ///
297    /// This function doesn't block if `is_ready` returns true.
298    pub fn get_u64(&self) -> u64 {
299        let mut ctxt = self.context.make_current();
300        self.deactivate(&mut ctxt);
301
302        if !self.has_been_used.get() {
303            return 0;
304        }
305
306        Buffer::<u8>::unbind_query(&mut ctxt);
307
308        unsafe {
309            let mut value = 0;
310            if self.raw_get_u64(&mut ctxt, &mut value).is_ok() {
311                return value;
312            }
313
314            let mut value = 0;
315            self.raw_get_u32(&mut ctxt, &mut value);
316            value as u64
317        }
318    }
319
320    unsafe fn raw_get_u64(&self, ctxt: &mut CommandContext<'_>, target: *mut gl::types::GLuint64)
321                          -> Result<(), ()>
322    {
323        if ctxt.version >= &Version(Api::Gl, 3, 3) {
324            ctxt.gl.GetQueryObjectui64v(self.id, gl::QUERY_RESULT, target);
325            Ok(())
326
327        } else {
328            Err(())
329        }
330    }
331
332    /// Returns the value of the query. Blocks until it is available.
333    ///
334    /// This function doesn't block if `is_ready` returns true.
335    #[inline]
336    pub fn get_bool(&self) -> bool {
337        self.get_u32() != 0
338    }
339
340    /// If the query is active, unactivates it.
341    fn deactivate(&self, ctxt: &mut CommandContext<'_>) {
342        if ctxt.state.samples_passed_query == self.id {
343            unsafe { raw_end_query(ctxt, gl::SAMPLES_PASSED) };
344            ctxt.state.samples_passed_query = 0;
345        }
346
347        if ctxt.state.any_samples_passed_query == self.id {
348            unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED) };
349            ctxt.state.any_samples_passed_query = 0;
350        }
351
352        if ctxt.state.any_samples_passed_conservative_query == self.id {
353            unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE) };
354            ctxt.state.any_samples_passed_conservative_query = 0;
355        }
356
357        if ctxt.state.primitives_generated_query == self.id {
358            unsafe { raw_end_query(ctxt, gl::PRIMITIVES_GENERATED) };
359            ctxt.state.primitives_generated_query = 0;
360        }
361
362        if ctxt.state.transform_feedback_primitives_written_query == self.id {
363            unsafe { raw_end_query(ctxt, gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) };
364            ctxt.state.transform_feedback_primitives_written_query = 0;
365        }
366
367        if ctxt.state.time_elapsed_query == self.id {
368            unsafe { raw_end_query(ctxt, gl::TIME_ELAPSED) };
369            ctxt.state.time_elapsed_query = 0;
370        }
371    }
372}
373
374impl Drop for RawQuery {
375    fn drop(&mut self) {
376        let mut ctxt = self.context.make_current();
377        self.deactivate(&mut ctxt);
378
379        if let Some((id, _)) = ctxt.state.conditional_render {
380            if id == self.id {
381                RawQuery::end_conditional_render(&mut ctxt);
382            }
383        }
384
385        unsafe {
386            if ctxt.version >= &Version(Api::Gl, 1, 5) ||
387               ctxt.version >= &Version(Api::GlEs, 3, 0)
388            {
389                ctxt.gl.DeleteQueries(1, [self.id].as_ptr());
390
391            } else if ctxt.extensions.gl_arb_occlusion_query {
392                ctxt.gl.DeleteQueriesARB(1, [self.id].as_ptr());
393
394            } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
395                ctxt.gl.DeleteQueriesEXT(1, [self.id].as_ptr());
396
397            } else {
398                unreachable!();
399            }
400        }
401    }
402}
403
404impl QueryExt for RawQuery {
405    fn begin_query(&self, ctxt: &mut CommandContext<'_>) -> Result<(), DrawError> {
406        match self.ty {
407            QueryType::SamplesPassed => {
408                if ctxt.state.any_samples_passed_query != 0 {
409                    ctxt.state.any_samples_passed_query = 0;
410                    unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED); }
411                }
412
413                if ctxt.state.any_samples_passed_conservative_query != 0 {
414                    ctxt.state.any_samples_passed_conservative_query = 0;
415                    unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE); }
416                }
417
418                if ctxt.state.samples_passed_query != self.id {
419                    if self.has_been_used.get() {
420                        return Err(DrawError::WrongQueryOperation);
421                    }
422
423                    unsafe {
424                        if ctxt.state.samples_passed_query != 0 {
425                            raw_end_query(ctxt, gl::SAMPLES_PASSED);
426                        }
427                        raw_begin_query(ctxt, gl::SAMPLES_PASSED, self.id);
428                    }
429
430                    self.has_been_used.set(true);
431                    ctxt.state.samples_passed_query = self.id;
432                }
433            },
434
435            QueryType::AnySamplesPassed => {
436                if ctxt.state.samples_passed_query != 0 {
437                    ctxt.state.samples_passed_query = 0;
438                    unsafe { raw_end_query(ctxt, gl::SAMPLES_PASSED); }
439                }
440
441                if ctxt.state.any_samples_passed_conservative_query != 0 {
442                    ctxt.state.any_samples_passed_conservative_query = 0;
443                    unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE); }
444                }
445
446                if ctxt.state.any_samples_passed_query != self.id {
447                    if self.has_been_used.get() {
448                        return Err(DrawError::WrongQueryOperation);
449                    }
450
451                    unsafe {
452                        if ctxt.state.any_samples_passed_query != 0 {
453                            raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED);
454                        }
455                        raw_begin_query(ctxt, gl::ANY_SAMPLES_PASSED, self.id);
456                    }
457
458                    self.has_been_used.set(true);
459                    ctxt.state.any_samples_passed_query = self.id;
460                }
461            },
462
463            QueryType::AnySamplesPassedConservative => {
464                if ctxt.state.samples_passed_query != 0 {
465                    ctxt.state.samples_passed_query = 0;
466                    unsafe { raw_end_query(ctxt, gl::SAMPLES_PASSED); }
467                }
468
469                if ctxt.state.any_samples_passed_query != 0 {
470                    ctxt.state.any_samples_passed_query = 0;
471                    unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED); }
472                }
473
474                if ctxt.state.any_samples_passed_conservative_query != self.id {
475                    if self.has_been_used.get() {
476                        return Err(DrawError::WrongQueryOperation);
477                    }
478
479                    unsafe {
480                        if ctxt.state.any_samples_passed_conservative_query != 0 {
481                            raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE);
482                        }
483                        raw_begin_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE, self.id);
484                    }
485
486                    self.has_been_used.set(true);
487                    ctxt.state.any_samples_passed_conservative_query = self.id;
488                }
489            },
490
491            QueryType::TimeElapsed => {
492                if ctxt.state.time_elapsed_query != self.id {
493                    if self.has_been_used.get() {
494                        return Err(DrawError::WrongQueryOperation);
495                    }
496
497                    unsafe {
498                        if ctxt.state.time_elapsed_query != 0 {
499                            raw_end_query(ctxt, gl::TIME_ELAPSED);
500                        }
501                        raw_begin_query(ctxt, gl::TIME_ELAPSED, self.id);
502                    }
503
504                    self.has_been_used.set(true);
505                    ctxt.state.time_elapsed_query = self.id;
506                }
507            },
508
509            QueryType::Timestamp => panic!(),
510
511            QueryType::PrimitivesGenerated => {
512                if ctxt.state.primitives_generated_query != self.id {
513                    if self.has_been_used.get() {
514                        return Err(DrawError::WrongQueryOperation);
515                    }
516
517                    unsafe {
518                        if ctxt.state.primitives_generated_query != 0 {
519                            raw_end_query(ctxt, gl::PRIMITIVES_GENERATED);
520                        }
521                        raw_begin_query(ctxt, gl::PRIMITIVES_GENERATED, self.id);
522                    }
523
524                    self.has_been_used.set(true);
525                    ctxt.state.primitives_generated_query = self.id;
526                }
527            },
528
529            QueryType::TransformFeedbackPrimitivesWritten => {
530                if ctxt.state.transform_feedback_primitives_written_query != self.id {
531                    if self.has_been_used.get() {
532                        return Err(DrawError::WrongQueryOperation);
533                    }
534
535                    unsafe {
536                        if ctxt.state.transform_feedback_primitives_written_query != 0 {
537                            raw_end_query(ctxt, gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
538                        }
539                        raw_begin_query(ctxt, gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, self.id);
540                    }
541
542                    self.has_been_used.set(true);
543                    ctxt.state.transform_feedback_primitives_written_query = self.id;
544                }
545            },
546        };
547
548        Ok(())
549    }
550
551    fn end_samples_passed_query(ctxt: &mut CommandContext<'_>) {
552        if ctxt.state.samples_passed_query != 0 {
553            ctxt.state.samples_passed_query = 0;
554            unsafe { raw_end_query(ctxt, gl::SAMPLES_PASSED); }
555        }
556
557        if ctxt.state.any_samples_passed_query != 0 {
558            ctxt.state.any_samples_passed_query = 0;
559            unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED); }
560        }
561
562        if ctxt.state.any_samples_passed_conservative_query != 0 {
563            ctxt.state.any_samples_passed_conservative_query = 0;
564            unsafe { raw_end_query(ctxt, gl::ANY_SAMPLES_PASSED_CONSERVATIVE); }
565        }
566    }
567
568    #[inline]
569    fn end_time_elapsed_query(ctxt: &mut CommandContext<'_>) {
570        if ctxt.state.time_elapsed_query != 0 {
571            ctxt.state.time_elapsed_query = 0;
572            unsafe { raw_end_query(ctxt, gl::TIME_ELAPSED); }
573        }
574    }
575
576    #[inline]
577    fn end_primitives_generated_query(ctxt: &mut CommandContext<'_>) {
578        if ctxt.state.primitives_generated_query != 0 {
579            ctxt.state.primitives_generated_query = 0;
580            unsafe { raw_end_query(ctxt, gl::PRIMITIVES_GENERATED); }
581        }
582    }
583
584    #[inline]
585    fn end_transform_feedback_primitives_written_query(ctxt: &mut CommandContext<'_>) {
586        if ctxt.state.transform_feedback_primitives_written_query != 0 {
587            ctxt.state.transform_feedback_primitives_written_query = 0;
588            unsafe { raw_end_query(ctxt, gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); }
589        }
590    }
591
592    fn begin_conditional_render(&self, ctxt: &mut CommandContext<'_>, wait: bool, per_region: bool) {
593        let new_mode = match (wait, per_region) {
594            (true, true) => gl::QUERY_BY_REGION_WAIT,
595            (true, false) => gl::QUERY_WAIT,
596            (false, true) => gl::QUERY_BY_REGION_NO_WAIT,
597            (false, false) => gl::QUERY_NO_WAIT,
598        };
599
600        // returning if the active conditional rendering is already good
601        if let Some((old_id, old_mode)) = ctxt.state.conditional_render {
602            if old_id == self.id {
603                // if the new mode is "no_wait" but he old mode is "wait",
604                // then we don't need to change it
605                match (new_mode, old_mode) {
606                    (a, b) if a == b => return,
607                    (gl::QUERY_NO_WAIT, gl::QUERY_WAIT) => return,
608                    (gl::QUERY_BY_REGION_NO_WAIT, gl::QUERY_BY_REGION_WAIT) => return,
609                    _ => (),
610                }
611            }
612        }
613
614        // de-activating the existing conditional render first
615        if ctxt.state.conditional_render.is_some() {
616            RawQuery::end_conditional_render(ctxt);
617        }
618
619        // de-activating the query
620        self.deactivate(ctxt);
621
622        // activating
623        if ctxt.version >= &Version(Api::Gl, 3, 0) {
624            unsafe { ctxt.gl.BeginConditionalRender(self.id, new_mode) };
625        } else if ctxt.extensions.gl_nv_conditional_render {
626            unsafe { ctxt.gl.BeginConditionalRenderNV(self.id, new_mode) };
627        } else {
628            unreachable!();
629        }
630
631        ctxt.state.conditional_render = Some((self.id, new_mode));
632    }
633
634    fn end_conditional_render(ctxt: &mut CommandContext<'_>) {
635        if ctxt.state.conditional_render.is_none() {
636            return;
637        }
638
639        if ctxt.version >= &Version(Api::Gl, 3, 0) {
640            unsafe { ctxt.gl.EndConditionalRender(); }
641        } else if ctxt.extensions.gl_nv_conditional_render {
642            unsafe { ctxt.gl.EndConditionalRenderNV(); }
643        } else {
644            unreachable!();
645        }
646
647        ctxt.state.conditional_render = None;
648    }
649
650    fn is_unused(&self) -> bool {
651        !self.has_been_used.get()
652    }
653}
654
655impl fmt::Debug for RawQuery {
656    #[inline]
657    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
658        write!(fmt, "Query object #{}", self.id)
659    }
660}
661
662impl GlObject for RawQuery {
663    type Id = gl::types::GLuint;
664
665    #[inline]
666    fn get_id(&self) -> gl::types::GLuint {
667        self.id
668    }
669}
670
671/// Calls `glBeginQuery`.
672///
673/// # Unsafe
674///
675/// The type of query must be guaranteed to be supported by the backend.
676/// The id of the query must be valid.
677///
678unsafe fn raw_begin_query(ctxt: &mut CommandContext<'_>, ty: gl::types::GLenum, id: gl::types::GLuint) {
679    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
680       ctxt.version >= &Version(Api::GlEs, 3, 0)
681    {
682        ctxt.gl.BeginQuery(ty, id);
683
684    } else if ctxt.extensions.gl_arb_occlusion_query {
685        ctxt.gl.BeginQueryARB(ty, id);
686
687    } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
688        ctxt.gl.BeginQueryEXT(ty, id);
689
690    } else {
691        unreachable!();
692    }
693}
694
695/// Calls `glEndQuery`.
696///
697/// # Unsafe
698///
699/// The type of query must be guaranteed to be supported by the backend.
700unsafe fn raw_end_query(ctxt: &mut CommandContext<'_>, ty: gl::types::GLenum) {
701    if ctxt.version >= &Version(Api::Gl, 1, 5) ||
702       ctxt.version >= &Version(Api::GlEs, 3, 0)
703    {
704        ctxt.gl.EndQuery(ty);
705
706    } else if ctxt.extensions.gl_arb_occlusion_query {
707        ctxt.gl.EndQueryARB(ty);
708
709    } else if ctxt.extensions.gl_ext_occlusion_query_boolean {
710        ctxt.gl.EndQueryEXT(ty);
711
712    } else {
713        unreachable!();
714    }
715}
716
717macro_rules! impl_helper {
718    ($name:ident, $ret:ty, $get_fn:ident) => {
719        impl $name {
720            /// Queries the counter to see if the result is already available.
721            #[inline]
722            pub fn is_ready(&self) -> bool {
723                self.query.is_ready()
724            }
725
726            /// Returns the value of the query. Blocks until it is available.
727            ///
728            /// This function doesn't block if `is_ready` would return true.
729            ///
730            /// Note that you are strongly discouraged from calling this in the middle of the
731            /// rendering process, as it may block for a long time.
732            ///
733            /// Queries should either have their result written into a buffer, be used for
734            /// conditional rendering, or stored and checked during the next frame.
735            #[inline]
736            pub fn get(self) -> $ret {
737                self.query.$get_fn()
738            }
739
740            /// Writes the result of the query to a buffer when it is available.
741            ///
742            /// This function doesn't block. Instead it submits a commands to the GPU's commands
743            /// queue and orders the GPU to write the result of the query to a buffer.
744            ///
745            /// This operation is not necessarily supported everywhere.
746            #[inline]
747            pub fn to_buffer_u32(&self, target: BufferSlice<'_, u32>)
748                                 -> Result<(), ToBufferError>
749            {
750                self.query.write_u32_to_buffer(target)
751            }
752        }
753
754        impl GlObject for $name {
755            type Id = gl::types::GLuint;
756
757            #[inline]
758            fn get_id(&self) -> gl::types::GLuint {
759                self.query.get_id()
760            }
761        }
762
763        impl QueryExt for $name {
764            #[inline]
765            fn begin_query(&self, ctxt: &mut CommandContext<'_>) -> Result<(), DrawError> {
766                self.query.begin_query(ctxt)
767            }
768
769            #[inline]
770            fn end_samples_passed_query(ctxt: &mut CommandContext<'_>) {
771                RawQuery::end_samples_passed_query(ctxt)
772            }
773
774            #[inline]
775            fn end_time_elapsed_query(ctxt: &mut CommandContext<'_>) {
776                RawQuery::end_time_elapsed_query(ctxt)
777            }
778
779            #[inline]
780            fn end_primitives_generated_query(ctxt: &mut CommandContext<'_>) {
781                RawQuery::end_primitives_generated_query(ctxt)
782            }
783
784            #[inline]
785            fn end_transform_feedback_primitives_written_query(ctxt: &mut CommandContext<'_>) {
786                RawQuery::end_transform_feedback_primitives_written_query(ctxt)
787            }
788
789            #[inline]
790            fn begin_conditional_render(&self, ctxt: &mut CommandContext<'_>, wait: bool, per_region: bool) {
791                self.query.begin_conditional_render(ctxt, wait, per_region)
792            }
793
794            #[inline]
795            fn end_conditional_render(ctxt: &mut CommandContext<'_>) {
796                RawQuery::end_conditional_render(ctxt)
797            }
798
799            #[inline]
800            fn is_unused(&self) -> bool {
801                self.query.is_unused()
802            }
803        }
804    };
805}
806
807/// A query that allows you to know the number of samples written to the output during the
808/// draw operations where this query was active.
809///
810/// If you just want to know whether or not some samples have been written, you should use
811/// a `AnySamplesPassedQuery` query instead.
812#[derive(Debug)]
813pub struct SamplesPassedQuery {
814    query: RawQuery,
815}
816
817impl SamplesPassedQuery {
818    /// Builds a new query.
819    #[inline]
820    pub fn new<F: ?Sized>(facade: &F) -> Result<SamplesPassedQuery, QueryCreationError> where F: Facade {
821        RawQuery::new(facade, QueryType::SamplesPassed).map(|q| SamplesPassedQuery { query: q })
822    }
823}
824
825impl_helper!(SamplesPassedQuery, u32, get_u32);
826
827/// A query that allows you to know the number of nanoseconds that have elapsed
828/// during the draw operations.
829///
830/// TODO: not sure that it's nanoseconds
831#[derive(Debug)]
832pub struct TimeElapsedQuery {
833    query: RawQuery,
834}
835
836impl TimeElapsedQuery {
837    /// Builds a new query.
838    #[inline]
839    pub fn new<F: ?Sized>(facade: &F) -> Result<TimeElapsedQuery, QueryCreationError> where F: Facade {
840        RawQuery::new(facade, QueryType::TimeElapsed).map(|q| TimeElapsedQuery { query: q })
841    }
842}
843
844impl_helper!(TimeElapsedQuery, u32, get_u32);
845
846/// A query type that allows you to know whether any sample has been written to the output during
847/// the operations executed with this query.
848///
849/// ## OpenGL
850///
851/// This is usually a query of type `GL_ANY_SAMPLES_PASSED` or
852/// `GL_ANY_SAMPLES_PASSED_CONSERVATIVE`.
853///
854/// However if the backend doesn't support conservative queries, glium will automatically fall
855/// back to a non-conservative query. If the backend doesn't support either types but supports
856/// `GL_SAMPLES_PASSED`, then glium will automatically use a `GL_SAMPLES_PASSED` query instead.
857#[derive(Debug)]
858pub struct AnySamplesPassedQuery {
859    query: RawQuery,
860}
861
862impl AnySamplesPassedQuery {
863    /// Builds a new query.
864    ///
865    /// If you pass `true` for `conservative`, then OpenGL may use a less accurate algorithm,
866    /// leading to a faster result but with more false positives.
867    pub fn new<F: ?Sized>(facade: &F, conservative: bool)
868                  -> Result<AnySamplesPassedQuery, QueryCreationError>
869                  where F: Facade
870    {
871        if conservative {
872            if let Ok(q) = RawQuery::new(facade, QueryType::AnySamplesPassedConservative) {
873                return Ok(AnySamplesPassedQuery { query: q });
874            }
875        }
876
877        if let Ok(q) = RawQuery::new(facade, QueryType::AnySamplesPassed) {
878            Ok(AnySamplesPassedQuery { query: q })
879        } else if let Ok(q) = RawQuery::new(facade, QueryType::SamplesPassed) {
880            Ok(AnySamplesPassedQuery { query: q })
881        } else {
882            Err(QueryCreationError::NotSupported)
883        }
884    }
885}
886
887impl_helper!(AnySamplesPassedQuery, bool, get_bool);
888
889/// Query that allows you to know the number of primitives generated by the geometry shader.
890/// Will stay at `0` if you use it without any active geometry shader.
891#[derive(Debug)]
892pub struct PrimitivesGeneratedQuery {
893    query: RawQuery,
894}
895
896impl PrimitivesGeneratedQuery {
897    /// Builds a new query.
898    #[inline]
899    pub fn new<F: ?Sized>(facade: &F) -> Result<PrimitivesGeneratedQuery, QueryCreationError>
900                  where F: Facade
901    {
902        RawQuery::new(facade, QueryType::PrimitivesGenerated)
903                                                    .map(|q| PrimitivesGeneratedQuery { query: q })
904    }
905}
906
907impl_helper!(PrimitivesGeneratedQuery, u32, get_u32);
908
909/// Query that allows you to know the number of primitives generated by transform feedback.
910#[derive(Debug)]
911pub struct TransformFeedbackPrimitivesWrittenQuery {
912    query: RawQuery,
913}
914
915impl TransformFeedbackPrimitivesWrittenQuery {
916    /// Builds a new query.
917    #[inline]
918    pub fn new<F: ?Sized>(facade: &F) -> Result<TransformFeedbackPrimitivesWrittenQuery, QueryCreationError>
919                  where F: Facade
920    {
921        RawQuery::new(facade, QueryType::TransformFeedbackPrimitivesWritten)
922                                     .map(|q| TransformFeedbackPrimitivesWrittenQuery { query: q })
923    }
924}
925
926impl_helper!(TransformFeedbackPrimitivesWrittenQuery, u32, get_u32);