#
tokens: 44941/50000 4/78 files (page 4/9)
lines: on (toggle) GitHub
raw markdown copy reset
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 | 
```
Page 4/9FirstPrevNextLast