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 |
```