1use crate::vk;
2
3pub(crate) fn enumerate_two_call<T>(
8 call: impl Fn(*mut u32, *mut T) -> vk::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::Result>;
55
56pub(crate) fn check(result: vk::Result) -> VkResult<()> {
62 if result.as_raw() >= 0 {
63 Ok(())
64 } else {
65 Err(result)
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct VkError(pub vk::Result);
86
87impl std::fmt::Display for VkError {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(f, "{:?}", self.0)
90 }
91}
92
93impl std::error::Error for VkError {}
94
95impl From<vk::Result> for VkError {
96 fn from(r: vk::Result) -> Self {
97 Self(r)
98 }
99}
100
101#[derive(Debug)]
118pub enum LoadError {
119 Library(libloading::Error),
121 MissingEntryPoint,
123}
124
125impl std::fmt::Display for LoadError {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {
128 LoadError::Library(e) => write!(f, "failed to load Vulkan library: {e}"),
129 LoadError::MissingEntryPoint => {
130 f.write_str("vkGetInstanceProcAddr not found in Vulkan library")
131 }
132 }
133 }
134}
135
136impl std::error::Error for LoadError {
137 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
138 match self {
139 LoadError::Library(e) => Some(e),
140 LoadError::MissingEntryPoint => None,
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn check_success_returns_ok() {
151 assert!(check(vk::Result::SUCCESS).is_ok());
152 }
153
154 #[test]
155 fn check_negative_returns_err() {
156 let result = check(vk::Result::ERROR_OUT_OF_HOST_MEMORY);
157 assert_eq!(result, Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY));
158 }
159
160 #[test]
161 fn check_non_zero_success_codes_return_ok() {
162 assert!(check(vk::Result::INCOMPLETE).is_ok());
164 assert!(check(vk::Result::SUBOPTIMAL).is_ok());
165 }
166
167 #[test]
168 fn check_extension_error_codes_return_err() {
169 assert!(check(vk::Result::ERROR_OUT_OF_POOL_MEMORY).is_err());
171 assert!(check(vk::Result::ERROR_SURFACE_LOST).is_err());
172 assert!(check(vk::Result::ERROR_VALIDATION_FAILED).is_err());
173 }
174
175 #[test]
176 fn enumerate_two_call_returns_items() {
177 let result = enumerate_two_call(|count, data: *mut u32| {
179 unsafe { *count = 3 };
180 if !data.is_null() {
181 unsafe {
182 *data = 10;
183 *data.add(1) = 20;
184 *data.add(2) = 30;
185 }
186 }
187 vk::Result::SUCCESS
188 });
189 assert_eq!(result.expect("should succeed"), vec![10u32, 20, 30]);
190 }
191
192 #[test]
193 fn enumerate_two_call_returns_empty_on_zero_count() {
194 let result = enumerate_two_call::<u32>(|count, _data| {
195 unsafe { *count = 0 };
196 vk::Result::SUCCESS
197 });
198 assert!(result.expect("should succeed").is_empty());
199 }
200
201 #[test]
202 fn enumerate_two_call_propagates_error() {
203 let result =
204 enumerate_two_call::<u32>(|_count, _data| vk::Result::ERROR_OUT_OF_HOST_MEMORY);
205 assert_eq!(result.unwrap_err(), vk::Result::ERROR_OUT_OF_HOST_MEMORY);
206 }
207
208 #[test]
209 fn fill_two_call_returns_items() {
210 let result = fill_two_call(|count, data: *mut u64| {
211 unsafe { *count = 2 };
212 if !data.is_null() {
213 unsafe {
214 *data = 42u64;
215 *data.add(1) = 99;
216 }
217 }
218 });
219 assert_eq!(result, vec![42u64, 99]);
220 }
221
222 #[test]
223 fn fill_two_call_returns_empty_on_zero_count() {
224 let result = fill_two_call::<u32>(|count, _data| {
225 unsafe { *count = 0 };
226 });
227 assert!(result.is_empty());
228 }
229
230 #[test]
231 #[cfg(not(miri))] fn load_error_source_library_returns_some() {
233 let lib_err =
234 unsafe { libloading::Library::new("nonexistent_vulkan_lib.dll") }.unwrap_err();
235 let err = LoadError::Library(lib_err);
236 assert!(
237 std::error::Error::source(&err).is_some(),
238 "Library variant should have a source"
239 );
240 }
241
242 #[test]
243 fn load_error_source_missing_entry_point_returns_none() {
244 let err = LoadError::MissingEntryPoint;
245 assert!(
246 std::error::Error::source(&err).is_none(),
247 "MissingEntryPoint should have no source"
248 );
249 }
250
251 #[test]
252 fn load_error_display_missing_entry_point() {
253 let err = LoadError::MissingEntryPoint;
254 assert_eq!(
255 err.to_string(),
256 "vkGetInstanceProcAddr not found in Vulkan library"
257 );
258 }
259
260 #[test]
261 #[cfg(not(miri))] fn load_error_display_library() {
263 let lib_err =
265 unsafe { libloading::Library::new("nonexistent_vulkan_lib.dll") }.unwrap_err();
266 let err = LoadError::Library(lib_err);
267 assert!(err.to_string().contains("failed to load Vulkan library"));
268 }
269}