1use crate::vk;
2
3pub(crate) fn enumerate_two_call<T>(
8 call: impl Fn(*mut u32, *mut T) -> vk::enums::Result,
9) -> VkResult<Vec<T>> {
10 let mut count = 0u32;
11 check(call(&mut count, std::ptr::null_mut()))?;
12 let mut data = Vec::with_capacity(count as usize);
13 let result = call(&mut count, data.as_mut_ptr());
14 check(result)?;
15 unsafe { data.set_len(count as usize) };
17 Ok(data)
18}
19
20pub(crate) fn fill_two_call<T>(call: impl Fn(*mut u32, *mut T)) -> Vec<T> {
25 let mut count = 0u32;
26 call(&mut count, std::ptr::null_mut());
27 let mut data = Vec::with_capacity(count as usize);
28 call(&mut count, data.as_mut_ptr());
29 unsafe { data.set_len(count as usize) };
31 data
32}
33
34pub type VkResult<T> = std::result::Result<T, vk::enums::Result>;
55
56pub(crate) fn check(result: vk::enums::Result) -> VkResult<()> {
62 if result.as_raw() >= 0 {
63 Ok(())
64 } else {
65 Err(result)
66 }
67}
68
69#[derive(Debug)]
86pub enum LoadError {
87 Library(libloading::Error),
89 MissingEntryPoint,
91}
92
93impl std::fmt::Display for LoadError {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 LoadError::Library(e) => write!(f, "failed to load Vulkan library: {e}"),
97 LoadError::MissingEntryPoint => {
98 f.write_str("vkGetInstanceProcAddr not found in Vulkan library")
99 }
100 }
101 }
102}
103
104impl std::error::Error for LoadError {
105 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
106 match self {
107 LoadError::Library(e) => Some(e),
108 LoadError::MissingEntryPoint => None,
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn check_success_returns_ok() {
119 assert!(check(vk::enums::Result::SUCCESS).is_ok());
120 }
121
122 #[test]
123 fn check_negative_returns_err() {
124 let result = check(vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY);
125 assert_eq!(result, Err(vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY));
126 }
127
128 #[test]
129 fn check_non_zero_success_codes_return_ok() {
130 assert!(check(vk::enums::Result::INCOMPLETE).is_ok());
132 assert!(check(vk::enums::Result::SUBOPTIMAL).is_ok());
133 }
134
135 #[test]
136 fn check_extension_error_codes_return_err() {
137 assert!(check(vk::enums::Result::ERROR_OUT_OF_POOL_MEMORY).is_err());
139 assert!(check(vk::enums::Result::ERROR_SURFACE_LOST).is_err());
140 assert!(check(vk::enums::Result::ERROR_VALIDATION_FAILED).is_err());
141 }
142
143 #[test]
144 fn enumerate_two_call_returns_items() {
145 let result = enumerate_two_call(|count, data: *mut u32| {
147 unsafe { *count = 3 };
148 if !data.is_null() {
149 unsafe {
150 *data = 10;
151 *data.add(1) = 20;
152 *data.add(2) = 30;
153 }
154 }
155 vk::enums::Result::SUCCESS
156 });
157 assert_eq!(result.expect("should succeed"), vec![10u32, 20, 30]);
158 }
159
160 #[test]
161 fn enumerate_two_call_returns_empty_on_zero_count() {
162 let result = enumerate_two_call::<u32>(|count, _data| {
163 unsafe { *count = 0 };
164 vk::enums::Result::SUCCESS
165 });
166 assert!(result.expect("should succeed").is_empty());
167 }
168
169 #[test]
170 fn enumerate_two_call_propagates_error() {
171 let result =
172 enumerate_two_call::<u32>(|_count, _data| vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY);
173 assert_eq!(
174 result.unwrap_err(),
175 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
176 );
177 }
178
179 #[test]
180 fn fill_two_call_returns_items() {
181 let result = fill_two_call(|count, data: *mut u64| {
182 unsafe { *count = 2 };
183 if !data.is_null() {
184 unsafe {
185 *data = 42u64;
186 *data.add(1) = 99;
187 }
188 }
189 });
190 assert_eq!(result, vec![42u64, 99]);
191 }
192
193 #[test]
194 fn fill_two_call_returns_empty_on_zero_count() {
195 let result = fill_two_call::<u32>(|count, _data| {
196 unsafe { *count = 0 };
197 });
198 assert!(result.is_empty());
199 }
200
201 #[test]
202 #[cfg(not(miri))] fn load_error_source_library_returns_some() {
204 let lib_err =
205 unsafe { libloading::Library::new("nonexistent_vulkan_lib.dll") }.unwrap_err();
206 let err = LoadError::Library(lib_err);
207 assert!(
208 std::error::Error::source(&err).is_some(),
209 "Library variant should have a source"
210 );
211 }
212
213 #[test]
214 fn load_error_source_missing_entry_point_returns_none() {
215 let err = LoadError::MissingEntryPoint;
216 assert!(
217 std::error::Error::source(&err).is_none(),
218 "MissingEntryPoint should have no source"
219 );
220 }
221
222 #[test]
223 fn load_error_display_missing_entry_point() {
224 let err = LoadError::MissingEntryPoint;
225 assert_eq!(
226 err.to_string(),
227 "vkGetInstanceProcAddr not found in Vulkan library"
228 );
229 }
230
231 #[test]
232 #[cfg(not(miri))] fn load_error_display_library() {
234 let lib_err =
236 unsafe { libloading::Library::new("nonexistent_vulkan_lib.dll") }.unwrap_err();
237 let err = LoadError::Library(lib_err);
238 assert!(err.to_string().contains("failed to load Vulkan library"));
239 }
240}