1 // Copyright (c) 2016 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 //! Gather information about rendering, held in query pools. 11 //! 12 //! In Vulkan, queries are not created individually. Instead you manipulate **query pools**, which 13 //! represent a collection of queries. Whenever you use a query, you have to specify both the query 14 //! pool and the slot id within that query pool. 15 16 use crate::check_errors; 17 use crate::device::Device; 18 use crate::device::DeviceOwned; 19 use crate::DeviceSize; 20 use crate::Error; 21 use crate::OomError; 22 use crate::Success; 23 use crate::VulkanObject; 24 use std::error; 25 use std::ffi::c_void; 26 use std::fmt; 27 use std::mem::MaybeUninit; 28 use std::ops::Range; 29 use std::ptr; 30 use std::sync::Arc; 31 32 /// A collection of one or more queries of a particular type. 33 #[derive(Debug)] 34 pub struct QueryPool { 35 pool: ash::vk::QueryPool, 36 device: Arc<Device>, 37 num_slots: u32, 38 ty: QueryType, 39 } 40 41 impl QueryPool { 42 /// Builds a new query pool. new( device: Arc<Device>, ty: QueryType, num_slots: u32, ) -> Result<QueryPool, QueryPoolCreationError>43 pub fn new( 44 device: Arc<Device>, 45 ty: QueryType, 46 num_slots: u32, 47 ) -> Result<QueryPool, QueryPoolCreationError> { 48 let statistics = match ty { 49 QueryType::PipelineStatistics(flags) => { 50 if !device.enabled_features().pipeline_statistics_query { 51 return Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled); 52 } 53 54 flags.into() 55 } 56 QueryType::Occlusion | QueryType::Timestamp => { 57 ash::vk::QueryPipelineStatisticFlags::empty() 58 } 59 }; 60 61 let pool = unsafe { 62 let infos = ash::vk::QueryPoolCreateInfo { 63 flags: ash::vk::QueryPoolCreateFlags::empty(), 64 query_type: ty.into(), 65 query_count: num_slots, 66 pipeline_statistics: statistics, 67 ..Default::default() 68 }; 69 70 let mut output = MaybeUninit::uninit(); 71 let fns = device.fns(); 72 check_errors(fns.v1_0.create_query_pool( 73 device.internal_object(), 74 &infos, 75 ptr::null(), 76 output.as_mut_ptr(), 77 ))?; 78 output.assume_init() 79 }; 80 81 Ok(QueryPool { 82 pool, 83 device, 84 num_slots, 85 ty, 86 }) 87 } 88 89 /// Returns the [`QueryType`] that this query pool was created with. 90 #[inline] ty(&self) -> QueryType91 pub fn ty(&self) -> QueryType { 92 self.ty 93 } 94 95 /// Returns the number of query slots of this query pool. 96 #[inline] num_slots(&self) -> u3297 pub fn num_slots(&self) -> u32 { 98 self.num_slots 99 } 100 101 /// Returns a reference to a single query slot, or `None` if the index is out of range. 102 #[inline] query(&self, index: u32) -> Option<Query>103 pub fn query(&self, index: u32) -> Option<Query> { 104 if index < self.num_slots() { 105 Some(Query { pool: self, index }) 106 } else { 107 None 108 } 109 } 110 111 /// Returns a reference to a range of queries, or `None` if out of range. 112 /// 113 /// # Panic 114 /// 115 /// Panics if the range is empty. 116 #[inline] queries_range(&self, range: Range<u32>) -> Option<QueriesRange>117 pub fn queries_range(&self, range: Range<u32>) -> Option<QueriesRange> { 118 assert!(!range.is_empty()); 119 120 if range.end <= self.num_slots() { 121 Some(QueriesRange { pool: self, range }) 122 } else { 123 None 124 } 125 } 126 } 127 128 unsafe impl VulkanObject for QueryPool { 129 type Object = ash::vk::QueryPool; 130 131 #[inline] internal_object(&self) -> ash::vk::QueryPool132 fn internal_object(&self) -> ash::vk::QueryPool { 133 self.pool 134 } 135 } 136 137 unsafe impl DeviceOwned for QueryPool { 138 #[inline] device(&self) -> &Arc<Device>139 fn device(&self) -> &Arc<Device> { 140 &self.device 141 } 142 } 143 144 impl Drop for QueryPool { 145 #[inline] drop(&mut self)146 fn drop(&mut self) { 147 unsafe { 148 let fns = self.device.fns(); 149 fns.v1_0 150 .destroy_query_pool(self.device.internal_object(), self.pool, ptr::null()); 151 } 152 } 153 } 154 155 /// Error that can happen when creating a query pool. 156 #[derive(Clone, Debug, PartialEq, Eq)] 157 pub enum QueryPoolCreationError { 158 /// Not enough memory. 159 OomError(OomError), 160 /// A pipeline statistics pool was requested but the corresponding feature wasn't enabled. 161 PipelineStatisticsQueryFeatureNotEnabled, 162 } 163 164 impl error::Error for QueryPoolCreationError { 165 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>166 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 167 match *self { 168 QueryPoolCreationError::OomError(ref err) => Some(err), 169 _ => None, 170 } 171 } 172 } 173 174 impl fmt::Display for QueryPoolCreationError { 175 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>176 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 177 write!( 178 fmt, 179 "{}", 180 match *self { 181 QueryPoolCreationError::OomError(_) => "not enough memory available", 182 QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled => { 183 "a pipeline statistics pool was requested but the corresponding feature \ 184 wasn't enabled" 185 } 186 } 187 ) 188 } 189 } 190 191 impl From<OomError> for QueryPoolCreationError { 192 #[inline] from(err: OomError) -> QueryPoolCreationError193 fn from(err: OomError) -> QueryPoolCreationError { 194 QueryPoolCreationError::OomError(err) 195 } 196 } 197 198 impl From<Error> for QueryPoolCreationError { 199 #[inline] from(err: Error) -> QueryPoolCreationError200 fn from(err: Error) -> QueryPoolCreationError { 201 match err { 202 err @ Error::OutOfHostMemory => QueryPoolCreationError::OomError(OomError::from(err)), 203 err @ Error::OutOfDeviceMemory => QueryPoolCreationError::OomError(OomError::from(err)), 204 _ => panic!("unexpected error: {:?}", err), 205 } 206 } 207 } 208 209 /// A reference to a single query slot. 210 /// 211 /// This is created through [`QueryPool::query`]. 212 #[derive(Clone, Debug)] 213 pub struct Query<'a> { 214 pool: &'a QueryPool, 215 index: u32, 216 } 217 218 impl<'a> Query<'a> { 219 /// Returns a reference to the query pool. 220 #[inline] pool(&self) -> &'a QueryPool221 pub fn pool(&self) -> &'a QueryPool { 222 &self.pool 223 } 224 225 /// Returns the index of the query represented. 226 #[inline] index(&self) -> u32227 pub fn index(&self) -> u32 { 228 self.index 229 } 230 } 231 232 /// A reference to a range of queries. 233 /// 234 /// This is created through [`QueryPool::queries_range`]. 235 #[derive(Clone, Debug)] 236 pub struct QueriesRange<'a> { 237 pool: &'a QueryPool, 238 range: Range<u32>, 239 } 240 241 impl<'a> QueriesRange<'a> { 242 /// Returns a reference to the query pool. 243 #[inline] pool(&self) -> &'a QueryPool244 pub fn pool(&self) -> &'a QueryPool { 245 &self.pool 246 } 247 248 /// Returns the range of queries represented. 249 #[inline] range(&self) -> Range<u32>250 pub fn range(&self) -> Range<u32> { 251 self.range.clone() 252 } 253 254 /// Copies the results of this range of queries to a buffer on the CPU. 255 /// 256 /// [`self.pool().ty().result_size()`](QueryType::result_size) elements 257 /// will be written for each query in the range, plus 1 extra element per query if 258 /// [`QueryResultFlags::with_availability`] is enabled. 259 /// The provided buffer must be large enough to hold the data. 260 /// 261 /// `true` is returned if every result was available and written to the buffer. `false` 262 /// is returned if some results were not yet available; these will not be written to the buffer. 263 /// 264 /// See also [`copy_query_pool_results`](crate::command_buffer::AutoCommandBufferBuilder::copy_query_pool_results). get_results<T>( &self, destination: &mut [T], flags: QueryResultFlags, ) -> Result<bool, GetResultsError> where T: QueryResultElement,265 pub fn get_results<T>( 266 &self, 267 destination: &mut [T], 268 flags: QueryResultFlags, 269 ) -> Result<bool, GetResultsError> 270 where 271 T: QueryResultElement, 272 { 273 let stride = self.check_query_pool_results::<T>( 274 destination.as_ptr() as DeviceSize, 275 destination.len() as DeviceSize, 276 flags, 277 )?; 278 279 let result = unsafe { 280 let fns = self.pool.device.fns(); 281 check_errors(fns.v1_0.get_query_pool_results( 282 self.pool.device.internal_object(), 283 self.pool.internal_object(), 284 self.range.start, 285 self.range.end - self.range.start, 286 std::mem::size_of_val(destination), 287 destination.as_mut_ptr() as *mut c_void, 288 stride, 289 ash::vk::QueryResultFlags::from(flags) | T::FLAG, 290 ))? 291 }; 292 293 Ok(match result { 294 Success::Success => true, 295 Success::NotReady => false, 296 s => panic!("unexpected success value: {:?}", s), 297 }) 298 } 299 check_query_pool_results<T>( &self, buffer_start: DeviceSize, buffer_len: DeviceSize, flags: QueryResultFlags, ) -> Result<DeviceSize, GetResultsError> where T: QueryResultElement,300 pub(crate) fn check_query_pool_results<T>( 301 &self, 302 buffer_start: DeviceSize, 303 buffer_len: DeviceSize, 304 flags: QueryResultFlags, 305 ) -> Result<DeviceSize, GetResultsError> 306 where 307 T: QueryResultElement, 308 { 309 assert!(buffer_len > 0); 310 debug_assert!(buffer_start % std::mem::size_of::<T>() as DeviceSize == 0); 311 312 let count = self.range.end - self.range.start; 313 let per_query_len = self.pool.ty.result_size() + flags.with_availability as DeviceSize; 314 let required_len = per_query_len * count as DeviceSize; 315 316 if buffer_len < required_len { 317 return Err(GetResultsError::BufferTooSmall { 318 required_len: required_len as DeviceSize, 319 actual_len: buffer_len as DeviceSize, 320 }); 321 } 322 323 match self.pool.ty { 324 QueryType::Occlusion => (), 325 QueryType::PipelineStatistics(_) => (), 326 QueryType::Timestamp => { 327 if flags.partial { 328 return Err(GetResultsError::InvalidFlags); 329 } 330 } 331 } 332 333 Ok(per_query_len * std::mem::size_of::<T>() as DeviceSize) 334 } 335 } 336 337 /// Error that can happen when calling [`QueriesRange::get_results`]. 338 #[derive(Clone, Debug, PartialEq, Eq)] 339 pub enum GetResultsError { 340 /// The buffer is too small for the operation. 341 BufferTooSmall { 342 /// Required number of elements in the buffer. 343 required_len: DeviceSize, 344 /// Actual number of elements in the buffer. 345 actual_len: DeviceSize, 346 }, 347 /// The connection to the device has been lost. 348 DeviceLost, 349 /// The provided flags are not allowed for this type of query. 350 InvalidFlags, 351 /// Not enough memory. 352 OomError(OomError), 353 } 354 355 impl From<Error> for GetResultsError { 356 #[inline] from(err: Error) -> Self357 fn from(err: Error) -> Self { 358 match err { 359 Error::OutOfHostMemory | Error::OutOfDeviceMemory => { 360 Self::OomError(OomError::from(err)) 361 } 362 Error::DeviceLost => Self::DeviceLost, 363 _ => panic!("unexpected error: {:?}", err), 364 } 365 } 366 } 367 368 impl From<OomError> for GetResultsError { 369 #[inline] from(err: OomError) -> Self370 fn from(err: OomError) -> Self { 371 Self::OomError(err) 372 } 373 } 374 375 impl fmt::Display for GetResultsError { 376 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>377 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 378 write!( 379 fmt, 380 "{}", 381 match *self { 382 Self::BufferTooSmall { .. } => { 383 "the buffer is too small for the operation" 384 } 385 Self::DeviceLost => "the connection to the device has been lost", 386 Self::InvalidFlags => { 387 "the provided flags are not allowed for this type of query" 388 } 389 Self::OomError(_) => "not enough memory available", 390 } 391 ) 392 } 393 } 394 395 impl error::Error for GetResultsError { 396 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>397 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 398 match *self { 399 Self::OomError(ref err) => Some(err), 400 _ => None, 401 } 402 } 403 } 404 405 /// A trait for elements of buffers that can be used as a destination for query results. 406 /// 407 /// # Safety 408 /// This is implemented for `u32` and `u64`. Unless you really know what you're doing, you should 409 /// not implement this trait for any other type. 410 pub unsafe trait QueryResultElement { 411 const FLAG: ash::vk::QueryResultFlags; 412 } 413 414 unsafe impl QueryResultElement for u32 { 415 const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::empty(); 416 } 417 418 unsafe impl QueryResultElement for u64 { 419 const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::TYPE_64; 420 } 421 422 /// The type of query that a query pool should perform. 423 #[derive(Debug, Copy, Clone)] 424 pub enum QueryType { 425 /// Tracks the number of samples that pass per-fragment tests (e.g. the depth test). 426 Occlusion, 427 /// Tracks statistics on pipeline invocations and their input data. 428 PipelineStatistics(QueryPipelineStatisticFlags), 429 /// Writes timestamps at chosen points in a command buffer. 430 Timestamp, 431 } 432 433 impl QueryType { 434 /// Returns the number of [`QueryResultElement`]s that are needed to hold the result of a 435 /// single query of this type. 436 /// 437 /// - For `Occlusion` and `Timestamp` queries, this returns 1. 438 /// - For `PipelineStatistics` queries, this returns the number of statistics flags enabled. 439 /// 440 /// If the results are retrieved with [`QueryResultFlags::with_availability`] enabled, then 441 /// an additional element is required per query. 442 #[inline] result_size(&self) -> DeviceSize443 pub const fn result_size(&self) -> DeviceSize { 444 match self { 445 Self::Occlusion | Self::Timestamp => 1, 446 Self::PipelineStatistics(flags) => flags.count(), 447 } 448 } 449 } 450 451 impl From<QueryType> for ash::vk::QueryType { 452 #[inline] from(value: QueryType) -> Self453 fn from(value: QueryType) -> Self { 454 match value { 455 QueryType::Occlusion => ash::vk::QueryType::OCCLUSION, 456 QueryType::PipelineStatistics(_) => ash::vk::QueryType::PIPELINE_STATISTICS, 457 QueryType::Timestamp => ash::vk::QueryType::TIMESTAMP, 458 } 459 } 460 } 461 462 /// Flags that control how a query is to be executed. 463 #[derive(Clone, Copy, Debug, Default)] 464 pub struct QueryControlFlags { 465 /// For occlusion queries, specifies that the result must reflect the exact number of 466 /// tests passed. If not enabled, the query may return a result of 1 even if more fragments 467 /// passed the test. 468 pub precise: bool, 469 } 470 471 impl From<QueryControlFlags> for ash::vk::QueryControlFlags { 472 #[inline] from(value: QueryControlFlags) -> Self473 fn from(value: QueryControlFlags) -> Self { 474 let mut result = ash::vk::QueryControlFlags::empty(); 475 if value.precise { 476 result |= ash::vk::QueryControlFlags::PRECISE; 477 } 478 result 479 } 480 } 481 482 /// For pipeline statistics queries, the statistics that should be gathered. 483 #[derive(Clone, Copy, Debug, Default)] 484 pub struct QueryPipelineStatisticFlags { 485 /// Count the number of vertices processed by the input assembly. 486 pub input_assembly_vertices: bool, 487 /// Count the number of primitives processed by the input assembly. 488 pub input_assembly_primitives: bool, 489 /// Count the number of times a vertex shader is invoked. 490 pub vertex_shader_invocations: bool, 491 /// Count the number of times a geometry shader is invoked. 492 pub geometry_shader_invocations: bool, 493 /// Count the number of primitives generated by geometry shaders. 494 pub geometry_shader_primitives: bool, 495 /// Count the number of times the clipping stage is invoked on a primitive. 496 pub clipping_invocations: bool, 497 /// Count the number of primitives that are output by the clipping stage. 498 pub clipping_primitives: bool, 499 /// Count the number of times a fragment shader is invoked. 500 pub fragment_shader_invocations: bool, 501 /// Count the number of patches processed by a tessellation control shader. 502 pub tessellation_control_shader_patches: bool, 503 /// Count the number of times a tessellation evaluation shader is invoked. 504 pub tessellation_evaluation_shader_invocations: bool, 505 /// Count the number of times a compute shader is invoked. 506 pub compute_shader_invocations: bool, 507 } 508 509 impl QueryPipelineStatisticFlags { 510 #[inline] none() -> QueryPipelineStatisticFlags511 pub fn none() -> QueryPipelineStatisticFlags { 512 QueryPipelineStatisticFlags { 513 input_assembly_vertices: false, 514 input_assembly_primitives: false, 515 vertex_shader_invocations: false, 516 geometry_shader_invocations: false, 517 geometry_shader_primitives: false, 518 clipping_invocations: false, 519 clipping_primitives: false, 520 fragment_shader_invocations: false, 521 tessellation_control_shader_patches: false, 522 tessellation_evaluation_shader_invocations: false, 523 compute_shader_invocations: false, 524 } 525 } 526 527 /// Returns the number of flags that are set to `true`. 528 #[inline] count(&self) -> DeviceSize529 pub const fn count(&self) -> DeviceSize { 530 let &Self { 531 input_assembly_vertices, 532 input_assembly_primitives, 533 vertex_shader_invocations, 534 geometry_shader_invocations, 535 geometry_shader_primitives, 536 clipping_invocations, 537 clipping_primitives, 538 fragment_shader_invocations, 539 tessellation_control_shader_patches, 540 tessellation_evaluation_shader_invocations, 541 compute_shader_invocations, 542 } = self; 543 input_assembly_vertices as DeviceSize 544 + input_assembly_primitives as DeviceSize 545 + vertex_shader_invocations as DeviceSize 546 + geometry_shader_invocations as DeviceSize 547 + geometry_shader_primitives as DeviceSize 548 + clipping_invocations as DeviceSize 549 + clipping_primitives as DeviceSize 550 + fragment_shader_invocations as DeviceSize 551 + tessellation_control_shader_patches as DeviceSize 552 + tessellation_evaluation_shader_invocations as DeviceSize 553 + compute_shader_invocations as DeviceSize 554 } 555 556 /// Returns `true` if any flags referring to compute operations are set to `true`. 557 #[inline] is_compute(&self) -> bool558 pub const fn is_compute(&self) -> bool { 559 let &Self { 560 compute_shader_invocations, 561 .. 562 } = self; 563 compute_shader_invocations 564 } 565 566 /// Returns `true` if any flags referring to graphics operations are set to `true`. 567 #[inline] is_graphics(&self) -> bool568 pub const fn is_graphics(&self) -> bool { 569 let &Self { 570 input_assembly_vertices, 571 input_assembly_primitives, 572 vertex_shader_invocations, 573 geometry_shader_invocations, 574 geometry_shader_primitives, 575 clipping_invocations, 576 clipping_primitives, 577 fragment_shader_invocations, 578 tessellation_control_shader_patches, 579 tessellation_evaluation_shader_invocations, 580 .. 581 } = self; 582 input_assembly_vertices 583 || input_assembly_primitives 584 || vertex_shader_invocations 585 || geometry_shader_invocations 586 || geometry_shader_primitives 587 || clipping_invocations 588 || clipping_primitives 589 || fragment_shader_invocations 590 || tessellation_control_shader_patches 591 || tessellation_evaluation_shader_invocations 592 } 593 } 594 595 impl From<QueryPipelineStatisticFlags> for ash::vk::QueryPipelineStatisticFlags { from(value: QueryPipelineStatisticFlags) -> ash::vk::QueryPipelineStatisticFlags596 fn from(value: QueryPipelineStatisticFlags) -> ash::vk::QueryPipelineStatisticFlags { 597 let mut result = ash::vk::QueryPipelineStatisticFlags::empty(); 598 if value.input_assembly_vertices { 599 result |= ash::vk::QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES; 600 } 601 if value.input_assembly_primitives { 602 result |= ash::vk::QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES; 603 } 604 if value.vertex_shader_invocations { 605 result |= ash::vk::QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS; 606 } 607 if value.geometry_shader_invocations { 608 result |= ash::vk::QueryPipelineStatisticFlags::GEOMETRY_SHADER_INVOCATIONS; 609 } 610 if value.geometry_shader_primitives { 611 result |= ash::vk::QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES; 612 } 613 if value.clipping_invocations { 614 result |= ash::vk::QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS; 615 } 616 if value.clipping_primitives { 617 result |= ash::vk::QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES; 618 } 619 if value.fragment_shader_invocations { 620 result |= ash::vk::QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS; 621 } 622 if value.tessellation_control_shader_patches { 623 result |= ash::vk::QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES; 624 } 625 if value.tessellation_evaluation_shader_invocations { 626 result |= 627 ash::vk::QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS; 628 } 629 if value.compute_shader_invocations { 630 result |= ash::vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS; 631 } 632 result 633 } 634 } 635 636 /// Flags to control how the results of a query should be retrieved. 637 /// 638 /// `VK_QUERY_RESULT_64_BIT` is not included, as it is determined automatically via the 639 /// [`QueryResultElement`] trait. 640 #[derive(Clone, Copy, Debug, Default)] 641 pub struct QueryResultFlags { 642 /// Wait for the results to become available before writing the results. 643 pub wait: bool, 644 /// Write an additional element to the end of each query's results, indicating the availability 645 /// of the results: 646 /// - Nonzero: The results are available, and have been written to the element(s) preceding. 647 /// - Zero: The results are not yet available, and have not been written. 648 pub with_availability: bool, 649 /// Allow writing partial results to the buffer, instead of waiting until they are fully 650 /// available. 651 pub partial: bool, 652 } 653 654 impl From<QueryResultFlags> for ash::vk::QueryResultFlags { 655 #[inline] from(value: QueryResultFlags) -> Self656 fn from(value: QueryResultFlags) -> Self { 657 let mut result = ash::vk::QueryResultFlags::empty(); 658 if value.wait { 659 result |= ash::vk::QueryResultFlags::WAIT; 660 } 661 if value.with_availability { 662 result |= ash::vk::QueryResultFlags::WITH_AVAILABILITY; 663 } 664 if value.partial { 665 result |= ash::vk::QueryResultFlags::PARTIAL; 666 } 667 result 668 } 669 } 670 671 #[cfg(test)] 672 mod tests { 673 use crate::query::QueryPipelineStatisticFlags; 674 use crate::query::QueryPool; 675 use crate::query::QueryPoolCreationError; 676 use crate::query::QueryType; 677 678 #[test] pipeline_statistics_feature()679 fn pipeline_statistics_feature() { 680 let (device, _) = gfx_dev_and_queue!(); 681 682 let ty = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::none()); 683 match QueryPool::new(device, ty, 256) { 684 Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (), 685 _ => panic!(), 686 }; 687 } 688 } 689