This is page 4 of 9. Use http://codebase.md/mehmetoguzderin/shaderc-vkrunner-mcp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .devcontainer │ ├── devcontainer.json │ ├── docker-compose.yml │ └── Dockerfile ├── .gitattributes ├── .github │ └── workflows │ └── build-push-image.yml ├── .gitignore ├── .vscode │ └── mcp.json ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.adoc ├── shaderc-vkrunner-mcp.jpg ├── src │ └── main.rs └── vkrunner ├── .editorconfig ├── .gitignore ├── .gitlab-ci.yml ├── build.rs ├── Cargo.toml ├── COPYING ├── examples │ ├── compute-shader.shader_test │ ├── cooperative-matrix.shader_test │ ├── depth-buffer.shader_test │ ├── desc_set_and_binding.shader_test │ ├── entrypoint.shader_test │ ├── float-framebuffer.shader_test │ ├── frexp.shader_test │ ├── geometry.shader_test │ ├── indices.shader_test │ ├── layouts.shader_test │ ├── properties.shader_test │ ├── push-constants.shader_test │ ├── require-subgroup-size.shader_test │ ├── row-major.shader_test │ ├── spirv.shader_test │ ├── ssbo.shader_test │ ├── tolerance.shader_test │ ├── tricolore.shader_test │ ├── ubo.shader_test │ ├── vertex-data-piglit.shader_test │ └── vertex-data.shader_test ├── include │ ├── vk_video │ │ ├── vulkan_video_codec_av1std_decode.h │ │ ├── vulkan_video_codec_av1std_encode.h │ │ ├── vulkan_video_codec_av1std.h │ │ ├── vulkan_video_codec_h264std_decode.h │ │ ├── vulkan_video_codec_h264std_encode.h │ │ ├── vulkan_video_codec_h264std.h │ │ ├── vulkan_video_codec_h265std_decode.h │ │ ├── vulkan_video_codec_h265std_encode.h │ │ ├── vulkan_video_codec_h265std.h │ │ └── vulkan_video_codecs_common.h │ └── vulkan │ ├── vk_platform.h │ ├── vulkan_core.h │ └── vulkan.h ├── precompile-script.py ├── README.md ├── scripts │ └── update-vulkan.sh ├── src │ └── main.rs ├── test-build.sh └── vkrunner ├── allocate_store.rs ├── buffer.rs ├── compiler │ └── fake_process.rs ├── compiler.rs ├── config.rs ├── context.rs ├── enum_table.rs ├── env_var_test.rs ├── executor.rs ├── fake_vulkan.rs ├── features.rs ├── flush_memory.rs ├── format_table.rs ├── format.rs ├── half_float.rs ├── hex.rs ├── inspect.rs ├── lib.rs ├── logger.rs ├── make-enums.py ├── make-features.py ├── make-formats.py ├── make-pipeline-key-data.py ├── make-vulkan-funcs-data.py ├── parse_num.rs ├── pipeline_key_data.rs ├── pipeline_key.rs ├── pipeline_set.rs ├── requirements.rs ├── result.rs ├── script.rs ├── shader_stage.rs ├── slot.rs ├── small_float.rs ├── source.rs ├── stream.rs ├── temp_file.rs ├── tester.rs ├── tolerance.rs ├── util.rs ├── vbo.rs ├── vk.rs ├── vulkan_funcs_data.rs ├── vulkan_funcs.rs ├── window_format.rs └── window.rs ``` # Files -------------------------------------------------------------------------------- /vkrunner/vkrunner/pipeline_key.rs: -------------------------------------------------------------------------------- ```rust 1 | // vkrunner 2 | // 3 | // Copyright (C) 2018 Intel Corporation 4 | // Copyright 2023 Neil Roberts 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, including without limitation 9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | // and/or sell copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice (including the next 14 | // paragraph) shall be included in all copies or substantial portions of the 15 | // Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | use crate::shader_stage; 26 | use crate::vk; 27 | use crate::parse_num; 28 | use crate::hex; 29 | use crate::util; 30 | use std::fmt; 31 | use std::mem; 32 | 33 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 34 | pub enum Type { 35 | Graphics, 36 | Compute, 37 | } 38 | 39 | /// Notes whether the pipeline will be used to draw a rectangle or 40 | /// whether it will use the data in the `[vertex data]` section of the 41 | /// script. 42 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 43 | pub enum Source { 44 | Rectangle, 45 | VertexData, 46 | } 47 | 48 | /// The failure code returned by [set](Key::set) 49 | #[derive(Copy, Clone, Debug)] 50 | pub enum SetPropertyError<'a> { 51 | /// The property was not recognised by VkRunner 52 | NotFound { property: &'a str }, 53 | /// The property was recognised but the value string was not in a 54 | /// valid format 55 | InvalidValue { value: &'a str }, 56 | } 57 | 58 | impl<'a> fmt::Display for SetPropertyError<'a> { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | match self { 61 | SetPropertyError::NotFound { property } => { 62 | write!(f, "Unknown property: {}", property) 63 | }, 64 | SetPropertyError::InvalidValue { value } => { 65 | write!(f, "Invalid value: {}", value) 66 | }, 67 | } 68 | } 69 | } 70 | 71 | enum PropertyType { 72 | Bool, 73 | Int, 74 | Float, 75 | } 76 | 77 | struct Property { 78 | prop_type: PropertyType, 79 | num: usize, 80 | name: &'static str, 81 | } 82 | 83 | include!{"pipeline_key_data.rs"} 84 | 85 | struct EnumValue { 86 | name: &'static str, 87 | value: i32, 88 | } 89 | 90 | include!{"enum_table.rs"} 91 | 92 | /// A set of properties that can be used to create a VkPipeline. The 93 | /// intention is that this will work as a key so that the program can 94 | /// detect if the same state is used multiple times so it can reuse 95 | /// the same key. 96 | #[derive(Clone)] 97 | pub struct Key { 98 | pipeline_type: Type, 99 | source: Source, 100 | 101 | entrypoints: [Option<String>; shader_stage::N_STAGES], 102 | 103 | bool_properties: [bool; N_BOOL_PROPERTIES], 104 | int_properties: [i32; N_INT_PROPERTIES], 105 | float_properties: [f32; N_FLOAT_PROPERTIES], 106 | } 107 | 108 | impl Key { 109 | pub fn set_pipeline_type(&mut self, pipeline_type: Type) { 110 | self.pipeline_type = pipeline_type; 111 | } 112 | 113 | pub fn pipeline_type(&self) -> Type { 114 | self.pipeline_type 115 | } 116 | 117 | pub fn set_source(&mut self, source: Source) { 118 | self.source = source; 119 | } 120 | 121 | pub fn source(&self) -> Source { 122 | self.source 123 | } 124 | 125 | pub fn set_topology(&mut self, topology: vk::VkPrimitiveTopology) { 126 | self.int_properties[TOPOLOGY_PROP_NUM] = topology as i32; 127 | } 128 | 129 | pub fn set_patch_control_points(&mut self, patch_control_points: u32) { 130 | self.int_properties[PATCH_CONTROL_POINTS_PROP_NUM] = 131 | patch_control_points as i32; 132 | } 133 | 134 | pub fn set_entrypoint( 135 | &mut self, 136 | stage: shader_stage::Stage, 137 | entrypoint: String, 138 | ) { 139 | self.entrypoints[stage as usize] = Some(entrypoint); 140 | } 141 | 142 | pub fn entrypoint(&self, stage: shader_stage::Stage) -> &str { 143 | match &self.entrypoints[stage as usize] { 144 | Some(s) => &s[..], 145 | None => "main", 146 | } 147 | } 148 | 149 | fn find_prop( 150 | property: &str 151 | ) -> Result<&'static Property, SetPropertyError> { 152 | PROPERTIES.binary_search_by(|p| p.name.cmp(property)) 153 | .and_then(|pos| Ok(&PROPERTIES[pos])) 154 | .or_else(|_| Err(SetPropertyError::NotFound { property })) 155 | } 156 | 157 | fn set_bool<'a>( 158 | &mut self, 159 | prop: &Property, 160 | value: &'a str, 161 | ) -> Result<(), SetPropertyError<'a>> { 162 | let value = if value == "true" { 163 | true 164 | } else if value == "false" { 165 | false 166 | } else { 167 | match parse_num::parse_i32(value) { 168 | Ok((v, tail)) if tail.is_empty() => v != 0, 169 | _ => return Err(SetPropertyError::InvalidValue { value }), 170 | } 171 | }; 172 | 173 | self.bool_properties[prop.num] = value; 174 | 175 | Ok(()) 176 | } 177 | 178 | // Looks up the given enum name in ENUM_VALUES using a binary 179 | // search. Any trailing characters that can’t be part of an enum 180 | // name are cut. If successful it returns the enum value and a 181 | // slice containing the part of the name that was cut. Otherwise 182 | // returns None. 183 | fn lookup_enum(name: &str) -> Option<(i32, &str)> { 184 | let length = name 185 | .chars() 186 | .take_while(|&c| c.is_alphanumeric() || c == '_') 187 | .count(); 188 | 189 | let part = &name[0..length]; 190 | 191 | ENUM_VALUES.binary_search_by(|enum_value| enum_value.name.cmp(part)) 192 | .ok() 193 | .and_then(|pos| Some((ENUM_VALUES[pos].value, &name[length..]))) 194 | .or_else(|| None) 195 | } 196 | 197 | fn set_int<'a>( 198 | &mut self, 199 | prop: &Property, 200 | value: &'a str, 201 | ) -> Result<(), SetPropertyError<'a>> { 202 | let mut value_part = value; 203 | let mut num_value = 0i32; 204 | 205 | loop { 206 | value_part = value_part.trim_start(); 207 | 208 | if let Ok((v, tail)) = parse_num::parse_i32(value_part) { 209 | num_value |= v; 210 | value_part = tail; 211 | } else if let Some((v, tail)) = Key::lookup_enum(value_part) { 212 | num_value |= v; 213 | value_part = tail; 214 | } else { 215 | break Err(SetPropertyError::InvalidValue { value }); 216 | } 217 | 218 | value_part = value_part.trim_start(); 219 | 220 | if value_part.is_empty() { 221 | self.int_properties[prop.num] = num_value; 222 | break Ok(()); 223 | } 224 | 225 | // If there’s anything left after the number it must be 226 | // the ‘|’ operator 227 | if !value_part.starts_with('|') { 228 | break Err(SetPropertyError::InvalidValue { value }); 229 | } 230 | 231 | // Skip the ‘|’ character 232 | value_part = &value_part[1..]; 233 | } 234 | } 235 | 236 | fn set_float<'a>( 237 | &mut self, 238 | prop: &Property, 239 | value: &'a str, 240 | ) -> Result<(), SetPropertyError<'a>> { 241 | match hex::parse_f32(value) { 242 | Ok((v, tail)) if tail.is_empty() => { 243 | self.float_properties[prop.num] = v; 244 | Ok(()) 245 | }, 246 | _ => Err(SetPropertyError::InvalidValue { value }), 247 | } 248 | } 249 | 250 | /// Set a property on the pipeline key. The property name is one 251 | /// of the members of any of the structs pointed to by 252 | /// `VkGraphicsPipelineCreateInfo`. For example, if `prop_name` is 253 | /// `"polygonMode"` then it will set the `polygonMode` field of 254 | /// the `VkPipelineRasterizationStateCreateInfo` struct pointed to 255 | /// by the `VkGraphicsPipelineCreateInfo` struct. 256 | /// 257 | /// The `value` will be interpreted depending on the type of the 258 | /// property. It will be one of the following three basic types: 259 | /// 260 | /// bool: The value can either be `true`, `false` or an integer 261 | /// value. If it’s an integer the bool will be true if the value 262 | /// is non-zero. 263 | /// 264 | /// int: The value can either be an integer value or one of the 265 | /// enum names used by the properties. You can also use the `|` 266 | /// operator to bitwise or values. This is useful for setting 267 | /// flags. For example `VK_COLOR_COMPONENT_R_BIT | 268 | /// VK_COLOR_COMPONENT_G_BIT` can be used as a value to set the 269 | /// `colorWriteMask` property. 270 | /// 271 | /// float: The value will be interperted as a floating-point value 272 | /// using [hex::parse_f32]. 273 | pub fn set<'a>( 274 | &mut self, 275 | prop_name: &'a str, 276 | value: &'a str 277 | ) -> Result<(), SetPropertyError<'a>> { 278 | let prop = Key::find_prop(prop_name)?; 279 | 280 | let value = value.trim(); 281 | 282 | match prop.prop_type { 283 | PropertyType::Bool => self.set_bool(prop, value), 284 | PropertyType::Int => self.set_int(prop, value), 285 | PropertyType::Float => self.set_float(prop, value), 286 | } 287 | } 288 | 289 | fn alloc_struct_size( 290 | buf: &mut Vec<u8>, 291 | struct_type: vk::VkStructureType, 292 | size: usize, 293 | align: usize 294 | ) -> usize { 295 | let offset = util::align(buf.len(), align); 296 | buf.resize(offset + size, 0); 297 | buf[offset..offset + mem::size_of::<vk::VkStructureType>()] 298 | .copy_from_slice(&struct_type.to_ne_bytes()); 299 | offset 300 | } 301 | 302 | fn alloc_struct<T>( 303 | buf: &mut Vec<u8>, 304 | struct_type: vk::VkStructureType, 305 | ) -> usize { 306 | Key::alloc_struct_size( 307 | buf, 308 | struct_type, 309 | mem::size_of::<T>(), 310 | mem::align_of::<T>(), 311 | ) 312 | } 313 | 314 | fn alloc_create_info() -> Box<[u8]> { 315 | let mut buf = Vec::new(); 316 | 317 | // Allocate all of the structures before setting the pointers 318 | // because the addresses will change when the vec grows 319 | let base_offset = 320 | Key::alloc_struct::<vk::VkGraphicsPipelineCreateInfo>( 321 | &mut buf, 322 | vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 323 | ); 324 | 325 | assert_eq!(base_offset, 0); 326 | 327 | let input_assembly = 328 | Key::alloc_struct::<vk::VkPipelineInputAssemblyStateCreateInfo>( 329 | &mut buf, 330 | vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 331 | ); 332 | let tessellation = 333 | Key::alloc_struct::<vk::VkPipelineTessellationStateCreateInfo>( 334 | &mut buf, 335 | vk::VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 336 | ); 337 | let rasterization = 338 | Key::alloc_struct::<vk::VkPipelineRasterizationStateCreateInfo>( 339 | &mut buf, 340 | vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 341 | ); 342 | let color_blend = 343 | Key::alloc_struct::<vk::VkPipelineColorBlendStateCreateInfo>( 344 | &mut buf, 345 | vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 346 | ); 347 | let color_blend_attachment = 348 | Key::alloc_struct::<vk::VkPipelineColorBlendAttachmentState>( 349 | &mut buf, 350 | 0, // no struture type 351 | ); 352 | let depth_stencil = 353 | Key::alloc_struct::<vk::VkPipelineDepthStencilStateCreateInfo>( 354 | &mut buf, 355 | vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 356 | ); 357 | 358 | let mut buf = buf.into_boxed_slice(); 359 | 360 | let create_info = unsafe { 361 | &mut *(buf.as_mut_ptr() as *mut vk::VkGraphicsPipelineCreateInfo) 362 | }; 363 | 364 | let base_ptr = buf.as_ptr() as *const u8; 365 | 366 | // SAFETY: The pointer adds should all be within the single 367 | // allocated buf 368 | unsafe { 369 | create_info.pInputAssemblyState = 370 | base_ptr.add(input_assembly).cast(); 371 | create_info.pTessellationState = 372 | base_ptr.add(tessellation).cast(); 373 | create_info.pRasterizationState = 374 | base_ptr.add(rasterization).cast(); 375 | create_info.pColorBlendState = 376 | base_ptr.add(color_blend).cast(); 377 | create_info.pDepthStencilState = 378 | base_ptr.add(depth_stencil).cast(); 379 | 380 | // We need to transmute to get rid of the const 381 | let color_blend: &mut vk::VkPipelineColorBlendStateCreateInfo = 382 | mem::transmute(create_info.pColorBlendState); 383 | color_blend.pAttachments = 384 | base_ptr.add(color_blend_attachment).cast(); 385 | } 386 | 387 | buf 388 | } 389 | 390 | /// Allocates a `VkGraphicsPipelineCreateInfo` struct inside a 391 | /// boxed u8 slice along with the 392 | /// `VkPipelineInputAssemblyStateCreateInfo`, 393 | /// `VkPipelineTessellationStateCreateInfo`, 394 | /// `VkPipelineRasterizationStateCreateInfo`, 395 | /// `VkPipelineColorBlendStateCreateInfo`, 396 | /// `VkPipelineColorBlendAttachmentState` and 397 | /// `VkPipelineDepthStencilStateCreateInfo` structs that it points 398 | /// to. The properties from the pipeline key are filled in and the 399 | /// `sType` fields are given the appropriate values. All other 400 | /// fields are initialised to zero. The structs need to be in a 401 | /// box because they contain pointers to each other which means 402 | /// the structs won’t work correctly if the array is moved to a 403 | /// different address. The `VkGraphicsPipelineCreateInfo` is at 404 | /// the start of the array and the other structs can be found by 405 | /// following the internal pointers. 406 | pub fn to_create_info(&self) -> Box<[u8]> { 407 | let mut buf = Key::alloc_create_info(); 408 | let create_info = unsafe { 409 | &mut *(buf.as_mut_ptr() as *mut vk::VkGraphicsPipelineCreateInfo) 410 | }; 411 | unsafe { 412 | mem::transmute::<_, &mut vk::VkPipelineColorBlendStateCreateInfo>( 413 | create_info.pColorBlendState 414 | ).attachmentCount = 1; 415 | } 416 | copy_properties_to_create_info(self, create_info); 417 | buf 418 | } 419 | } 420 | 421 | impl PartialEq for Key { 422 | fn eq(&self, other: &Key) -> bool { 423 | if self.pipeline_type != other.pipeline_type { 424 | return false; 425 | } 426 | 427 | match self.pipeline_type { 428 | Type::Graphics => { 429 | if self.source != other.source 430 | || self.bool_properties != other.bool_properties 431 | || self.int_properties != other.int_properties 432 | || self.float_properties != other.float_properties 433 | { 434 | return false; 435 | } 436 | 437 | // Check the entrypoints for all of the stages except 438 | // the compute stage because that doesn’t affect 439 | // pipelines used for graphics. 440 | for i in 0..shader_stage::N_STAGES { 441 | if i == shader_stage::Stage::Compute as usize { 442 | continue; 443 | } 444 | if self.entrypoints[i].ne(&other.entrypoints[i]) { 445 | return false; 446 | } 447 | } 448 | 449 | true 450 | }, 451 | Type::Compute => { 452 | // All of the properties only have an effect when the 453 | // pipeline is used for graphics so the only thing we 454 | // care about is the compute entrypoint. 455 | self.entrypoints[shader_stage::Stage::Compute as usize].eq( 456 | &other.entrypoints[shader_stage::Stage::Compute as usize] 457 | ) 458 | }, 459 | } 460 | } 461 | } 462 | 463 | // Custom debug implementation for Key that reports the properties 464 | // using the property names instead of confusing arrays. 465 | impl fmt::Debug for Key { 466 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 467 | write!( 468 | f, 469 | "Key {{ pipeline_type: {:?}, \ 470 | source: {:?}, \ 471 | entrypoints: {:?}", 472 | self.pipeline_type, 473 | self.source, 474 | &self.entrypoints, 475 | )?; 476 | 477 | for prop in PROPERTIES.iter() { 478 | write!(f, ", {}: ", prop.name)?; 479 | 480 | match prop.prop_type { 481 | PropertyType::Bool => { 482 | write!(f, "{}", self.bool_properties[prop.num])?; 483 | }, 484 | PropertyType::Int => { 485 | write!(f, "{}", self.int_properties[prop.num])?; 486 | }, 487 | PropertyType::Float => { 488 | write!(f, "{}", self.float_properties[prop.num])?; 489 | }, 490 | } 491 | } 492 | 493 | write!(f, " }}") 494 | } 495 | } 496 | 497 | #[cfg(test)] 498 | mod test { 499 | use super::*; 500 | use shader_stage::Stage; 501 | 502 | fn get_create_info_topology(key: &Key) -> vk::VkPrimitiveTopology { 503 | let s = key.to_create_info(); 504 | let create_info = unsafe { 505 | mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>( 506 | s.as_ptr() 507 | ) 508 | }; 509 | unsafe { 510 | (*create_info.pInputAssemblyState).topology 511 | } 512 | } 513 | 514 | fn get_create_info_pcp(key: &Key) -> u32 { 515 | let s = key.to_create_info(); 516 | let create_info = unsafe { 517 | mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>( 518 | s.as_ptr() 519 | ) 520 | }; 521 | unsafe { 522 | (*create_info.pTessellationState).patchControlPoints 523 | } 524 | } 525 | 526 | #[test] 527 | fn test_key_base() { 528 | let mut key = Key::default(); 529 | 530 | key.set_pipeline_type(Type::Graphics); 531 | assert_eq!(key.pipeline_type(), Type::Graphics); 532 | key.set_pipeline_type(Type::Compute); 533 | assert_eq!(key.pipeline_type(), Type::Compute); 534 | key.set_source(Source::Rectangle); 535 | assert_eq!(key.source(), Source::Rectangle); 536 | key.set_source(Source::VertexData); 537 | assert_eq!(key.source(), Source::VertexData); 538 | 539 | key.set_topology(vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST); 540 | assert_eq!( 541 | get_create_info_topology(&key), 542 | vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 543 | ); 544 | key.set_topology(vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); 545 | assert_eq!( 546 | get_create_info_topology(&key), 547 | vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 548 | ); 549 | 550 | key.set_patch_control_points(5); 551 | assert_eq!(get_create_info_pcp(&key), 5); 552 | key.set_patch_control_points(u32::MAX); 553 | assert_eq!(get_create_info_pcp(&key), u32::MAX); 554 | 555 | key.set_entrypoint(Stage::Vertex, "mystery_vortex".to_owned()); 556 | assert_eq!(key.entrypoint(Stage::Vertex), "mystery_vortex"); 557 | key.set_entrypoint(Stage::Fragment, "fraggle_rock".to_owned()); 558 | assert_eq!(key.entrypoint(Stage::Fragment), "fraggle_rock"); 559 | assert_eq!(key.entrypoint(Stage::Geometry), "main"); 560 | } 561 | 562 | #[test] 563 | fn test_all_props() { 564 | let mut key = Key::default(); 565 | 566 | // Check that setting all of the props works without returning 567 | // an error 568 | for prop in PROPERTIES.iter() { 569 | assert!(key.set(prop.name, "1").is_ok()); 570 | } 571 | } 572 | 573 | fn check_bool_prop(value: &str) -> vk::VkBool32 { 574 | let mut key = Key::default(); 575 | 576 | assert!(key.set("depthTestEnable", value).is_ok()); 577 | 578 | let s = key.to_create_info(); 579 | let create_info = unsafe { 580 | mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>( 581 | s.as_ptr() 582 | ) 583 | }; 584 | unsafe { 585 | (*create_info.pDepthStencilState).depthTestEnable 586 | } 587 | } 588 | 589 | #[test] 590 | fn test_bool_props() { 591 | assert_eq!(check_bool_prop("true"), 1); 592 | assert_eq!(check_bool_prop("false"), 0); 593 | assert_eq!(check_bool_prop(" true "), 1); 594 | assert_eq!(check_bool_prop(" false "), 0); 595 | assert_eq!(check_bool_prop("1"), 1); 596 | assert_eq!(check_bool_prop("42"), 1); 597 | assert_eq!(check_bool_prop(" 0x42 "), 1); 598 | assert_eq!(check_bool_prop("0"), 0); 599 | assert_eq!(check_bool_prop(" -0 "), 0); 600 | 601 | let e = Key::default().set("depthTestEnable", "foo").unwrap_err(); 602 | assert_eq!(e.to_string(), "Invalid value: foo"); 603 | let e = Key::default().set("stencilTestEnable", "9 foo").unwrap_err(); 604 | assert_eq!(e.to_string(), "Invalid value: 9 foo"); 605 | let e = Key::default().set("stencilTestEnable", "true fo").unwrap_err(); 606 | assert_eq!(e.to_string(), "Invalid value: true fo"); 607 | } 608 | 609 | fn check_int_prop(value: &str) -> u32 { 610 | let mut key = Key::default(); 611 | 612 | assert!(key.set("patchControlPoints", value).is_ok()); 613 | 614 | get_create_info_pcp(&key) 615 | } 616 | 617 | #[test] 618 | fn test_int_props() { 619 | assert_eq!(check_int_prop("0"), 0); 620 | assert_eq!(check_int_prop("1"), 1); 621 | assert_eq!(check_int_prop("-1"), u32::MAX); 622 | assert_eq!(check_int_prop(" 42 "), 42); 623 | assert_eq!(check_int_prop(" 8 | 1 "), 9); 624 | assert_eq!(check_int_prop("6|16|1"), 23); 625 | assert_eq!(check_int_prop("010"), 8); 626 | assert_eq!(check_int_prop("0x80|010"), 0x88); 627 | assert_eq!( 628 | check_int_prop("VK_COLOR_COMPONENT_R_BIT"), 629 | vk::VK_COLOR_COMPONENT_R_BIT, 630 | ); 631 | assert_eq!( 632 | check_int_prop( 633 | "VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT" 634 | ), 635 | vk::VK_COLOR_COMPONENT_R_BIT | vk::VK_COLOR_COMPONENT_G_BIT, 636 | ); 637 | 638 | let e = Key::default().set("patchControlPoints", "").unwrap_err(); 639 | assert_eq!(e.to_string(), "Invalid value: "); 640 | let e = Key::default().set("patchControlPoints", "9 |").unwrap_err(); 641 | assert_eq!(e.to_string(), "Invalid value: 9 |"); 642 | let e = Key::default().set("patchControlPoints", "|9").unwrap_err(); 643 | assert_eq!(e.to_string(), "Invalid value: |9"); 644 | let e = Key::default().set("patchControlPoints", "9|foo").unwrap_err(); 645 | assert_eq!(e.to_string(), "Invalid value: 9|foo"); 646 | let e = Key::default().set("patchControlPoints", "9foo").unwrap_err(); 647 | assert_eq!(e.to_string(), "Invalid value: 9foo"); 648 | 649 | let mut key = Key::default(); 650 | 651 | // Check that all enum values can be set without error 652 | for e in ENUM_VALUES.iter() { 653 | assert!(key.set("srcColorBlendFactor", e.name).is_ok()); 654 | } 655 | } 656 | 657 | fn check_float_prop(value: &str) -> f32 { 658 | let mut key = Key::default(); 659 | 660 | assert!(key.set("depthBiasClamp", value).is_ok()); 661 | 662 | let s = key.to_create_info(); 663 | let create_info = unsafe { 664 | mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>( 665 | s.as_ptr() 666 | ) 667 | }; 668 | unsafe { 669 | (*create_info.pRasterizationState).depthBiasClamp 670 | } 671 | } 672 | 673 | #[test] 674 | fn test_float_props() { 675 | assert_eq!(check_float_prop("1"), 1.0); 676 | assert_eq!(check_float_prop("-1"), -1.0); 677 | assert_eq!(check_float_prop("1.0e1"), 10.0); 678 | assert_eq!(check_float_prop(" 0x3F800000 "), 1.0); 679 | 680 | let e = Key::default().set("lineWidth", "0.3 foo").unwrap_err(); 681 | assert_eq!(e.to_string(), "Invalid value: 0.3 foo"); 682 | let e = Key::default().set("lineWidth", "foo").unwrap_err(); 683 | assert_eq!(e.to_string(), "Invalid value: foo"); 684 | } 685 | 686 | #[test] 687 | fn test_unknown_property() { 688 | let e = Key::default().set("unicornCount", "2").unwrap_err(); 689 | assert_eq!(e.to_string(), "Unknown property: unicornCount"); 690 | } 691 | 692 | #[test] 693 | fn test_struct_types() { 694 | let s = Key::default().to_create_info(); 695 | 696 | let create_info = unsafe { 697 | mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>( 698 | s.as_ptr() 699 | ) 700 | }; 701 | unsafe { 702 | assert_eq!( 703 | (*create_info.pDepthStencilState).sType, 704 | vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 705 | ); 706 | assert_eq!( 707 | (*create_info.pColorBlendState).sType, 708 | vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 709 | ); 710 | } 711 | } 712 | 713 | #[test] 714 | fn test_eq() { 715 | let mut key_a = Key::default(); 716 | let mut key_b = key_a.clone(); 717 | 718 | assert!(key_a.eq(&key_b)); 719 | 720 | key_a.set_source(Source::VertexData); 721 | assert!(!key_a.eq(&key_b)); 722 | key_b.set_source(Source::VertexData); 723 | assert!(key_a.eq(&key_b)); 724 | 725 | assert!(key_a.set("depthClampEnable", "true").is_ok()); 726 | assert!(!key_a.eq(&key_b)); 727 | assert!(key_b.set("depthClampEnable", "true").is_ok()); 728 | assert!(key_a.eq(&key_b)); 729 | 730 | assert!(key_a.set("colorWriteMask", "1").is_ok()); 731 | assert!(!key_a.eq(&key_b)); 732 | assert!(key_b.set("colorWriteMask", "1").is_ok()); 733 | assert!(key_a.eq(&key_b)); 734 | 735 | assert!(key_a.set("lineWidth", "3.0").is_ok()); 736 | assert!(!key_a.eq(&key_b)); 737 | assert!(key_b.set("lineWidth", "3.0").is_ok()); 738 | assert!(key_a.eq(&key_b)); 739 | 740 | key_a.set_entrypoint(Stage::TessEval, "durberville".to_owned()); 741 | assert!(!key_a.eq(&key_b)); 742 | key_b.set_entrypoint(Stage::TessEval, "durberville".to_owned()); 743 | assert!(key_a.eq(&key_b)); 744 | 745 | // Setting the compute entry point shouldn’t affect the 746 | // equality for graphics pipelines 747 | key_a.set_entrypoint(Stage::Compute, "saysno".to_owned()); 748 | assert!(key_a.eq(&key_b)); 749 | key_b.set_entrypoint(Stage::Compute, "saysno".to_owned()); 750 | assert!(key_a.eq(&key_b)); 751 | 752 | key_a.set_pipeline_type(Type::Compute); 753 | assert!(!key_a.eq(&key_b)); 754 | key_b.set_pipeline_type(Type::Compute); 755 | assert!(key_a.eq(&key_b)); 756 | 757 | // Setting properties shouldn’t affect the equality for compute shaders 758 | assert!(key_a.set("lineWidth", "5.0").is_ok()); 759 | assert!(key_a.eq(&key_b)); 760 | key_a.set_source(Source::Rectangle); 761 | assert!(key_a.eq(&key_b)); 762 | key_a.set_entrypoint(Stage::TessCtrl, "yes".to_owned()); 763 | assert!(key_a.eq(&key_b)); 764 | 765 | // Setting the compute entrypoint however should affect the equality 766 | key_a.set_entrypoint(Stage::Compute, "rclub".to_owned()); 767 | assert!(!key_a.eq(&key_b)); 768 | key_b.set_entrypoint(Stage::Compute, "rclub".to_owned()); 769 | assert!(key_a.eq(&key_b)); 770 | } 771 | 772 | #[test] 773 | fn test_debug() { 774 | let mut key = Key::default(); 775 | 776 | assert!(key.set("depthWriteEnable", "true").is_ok()); 777 | assert!(key.set("colorWriteMask", "1").is_ok()); 778 | assert!(key.set("lineWidth", "42.0").is_ok()); 779 | 780 | assert_eq!( 781 | format!("{:?}", key), 782 | "Key { \ 783 | pipeline_type: Graphics, \ 784 | source: Rectangle, \ 785 | entrypoints: [None, None, None, None, None, None], \ 786 | alphaBlendOp: 0, \ 787 | back.compareMask: -1, \ 788 | back.compareOp: 7, \ 789 | back.depthFailOp: 0, \ 790 | back.failOp: 0, \ 791 | back.passOp: 0, \ 792 | back.reference: 0, \ 793 | back.writeMask: -1, \ 794 | blendEnable: false, \ 795 | colorBlendOp: 0, \ 796 | colorWriteMask: 1, \ 797 | cullMode: 0, \ 798 | depthBiasClamp: 0, \ 799 | depthBiasConstantFactor: 0, \ 800 | depthBiasEnable: false, \ 801 | depthBiasSlopeFactor: 0, \ 802 | depthBoundsTestEnable: false, \ 803 | depthClampEnable: false, \ 804 | depthCompareOp: 1, \ 805 | depthTestEnable: false, \ 806 | depthWriteEnable: true, \ 807 | dstAlphaBlendFactor: 7, \ 808 | dstColorBlendFactor: 7, \ 809 | front.compareMask: -1, \ 810 | front.compareOp: 7, \ 811 | front.depthFailOp: 0, \ 812 | front.failOp: 0, \ 813 | front.passOp: 0, \ 814 | front.reference: 0, \ 815 | front.writeMask: -1, \ 816 | frontFace: 0, \ 817 | lineWidth: 42, \ 818 | logicOp: 15, \ 819 | logicOpEnable: false, \ 820 | maxDepthBounds: 0, \ 821 | minDepthBounds: 0, \ 822 | patchControlPoints: 0, \ 823 | polygonMode: 0, \ 824 | primitiveRestartEnable: false, \ 825 | rasterizerDiscardEnable: false, \ 826 | srcAlphaBlendFactor: 6, \ 827 | srcColorBlendFactor: 6, \ 828 | stencilTestEnable: false, \ 829 | topology: 4 \ 830 | }", 831 | ); 832 | } 833 | } 834 | ``` -------------------------------------------------------------------------------- /vkrunner/vkrunner/vbo.rs: -------------------------------------------------------------------------------- ```rust 1 | // Copyright © 2011, 2016, 2018 Intel Corporation 2 | // Copyright 2023 Neil Roberts 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice (including the next 12 | // paragraph) shall be included in all copies or substantial portions of the 13 | // Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | // Based on piglit-vbo.cpp 24 | 25 | //! This module adds the facility for specifying vertex data to 26 | //! VkRunner tests using a columnar text format, for example: 27 | //! 28 | //! ```text 29 | //! 0/r32g32b32_sfloat 1/r32_uint 3/int/int 4/int/int 30 | //! 0.0 0.0 0.0 10 0 0 # comment 31 | //! 0.0 1.0 0.0 5 1 1 32 | //! 1.0 1.0 0.0 0 0 1 33 | //! ``` 34 | //! 35 | //! The format consists of a row of column headers followed by any 36 | //! number of rows of data. Each column header has the form 37 | //! `ATTRLOC/FORMAT` where `ATTRLOC` is the location of the vertex 38 | //! attribute to be bound to this column and FORMAT is the name of a 39 | //! VkFormat minus the `VK_FORMAT` prefix. 40 | //! 41 | //! Alternatively the column header can use something closer to the 42 | //! Piglit format like `ATTRLOC/GL_TYPE/GLSL_TYPE`. `GL_TYPE` is the 43 | //! GL type of data that follows (“`half`”, “`float`”, “`double`”, 44 | //! “`byte`”, “`ubyte`”, “`short`”, “`ushort`”, “`int`” or “`uint`”), 45 | //! `GLSL_TYPE` is the GLSL type of the data (“`int`”, “`uint`”, 46 | //! “`float`”, “`double`”, “`ivec*`”, “`uvec*`”, “`vec*`”, “`dvec*`”). 47 | //! 48 | //! The data follows the column headers in space-separated form. `#` 49 | //! can be used for comments, as in shell scripts. 50 | //! 51 | //! The text can be parsed either by using the [`str::parse::<Vbo>`] 52 | //! method to parse an entire string, or by constructing a [Parser] 53 | //! object to parse the data line-by-line. 54 | 55 | use crate::format::{Format, Mode}; 56 | use crate::{util, parse_num, hex}; 57 | use std::fmt; 58 | use std::cell::RefCell; 59 | 60 | #[derive(Debug, Clone)] 61 | pub enum Error { 62 | InvalidHeader(String), 63 | InvalidData(String), 64 | } 65 | 66 | /// Struct representing a blob of structured data that can be used as 67 | /// vertex inputs to Vulkan. The Vbo can be constructed either by 68 | /// parsing an entire string slice with the [str::parse] method or by 69 | /// parsing the source line-by-line by constructing a [Parser] object. 70 | #[derive(Debug)] 71 | pub struct Vbo { 72 | // Description of each attribute 73 | attribs: Box<[Attrib]>, 74 | // Raw data buffer containing parsed numbers 75 | raw_data: Box<[u8]>, 76 | // Number of bytes in each row of raw_data 77 | stride: usize, 78 | } 79 | 80 | #[derive(Debug)] 81 | pub struct Attrib { 82 | format: &'static Format, 83 | 84 | // Vertex location 85 | location: u32, 86 | // Byte offset into the vertex data of this attribute 87 | offset: usize, 88 | } 89 | 90 | impl fmt::Display for Error { 91 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 92 | match &self { 93 | Error::InvalidHeader(s) => write!(f, "{}", s), 94 | Error::InvalidData(s) => write!(f, "{}", s), 95 | } 96 | } 97 | } 98 | 99 | impl Vbo { 100 | #[inline] 101 | pub fn attribs(&self) -> &[Attrib] { 102 | self.attribs.as_ref() 103 | } 104 | 105 | #[inline] 106 | pub fn raw_data(&self) -> &[u8] { 107 | self.raw_data.as_ref() 108 | } 109 | 110 | #[inline] 111 | pub fn stride(&self) -> usize { 112 | self.stride 113 | } 114 | } 115 | 116 | impl Attrib { 117 | #[inline] 118 | pub fn format(&self) -> &'static Format { 119 | self.format 120 | } 121 | 122 | #[inline] 123 | pub fn location(&self) -> u32 { 124 | self.location 125 | } 126 | 127 | #[inline] 128 | pub fn offset(&self) -> usize { 129 | self.offset 130 | } 131 | } 132 | 133 | /// Helper struct to construct a [Vbo] by parsing data line-by-line. 134 | /// Construct the parser with [new](Parser::new) and then add each 135 | /// line with [parse_line](Parser::parse_line). When the vbo data is 136 | /// complete call [into_vbo](Parser::into_vbo) to finish the parsing 137 | /// and convert the parser into the final Vbo. 138 | #[derive(Debug)] 139 | pub struct Parser { 140 | // None if we haven’t parsed the header line yet, otherwise an 141 | // array of attribs 142 | attribs: Option<Box<[Attrib]>>, 143 | 144 | raw_data: RefCell<Vec<u8>>, 145 | 146 | stride: usize, 147 | } 148 | 149 | macro_rules! invalid_header { 150 | ($data:expr, $($message:expr),+) => { 151 | return Err(Error::InvalidHeader(format!($($message),+))) 152 | }; 153 | } 154 | 155 | macro_rules! invalid_data { 156 | ($data:expr, $($message:expr),+) => { 157 | return Err(Error::InvalidData(format!($($message),+))) 158 | }; 159 | } 160 | 161 | impl std::str::FromStr for Vbo { 162 | type Err = Error; 163 | 164 | fn from_str(s: &str) -> Result<Vbo, Error> { 165 | let mut data = Parser::new(); 166 | 167 | for line in s.lines() { 168 | data.parse_line(line)?; 169 | } 170 | 171 | data.into_vbo() 172 | } 173 | } 174 | 175 | impl Parser { 176 | pub fn new() -> Parser { 177 | Parser { 178 | raw_data: RefCell::new(Vec::new()), 179 | stride: 0, 180 | attribs: None, 181 | } 182 | } 183 | 184 | fn trim_line(line: &str) -> &str { 185 | let line = line.trim_start(); 186 | 187 | // Remove comments at the end of the line 188 | let line = match line.trim_start().find('#') { 189 | Some(end) => &line[0..end], 190 | None => line, 191 | }; 192 | 193 | line.trim_end() 194 | } 195 | 196 | fn lookup_gl_type(&self, gl_type: &str) -> Result<(Mode, usize), Error> { 197 | struct GlType { 198 | name: &'static str, 199 | mode: Mode, 200 | bit_size: usize, 201 | } 202 | static GL_TYPES: [GlType; 9] = [ 203 | GlType { name: "byte", mode: Mode::SINT, bit_size: 8 }, 204 | GlType { name: "ubyte", mode: Mode::UINT, bit_size: 8 }, 205 | GlType { name: "short", mode: Mode::SINT, bit_size: 16 }, 206 | GlType { name: "ushort", mode: Mode::UINT, bit_size: 16 }, 207 | GlType { name: "int", mode: Mode::SINT, bit_size: 32 }, 208 | GlType { name: "uint", mode: Mode::UINT, bit_size: 32 }, 209 | GlType { name: "half", mode: Mode::SFLOAT, bit_size: 16 }, 210 | GlType { name: "float", mode: Mode::SFLOAT, bit_size: 32 }, 211 | GlType { name: "double", mode: Mode::SFLOAT, bit_size: 64 }, 212 | ]; 213 | 214 | match GL_TYPES.iter().find(|t| t.name == gl_type) { 215 | Some(t) => Ok((t.mode, t.bit_size)), 216 | None => invalid_header!(self, "Unknown GL type: {}", gl_type), 217 | } 218 | } 219 | 220 | fn components_for_glsl_type( 221 | &self, 222 | glsl_type: &str 223 | ) -> Result<usize, Error> { 224 | if ["int", "uint", "float", "double"].contains(&glsl_type) { 225 | return Ok(1); 226 | } 227 | 228 | let vec_part = match glsl_type.chars().next() { 229 | Some('i' | 'u' | 'd') => &glsl_type[1..], 230 | _ => glsl_type, 231 | }; 232 | 233 | if !vec_part.starts_with("vec") { 234 | invalid_header!(self, "Unknown GLSL type: {}", glsl_type); 235 | } 236 | 237 | match vec_part[3..].parse::<usize>() { 238 | Ok(n) if n >= 2 && n <= 4 => Ok(n), 239 | _ => invalid_header!(self, "Invalid vec size: {}", glsl_type), 240 | } 241 | } 242 | 243 | fn decode_type( 244 | &self, 245 | gl_type: &str, 246 | glsl_type: &str 247 | ) -> Result<&'static Format, Error> { 248 | let (mode, bit_size) = self.lookup_gl_type(gl_type)?; 249 | let n_components = self.components_for_glsl_type(glsl_type)?; 250 | 251 | match Format::lookup_by_details(bit_size, mode, n_components) { 252 | Some(f) => Ok(f), 253 | None => { 254 | invalid_header!( 255 | self, 256 | "Invalid type combo: {}/{}", 257 | gl_type, 258 | glsl_type 259 | ); 260 | }, 261 | } 262 | } 263 | 264 | fn parse_attrib( 265 | &mut self, 266 | s: &str, 267 | offset: usize 268 | ) -> Result<Attrib, Error> { 269 | let mut parts = s.split('/'); 270 | 271 | let location = match parts.next().unwrap().parse::<u32>() { 272 | Ok(n) => n, 273 | Err(_) => invalid_header!(self, "Invalid attrib location in {}", s), 274 | }; 275 | 276 | let format_name = match parts.next() { 277 | Some(n) => n, 278 | None => { 279 | invalid_header!( 280 | self, 281 | "Column headers must be in the form \ 282 | location/format. Got: {}", 283 | s 284 | ); 285 | }, 286 | }; 287 | 288 | let format = match parts.next() { 289 | None => match Format::lookup_by_name(format_name) { 290 | None => { 291 | invalid_header!( 292 | self, 293 | "Unknown format: {}", 294 | format_name 295 | ); 296 | }, 297 | Some(f) => f, 298 | }, 299 | Some(glsl_type) => { 300 | if let Some(_) = parts.next() { 301 | invalid_header!( 302 | self, 303 | "Extra data at end of column header: {}", 304 | s 305 | ); 306 | } 307 | 308 | self.decode_type(format_name, glsl_type)? 309 | }, 310 | }; 311 | 312 | Ok(Attrib { 313 | format, 314 | location, 315 | offset: util::align(offset, format.alignment()), 316 | }) 317 | } 318 | 319 | fn parse_header_line(&mut self, line: &str) -> Result<(), Error> { 320 | let mut attribs = Vec::new(); 321 | let mut stride = 0; 322 | let mut max_alignment = 1; 323 | 324 | for attrib in line.split_whitespace() { 325 | let attrib = self.parse_attrib(attrib, stride)?; 326 | 327 | stride = attrib.offset + attrib.format.size(); 328 | 329 | let alignment = attrib.format.alignment(); 330 | 331 | if alignment > max_alignment { 332 | max_alignment = alignment; 333 | } 334 | 335 | attribs.push(attrib); 336 | } 337 | 338 | self.attribs = Some(attribs.into_boxed_slice()); 339 | self.stride = util::align(stride, max_alignment); 340 | 341 | Ok(()) 342 | } 343 | 344 | #[inline] 345 | fn write_bytes(data: &mut [u8], bytes: &[u8]) { 346 | data[0..bytes.len()].copy_from_slice(bytes); 347 | } 348 | 349 | fn parse_unsigned_datum<'a>( 350 | &self, 351 | bit_size: usize, 352 | text: &'a str, 353 | data: &mut [u8], 354 | ) -> Result<&'a str, Error> { 355 | match bit_size { 356 | 8 => match parse_num::parse_u8(text) { 357 | Err(_) => { 358 | invalid_data!(self, "Couldn’t parse as unsigned byte") 359 | }, 360 | Ok((v, tail)) => { 361 | Parser::write_bytes(data, &v.to_ne_bytes()); 362 | Ok(tail) 363 | }, 364 | }, 365 | 16 => match parse_num::parse_u16(text) { 366 | Err(_) => { 367 | invalid_data!(self, "Couldn’t parse as unsigned short") 368 | }, 369 | Ok((v, tail)) => { 370 | Parser::write_bytes(data, &v.to_ne_bytes()); 371 | Ok(tail) 372 | }, 373 | }, 374 | 32 => match parse_num::parse_u32(text) { 375 | Err(_) => { 376 | invalid_data!(self, "Couldn’t parse as unsigned int") 377 | }, 378 | Ok((v, tail)) => { 379 | Parser::write_bytes(data, &v.to_ne_bytes()); 380 | Ok(tail) 381 | }, 382 | }, 383 | 64 => match parse_num::parse_u64(text) { 384 | Err(_) => { 385 | invalid_data!(self, "Couldn’t parse as unsigned long") 386 | }, 387 | Ok((v, tail)) => { 388 | Parser::write_bytes(data, &v.to_ne_bytes()); 389 | Ok(tail) 390 | }, 391 | }, 392 | _ => unreachable!("unexpected bit size {}", bit_size), 393 | } 394 | } 395 | 396 | fn parse_signed_datum<'a>( 397 | &self, 398 | bit_size: usize, 399 | text: &'a str, 400 | data: &mut [u8], 401 | ) -> Result<&'a str, Error> { 402 | match bit_size { 403 | 8 => match parse_num::parse_i8(text) { 404 | Err(_) => { 405 | invalid_data!(self, "Couldn’t parse as signed byte") 406 | }, 407 | Ok((v, tail)) => { 408 | Parser::write_bytes(data, &v.to_ne_bytes()); 409 | Ok(tail) 410 | }, 411 | }, 412 | 16 => match parse_num::parse_i16(text) { 413 | Err(_) => { 414 | invalid_data!(self, "Couldn’t parse as signed short") 415 | }, 416 | Ok((v, tail)) => { 417 | Parser::write_bytes(data, &v.to_ne_bytes()); 418 | Ok(tail) 419 | }, 420 | }, 421 | 32 => match parse_num::parse_i32(text) { 422 | Err(_) => { 423 | invalid_data!(self, "Couldn’t parse as signed int") 424 | }, 425 | Ok((v, tail)) => { 426 | Parser::write_bytes(data, &v.to_ne_bytes()); 427 | Ok(tail) 428 | }, 429 | }, 430 | 64 => match parse_num::parse_i64(text) { 431 | Err(_) => { 432 | invalid_data!(self, "Couldn’t parse as signed long") 433 | }, 434 | Ok((v, tail)) => { 435 | Parser::write_bytes(data, &v.to_ne_bytes()); 436 | Ok(tail) 437 | }, 438 | }, 439 | _ => unreachable!("unexpected bit size {}", bit_size), 440 | } 441 | } 442 | 443 | fn parse_float_datum<'a>( 444 | &self, 445 | bit_size: usize, 446 | text: &'a str, 447 | data: &mut [u8], 448 | ) -> Result<&'a str, Error> { 449 | match bit_size { 450 | 16 => match hex::parse_half_float(text) { 451 | Err(_) => { 452 | invalid_data!(self, "Couldn’t parse as half float") 453 | }, 454 | Ok((v, tail)) => { 455 | Parser::write_bytes(data, &v.to_ne_bytes()); 456 | Ok(tail) 457 | }, 458 | }, 459 | 32 => match hex::parse_f32(text) { 460 | Err(_) => { 461 | invalid_data!(self, "Couldn’t parse as float") 462 | }, 463 | Ok((v, tail)) => { 464 | Parser::write_bytes(data, &v.to_ne_bytes()); 465 | Ok(tail) 466 | }, 467 | }, 468 | 64 => match hex::parse_f64(text) { 469 | Err(_) => { 470 | invalid_data!(self, "Couldn’t parse as double") 471 | }, 472 | Ok((v, tail)) => { 473 | Parser::write_bytes(data, &v.to_ne_bytes()); 474 | Ok(tail) 475 | }, 476 | }, 477 | _ => unreachable!("unexpected bit size {}", bit_size), 478 | } 479 | } 480 | 481 | // Parse a single number (floating point or integral) from one of 482 | // the data rows and store it at the start of the `data` slice. If 483 | // successful it returns the slice in `text` after the number. 484 | // Otherwise it returns an error. 485 | fn parse_datum<'a>( 486 | &self, 487 | mode: Mode, 488 | bit_size: usize, 489 | text: &'a str, 490 | data: &mut [u8], 491 | ) -> Result<&'a str, Error> { 492 | match mode { 493 | Mode::SFLOAT => self.parse_float_datum(bit_size, text, data), 494 | Mode::UNORM | Mode::USCALED | Mode::UINT | Mode::SRGB => { 495 | self.parse_unsigned_datum(bit_size, text, data) 496 | }, 497 | Mode::SNORM | Mode::SSCALED | Mode::SINT => { 498 | self.parse_signed_datum(bit_size, text, data) 499 | }, 500 | Mode::UFLOAT => { 501 | // This shouldn’t happen because all of the UFLOAT 502 | // formats are packed so the data will be integers. 503 | unreachable!("unexpected UFLOAT component"); 504 | }, 505 | } 506 | } 507 | 508 | // Parse each component of an unpacked data format (floating point 509 | // or integral) from one of the data rows and store it at the 510 | // start of the `data` slice. If successful it returns the slice 511 | // in `text` after the values. Otherwise it returns an error. 512 | fn parse_unpacked_data<'a>( 513 | &self, 514 | format: &'static Format, 515 | mut text: &'a str, 516 | mut data: &mut [u8] 517 | ) -> Result<&'a str, Error> { 518 | for part in format.parts() { 519 | text = self.parse_datum(part.mode, part.bits, text, data)?; 520 | data = &mut data[part.bits / 8..]; 521 | } 522 | 523 | Ok(text) 524 | } 525 | 526 | fn parse_data_line(&mut self, mut line: &str) -> Result<(), Error> { 527 | // Allocate space in raw_data for this line 528 | let old_length = self.raw_data.borrow().len(); 529 | self.raw_data.borrow_mut().resize(old_length + self.stride, 0); 530 | 531 | for attrib in self.attribs.as_ref().unwrap().iter() { 532 | let data_ptr = 533 | &mut self.raw_data.borrow_mut()[old_length + attrib.offset..]; 534 | 535 | match attrib.format.packed_size() { 536 | Some(packed_size) => { 537 | line = self.parse_unsigned_datum( 538 | packed_size, 539 | line, 540 | data_ptr 541 | )?; 542 | }, 543 | None => { 544 | line = self.parse_unpacked_data( 545 | attrib.format, 546 | line, 547 | data_ptr 548 | )?; 549 | } 550 | } 551 | } 552 | 553 | if !line.trim_end().is_empty() { 554 | invalid_data!(self, "Extra data at end of line"); 555 | } 556 | 557 | Ok(()) 558 | } 559 | 560 | /// Add one line of data to the vbo. Returns an error if the line 561 | /// is invalid. 562 | pub fn parse_line(&mut self, line: &str) -> Result<(), Error> { 563 | let line = Parser::trim_line(line); 564 | 565 | // Ignore blank or comment-only lines */ 566 | if line.len() <= 0 { 567 | return Ok(()); 568 | } 569 | 570 | if self.attribs.is_none() { 571 | self.parse_header_line(line)?; 572 | } else { 573 | self.parse_data_line(line)?; 574 | } 575 | 576 | Ok(()) 577 | } 578 | 579 | /// Call this at the end of parsing to convert the parser into the 580 | /// final Vbo. This can fail if the parser didn’t have enough data 581 | /// to complete the vbo. 582 | pub fn into_vbo(mut self) -> Result<Vbo, Error> { 583 | let attribs = match self.attribs.take() { 584 | None => invalid_header!(data, "Missing header line"), 585 | Some(a) => a, 586 | }; 587 | 588 | Ok(Vbo { 589 | attribs, 590 | raw_data: self.raw_data.into_inner().into_boxed_slice(), 591 | stride: self.stride, 592 | }) 593 | } 594 | } 595 | 596 | #[cfg(test)] 597 | mod test { 598 | use super::*; 599 | use crate::vk; 600 | 601 | #[test] 602 | fn test_general() { 603 | let source = "# position color \n\ 604 | 0/R32G32_SFLOAT 1/A8B8G8R8_UNORM_PACK32 \n\ 605 | \n\ 606 | # Top-left red \n\ 607 | -1 -1 0xff0000ff \n\ 608 | 0 -1 0xff1200ff"; 609 | 610 | let vbo = source.parse::<Vbo>().unwrap(); 611 | 612 | assert_eq!(vbo.attribs().len(), 2); 613 | assert_eq!(vbo.stride(), 4 * 3); 614 | 615 | assert_eq!( 616 | vbo.attribs()[0].format(), 617 | Format::lookup_by_vk_format(vk::VK_FORMAT_R32G32_SFLOAT), 618 | ); 619 | assert_eq!(vbo.attribs()[0].location(), 0); 620 | assert_eq!(vbo.attribs()[0].offset(), 0); 621 | assert_eq!( 622 | vbo.attribs()[1].format(), 623 | Format::lookup_by_vk_format(vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32), 624 | ); 625 | assert_eq!(vbo.attribs()[1].location(), 1); 626 | assert_eq!(vbo.attribs()[1].offset(), 8); 627 | 628 | assert_eq!(vbo.raw_data().len() % vbo.stride(), 0); 629 | 630 | let mut expected_data = Vec::<u8>::new(); 631 | expected_data.extend(&(-1.0f32).to_ne_bytes()); 632 | expected_data.extend(&(-1.0f32).to_ne_bytes()); 633 | expected_data.extend(0xff0000ffu32.to_ne_bytes()); 634 | expected_data.extend(0.0f32.to_ne_bytes()); 635 | expected_data.extend(&(-1.0f32).to_ne_bytes()); 636 | expected_data.extend(0xff1200ffu32.to_ne_bytes()); 637 | assert_eq!(vbo.raw_data(), &expected_data); 638 | } 639 | 640 | #[test] 641 | fn test_no_header() { 642 | let err = "".parse::<Vbo>().unwrap_err(); 643 | assert_eq!(err.to_string(), "Missing header line"); 644 | assert!(matches!(err, Error::InvalidHeader(_))); 645 | } 646 | 647 | #[test] 648 | fn test_line_comment() { 649 | let source = "0/R32_SFLOAT\n\ 650 | 42.0 # the next number is ignored 32.0"; 651 | let vbo = source.parse::<Vbo>().unwrap(); 652 | assert_eq!(vbo.raw_data(), &42.0f32.to_ne_bytes()); 653 | } 654 | 655 | fn test_gl_type(name: &str, values: &str, expected_bytes: &[u8]) { 656 | let source = format!("0/{}/int\n{}", name, values); 657 | let vbo = source.parse::<Vbo>().unwrap(); 658 | assert_eq!(vbo.raw_data(), expected_bytes); 659 | assert_eq!(vbo.attribs().len(), 1); 660 | assert_eq!(vbo.attribs()[0].location, 0); 661 | assert_eq!(vbo.attribs()[0].format.parts().len(), 1); 662 | assert_eq!( 663 | vbo.attribs()[0].format.parts()[0].bits, 664 | expected_bytes.len() * 8 665 | ); 666 | } 667 | 668 | fn test_glsl_type(name: &str, values: &str, expected_floats: &[f32]) { 669 | let source = format!("1/float/{}\n{}", name, values); 670 | let vbo = source.parse::<Vbo>().unwrap(); 671 | let expected_bytes = expected_floats 672 | .iter() 673 | .map(|v| v.to_ne_bytes()) 674 | .flatten() 675 | .collect::<Vec<u8>>(); 676 | assert_eq!(vbo.raw_data(), &expected_bytes); 677 | assert_eq!(vbo.attribs().len(), 1); 678 | assert_eq!(vbo.attribs()[0].location, 1); 679 | assert_eq!( 680 | vbo.attribs()[0].format.parts().len(), 681 | expected_floats.len() 682 | ); 683 | for part in vbo.attribs()[0].format.parts() { 684 | assert_eq!(part.bits, 32); 685 | } 686 | } 687 | 688 | #[test] 689 | fn test_piglit_style_format() { 690 | test_gl_type("byte", "-42", &(-42i8).to_ne_bytes()); 691 | test_gl_type("ubyte", "42", &[42u8]); 692 | test_gl_type("short", "-30000", &(-30000i16).to_ne_bytes()); 693 | test_gl_type("ushort", "65534", &65534u16.to_ne_bytes()); 694 | test_gl_type("int", "-70000", &(-70000i32).to_ne_bytes()); 695 | test_gl_type("uint", "0xffffffff", &u32::MAX.to_ne_bytes()); 696 | test_gl_type("half", "-2", &0xc000u16.to_ne_bytes()); 697 | test_gl_type("float", "1.0000", &1.0f32.to_ne_bytes()); 698 | test_gl_type("double", "32.0000", &32.0f64.to_ne_bytes()); 699 | 700 | let err = "1/uverylong/int".parse::<Vbo>().unwrap_err(); 701 | assert_eq!(&err.to_string(), "Unknown GL type: uverylong"); 702 | assert!(matches!(err, Error::InvalidHeader(_))); 703 | 704 | test_glsl_type("int", "1.0", &[1.0]); 705 | test_glsl_type("uint", "2.0", &[2.0]); 706 | test_glsl_type("float", "3.0", &[3.0]); 707 | test_glsl_type("double", "4.0", &[4.0]); 708 | test_glsl_type("vec2", "1.0 2.0", &[1.0, 2.0]); 709 | test_glsl_type("vec3", "1.0 2.0 3.0", &[1.0, 2.0, 3.0]); 710 | test_glsl_type("vec4", "1.0 2.0 3.0 4.0", &[1.0, 2.0, 3.0, 4.0]); 711 | test_glsl_type("ivec2", "1.0 2.0", &[1.0, 2.0]); 712 | test_glsl_type("uvec2", "1.0 2.0", &[1.0, 2.0]); 713 | test_glsl_type("dvec2", "1.0 2.0", &[1.0, 2.0]); 714 | 715 | let err = "1/int/ituple2".parse::<Vbo>().unwrap_err(); 716 | assert_eq!(&err.to_string(), "Unknown GLSL type: ituple2"); 717 | assert!(matches!(err, Error::InvalidHeader(_))); 718 | 719 | let err = "1/int/ivecfoo".parse::<Vbo>().unwrap_err(); 720 | assert_eq!(&err.to_string(), "Invalid vec size: ivecfoo"); 721 | assert!(matches!(err, Error::InvalidHeader(_))); 722 | 723 | let err = "1/int/vec1".parse::<Vbo>().unwrap_err(); 724 | assert_eq!(&err.to_string(), "Invalid vec size: vec1"); 725 | assert!(matches!(err, Error::InvalidHeader(_))); 726 | 727 | let err = "1/int/dvec5".parse::<Vbo>().unwrap_err(); 728 | assert_eq!(&err.to_string(), "Invalid vec size: dvec5"); 729 | assert!(matches!(err, Error::InvalidHeader(_))); 730 | } 731 | 732 | #[test] 733 | fn test_bad_attrib() { 734 | let err = "foo/int/int".parse::<Vbo>().unwrap_err(); 735 | assert_eq!(&err.to_string(), "Invalid attrib location in foo/int/int"); 736 | assert!(matches!(err, Error::InvalidHeader(_))); 737 | 738 | assert_eq!( 739 | "12".parse::<Vbo>().unwrap_err().to_string(), 740 | "Column headers must be in the form location/format. \ 741 | Got: 12", 742 | ); 743 | 744 | assert_eq!( 745 | "1/R76_SFLOAT".parse::<Vbo>().unwrap_err().to_string(), 746 | "Unknown format: R76_SFLOAT", 747 | ); 748 | 749 | assert_eq!( 750 | "1/int/int/more_int".parse::<Vbo>().unwrap_err().to_string(), 751 | "Extra data at end of column header: 1/int/int/more_int", 752 | ); 753 | } 754 | 755 | #[test] 756 | fn test_alignment() { 757 | let source = "1/R8_UNORM 2/R64_SFLOAT 3/R8_UNORM\n \ 758 | 1 12.0 24"; 759 | let vbo = source.parse::<Vbo>().unwrap(); 760 | assert_eq!(vbo.attribs().len(), 3); 761 | assert_eq!(vbo.attribs()[0].offset, 0); 762 | assert_eq!(vbo.attribs()[0].format.parts()[0].bits, 8); 763 | assert_eq!(vbo.attribs()[1].offset, 8); 764 | assert_eq!(vbo.attribs()[1].format.parts()[0].bits, 64); 765 | assert_eq!(vbo.attribs()[2].offset, 16); 766 | assert_eq!(vbo.attribs()[2].format.parts()[0].bits, 8); 767 | assert_eq!(vbo.stride, 24); 768 | } 769 | 770 | fn test_type(format: &str, values: &str, expected_bytes: &[u8]) { 771 | // Add an extra attribute so we can test it got the right offset 772 | let source = format!("8/{} 9/R8_UNORM\n{} 42", format, values); 773 | let vbo = source.parse::<Vbo>().unwrap(); 774 | let mut full_expected_bytes = expected_bytes.to_owned(); 775 | full_expected_bytes.push(42); 776 | assert!(vbo.stride() >= expected_bytes.len() + 1); 777 | full_expected_bytes.resize(vbo.stride(), 0); 778 | assert_eq!(vbo.raw_data(), full_expected_bytes); 779 | assert_eq!(vbo.attribs().len(), 2); 780 | assert_eq!( 781 | vbo.attribs()[0].format.parts()[0].bits, 782 | expected_bytes.len() * 8 783 | ); 784 | } 785 | 786 | fn test_value_error(format: &str, error_text: &str) { 787 | let source = format!("0/{}\nfoo", format); 788 | let err = source.parse::<Vbo>().unwrap_err(); 789 | assert_eq!(&err.to_string(), error_text); 790 | } 791 | 792 | #[test] 793 | fn test_parse_datum() { 794 | test_type("R8_UNORM", "12", &[12u8]); 795 | test_value_error("R8_UNORM", "Couldn’t parse as unsigned byte"); 796 | test_type("R16_UNORM", "65000", &65000u16.to_ne_bytes()); 797 | test_value_error("R16_USCALED", "Couldn’t parse as unsigned short"); 798 | test_type("R32_UINT", "66000", &66000u32.to_ne_bytes()); 799 | test_value_error("R32_UINT", "Couldn’t parse as unsigned int"); 800 | test_type("R64_UINT", "0xffffffffffffffff", &u64::MAX.to_ne_bytes()); 801 | test_value_error("R64_UINT", "Couldn’t parse as unsigned long"); 802 | 803 | test_type("R8_SNORM", "-12", &(-12i8).to_ne_bytes()); 804 | test_value_error("R8_SNORM", "Couldn’t parse as signed byte"); 805 | test_type("R16_SNORM", "-32768", &(-32768i16).to_ne_bytes()); 806 | test_value_error("R16_SNORM", "Couldn’t parse as signed short"); 807 | test_type("R32_SINT", "-66000", &(-66000i32).to_ne_bytes()); 808 | test_value_error("R32_SINT", "Couldn’t parse as signed int"); 809 | test_type("R64_SINT", "0x7fffffffffffffff", &i64::MAX.to_ne_bytes()); 810 | test_value_error("R64_SINT", "Couldn’t parse as signed long"); 811 | 812 | test_type("R16_SFLOAT", "0xc000", &0xc000u16.to_ne_bytes()); 813 | test_type("R16_SFLOAT", "-2", &0xc000u16.to_ne_bytes()); 814 | test_value_error("R16_SFLOAT", "Couldn’t parse as half float"); 815 | test_type("R32_SFLOAT", "-2", &(-2.0f32).to_ne_bytes()); 816 | test_value_error("R32_SFLOAT", "Couldn’t parse as float"); 817 | test_type("R64_SFLOAT", "-4", &(-4.0f64).to_ne_bytes()); 818 | test_value_error("R64_SFLOAT", "Couldn’t parse as double"); 819 | } 820 | 821 | #[test] 822 | fn test_packed_data() { 823 | let source = "1/B10G11R11_UFLOAT_PACK32\n\ 824 | 0xfedcba98"; 825 | let vbo = source.parse::<Vbo>().unwrap(); 826 | assert_eq!(vbo.raw_data(), &0xfedcba98u32.to_ne_bytes()); 827 | } 828 | 829 | #[test] 830 | fn test_trailing_data() { 831 | let source = "1/R8_UNORM\n\ 832 | 23 25 "; 833 | let err = source.parse::<Vbo>().unwrap_err(); 834 | assert_eq!(err.to_string(), "Extra data at end of line"); 835 | } 836 | } 837 | ``` -------------------------------------------------------------------------------- /vkrunner/vkrunner/window.rs: -------------------------------------------------------------------------------- ```rust 1 | // vkrunner 2 | // 3 | // Copyright (C) 2013, 2014, 2015, 2017, 2023 Neil Roberts 4 | // Copyright (C) 2019 Google LLC 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, including without limitation 9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | // and/or sell copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice (including the next 14 | // paragraph) shall be included in all copies or substantial portions of the 15 | // Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | use crate::context::Context; 26 | use crate::window_format::WindowFormat; 27 | use crate::format::{Format, Component}; 28 | use crate::vk; 29 | use crate::result; 30 | use crate::vulkan_funcs; 31 | use crate::buffer::{self, MappedMemory, DeviceMemory, Buffer}; 32 | use std::rc::Rc; 33 | use std::fmt; 34 | use std::ffi::c_void; 35 | use std::ptr; 36 | 37 | /// Struct containing the framebuffer and all of the objects on top of 38 | /// the [Context] needed to construct it. It also keeps a reference to 39 | /// the Context which can be retrieved publically so this works as a 40 | /// central object to share the essential Vulkan resources used for 41 | /// running tests. 42 | #[derive(Debug)] 43 | pub struct Window { 44 | format: WindowFormat, 45 | 46 | // These are listed in the reverse order that they are created so 47 | // that they will be destroyed in the right order too 48 | 49 | need_linear_memory_invalidate: bool, 50 | linear_memory_stride: usize, 51 | linear_memory_map: MappedMemory, 52 | linear_memory: DeviceMemory, 53 | linear_buffer: Buffer, 54 | 55 | framebuffer: Framebuffer, 56 | 57 | _depth_stencil_resources: Option<DepthStencilResources>, 58 | 59 | _color_image_view: ImageView, 60 | _memory: DeviceMemory, 61 | color_image: Image, 62 | 63 | // The first render pass is used for the first render and has a 64 | // loadOp of DONT_CARE. The second is used for subsequent renders 65 | // and loads the framebuffer contents. 66 | render_pass: [RenderPass; 2], 67 | 68 | context: Rc<Context>, 69 | } 70 | 71 | #[derive(Debug)] 72 | struct DepthStencilResources { 73 | // These are listed in the reverse order that they are created so 74 | // that they will be destroyed in the right order too 75 | image_view: ImageView, 76 | _memory: DeviceMemory, 77 | _image: Image, 78 | } 79 | 80 | #[derive(Debug)] 81 | pub enum WindowError { 82 | IncompatibleFormat(String), 83 | RenderPassError, 84 | ImageError, 85 | ImageViewError, 86 | BufferError(buffer::Error), 87 | FramebufferError, 88 | } 89 | 90 | impl fmt::Display for WindowError { 91 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 | match self { 93 | WindowError::IncompatibleFormat(s) => write!(f, "{}", s), 94 | WindowError::BufferError(e) => e.fmt(f), 95 | WindowError::RenderPassError => write!( 96 | f, 97 | "Error creating render pass", 98 | ), 99 | WindowError::ImageError => write!( 100 | f, 101 | "Error creating vkImage", 102 | ), 103 | WindowError::ImageViewError => write!( 104 | f, 105 | "Error creating vkImageView", 106 | ), 107 | WindowError::FramebufferError => write!( 108 | f, 109 | "Error creating vkFramebuffer", 110 | ), 111 | } 112 | } 113 | } 114 | 115 | impl WindowError { 116 | pub fn result(&self) -> result::Result { 117 | match self { 118 | WindowError::IncompatibleFormat(_) => result::Result::Skip, 119 | WindowError::RenderPassError => result::Result::Fail, 120 | WindowError::ImageError => result::Result::Fail, 121 | WindowError::ImageViewError => result::Result::Fail, 122 | WindowError::FramebufferError => result::Result::Fail, 123 | WindowError::BufferError(_) => result::Result::Fail, 124 | } 125 | } 126 | } 127 | 128 | impl From<buffer::Error> for WindowError { 129 | fn from(e: buffer::Error) -> WindowError { 130 | WindowError::BufferError(e) 131 | } 132 | } 133 | 134 | fn check_format( 135 | context: &Context, 136 | format: &Format, 137 | flags: vk::VkFormatFeatureFlags, 138 | ) -> bool { 139 | let mut format_properties: vk::VkFormatProperties = Default::default(); 140 | 141 | unsafe { 142 | context.instance().vkGetPhysicalDeviceFormatProperties.unwrap()( 143 | context.physical_device(), 144 | format.vk_format, 145 | &mut format_properties as *mut vk::VkFormatProperties, 146 | ); 147 | } 148 | 149 | format_properties.optimalTilingFeatures & flags == flags 150 | } 151 | 152 | fn check_window_format( 153 | context: &Context, 154 | window_format: &WindowFormat, 155 | ) -> Result<(), WindowError> { 156 | if !check_format( 157 | context, 158 | window_format.color_format, 159 | vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT 160 | | vk::VK_FORMAT_FEATURE_BLIT_SRC_BIT, 161 | ) { 162 | return Err(WindowError::IncompatibleFormat(format!( 163 | "Format {} is not supported as a color attachment and blit source", 164 | window_format.color_format.name, 165 | ))); 166 | } 167 | 168 | if let Some(depth_stencil_format) = window_format.depth_stencil_format { 169 | if !check_format( 170 | context, 171 | depth_stencil_format, 172 | vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, 173 | ) { 174 | return Err(WindowError::IncompatibleFormat(format!( 175 | "Format {} is not supported as a depth/stencil attachment", 176 | depth_stencil_format.name, 177 | ))); 178 | } 179 | } 180 | 181 | Ok(()) 182 | } 183 | 184 | #[derive(Debug)] 185 | struct RenderPass { 186 | render_pass: vk::VkRenderPass, 187 | // Needed for the destructor 188 | context: Rc<Context>, 189 | } 190 | 191 | impl RenderPass { 192 | fn new( 193 | context: Rc<Context>, 194 | window_format: &WindowFormat, 195 | first_render: bool, 196 | ) -> Result<RenderPass, WindowError> { 197 | let has_stencil = match window_format.depth_stencil_format { 198 | None => false, 199 | Some(format) => { 200 | format 201 | .parts() 202 | .into_iter() 203 | .find(|p| p.component == Component::S) 204 | .is_some() 205 | }, 206 | }; 207 | 208 | let attachment_descriptions = [ 209 | vk::VkAttachmentDescription { 210 | flags: 0, 211 | format: window_format.color_format.vk_format, 212 | samples: vk::VK_SAMPLE_COUNT_1_BIT, 213 | loadOp: if first_render { 214 | vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE 215 | } else { 216 | vk::VK_ATTACHMENT_LOAD_OP_LOAD 217 | }, 218 | storeOp: vk::VK_ATTACHMENT_STORE_OP_STORE, 219 | stencilLoadOp: vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, 220 | stencilStoreOp: vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, 221 | initialLayout: if first_render { 222 | vk::VK_IMAGE_LAYOUT_UNDEFINED 223 | } else { 224 | vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 225 | }, 226 | finalLayout: vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 227 | }, 228 | vk::VkAttachmentDescription { 229 | flags: 0, 230 | format: match window_format.depth_stencil_format { 231 | Some(f) => f.vk_format, 232 | None => 0, 233 | }, 234 | samples: vk::VK_SAMPLE_COUNT_1_BIT, 235 | loadOp: if first_render { 236 | vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE 237 | } else { 238 | vk::VK_ATTACHMENT_LOAD_OP_LOAD 239 | }, 240 | storeOp: vk::VK_ATTACHMENT_STORE_OP_STORE, 241 | stencilLoadOp: if first_render || !has_stencil { 242 | vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE 243 | } else { 244 | vk::VK_ATTACHMENT_LOAD_OP_LOAD 245 | }, 246 | stencilStoreOp: if has_stencil { 247 | vk::VK_ATTACHMENT_STORE_OP_STORE 248 | } else { 249 | vk::VK_ATTACHMENT_STORE_OP_DONT_CARE 250 | }, 251 | initialLayout: if first_render { 252 | vk::VK_IMAGE_LAYOUT_UNDEFINED 253 | } else { 254 | vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL 255 | }, 256 | finalLayout: 257 | vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 258 | }, 259 | ]; 260 | 261 | let color_attachment_reference = vk::VkAttachmentReference { 262 | attachment: 0, 263 | layout: vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 264 | }; 265 | let depth_stencil_attachment_reference = vk::VkAttachmentReference { 266 | attachment: 1, 267 | layout: vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 268 | }; 269 | 270 | let subpass_descriptions = [ 271 | vk::VkSubpassDescription { 272 | flags: 0, 273 | pipelineBindPoint: vk::VK_PIPELINE_BIND_POINT_GRAPHICS, 274 | inputAttachmentCount: 0, 275 | pInputAttachments: ptr::null(), 276 | colorAttachmentCount: 1, 277 | pColorAttachments: ptr::addr_of!(color_attachment_reference), 278 | pResolveAttachments: ptr::null(), 279 | pDepthStencilAttachment: 280 | if window_format.depth_stencil_format.is_some() { 281 | ptr::addr_of!(depth_stencil_attachment_reference) 282 | } else { 283 | ptr::null() 284 | }, 285 | preserveAttachmentCount: 0, 286 | pPreserveAttachments: ptr::null(), 287 | }, 288 | ]; 289 | 290 | let render_pass_create_info = vk::VkRenderPassCreateInfo { 291 | sType: vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 292 | pNext: ptr::null(), 293 | flags: 0, 294 | attachmentCount: attachment_descriptions.len() as u32 295 | - window_format.depth_stencil_format.is_none() as u32, 296 | pAttachments: ptr::addr_of!(attachment_descriptions[0]), 297 | subpassCount: subpass_descriptions.len() as u32, 298 | pSubpasses: ptr::addr_of!(subpass_descriptions[0]), 299 | dependencyCount: 0, 300 | pDependencies: ptr::null(), 301 | }; 302 | 303 | let mut render_pass: vk::VkRenderPass = vk::null_handle(); 304 | 305 | let res = unsafe { 306 | context.device().vkCreateRenderPass.unwrap()( 307 | context.vk_device(), 308 | ptr::addr_of!(render_pass_create_info), 309 | ptr::null(), // allocator 310 | ptr::addr_of_mut!(render_pass) 311 | ) 312 | }; 313 | 314 | if res == vk::VK_SUCCESS { 315 | Ok(RenderPass { render_pass, context }) 316 | } else { 317 | Err(WindowError::RenderPassError) 318 | } 319 | } 320 | } 321 | 322 | impl Drop for RenderPass { 323 | fn drop(&mut self) { 324 | unsafe { 325 | self.context.device().vkDestroyRenderPass.unwrap()( 326 | self.context.vk_device(), 327 | self.render_pass, 328 | ptr::null(), // allocator 329 | ); 330 | } 331 | } 332 | } 333 | 334 | #[derive(Debug)] 335 | struct Image { 336 | image: vk::VkImage, 337 | // Needed for the destructor 338 | context: Rc<Context>, 339 | } 340 | 341 | impl Drop for Image { 342 | fn drop(&mut self) { 343 | unsafe { 344 | self.context.device().vkDestroyImage.unwrap()( 345 | self.context.vk_device(), 346 | self.image, 347 | ptr::null(), // allocator 348 | ); 349 | } 350 | } 351 | } 352 | 353 | impl Image { 354 | fn new_from_create_info( 355 | context: Rc<Context>, 356 | image_create_info: &vk::VkImageCreateInfo, 357 | ) -> Result<Image, WindowError> { 358 | let mut image: vk::VkImage = vk::null_handle(); 359 | 360 | let res = unsafe { 361 | context.device().vkCreateImage.unwrap()( 362 | context.vk_device(), 363 | image_create_info as *const vk::VkImageCreateInfo, 364 | ptr::null(), // allocator 365 | ptr::addr_of_mut!(image), 366 | ) 367 | }; 368 | 369 | if res == vk::VK_SUCCESS { 370 | Ok(Image { image, context }) 371 | } else { 372 | Err(WindowError::ImageError) 373 | } 374 | } 375 | 376 | fn new_color( 377 | context: Rc<Context>, 378 | window_format: &WindowFormat, 379 | ) -> Result<Image, WindowError> { 380 | let image_create_info = vk::VkImageCreateInfo { 381 | sType: vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 382 | pNext: ptr::null(), 383 | flags: 0, 384 | imageType: vk::VK_IMAGE_TYPE_2D, 385 | format: window_format.color_format.vk_format, 386 | extent: vk::VkExtent3D { 387 | width: window_format.width as u32, 388 | height: window_format.height as u32, 389 | depth: 1, 390 | }, 391 | mipLevels: 1, 392 | arrayLayers: 1, 393 | samples: vk::VK_SAMPLE_COUNT_1_BIT, 394 | tiling: vk::VK_IMAGE_TILING_OPTIMAL, 395 | usage: vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT 396 | | vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 397 | sharingMode: vk::VK_SHARING_MODE_EXCLUSIVE, 398 | queueFamilyIndexCount: 0, 399 | pQueueFamilyIndices: ptr::null(), 400 | initialLayout: vk::VK_IMAGE_LAYOUT_UNDEFINED, 401 | }; 402 | 403 | Image::new_from_create_info(context, &image_create_info) 404 | } 405 | 406 | fn new_depth_stencil( 407 | context: Rc<Context>, 408 | format: &Format, 409 | width: usize, 410 | height: usize, 411 | ) -> Result<Image, WindowError> { 412 | let image_create_info = vk::VkImageCreateInfo { 413 | sType: vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 414 | pNext: ptr::null(), 415 | flags: 0, 416 | imageType: vk::VK_IMAGE_TYPE_2D, 417 | format: format.vk_format, 418 | extent: vk::VkExtent3D { 419 | width: width as u32, 420 | height: height as u32, 421 | depth: 1, 422 | }, 423 | mipLevels: 1, 424 | arrayLayers: 1, 425 | samples: vk::VK_SAMPLE_COUNT_1_BIT, 426 | tiling: vk::VK_IMAGE_TILING_OPTIMAL, 427 | usage: vk::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 428 | sharingMode: vk::VK_SHARING_MODE_EXCLUSIVE, 429 | queueFamilyIndexCount: 0, 430 | pQueueFamilyIndices: ptr::null(), 431 | initialLayout: vk::VK_IMAGE_LAYOUT_UNDEFINED, 432 | }; 433 | 434 | Image::new_from_create_info(context, &image_create_info) 435 | } 436 | } 437 | 438 | #[derive(Debug)] 439 | struct ImageView { 440 | image_view: vk::VkImageView, 441 | // Needed for the destructor 442 | context: Rc<Context>, 443 | } 444 | 445 | impl Drop for ImageView { 446 | fn drop(&mut self) { 447 | unsafe { 448 | self.context.device().vkDestroyImageView.unwrap()( 449 | self.context.vk_device(), 450 | self.image_view, 451 | ptr::null(), // allocator 452 | ); 453 | } 454 | } 455 | } 456 | 457 | impl ImageView { 458 | fn new( 459 | context: Rc<Context>, 460 | format: &Format, 461 | image: vk::VkImage, 462 | aspect_mask: vk::VkImageAspectFlags, 463 | ) -> Result<ImageView, WindowError> { 464 | let image_view_create_info = vk::VkImageViewCreateInfo { 465 | sType: vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 466 | pNext: ptr::null(), 467 | flags: 0, 468 | image, 469 | viewType: vk::VK_IMAGE_VIEW_TYPE_2D, 470 | format: format.vk_format, 471 | components: vk::VkComponentMapping { 472 | r: vk::VK_COMPONENT_SWIZZLE_R, 473 | g: vk::VK_COMPONENT_SWIZZLE_G, 474 | b: vk::VK_COMPONENT_SWIZZLE_B, 475 | a: vk::VK_COMPONENT_SWIZZLE_A, 476 | }, 477 | subresourceRange: vk::VkImageSubresourceRange { 478 | aspectMask: aspect_mask, 479 | baseMipLevel: 0, 480 | levelCount: 1, 481 | baseArrayLayer: 0, 482 | layerCount: 1 483 | }, 484 | }; 485 | 486 | let mut image_view: vk::VkImageView = vk::null_handle(); 487 | 488 | let res = unsafe { 489 | context.device().vkCreateImageView.unwrap()( 490 | context.vk_device(), 491 | ptr::addr_of!(image_view_create_info), 492 | ptr::null(), // allocator 493 | ptr::addr_of_mut!(image_view), 494 | ) 495 | }; 496 | 497 | if res == vk::VK_SUCCESS { 498 | Ok(ImageView { image_view, context }) 499 | } else { 500 | Err(WindowError::ImageViewError) 501 | } 502 | } 503 | } 504 | 505 | impl DepthStencilResources { 506 | fn new( 507 | context: Rc<Context>, 508 | format: &Format, 509 | width: usize, 510 | height: usize, 511 | ) -> Result<DepthStencilResources, WindowError> { 512 | let image = Image::new_depth_stencil( 513 | Rc::clone(&context), 514 | format, 515 | width, 516 | height, 517 | )?; 518 | let memory = DeviceMemory::new_image( 519 | Rc::clone(&context), 520 | 0, // memory_type_flags 521 | image.image, 522 | )?; 523 | let image_view = ImageView::new( 524 | context, 525 | format, 526 | image.image, 527 | format.depth_stencil_aspect_flags(), 528 | )?; 529 | 530 | Ok(DepthStencilResources { _image: image, _memory: memory, image_view }) 531 | } 532 | } 533 | 534 | #[derive(Debug)] 535 | struct Framebuffer { 536 | framebuffer: vk::VkFramebuffer, 537 | // Needed for the destructor 538 | context: Rc<Context>, 539 | } 540 | 541 | impl Drop for Framebuffer { 542 | fn drop(&mut self) { 543 | unsafe { 544 | self.context.device().vkDestroyFramebuffer.unwrap()( 545 | self.context.vk_device(), 546 | self.framebuffer, 547 | ptr::null(), // allocator 548 | ); 549 | } 550 | } 551 | } 552 | 553 | impl Framebuffer { 554 | fn new( 555 | context: Rc<Context>, 556 | window_format: &WindowFormat, 557 | render_pass: vk::VkRenderPass, 558 | color_image_view: vk::VkImageView, 559 | depth_stencil_image_view: Option<vk::VkImageView>, 560 | ) -> Result<Framebuffer, WindowError> { 561 | let mut attachments = vec![color_image_view]; 562 | 563 | if let Some(image_view) = depth_stencil_image_view { 564 | attachments.push(image_view); 565 | } 566 | 567 | let framebuffer_create_info = vk::VkFramebufferCreateInfo { 568 | sType: vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 569 | pNext: ptr::null(), 570 | flags: 0, 571 | renderPass: render_pass, 572 | attachmentCount: attachments.len() as u32, 573 | pAttachments: attachments.as_ptr(), 574 | width: window_format.width as u32, 575 | height: window_format.height as u32, 576 | layers: 1, 577 | }; 578 | 579 | let mut framebuffer: vk::VkFramebuffer = vk::null_handle(); 580 | 581 | let res = unsafe { 582 | context.device().vkCreateFramebuffer.unwrap()( 583 | context.vk_device(), 584 | ptr::addr_of!(framebuffer_create_info), 585 | ptr::null(), // allocator 586 | ptr::addr_of_mut!(framebuffer), 587 | ) 588 | }; 589 | 590 | if res == vk::VK_SUCCESS { 591 | Ok(Framebuffer { framebuffer, context }) 592 | } else { 593 | Err(WindowError::FramebufferError) 594 | } 595 | } 596 | } 597 | 598 | fn need_linear_memory_invalidate( 599 | context: &Context, 600 | linear_memory_type: u32, 601 | ) -> bool { 602 | context 603 | .memory_properties() 604 | .memoryTypes[linear_memory_type as usize] 605 | .propertyFlags 606 | & vk::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT == 0 607 | } 608 | 609 | impl Window { 610 | pub fn new( 611 | context: Rc<Context>, 612 | format: &WindowFormat, 613 | ) -> Result<Window, WindowError> { 614 | check_window_format(&context, format)?; 615 | 616 | let render_pass = [ 617 | RenderPass::new(Rc::clone(&context), format, true)?, 618 | RenderPass::new(Rc::clone(&context), format, false)?, 619 | ]; 620 | 621 | let color_image = Image::new_color(Rc::clone(&context), format)?; 622 | let memory = DeviceMemory::new_image( 623 | Rc::clone(&context), 624 | 0, // memory_type_flags 625 | color_image.image, 626 | )?; 627 | 628 | let color_image_view = ImageView::new( 629 | Rc::clone(&context), 630 | format.color_format, 631 | color_image.image, 632 | vk::VK_IMAGE_ASPECT_COLOR_BIT, 633 | )?; 634 | 635 | let depth_stencil_resources = match &format.depth_stencil_format { 636 | Some(depth_stencil_format) => Some(DepthStencilResources::new( 637 | Rc::clone(&context), 638 | depth_stencil_format, 639 | format.width, 640 | format.height, 641 | )?), 642 | None => None, 643 | }; 644 | 645 | let framebuffer = Framebuffer::new( 646 | Rc::clone(&context), 647 | format, 648 | render_pass[0].render_pass, 649 | color_image_view.image_view, 650 | depth_stencil_resources.as_ref().map(|r| r.image_view.image_view), 651 | )?; 652 | 653 | let linear_memory_stride = format.color_format.size() * format.width; 654 | 655 | let linear_buffer = Buffer::new( 656 | Rc::clone(&context), 657 | linear_memory_stride * format.height, 658 | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT, 659 | )?; 660 | let linear_memory = DeviceMemory::new_buffer( 661 | Rc::clone(&context), 662 | vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 663 | linear_buffer.buffer, 664 | )?; 665 | 666 | let linear_memory_map = MappedMemory::new( 667 | Rc::clone(&context), 668 | linear_memory.memory, 669 | )?; 670 | 671 | Ok(Window { 672 | format: format.clone(), 673 | 674 | need_linear_memory_invalidate: need_linear_memory_invalidate( 675 | &context, 676 | linear_memory.memory_type_index, 677 | ), 678 | linear_memory_stride, 679 | linear_memory_map, 680 | linear_memory, 681 | linear_buffer, 682 | 683 | framebuffer, 684 | 685 | _depth_stencil_resources: depth_stencil_resources, 686 | 687 | _color_image_view: color_image_view, 688 | _memory: memory, 689 | color_image, 690 | 691 | render_pass, 692 | 693 | context, 694 | }) 695 | } 696 | 697 | /// Retrieve the [Context] that the Window was created with. 698 | pub fn context(&self) -> &Rc<Context> { 699 | &self.context 700 | } 701 | 702 | /// Retrieve the [WindowFormat] that the Window was created for. 703 | pub fn format(&self) -> &WindowFormat { 704 | &self.format 705 | } 706 | 707 | /// Retrieve the [Device](vulkan_funcs::Device) that the 708 | /// Window was created from. This is just a convenience function 709 | /// for getting the device from the [Context]. 710 | pub fn device(&self) -> &vulkan_funcs::Device { 711 | self.context.device() 712 | } 713 | 714 | /// Retrieve the [VkDevice](vk::VkDevice) that the window was 715 | /// created from. This is just a convenience function for getting 716 | /// the device from the [Context]. 717 | pub fn vk_device(&self) -> vk::VkDevice { 718 | self.context.vk_device() 719 | } 720 | 721 | /// Get the two [VkRenderPasses](vk::VkRenderPass) that were 722 | /// created for the window. The first render pass should be used 723 | /// for the first render and the second one should be used for all 724 | /// subsequent renders. 725 | pub fn render_passes(&self) -> [vk::VkRenderPass; 2] { 726 | [ 727 | self.render_pass[0].render_pass, 728 | self.render_pass[1].render_pass, 729 | ] 730 | } 731 | 732 | /// Get the vulkan handle to the linear memory that can be used to 733 | /// copy framebuffer results into in order to inspect it. 734 | pub fn linear_memory(&self) -> vk::VkDeviceMemory { 735 | self.linear_memory.memory 736 | } 737 | 738 | /// Get the `VkBuffer` that represents the linear memory buffer 739 | pub fn linear_buffer(&self) -> vk::VkBuffer { 740 | self.linear_buffer.buffer 741 | } 742 | 743 | /// Get the pointer to the mapping that the Window holds to 744 | /// examine the linear memory buffer. 745 | pub fn linear_memory_map(&self) -> *const c_void { 746 | self.linear_memory_map.pointer 747 | } 748 | 749 | /// Get the stride of the linear memory buffer 750 | pub fn linear_memory_stride(&self) -> usize { 751 | self.linear_memory_stride 752 | } 753 | 754 | /// Return whether the mapping for the linear memory buffer owned 755 | /// by the Window needs to be invalidated with 756 | /// `vkInvalidateMappedMemoryRanges` before it can be read after 757 | /// it has been modified. This is will be true if the memory type 758 | /// used for the linear memory buffer doesn’t have the 759 | /// `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set. 760 | pub fn need_linear_memory_invalidate(&self) -> bool { 761 | self.need_linear_memory_invalidate 762 | } 763 | 764 | /// Return the `VkFramebuffer` that was created for the window. 765 | pub fn framebuffer(&self) -> vk::VkFramebuffer { 766 | self.framebuffer.framebuffer 767 | } 768 | 769 | /// Return the `VkImage` that was created for the color buffer of 770 | /// the window. 771 | pub fn color_image(&self) -> vk::VkImage { 772 | self.color_image.image 773 | } 774 | } 775 | 776 | #[cfg(test)] 777 | mod test { 778 | use super::*; 779 | use crate::fake_vulkan::{FakeVulkan, HandleType}; 780 | use crate::requirements::Requirements; 781 | 782 | fn base_fake_vulkan() -> Box<FakeVulkan> { 783 | let mut fake_vulkan = FakeVulkan::new(); 784 | 785 | fake_vulkan.physical_devices.push(Default::default()); 786 | fake_vulkan.physical_devices[0].format_properties.insert( 787 | vk::VK_FORMAT_B8G8R8A8_UNORM, 788 | vk::VkFormatProperties { 789 | linearTilingFeatures: 0, 790 | optimalTilingFeatures: 791 | vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT 792 | | vk::VK_FORMAT_FEATURE_BLIT_SRC_BIT, 793 | bufferFeatures: 0, 794 | }, 795 | ); 796 | 797 | fake_vulkan.physical_devices[0].format_properties.insert( 798 | vk::VK_FORMAT_D24_UNORM_S8_UINT, 799 | vk::VkFormatProperties { 800 | linearTilingFeatures: 0, 801 | optimalTilingFeatures: 802 | vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, 803 | bufferFeatures: 0, 804 | }, 805 | ); 806 | 807 | let memory_properties = 808 | &mut fake_vulkan.physical_devices[0].memory_properties; 809 | memory_properties.memoryTypes[0].propertyFlags = 810 | vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; 811 | memory_properties.memoryTypeCount = 1; 812 | fake_vulkan.memory_requirements.memoryTypeBits = 1; 813 | 814 | fake_vulkan 815 | } 816 | 817 | fn get_render_pass_attachments( 818 | fake_vulkan: &mut FakeVulkan, 819 | render_pass: vk::VkRenderPass, 820 | ) -> &[vk::VkAttachmentDescription] { 821 | match &fake_vulkan.get_handle(render_pass).data { 822 | HandleType::RenderPass { attachments } => attachments.as_slice(), 823 | _ => unreachable!("mismatched handle type"), 824 | } 825 | } 826 | 827 | struct TestResources { 828 | // These two need to be dropped in this order 829 | context: Rc<Context>, 830 | fake_vulkan: Box<FakeVulkan>, 831 | } 832 | 833 | fn test_simple_function_error( 834 | function_name: &str, 835 | expected_error_string: &str, 836 | ) -> TestResources { 837 | let mut fake_vulkan = base_fake_vulkan(); 838 | fake_vulkan.queue_result( 839 | function_name.to_string(), 840 | vk::VK_ERROR_UNKNOWN 841 | ); 842 | 843 | fake_vulkan.set_override(); 844 | let context = Rc::new(Context::new( 845 | &Requirements::new(), 846 | None 847 | ).unwrap()); 848 | 849 | let err = Window::new( 850 | Rc::clone(&context), 851 | &Default::default(), // format 852 | ).unwrap_err(); 853 | 854 | assert_eq!(&err.to_string(), expected_error_string); 855 | assert_eq!(err.result(), result::Result::Fail); 856 | 857 | TestResources { context, fake_vulkan } 858 | } 859 | 860 | #[test] 861 | fn basic() { 862 | let mut fake_vulkan = base_fake_vulkan(); 863 | 864 | fake_vulkan.set_override(); 865 | let context = Rc::new(Context::new( 866 | &Requirements::new(), 867 | None 868 | ).unwrap()); 869 | 870 | let window = Window::new( 871 | Rc::clone(&context), 872 | &Default::default() // format 873 | ).unwrap(); 874 | 875 | assert!(Rc::ptr_eq(window.context(), &context)); 876 | assert_eq!( 877 | window.format().color_format.vk_format, 878 | vk::VK_FORMAT_B8G8R8A8_UNORM 879 | ); 880 | assert_eq!( 881 | window.device() as *const vulkan_funcs::Device, 882 | context.device() as *const vulkan_funcs::Device, 883 | ); 884 | assert_eq!(window.vk_device(), context.vk_device()); 885 | assert_ne!(window.render_passes()[0], window.render_passes()[1]); 886 | assert!(window.linear_memory() != vk::null_handle()); 887 | assert!(window.linear_buffer() != vk::null_handle()); 888 | assert!(!window.linear_memory_map().is_null()); 889 | assert_eq!( 890 | window.linear_memory_stride(), 891 | window.format().color_format.size() 892 | * window.format().width 893 | ); 894 | assert!(window.need_linear_memory_invalidate()); 895 | assert!(window.framebuffer() != vk::null_handle()); 896 | assert!(window.color_image() != vk::null_handle()); 897 | 898 | let rp = get_render_pass_attachments( 899 | fake_vulkan.as_mut(), 900 | window.render_passes()[0] 901 | ); 902 | assert_eq!(rp.len(), 1); 903 | assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 904 | assert_eq!(rp[0].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED); 905 | assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 906 | 907 | let rp = get_render_pass_attachments( 908 | fake_vulkan.as_mut(), 909 | window.render_passes()[1] 910 | ); 911 | assert_eq!(rp.len(), 1); 912 | assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD); 913 | assert_eq!( 914 | rp[0].initialLayout, 915 | vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 916 | ); 917 | assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 918 | } 919 | 920 | #[test] 921 | fn depth_stencil() { 922 | let mut fake_vulkan = base_fake_vulkan(); 923 | 924 | let mut depth_format = WindowFormat::default(); 925 | 926 | depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format( 927 | vk::VK_FORMAT_D24_UNORM_S8_UINT, 928 | )); 929 | 930 | fake_vulkan.set_override(); 931 | let context = Rc::new(Context::new( 932 | &Requirements::new(), 933 | None 934 | ).unwrap()); 935 | 936 | let window = Window::new( 937 | Rc::clone(&context), 938 | &depth_format, 939 | ).unwrap(); 940 | 941 | let rp = get_render_pass_attachments( 942 | fake_vulkan.as_mut(), 943 | window.render_passes()[0] 944 | ); 945 | assert_eq!(rp.len(), 2); 946 | assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 947 | assert_eq!(rp[0].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED); 948 | assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 949 | assert_eq!(rp[1].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 950 | assert_eq!(rp[1].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED); 951 | assert_eq!(rp[1].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 952 | assert_eq!(rp[1].stencilStoreOp, vk::VK_ATTACHMENT_STORE_OP_STORE); 953 | 954 | let rp = get_render_pass_attachments( 955 | fake_vulkan.as_mut(), 956 | window.render_passes()[1] 957 | ); 958 | assert_eq!(rp.len(), 2); 959 | assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD); 960 | assert_eq!( 961 | rp[0].initialLayout, 962 | vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 963 | ); 964 | assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE); 965 | assert_eq!(rp[1].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD); 966 | assert_eq!( 967 | rp[1].initialLayout, 968 | vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL 969 | ); 970 | assert_eq!(rp[1].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD); 971 | assert_eq!(rp[1].stencilStoreOp, vk::VK_ATTACHMENT_STORE_OP_STORE); 972 | } 973 | 974 | #[test] 975 | fn incompatible_format() { 976 | let mut fake_vulkan = base_fake_vulkan(); 977 | 978 | fake_vulkan 979 | .physical_devices[0] 980 | .format_properties 981 | .insert( 982 | vk::VK_FORMAT_R8_UNORM, 983 | vk::VkFormatProperties { 984 | linearTilingFeatures: 0, 985 | optimalTilingFeatures: 0, 986 | bufferFeatures: 0, 987 | }, 988 | ); 989 | 990 | let mut depth_format = WindowFormat::default(); 991 | 992 | depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format( 993 | vk::VK_FORMAT_R8_UNORM, 994 | )); 995 | 996 | fake_vulkan.set_override(); 997 | let context = Rc::new(Context::new( 998 | &Requirements::new(), 999 | None 1000 | ).unwrap()); 1001 | 1002 | let err = Window::new( 1003 | Rc::clone(&context), 1004 | &depth_format, 1005 | ).unwrap_err(); 1006 | 1007 | assert_eq!( 1008 | &err.to_string(), 1009 | "Format R8_UNORM is not supported as a depth/stencil attachment", 1010 | ); 1011 | assert_eq!(err.result(), result::Result::Skip); 1012 | 1013 | fake_vulkan 1014 | .physical_devices[0] 1015 | .format_properties 1016 | .get_mut(&vk::VK_FORMAT_B8G8R8A8_UNORM) 1017 | .unwrap() 1018 | .optimalTilingFeatures 1019 | &= !vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; 1020 | 1021 | fake_vulkan.set_override(); 1022 | let context = Rc::new(Context::new( 1023 | &Requirements::new(), 1024 | None 1025 | ).unwrap()); 1026 | 1027 | let err = Window::new( 1028 | Rc::clone(&context), 1029 | &Default::default(), // format 1030 | ).unwrap_err(); 1031 | 1032 | assert_eq!( 1033 | &err.to_string(), 1034 | "Format B8G8R8A8_UNORM is not supported as a color attachment \ 1035 | and blit source", 1036 | ); 1037 | assert_eq!(err.result(), result::Result::Skip); 1038 | } 1039 | 1040 | #[test] 1041 | fn render_pass_error() { 1042 | let mut res = test_simple_function_error( 1043 | "vkCreateRenderPass", 1044 | "Error creating render pass" 1045 | ); 1046 | 1047 | // Try making the second render pass fail too 1048 | res.fake_vulkan.queue_result( 1049 | "vkCreateRenderPass".to_string(), 1050 | vk::VK_SUCCESS 1051 | ); 1052 | res.fake_vulkan.queue_result( 1053 | "vkCreateRenderPass".to_string(), 1054 | vk::VK_ERROR_UNKNOWN 1055 | ); 1056 | 1057 | let err = Window::new( 1058 | Rc::clone(&res.context), 1059 | &Default::default(), // format 1060 | ).unwrap_err(); 1061 | 1062 | assert_eq!(&err.to_string(), "Error creating render pass"); 1063 | } 1064 | 1065 | #[test] 1066 | fn image_error() { 1067 | let mut res = test_simple_function_error( 1068 | "vkCreateImage", 1069 | "Error creating vkImage" 1070 | ); 1071 | 1072 | // Also try the depth/stencil image 1073 | res.fake_vulkan.queue_result( 1074 | "vkCreateImage".to_string(), 1075 | vk::VK_SUCCESS 1076 | ); 1077 | res.fake_vulkan.queue_result( 1078 | "vkCreateImage".to_string(), 1079 | vk::VK_ERROR_UNKNOWN 1080 | ); 1081 | 1082 | let mut depth_format = WindowFormat::default(); 1083 | 1084 | depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format( 1085 | vk::VK_FORMAT_D24_UNORM_S8_UINT, 1086 | )); 1087 | 1088 | let err = Window::new( 1089 | Rc::clone(&res.context), 1090 | &depth_format, 1091 | ).unwrap_err(); 1092 | 1093 | assert_eq!(&err.to_string(), "Error creating vkImage"); 1094 | } 1095 | 1096 | #[test] 1097 | fn image_view_error() { 1098 | let mut res = test_simple_function_error( 1099 | "vkCreateImageView", 1100 | "Error creating vkImageView" 1101 | ); 1102 | 1103 | // Also try the depth/stencil image 1104 | res.fake_vulkan.queue_result( 1105 | "vkCreateImageView".to_string(), 1106 | vk::VK_SUCCESS 1107 | ); 1108 | res.fake_vulkan.queue_result( 1109 | "vkCreateImageView".to_string(), 1110 | vk::VK_ERROR_UNKNOWN 1111 | ); 1112 | 1113 | let mut depth_format = WindowFormat::default(); 1114 | 1115 | depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format( 1116 | vk::VK_FORMAT_D24_UNORM_S8_UINT, 1117 | )); 1118 | 1119 | let err = Window::new( 1120 | Rc::clone(&res.context), 1121 | &depth_format, 1122 | ).unwrap_err(); 1123 | 1124 | assert_eq!(&err.to_string(), "Error creating vkImageView"); 1125 | } 1126 | 1127 | #[test] 1128 | fn allocate_store_error() { 1129 | let mut res = test_simple_function_error( 1130 | "vkAllocateMemory", 1131 | "vkAllocateMemory failed" 1132 | ); 1133 | 1134 | // Make the second allocate fail to so that the depth/stencil 1135 | // image will fail 1136 | res.fake_vulkan.queue_result( 1137 | "vkAllocateMemory".to_string(), 1138 | vk::VK_SUCCESS, 1139 | ); 1140 | res.fake_vulkan.queue_result( 1141 | "vkAllocateMemory".to_string(), 1142 | vk::VK_ERROR_UNKNOWN, 1143 | ); 1144 | 1145 | let mut depth_format = WindowFormat::default(); 1146 | 1147 | depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format( 1148 | vk::VK_FORMAT_D24_UNORM_S8_UINT, 1149 | )); 1150 | 1151 | let err = Window::new( 1152 | Rc::clone(&res.context), 1153 | &depth_format, 1154 | ).unwrap_err(); 1155 | 1156 | assert_eq!(&err.to_string(), "vkAllocateMemory failed"); 1157 | assert_eq!(err.result(), result::Result::Fail); 1158 | 1159 | // Remove the host visible bit so the linear memory won’t allocate 1160 | let memory_properties = 1161 | &mut res.fake_vulkan.physical_devices[0].memory_properties; 1162 | memory_properties.memoryTypes[0].propertyFlags &= 1163 | !vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; 1164 | 1165 | res.fake_vulkan.set_override(); 1166 | let context = Rc::new(Context::new( 1167 | &Requirements::new(), 1168 | None 1169 | ).unwrap()); 1170 | 1171 | let err = Window::new( 1172 | Rc::clone(&context), 1173 | &Default::default(), // format 1174 | ).unwrap_err(); 1175 | 1176 | assert_eq!( 1177 | &err.to_string(), 1178 | "Couldn’t find suitable memory type to allocate buffer", 1179 | ); 1180 | assert_eq!(err.result(), result::Result::Fail); 1181 | } 1182 | 1183 | #[test] 1184 | fn map_memory_error() { 1185 | test_simple_function_error("vkMapMemory", "vkMapMemory failed"); 1186 | } 1187 | 1188 | #[test] 1189 | fn buffer_error() { 1190 | test_simple_function_error("vkCreateBuffer", "Error creating vkBuffer"); 1191 | } 1192 | 1193 | #[test] 1194 | fn framebuffer_error() { 1195 | test_simple_function_error( 1196 | "vkCreateFramebuffer", 1197 | "Error creating vkFramebuffer", 1198 | ); 1199 | } 1200 | 1201 | #[test] 1202 | fn rectangular_framebuffer() { 1203 | let fake_vulkan = base_fake_vulkan(); 1204 | 1205 | fake_vulkan.set_override(); 1206 | let context = Rc::new(Context::new( 1207 | &Requirements::new(), 1208 | None 1209 | ).unwrap()); 1210 | 1211 | let mut format = WindowFormat::default(); 1212 | 1213 | format.width = 200; 1214 | format.height = 100; 1215 | 1216 | let window = Window::new( 1217 | Rc::clone(&context), 1218 | &format, 1219 | ).unwrap(); 1220 | 1221 | assert_eq!( 1222 | window.linear_memory_stride(), 1223 | 200 * format.color_format.size() 1224 | ); 1225 | 1226 | let HandleType::Memory { ref contents, .. } = 1227 | fake_vulkan.get_handle(window.linear_memory()).data 1228 | else { unreachable!("Mismatched handle"); }; 1229 | 1230 | assert_eq!( 1231 | contents.len(), 1232 | window.linear_memory_stride() * 100, 1233 | ); 1234 | } 1235 | } 1236 | ``` -------------------------------------------------------------------------------- /vkrunner/vkrunner/context.rs: -------------------------------------------------------------------------------- ```rust 1 | // vkrunner 2 | // 3 | // Copyright (C) 2013, 2014, 2015, 2017, 2023 Neil Roberts 4 | // Copyright (C) 2018 Intel Corporation 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, including without limitation 9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | // and/or sell copies of the Software, and to permit persons to whom the 11 | // Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice (including the next 14 | // paragraph) shall be included in all copies or substantial portions of the 15 | // Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | // DEALINGS IN THE SOFTWARE. 24 | 25 | use crate::vk; 26 | use crate::requirements::{self, Requirements}; 27 | use crate::vulkan_funcs; 28 | use crate::util::env_var_as_boolean; 29 | use crate::result; 30 | use std::ffi::{c_char, c_void, CStr}; 31 | use std::fmt; 32 | use std::ptr; 33 | 34 | /// Struct containing the VkDevice and accessories such as a 35 | /// VkCommandPool, VkQueue and the function pointers from 36 | /// [vulkan_funcs]. 37 | #[derive(Debug)] 38 | pub struct Context { 39 | // These three need to be in this order so that they will be 40 | // dropped in the correct order. 41 | device_pair: DevicePair, 42 | instance_pair: InstancePair, 43 | // This isn’t read anywhere but we want to keep it alive for the 44 | // duration of the Context so that the library won’t be unloaded. 45 | _vklib: Option<Box<vulkan_funcs::Library>>, 46 | 47 | physical_device: vk::VkPhysicalDevice, 48 | 49 | memory_properties: vk::VkPhysicalDeviceMemoryProperties, 50 | 51 | command_pool: vk::VkCommandPool, 52 | command_buffer: vk::VkCommandBuffer, 53 | fence: vk::VkFence, 54 | 55 | queue: vk::VkQueue, 56 | 57 | always_flush_memory: bool, 58 | } 59 | 60 | /// Error returned by [Context::new] 61 | #[derive(Debug)] 62 | pub enum Error { 63 | FuncsError(vulkan_funcs::Error), 64 | RequirementsError(requirements::Error), 65 | EnumerateInstanceExtensionPropertiesFailed, 66 | MissingInstanceExtension(String), 67 | IncompatibleDriver, 68 | CreateInstanceFailed, 69 | CreateDeviceFailed, 70 | CreateCommandPoolFailed, 71 | CommandBufferAllocateFailed, 72 | CreateFenceFailed, 73 | EnumeratePhysicalDevicesFailed, 74 | NoDevices, 75 | /// None of the drivers succeeded and the vector is an error for 76 | /// each possible driver. 77 | DeviceErrors(Vec<Error>), 78 | NoGraphicsQueueFamily, 79 | InvalidDeviceId { device_id: usize, n_devices: u32 }, 80 | } 81 | 82 | impl From<vulkan_funcs::Error> for Error { 83 | fn from(error: vulkan_funcs::Error) -> Error { 84 | Error::FuncsError(error) 85 | } 86 | } 87 | 88 | impl From<requirements::Error> for Error { 89 | fn from(error: requirements::Error) -> Error { 90 | Error::RequirementsError(error) 91 | } 92 | } 93 | 94 | impl fmt::Display for Error { 95 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 96 | match self { 97 | Error::FuncsError(e) => e.fmt(f), 98 | Error::RequirementsError(e) => e.fmt(f), 99 | Error::EnumerateInstanceExtensionPropertiesFailed => { 100 | write!(f, "vkEnumerateInstanceExtensionProperties failed") 101 | }, 102 | Error::MissingInstanceExtension(s) => { 103 | write!(f, "Missing instance extension: {}", s) 104 | }, 105 | Error::IncompatibleDriver => { 106 | write!( 107 | f, 108 | "vkCreateInstance reported VK_ERROR_INCOMPATIBLE_DRIVER", 109 | ) 110 | }, 111 | Error::CreateInstanceFailed => write!(f, "vkCreateInstance failed"), 112 | Error::CreateDeviceFailed => write!(f, "vkCreateDevice failed"), 113 | Error::CreateCommandPoolFailed => { 114 | write!(f, "vkCreateCommandPool failed") 115 | }, 116 | Error::CommandBufferAllocateFailed => { 117 | write!(f, "vkCommandBufferAllocate failed") 118 | }, 119 | Error::CreateFenceFailed => { 120 | write!(f, "vkCreateFence failed") 121 | }, 122 | Error::EnumeratePhysicalDevicesFailed => { 123 | write!(f, "vkEnumeratePhysicalDevices failed") 124 | }, 125 | Error::NoDevices => { 126 | write!(f, "The Vulkan instance reported zero drivers") 127 | }, 128 | Error::DeviceErrors(errors) => { 129 | for (i, error) in errors.iter().enumerate() { 130 | if i > 0 { 131 | writeln!(f)?; 132 | } 133 | 134 | write!(f, "{}: {}", i, error)?; 135 | } 136 | Ok(()) 137 | }, 138 | Error::NoGraphicsQueueFamily => { 139 | write!( 140 | f, 141 | "Device has no graphics queue family" 142 | ) 143 | }, 144 | &Error::InvalidDeviceId { device_id, n_devices } => { 145 | write!( 146 | f, 147 | "Device {} was selected but the Vulkan instance only \ 148 | reported {} device{}.", 149 | device_id.saturating_add(1), 150 | n_devices, 151 | if n_devices == 1 { "" } else { "s" }, 152 | ) 153 | }, 154 | } 155 | } 156 | } 157 | 158 | impl Error { 159 | pub fn result(&self) -> result::Result { 160 | match self { 161 | Error::FuncsError(_) => result::Result::Fail, 162 | Error::RequirementsError(e) => e.result(), 163 | Error::EnumerateInstanceExtensionPropertiesFailed => { 164 | result::Result::Fail 165 | }, 166 | Error::MissingInstanceExtension(_) => result::Result::Skip, 167 | Error::IncompatibleDriver => result::Result::Skip, 168 | Error::CreateInstanceFailed => result::Result::Fail, 169 | Error::CreateDeviceFailed => result::Result::Fail, 170 | Error::CreateCommandPoolFailed => result::Result::Fail, 171 | Error::CommandBufferAllocateFailed => result::Result::Fail, 172 | Error::CreateFenceFailed => result::Result::Fail, 173 | Error::EnumeratePhysicalDevicesFailed => result::Result::Fail, 174 | Error::NoDevices => result::Result::Skip, 175 | Error::DeviceErrors(errors) => { 176 | // If all of the errors were Fail then we’ll return 177 | // failure overall, otherwise we’ll return Skip. 178 | if errors.iter().all(|e| e.result() == result::Result::Fail) { 179 | result::Result::Fail 180 | } else { 181 | result::Result::Skip 182 | } 183 | }, 184 | Error::NoGraphicsQueueFamily => result::Result::Skip, 185 | Error::InvalidDeviceId { .. } => result::Result::Fail, 186 | } 187 | } 188 | } 189 | 190 | struct GetInstanceProcClosure<'a> { 191 | vklib: &'a vulkan_funcs::Library, 192 | vk_instance: vk::VkInstance, 193 | } 194 | 195 | extern "C" fn get_instance_proc( 196 | func_name: *const c_char, 197 | user_data: *const c_void, 198 | ) -> *const c_void { 199 | unsafe { 200 | let data: &GetInstanceProcClosure = &*user_data.cast(); 201 | let vklib = data.vklib; 202 | std::mem::transmute( 203 | vklib.vkGetInstanceProcAddr.unwrap()( 204 | data.vk_instance, 205 | func_name.cast() 206 | ) 207 | ) 208 | } 209 | } 210 | 211 | // ext is a zero-terminated byte array, as one of the constants in 212 | // vulkan_bindings. 213 | fn check_instance_extension( 214 | vklib: &vulkan_funcs::Library, 215 | ext: &[u8], 216 | ) -> Result<(), Error> { 217 | let mut count: u32 = 0; 218 | 219 | let res = unsafe { 220 | vklib.vkEnumerateInstanceExtensionProperties.unwrap()( 221 | ptr::null(), // layerName 222 | ptr::addr_of_mut!(count), 223 | ptr::null_mut(), // props 224 | ) 225 | }; 226 | 227 | if res != vk::VK_SUCCESS { 228 | return Err(Error::EnumerateInstanceExtensionPropertiesFailed); 229 | } 230 | 231 | let mut props = Vec::<vk::VkExtensionProperties>::new(); 232 | props.resize_with(count as usize, Default::default); 233 | 234 | let res = unsafe { 235 | vklib.vkEnumerateInstanceExtensionProperties.unwrap()( 236 | ptr::null(), // layerName 237 | ptr::addr_of_mut!(count), 238 | props.as_mut_ptr(), 239 | ) 240 | }; 241 | 242 | if res != vk::VK_SUCCESS { 243 | return Err(Error::EnumerateInstanceExtensionPropertiesFailed); 244 | } 245 | 246 | 'prop_loop: for prop in props.iter() { 247 | assert!(ext.len() <= prop.extensionName.len()); 248 | 249 | // Can’t just compare the slices because one is i8 and the 250 | // other is u8. ext should include the null terminator so this 251 | // will check for null too. 252 | 253 | for (i, &c) in ext.iter().enumerate() { 254 | if c as c_char != prop.extensionName[i] { 255 | continue 'prop_loop; 256 | } 257 | } 258 | 259 | // If we made it here then it’s the same extension name 260 | return Ok(()); 261 | } 262 | 263 | let ext_name = CStr::from_bytes_with_nul(ext).unwrap(); 264 | 265 | Err(Error::MissingInstanceExtension(ext_name.to_string_lossy().to_string())) 266 | } 267 | 268 | // Struct that contains a VkInstance and its function pointers from 269 | // vulkan_funcs. This is needed so that it can have a drop 270 | // implementation that uses the function pointers to free the 271 | // instance. 272 | #[derive(Debug)] 273 | struct InstancePair { 274 | is_external: bool, 275 | vk_instance: vk::VkInstance, 276 | vkinst: Box<vulkan_funcs::Instance>, 277 | } 278 | 279 | impl InstancePair { 280 | fn new( 281 | vklib: &vulkan_funcs::Library, 282 | requirements: &Requirements 283 | ) -> Result<InstancePair, Error> { 284 | let application_info = vk::VkApplicationInfo { 285 | sType: vk::VK_STRUCTURE_TYPE_APPLICATION_INFO, 286 | pNext: ptr::null(), 287 | pApplicationName: "vkrunner\0".as_ptr().cast(), 288 | applicationVersion: 0, 289 | pEngineName: ptr::null(), 290 | engineVersion: 0, 291 | apiVersion: requirements.version(), 292 | }; 293 | 294 | let mut instance_create_info = vk::VkInstanceCreateInfo { 295 | sType: vk::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 296 | pNext: ptr::null(), 297 | flags: 0, 298 | pApplicationInfo: ptr::addr_of!(application_info), 299 | enabledLayerCount: 0, 300 | ppEnabledLayerNames: ptr::null(), 301 | enabledExtensionCount: 0, 302 | ppEnabledExtensionNames: ptr::null(), 303 | }; 304 | 305 | let ext = vk::VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; 306 | let structures = requirements.c_structures(); 307 | 308 | let mut enabled_extensions = Vec::<*const c_char>::new(); 309 | 310 | if structures.is_some() { 311 | check_instance_extension(vklib, ext)?; 312 | enabled_extensions.push(ext.as_ptr().cast()); 313 | } 314 | 315 | instance_create_info.enabledExtensionCount = 316 | enabled_extensions.len() as u32; 317 | instance_create_info.ppEnabledExtensionNames = 318 | enabled_extensions.as_ptr(); 319 | 320 | // VkInstance is a dispatchable handle so it is always a 321 | // pointer and we can’t use vk::null_handle. 322 | let mut vk_instance = ptr::null_mut(); 323 | 324 | let res = unsafe { 325 | vklib.vkCreateInstance.unwrap()( 326 | ptr::addr_of!(instance_create_info), 327 | ptr::null(), // allocator 328 | ptr::addr_of_mut!(vk_instance), 329 | ) 330 | }; 331 | 332 | match res { 333 | vk::VK_ERROR_INCOMPATIBLE_DRIVER => Err(Error::IncompatibleDriver), 334 | vk::VK_SUCCESS => { 335 | let vkinst = unsafe { 336 | let closure = GetInstanceProcClosure { 337 | vklib, 338 | vk_instance, 339 | }; 340 | 341 | Box::new(vulkan_funcs::Instance::new( 342 | get_instance_proc, 343 | ptr::addr_of!(closure).cast(), 344 | )) 345 | }; 346 | 347 | Ok(InstancePair { 348 | is_external: false, 349 | vk_instance, 350 | vkinst, 351 | }) 352 | }, 353 | _ => Err(Error::CreateInstanceFailed), 354 | } 355 | } 356 | 357 | fn new_external( 358 | get_instance_proc_cb: vulkan_funcs::GetInstanceProcFunc, 359 | user_data: *const c_void, 360 | ) -> InstancePair { 361 | InstancePair { 362 | is_external: true, 363 | vk_instance: ptr::null_mut(), 364 | vkinst: unsafe { 365 | Box::new(vulkan_funcs::Instance::new( 366 | get_instance_proc_cb, 367 | user_data, 368 | )) 369 | }, 370 | } 371 | } 372 | } 373 | 374 | impl Drop for InstancePair { 375 | fn drop(&mut self) { 376 | if !self.is_external { 377 | unsafe { 378 | self.vkinst.vkDestroyInstance.unwrap()( 379 | self.vk_instance, 380 | ptr::null(), // allocator 381 | ); 382 | } 383 | } 384 | } 385 | } 386 | 387 | // Struct that contains a VkDevice and its function pointers from 388 | // vulkan_funcs. This is needed so that it can have a drop 389 | // implementation that uses the function pointers to free the device. 390 | #[derive(Debug)] 391 | struct DevicePair { 392 | is_external: bool, 393 | device: vk::VkDevice, 394 | vkdev: Box<vulkan_funcs::Device>, 395 | } 396 | 397 | impl DevicePair { 398 | fn new( 399 | instance_pair: &InstancePair, 400 | requirements: &Requirements, 401 | physical_device: vk::VkPhysicalDevice, 402 | queue_family: u32, 403 | ) -> Result<DevicePair, Error> { 404 | let structures = requirements.c_structures(); 405 | let base_features = requirements.c_base_features(); 406 | let extensions = requirements.c_extensions(); 407 | 408 | let queue_priorities = [1.0f32]; 409 | 410 | let queue_create_info = vk::VkDeviceQueueCreateInfo { 411 | sType: vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 412 | pNext: ptr::null(), 413 | flags: 0, 414 | queueFamilyIndex: queue_family, 415 | queueCount: 1, 416 | pQueuePriorities: queue_priorities.as_ptr(), 417 | }; 418 | 419 | let device_create_info = vk::VkDeviceCreateInfo { 420 | sType: vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 421 | pNext: structures 422 | .and_then(|s| Some(s.as_ptr().cast())) 423 | .unwrap_or(ptr::null()), 424 | flags: 0, 425 | queueCreateInfoCount: 1, 426 | pQueueCreateInfos: ptr::addr_of!(queue_create_info), 427 | enabledLayerCount: 0, 428 | ppEnabledLayerNames: ptr::null(), 429 | enabledExtensionCount: extensions.len() as u32, 430 | ppEnabledExtensionNames: if extensions.len() > 0 { 431 | extensions.as_ptr().cast() 432 | } else { 433 | ptr::null() 434 | }, 435 | pEnabledFeatures: base_features, 436 | }; 437 | 438 | // VkDevice is a dispatchable handle so it is always a pointer 439 | // and we can’t use vk::null_handle. 440 | let mut device = ptr::null_mut(); 441 | 442 | let res = unsafe { 443 | instance_pair.vkinst.vkCreateDevice.unwrap()( 444 | physical_device, 445 | ptr::addr_of!(device_create_info), 446 | ptr::null(), // allocator 447 | ptr::addr_of_mut!(device), 448 | ) 449 | }; 450 | 451 | if res == vk::VK_SUCCESS { 452 | Ok(DevicePair { 453 | is_external: false, 454 | device, 455 | vkdev: Box::new(vulkan_funcs::Device::new( 456 | &instance_pair.vkinst, 457 | device, 458 | )), 459 | }) 460 | } else { 461 | Err(Error::CreateDeviceFailed) 462 | } 463 | } 464 | 465 | fn new_external( 466 | instance_pair: &InstancePair, 467 | device: vk::VkDevice, 468 | ) -> DevicePair { 469 | DevicePair { 470 | is_external: true, 471 | device, 472 | vkdev: Box::new(vulkan_funcs::Device::new( 473 | &instance_pair.vkinst, 474 | device, 475 | )), 476 | } 477 | } 478 | } 479 | 480 | impl Drop for DevicePair { 481 | fn drop(&mut self) { 482 | if !self.is_external { 483 | unsafe { 484 | self.vkdev.vkDestroyDevice.unwrap()( 485 | self.device, 486 | ptr::null(), // allocator 487 | ); 488 | } 489 | } 490 | } 491 | } 492 | 493 | fn free_resources( 494 | device_pair: &DevicePair, 495 | mut command_pool: Option<vk::VkCommandPool>, 496 | mut command_buffer: Option<vk::VkCommandBuffer>, 497 | mut fence: Option<vk::VkFence>, 498 | ) { 499 | unsafe { 500 | if let Some(fence) = fence.take() { 501 | device_pair.vkdev.vkDestroyFence.unwrap()( 502 | device_pair.device, 503 | fence, 504 | ptr::null() // allocator 505 | ); 506 | } 507 | 508 | if let Some(mut command_buffer) = command_buffer.take() { 509 | device_pair.vkdev.vkFreeCommandBuffers.unwrap()( 510 | device_pair.device, 511 | command_pool.unwrap(), 512 | 1, // commandBufferCount 513 | ptr::addr_of_mut!(command_buffer), 514 | ); 515 | } 516 | 517 | if let Some(command_pool) = command_pool.take() { 518 | device_pair.vkdev.vkDestroyCommandPool.unwrap()( 519 | device_pair.device, 520 | command_pool, 521 | ptr::null() // allocator 522 | ); 523 | } 524 | } 525 | } 526 | 527 | // This is a helper struct for creating the rest of the context 528 | // resources. Its members are optional so that it can handle 529 | // destruction if an error occurs between creating one of the 530 | // resources. 531 | struct DeviceResources<'a> { 532 | device_pair: &'a DevicePair, 533 | 534 | command_pool: Option<vk::VkCommandPool>, 535 | command_buffer: Option<vk::VkCommandBuffer>, 536 | fence: Option<vk::VkFence>, 537 | } 538 | 539 | impl<'a> DeviceResources<'a> { 540 | fn create_command_pool( 541 | &mut self, 542 | queue_family: u32, 543 | ) -> Result<(), Error> { 544 | let command_pool_create_info = vk::VkCommandPoolCreateInfo { 545 | sType: vk::VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 546 | pNext: ptr::null(), 547 | flags: vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, 548 | queueFamilyIndex: queue_family, 549 | }; 550 | let mut command_pool = vk::null_handle(); 551 | let res = unsafe { 552 | self.device_pair.vkdev.vkCreateCommandPool.unwrap()( 553 | self.device_pair.device, 554 | ptr::addr_of!(command_pool_create_info), 555 | ptr::null(), // allocator 556 | ptr::addr_of_mut!(command_pool), 557 | ) 558 | }; 559 | 560 | if res != vk::VK_SUCCESS { 561 | return Err(Error::CreateCommandPoolFailed); 562 | } 563 | 564 | self.command_pool = Some(command_pool); 565 | 566 | Ok(()) 567 | } 568 | 569 | fn allocate_command_buffer(&mut self) -> Result<(), Error> { 570 | let command_buffer_allocate_info = vk::VkCommandBufferAllocateInfo { 571 | sType: vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 572 | pNext: ptr::null(), 573 | commandPool: self.command_pool.unwrap(), 574 | level: vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY, 575 | commandBufferCount: 1, 576 | }; 577 | // VkCommandBuffer is a dispatchable handle so it is always a 578 | // pointer and we can’t use vk::null_handle. 579 | let mut command_buffer = ptr::null_mut(); 580 | let res = unsafe { 581 | self.device_pair.vkdev.vkAllocateCommandBuffers.unwrap()( 582 | self.device_pair.device, 583 | ptr::addr_of!(command_buffer_allocate_info), 584 | ptr::addr_of_mut!(command_buffer), 585 | ) 586 | }; 587 | 588 | if res != vk::VK_SUCCESS { 589 | return Err(Error::CommandBufferAllocateFailed); 590 | } 591 | 592 | self.command_buffer = Some(command_buffer); 593 | 594 | Ok(()) 595 | } 596 | 597 | fn create_fence(&mut self) -> Result<(), Error> { 598 | let fence_create_info = vk::VkFenceCreateInfo { 599 | sType: vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 600 | pNext: ptr::null(), 601 | flags: 0, 602 | }; 603 | let mut fence = vk::null_handle(); 604 | let res = unsafe { 605 | self.device_pair.vkdev.vkCreateFence.unwrap()( 606 | self.device_pair.device, 607 | ptr::addr_of!(fence_create_info), 608 | ptr::null(), // allocator 609 | ptr::addr_of_mut!(fence), 610 | ) 611 | }; 612 | if res != vk::VK_SUCCESS { 613 | return Err(Error::CreateFenceFailed); 614 | } 615 | 616 | self.fence = Some(fence); 617 | 618 | Ok(()) 619 | } 620 | 621 | fn new( 622 | device_pair: &'a DevicePair, 623 | queue_family: u32, 624 | ) -> Result<DeviceResources<'a>, Error> { 625 | let mut device_resources = DeviceResources { 626 | device_pair, 627 | command_pool: None, 628 | command_buffer: None, 629 | fence: None, 630 | }; 631 | 632 | device_resources.create_command_pool(queue_family)?; 633 | device_resources.allocate_command_buffer()?; 634 | device_resources.create_fence()?; 635 | 636 | Ok(device_resources) 637 | } 638 | } 639 | 640 | impl<'a> Drop for DeviceResources<'a> { 641 | fn drop(&mut self) { 642 | free_resources( 643 | self.device_pair, 644 | self.command_pool.take(), 645 | self.command_buffer.take(), 646 | self.fence.take(), 647 | ); 648 | } 649 | } 650 | 651 | fn find_queue_family( 652 | instance_pair: &InstancePair, 653 | physical_device: vk::VkPhysicalDevice, 654 | ) -> Result<u32, Error> { 655 | let vkinst = instance_pair.vkinst.as_ref(); 656 | 657 | let mut count = 0u32; 658 | 659 | unsafe { 660 | vkinst.vkGetPhysicalDeviceQueueFamilyProperties.unwrap()( 661 | physical_device, 662 | ptr::addr_of_mut!(count), 663 | ptr::null_mut(), // queues 664 | ); 665 | } 666 | 667 | let mut queues = Vec::<vk::VkQueueFamilyProperties>::new(); 668 | queues.resize_with(count as usize, Default::default); 669 | 670 | unsafe { 671 | vkinst.vkGetPhysicalDeviceQueueFamilyProperties.unwrap()( 672 | physical_device, 673 | ptr::addr_of_mut!(count), 674 | queues.as_mut_ptr(), 675 | ); 676 | } 677 | 678 | for (i, queue) in queues.into_iter().enumerate() { 679 | if queue.queueFlags & vk::VK_QUEUE_GRAPHICS_BIT != 0 && 680 | queue.queueCount >= 1 681 | { 682 | return Ok(i as u32); 683 | } 684 | } 685 | 686 | Err(Error::NoGraphicsQueueFamily) 687 | } 688 | 689 | // Checks whether the chosen physical device can be used and has an 690 | // appropriate queue family. If so, it returns the index of the queue 691 | // family, otherwise an error. 692 | fn check_physical_device( 693 | instance_pair: &InstancePair, 694 | requirements: &Requirements, 695 | physical_device: vk::VkPhysicalDevice, 696 | ) -> Result<u32, Error> { 697 | requirements.check( 698 | instance_pair.vkinst.as_ref(), 699 | physical_device 700 | )?; 701 | 702 | find_queue_family(instance_pair, physical_device) 703 | } 704 | 705 | // Checks all of the physical devices advertised by the instance. If 706 | // it can found one matching the requirements then it returns it along 707 | // with a queue family index of the first graphics queue. Otherwise 708 | // returns an error. 709 | fn find_physical_device( 710 | instance_pair: &InstancePair, 711 | requirements: &Requirements, 712 | device_id: Option<usize>, 713 | ) -> Result<(vk::VkPhysicalDevice, u32), Error> { 714 | let vkinst = instance_pair.vkinst.as_ref(); 715 | 716 | let mut count = 0u32; 717 | 718 | let res = unsafe { 719 | vkinst.vkEnumeratePhysicalDevices.unwrap()( 720 | instance_pair.vk_instance, 721 | ptr::addr_of_mut!(count), 722 | ptr::null_mut(), // devices 723 | ) 724 | }; 725 | 726 | if res != vk::VK_SUCCESS { 727 | return Err(Error::EnumeratePhysicalDevicesFailed); 728 | } 729 | 730 | let mut devices = Vec::<vk::VkPhysicalDevice>::new(); 731 | devices.resize(count as usize, ptr::null_mut()); 732 | 733 | let res = unsafe { 734 | vkinst.vkEnumeratePhysicalDevices.unwrap()( 735 | instance_pair.vk_instance, 736 | ptr::addr_of_mut!(count), 737 | devices.as_mut_ptr(), 738 | ) 739 | }; 740 | 741 | if res != vk::VK_SUCCESS { 742 | return Err(Error::EnumeratePhysicalDevicesFailed); 743 | } 744 | 745 | if let Some(device_id) = device_id { 746 | if device_id >= count as usize { 747 | return Err(Error::InvalidDeviceId { device_id, n_devices: count }); 748 | } else { 749 | return match check_physical_device( 750 | instance_pair, 751 | requirements, 752 | devices[device_id] 753 | ) { 754 | Ok(queue_family) => Ok((devices[device_id], queue_family)), 755 | Err(e) => Err(e), 756 | }; 757 | } 758 | } 759 | 760 | // Collect all of the errors into an array so we can report all of 761 | // them in a combined error message if we can’t find a good 762 | // device. 763 | let mut errors = Vec::<Error>::new(); 764 | 765 | for device in devices { 766 | match check_physical_device( 767 | instance_pair, 768 | requirements, 769 | device 770 | ) { 771 | Ok(queue_family) => return Ok((device, queue_family)), 772 | Err(e) => { 773 | errors.push(e); 774 | }, 775 | } 776 | } 777 | 778 | match errors.len() { 779 | 0 => Err(Error::NoDevices), 780 | 1 => Err(errors.pop().unwrap()), 781 | _ => Err(Error::DeviceErrors(errors)), 782 | } 783 | } 784 | 785 | impl Context { 786 | fn new_internal( 787 | vklib: Option<Box<vulkan_funcs::Library>>, 788 | instance_pair: InstancePair, 789 | physical_device: vk::VkPhysicalDevice, 790 | queue_family: u32, 791 | device_pair: DevicePair, 792 | ) -> Result<Context, Error> { 793 | let mut memory_properties = 794 | vk::VkPhysicalDeviceMemoryProperties::default(); 795 | 796 | unsafe { 797 | instance_pair.vkinst.vkGetPhysicalDeviceMemoryProperties.unwrap()( 798 | physical_device, 799 | ptr::addr_of_mut!(memory_properties), 800 | ); 801 | } 802 | 803 | // VkQueue is a dispatchable handle so it is always a pointer 804 | // and we can’t use vk::null_handle. 805 | let mut queue = ptr::null_mut(); 806 | 807 | unsafe { 808 | device_pair.vkdev.vkGetDeviceQueue.unwrap()( 809 | device_pair.device, 810 | queue_family, 811 | 0, // queueIndex 812 | ptr::addr_of_mut!(queue) 813 | ); 814 | } 815 | 816 | let mut resources = DeviceResources::new( 817 | &device_pair, 818 | queue_family, 819 | )?; 820 | 821 | let command_pool = resources.command_pool.take().unwrap(); 822 | let command_buffer = resources.command_buffer.take().unwrap(); 823 | let fence = resources.fence.take().unwrap(); 824 | drop(resources); 825 | 826 | Ok(Context { 827 | _vklib: vklib, 828 | instance_pair, 829 | device_pair, 830 | physical_device, 831 | memory_properties, 832 | command_pool, 833 | command_buffer, 834 | fence, 835 | queue, 836 | always_flush_memory: env_var_as_boolean( 837 | "VKRUNNER_ALWAYS_FLUSH_MEMORY", 838 | false 839 | ) 840 | }) 841 | } 842 | 843 | /// Constructs a Context or returns [Error::Failure] if an 844 | /// error occurred while constructing it or 845 | /// [Error::Incompatible] if the requirements couldn’t be 846 | /// met. `device_id` can optionally be set to limit the device 847 | /// selection to an index in the list returned by 848 | /// `vkEnumeratePhysicalDevices`. 849 | pub fn new( 850 | requirements: &Requirements, 851 | device_id: Option<usize>, 852 | ) -> Result<Context, Error> { 853 | let vklib = Box::new(vulkan_funcs::Library::new()?); 854 | 855 | let instance_pair = InstancePair::new(vklib.as_ref(), requirements)?; 856 | 857 | let (physical_device, queue_family) = find_physical_device( 858 | &instance_pair, 859 | requirements, 860 | device_id, 861 | )?; 862 | 863 | let device_pair = DevicePair::new( 864 | &instance_pair, 865 | requirements, 866 | physical_device, 867 | queue_family, 868 | )?; 869 | 870 | Context::new_internal( 871 | Some(vklib), 872 | instance_pair, 873 | physical_device, 874 | queue_family, 875 | device_pair 876 | ) 877 | } 878 | 879 | /// Constructs a Context from a VkDevice created externally. The 880 | /// VkDevice won’t be freed when the context is dropped. It is the 881 | /// caller’s responsibility to keep the device alive during the 882 | /// lifetime of the Context. It also needs to ensure that any 883 | /// features and extensions that might be used during script 884 | /// execution were enabled when the device was created. 885 | /// 886 | /// `get_instance_proc_cb` will be called to get the 887 | /// instance-level functions that the context needs. The rest of 888 | /// the functions will be retrieved using the 889 | /// `vkGetDeviceProcAddr` function that that returns. `user_data` 890 | /// will be passed to the function. 891 | pub fn new_with_device( 892 | get_instance_proc_cb: vulkan_funcs::GetInstanceProcFunc, 893 | user_data: *const c_void, 894 | physical_device: vk::VkPhysicalDevice, 895 | queue_family: u32, 896 | device: vk::VkDevice 897 | ) -> Result<Context, Error> { 898 | let instance_pair = InstancePair::new_external( 899 | get_instance_proc_cb, 900 | user_data, 901 | ); 902 | 903 | let device_pair = DevicePair::new_external( 904 | &instance_pair, 905 | device, 906 | ); 907 | 908 | Context::new_internal( 909 | None, // vklib 910 | instance_pair, 911 | physical_device, 912 | queue_family, 913 | device_pair, 914 | ) 915 | } 916 | 917 | /// Get the instance function pointers 918 | #[inline] 919 | pub fn instance(&self) -> &vulkan_funcs::Instance { 920 | &self.instance_pair.vkinst 921 | } 922 | 923 | /// Get the VkDevice handle 924 | #[inline] 925 | pub fn vk_device(&self) -> vk::VkDevice { 926 | self.device_pair.device 927 | } 928 | 929 | /// Get the device function pointers 930 | #[inline] 931 | pub fn device(&self) -> &vulkan_funcs::Device { 932 | &self.device_pair.vkdev 933 | } 934 | 935 | /// Get the physical device that was used to create this context. 936 | #[inline] 937 | pub fn physical_device(&self) -> vk::VkPhysicalDevice { 938 | self.physical_device 939 | } 940 | 941 | /// Get the memory properties struct for the physical device. This 942 | /// is queried from the physical device once when the context is 943 | /// constructed and cached for later use so this method is very 944 | /// cheap. 945 | #[inline] 946 | pub fn memory_properties(&self) -> &vk::VkPhysicalDeviceMemoryProperties { 947 | &self.memory_properties 948 | } 949 | 950 | /// Get the single shared command buffer that is associated with 951 | /// the context. 952 | #[inline] 953 | pub fn command_buffer(&self) -> vk::VkCommandBuffer { 954 | self.command_buffer 955 | } 956 | 957 | /// Get the single shared fence that is associated with the 958 | /// context. 959 | #[inline] 960 | pub fn fence(&self) -> vk::VkFence { 961 | self.fence 962 | } 963 | 964 | /// Get the queue chosen for this context. 965 | #[inline] 966 | pub fn queue(&self) -> vk::VkQueue { 967 | self.queue 968 | } 969 | 970 | /// Returns whether the memory should always be flushed regardless 971 | /// of whether the `VK_MEMORY_PROPERTY_HOST_COHERENT` bit is set 972 | /// in the [memory_properties](Context::memory_properties). This 973 | /// is mainly for testing purposes and can be enabled by setting 974 | /// the `VKRUNNER_ALWAYS_FLUSH_MEMORY` environment variable to 975 | /// `true`. 976 | #[inline] 977 | pub fn always_flush_memory(&self) -> bool { 978 | self.always_flush_memory 979 | } 980 | } 981 | 982 | impl Drop for Context { 983 | fn drop(&mut self) { 984 | free_resources( 985 | &self.device_pair, 986 | Some(self.command_pool), 987 | Some(self.command_buffer), 988 | Some(self.fence), 989 | ); 990 | } 991 | } 992 | 993 | #[cfg(test)] 994 | mod test { 995 | use super::*; 996 | use crate::fake_vulkan::{FakeVulkan, HandleType}; 997 | use crate::env_var_test::EnvVarLock; 998 | 999 | #[test] 1000 | fn base() { 1001 | let mut fake_vulkan = FakeVulkan::new(); 1002 | fake_vulkan.physical_devices.push(Default::default()); 1003 | fake_vulkan.physical_devices[0].memory_properties.memoryTypeCount = 3; 1004 | 1005 | fake_vulkan.set_override(); 1006 | let context = Context::new(&Requirements::new(), None).unwrap(); 1007 | 1008 | assert!(!context.instance_pair.vk_instance.is_null()); 1009 | assert!(context.instance().vkCreateDevice.is_some()); 1010 | assert!(!context.vk_device().is_null()); 1011 | assert!(context.device().vkCreateCommandPool.is_some()); 1012 | assert_eq!( 1013 | context.physical_device(), 1014 | fake_vulkan.index_to_physical_device(0) 1015 | ); 1016 | assert_eq!(context.memory_properties().memoryTypeCount, 3); 1017 | assert!(!context.command_buffer().is_null()); 1018 | assert!(context.fence() != vk::null_handle()); 1019 | assert_eq!(FakeVulkan::unmake_queue(context.queue()), (0, 0)); 1020 | } 1021 | 1022 | #[test] 1023 | fn no_devices() { 1024 | let fake_vulkan = FakeVulkan::new(); 1025 | fake_vulkan.set_override(); 1026 | let err = Context::new(&Requirements::new(), None).unwrap_err(); 1027 | assert_eq!( 1028 | &err.to_string(), 1029 | "The Vulkan instance reported zero drivers" 1030 | ); 1031 | } 1032 | 1033 | #[test] 1034 | fn check_instance_extension() { 1035 | let mut fake_vulkan = FakeVulkan::new(); 1036 | fake_vulkan.physical_devices.push(Default::default()); 1037 | 1038 | let mut reqs = Requirements::new(); 1039 | // Add a requirement so that c_structures won’t be NULL 1040 | reqs.add("shaderInt8"); 1041 | 1042 | // Make vkEnumerateInstanceExtensionProperties fail 1043 | fake_vulkan.queue_result( 1044 | "vkEnumerateInstanceExtensionProperties".to_string(), 1045 | vk::VK_ERROR_UNKNOWN, 1046 | ); 1047 | fake_vulkan.set_override(); 1048 | let err = Context::new(&mut reqs, None).unwrap_err(); 1049 | assert_eq!( 1050 | err.to_string(), 1051 | "vkEnumerateInstanceExtensionProperties failed", 1052 | ); 1053 | 1054 | // Make it fail the second time too 1055 | fake_vulkan.queue_result( 1056 | "vkEnumerateInstanceExtensionProperties".to_string(), 1057 | vk::VK_SUCCESS, 1058 | ); 1059 | fake_vulkan.queue_result( 1060 | "vkEnumerateInstanceExtensionProperties".to_string(), 1061 | vk::VK_ERROR_UNKNOWN, 1062 | ); 1063 | fake_vulkan.set_override(); 1064 | let err = Context::new(&mut reqs, None).unwrap_err(); 1065 | assert_eq!( 1066 | err.to_string(), 1067 | "vkEnumerateInstanceExtensionProperties failed", 1068 | ); 1069 | 1070 | // Add an extension name with the same length but different characters 1071 | fake_vulkan.add_instance_extension( 1072 | "VK_KHR_get_physical_device_properties3" 1073 | ); 1074 | 1075 | // Add a shorter extension name 1076 | fake_vulkan.add_instance_extension("VK_KHR"); 1077 | // And a longer one 1078 | fake_vulkan.add_instance_extension( 1079 | "VK_KHR_get_physical_device_properties23" 1080 | ); 1081 | 1082 | fake_vulkan.set_override(); 1083 | let err = Context::new(&mut reqs, None).unwrap_err(); 1084 | assert_eq!( 1085 | err.to_string(), 1086 | "Missing instance extension: \ 1087 | VK_KHR_get_physical_device_properties2", 1088 | ); 1089 | } 1090 | 1091 | #[test] 1092 | fn extension_feature() { 1093 | let mut fake_vulkan = FakeVulkan::new(); 1094 | fake_vulkan.physical_devices.push(Default::default()); 1095 | fake_vulkan.add_instance_extension( 1096 | "VK_KHR_get_physical_device_properties2" 1097 | ); 1098 | 1099 | let mut reqs = Requirements::new(); 1100 | 1101 | reqs.add("multiview"); 1102 | 1103 | fake_vulkan.set_override(); 1104 | assert_eq!( 1105 | Context::new(&mut reqs, None).unwrap_err().to_string(), 1106 | "Missing required extension: VK_KHR_multiview", 1107 | ); 1108 | 1109 | fake_vulkan.physical_devices[0].add_extension("VK_KHR_multiview"); 1110 | fake_vulkan.physical_devices[0].multiview.multiview = vk::VK_TRUE; 1111 | 1112 | fake_vulkan.set_override(); 1113 | Context::new(&mut reqs, None).unwrap(); 1114 | } 1115 | 1116 | #[test] 1117 | fn multiple_mismatches() { 1118 | let mut fake_vulkan = FakeVulkan::new(); 1119 | 1120 | let mut reqs = Requirements::new(); 1121 | reqs.add("madeup_extension"); 1122 | 1123 | fake_vulkan.physical_devices.push(Default::default()); 1124 | fake_vulkan.physical_devices[0].properties.apiVersion = 1125 | crate::requirements::make_version(0, 1, 0); 1126 | fake_vulkan.physical_devices[0].add_extension("madeup_extension"); 1127 | 1128 | fake_vulkan.physical_devices.push(Default::default()); 1129 | fake_vulkan.physical_devices[1].queue_families.clear(); 1130 | fake_vulkan.physical_devices[1].add_extension("madeup_extension"); 1131 | 1132 | fake_vulkan.physical_devices.push(Default::default()); 1133 | 1134 | fake_vulkan.set_override(); 1135 | let err = Context::new(&mut reqs, None).unwrap_err(); 1136 | assert_eq!( 1137 | err.to_string(), 1138 | "0: Vulkan API version 1.0.0 required but the driver \ 1139 | reported 0.1.0\n\ 1140 | 1: Device has no graphics queue family\n\ 1141 | 2: Missing required extension: madeup_extension", 1142 | ); 1143 | assert_eq!(err.result(), result::Result::Skip); 1144 | 1145 | // Try making them one of them fail 1146 | fake_vulkan.queue_result( 1147 | "vkEnumerateDeviceExtensionProperties".to_string(), 1148 | vk::VK_ERROR_UNKNOWN, 1149 | ); 1150 | 1151 | fake_vulkan.set_override(); 1152 | let err = Context::new(&mut reqs, None).unwrap_err(); 1153 | assert_eq!( 1154 | err.to_string(), 1155 | "0: vkEnumerateDeviceExtensionProperties failed\n\ 1156 | 1: Device has no graphics queue family\n\ 1157 | 2: Missing required extension: madeup_extension", 1158 | ); 1159 | assert_eq!(err.result(), result::Result::Skip); 1160 | 1161 | // Try making all of them fail 1162 | fake_vulkan.physical_devices[0] = Default::default(); 1163 | fake_vulkan.physical_devices[1] = Default::default(); 1164 | fake_vulkan.queue_result( 1165 | "vkEnumerateDeviceExtensionProperties".to_string(), 1166 | vk::VK_ERROR_UNKNOWN, 1167 | ); 1168 | fake_vulkan.queue_result( 1169 | "vkEnumerateDeviceExtensionProperties".to_string(), 1170 | vk::VK_ERROR_UNKNOWN, 1171 | ); 1172 | fake_vulkan.queue_result( 1173 | "vkEnumerateDeviceExtensionProperties".to_string(), 1174 | vk::VK_ERROR_UNKNOWN, 1175 | ); 1176 | 1177 | fake_vulkan.set_override(); 1178 | let err = Context::new(&mut reqs, None).unwrap_err(); 1179 | assert_eq!( 1180 | err.to_string(), 1181 | "0: vkEnumerateDeviceExtensionProperties failed\n\ 1182 | 1: vkEnumerateDeviceExtensionProperties failed\n\ 1183 | 2: vkEnumerateDeviceExtensionProperties failed", 1184 | ); 1185 | assert_eq!(err.result(), result::Result::Fail); 1186 | 1187 | // Finally add a physical device that will succeed 1188 | fake_vulkan.physical_devices.push(Default::default()); 1189 | fake_vulkan.physical_devices[3].add_extension("madeup_extension"); 1190 | fake_vulkan.set_override(); 1191 | let context = Context::new(&mut reqs, None).unwrap(); 1192 | assert_eq!( 1193 | fake_vulkan.physical_device_to_index(context.physical_device), 1194 | 3, 1195 | ); 1196 | } 1197 | 1198 | #[test] 1199 | fn device_id() { 1200 | let mut fake_vulkan = FakeVulkan::new(); 1201 | for _ in 0..3 { 1202 | fake_vulkan.physical_devices.push(Default::default()); 1203 | } 1204 | 1205 | let mut reqs = Requirements::new(); 1206 | 1207 | // Try selecting each device 1208 | for i in 0..3 { 1209 | fake_vulkan.set_override(); 1210 | let context = Context::new(&mut reqs, Some(i)).unwrap(); 1211 | assert_eq!( 1212 | fake_vulkan.physical_device_to_index(context.physical_device), 1213 | i, 1214 | ); 1215 | } 1216 | 1217 | // Try selecting a non-existant device 1218 | fake_vulkan.set_override(); 1219 | let err = Context::new(&mut reqs, Some(3)).unwrap_err(); 1220 | assert_eq!( 1221 | err.to_string(), 1222 | "Device 4 was selected but the Vulkan instance only reported 3 \ 1223 | devices." 1224 | ); 1225 | 1226 | fake_vulkan.physical_devices.truncate(1); 1227 | fake_vulkan.set_override(); 1228 | let err = Context::new(&mut reqs, Some(3)).unwrap_err(); 1229 | assert_eq!( 1230 | err.to_string(), 1231 | "Device 4 was selected but the Vulkan instance only reported 1 \ 1232 | device." 1233 | ); 1234 | 1235 | // Try a failure 1236 | reqs.add("madeup_extension"); 1237 | fake_vulkan.physical_devices.push(Default::default()); 1238 | fake_vulkan.set_override(); 1239 | let err = Context::new(&mut reqs, Some(0)).unwrap_err(); 1240 | assert_eq!( 1241 | err.to_string(), 1242 | "Missing required extension: madeup_extension", 1243 | ); 1244 | } 1245 | 1246 | #[test] 1247 | fn always_flush_memory_false() { 1248 | let mut fake_vulkan = FakeVulkan::new(); 1249 | fake_vulkan.physical_devices.push(Default::default()); 1250 | let mut reqs = Requirements::new(); 1251 | 1252 | let _env_var_lock = EnvVarLock::new(&[ 1253 | ("VKRUNNER_ALWAYS_FLUSH_MEMORY", "false"), 1254 | ]); 1255 | 1256 | fake_vulkan.set_override(); 1257 | let context = Context::new(&mut reqs, None).unwrap(); 1258 | assert_eq!(context.always_flush_memory(), false); 1259 | } 1260 | 1261 | #[test] 1262 | fn always_flush_memory_true() { 1263 | let mut fake_vulkan = FakeVulkan::new(); 1264 | fake_vulkan.physical_devices.push(Default::default()); 1265 | let mut reqs = Requirements::new(); 1266 | 1267 | let _env_var_lock = EnvVarLock::new(&[ 1268 | ("VKRUNNER_ALWAYS_FLUSH_MEMORY", "true"), 1269 | ]); 1270 | 1271 | fake_vulkan.set_override(); 1272 | let context = Context::new(&mut reqs, None).unwrap(); 1273 | assert_eq!(context.always_flush_memory(), true); 1274 | } 1275 | 1276 | #[test] 1277 | fn new_with_device() { 1278 | let mut fake_vulkan = FakeVulkan::new(); 1279 | fake_vulkan.physical_devices.push(Default::default()); 1280 | 1281 | let device = fake_vulkan.add_dispatchable_handle(HandleType::Device); 1282 | 1283 | extern "C" fn no_create_device( 1284 | _physical_device: vk::VkPhysicalDevice, 1285 | _create_info: *const vk::VkDeviceCreateInfo, 1286 | _allocator: *const vk::VkAllocationCallbacks, 1287 | _device_out: *mut vk::VkDevice, 1288 | ) -> vk::VkResult { 1289 | unreachable!( 1290 | "vkCreateDevice shouldn’t be called for an external \ 1291 | device" 1292 | ); 1293 | } 1294 | 1295 | extern "C" fn no_destroy_device( 1296 | _device: vk::VkDevice, 1297 | _allocator: *const vk::VkAllocationCallbacks 1298 | ) { 1299 | unreachable!( 1300 | "vkDestroyDevice shouldn’t be called for an external \ 1301 | device" 1302 | ); 1303 | } 1304 | 1305 | extern "C" fn no_destroy_instance( 1306 | _instance: vk::VkInstance, 1307 | _allocator: *const vk::VkAllocationCallbacks 1308 | ) { 1309 | unreachable!( 1310 | "vkDestroyInstance shouldn’t be called for an external \ 1311 | device" 1312 | ); 1313 | } 1314 | 1315 | extern "C" fn get_device_proc_addr( 1316 | _device: vk::VkDevice, 1317 | name: *const c_char, 1318 | ) -> vk::PFN_vkVoidFunction { 1319 | unsafe { 1320 | std::mem::transmute(get_instance_proc_cb( 1321 | name.cast(), 1322 | (FakeVulkan::current() as *mut FakeVulkan).cast(), 1323 | )) 1324 | } 1325 | } 1326 | 1327 | extern "C" fn get_instance_proc_cb( 1328 | func_name: *const c_char, 1329 | user_data: *const c_void, 1330 | ) -> *const c_void { 1331 | let name = unsafe { 1332 | CStr::from_ptr(func_name.cast()).to_str().unwrap() 1333 | }; 1334 | 1335 | match name { 1336 | "vkGetDeviceProcAddr" => unsafe { 1337 | std::mem::transmute::<vk::PFN_vkGetDeviceProcAddr, _>( 1338 | Some(get_device_proc_addr) 1339 | ) 1340 | }, 1341 | "vkDestroyInstance" => unsafe { 1342 | std::mem::transmute::<vk::PFN_vkDestroyInstance, _>( 1343 | Some(no_destroy_instance) 1344 | ) 1345 | }, 1346 | "vkCreateDevice" => unsafe { 1347 | std::mem::transmute::<vk::PFN_vkCreateDevice, _>( 1348 | Some(no_create_device) 1349 | ) 1350 | }, 1351 | "vkDestroyDevice" => unsafe { 1352 | std::mem::transmute::<vk::PFN_vkDestroyDevice, _>( 1353 | Some(no_destroy_device) 1354 | ) 1355 | }, 1356 | _ => unsafe { 1357 | let fake_vulkan = &*(user_data.cast::<FakeVulkan>()); 1358 | std::mem::transmute( 1359 | fake_vulkan.get_function(func_name.cast()) 1360 | ) 1361 | }, 1362 | } 1363 | } 1364 | 1365 | let context = Context::new_with_device( 1366 | get_instance_proc_cb, 1367 | (fake_vulkan.as_ref() as *const FakeVulkan).cast(), 1368 | fake_vulkan.index_to_physical_device(0), 1369 | 3, // queue_family 1370 | device, 1371 | ).unwrap(); 1372 | 1373 | assert_eq!(context.instance_pair.vk_instance, ptr::null_mut()); 1374 | assert_eq!( 1375 | fake_vulkan.physical_device_to_index(context.physical_device()), 1376 | 0 1377 | ); 1378 | assert_eq!(context.vk_device(), device); 1379 | assert!(context.device_pair.is_external); 1380 | assert!(context.instance_pair.is_external); 1381 | 1382 | drop(context); 1383 | 1384 | fake_vulkan.get_dispatchable_handle_mut(device).freed = true; 1385 | } 1386 | } 1387 | ```