shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
//! Vulkan result helpers with human-readable error messages
//!
//! This module provides utilities for translating raw Vulkan error codes into
//! actionable, user-friendly error messages with recovery suggestions.

use ash::vk;
use std::fmt;

/// Human-readable explanation of a Vulkan error code with recovery suggestions
#[derive(Debug, Clone)]
pub struct VkErrorInfo {
    /// The raw Vulkan result code
    pub code: vk::Result,
    
    /// Human-readable error name
    pub name: &'static str,
    
    /// Detailed explanation of what this error means
    pub description: &'static str,
    
    /// Common causes of this error
    pub causes: &'static [&'static str],
    
    /// Suggested recovery actions
    pub solutions: &'static [&'static str],
}

impl VkErrorInfo {
    /// Get error information for a Vulkan result code
    pub fn from_result(result: vk::Result) -> Self {
        let (name, description, causes, solutions) = match result {
            vk::Result::ERROR_OUT_OF_HOST_MEMORY => (
                "ERROR_OUT_OF_HOST_MEMORY",
                "The system has run out of CPU memory",
                &[
                    "Allocating too many Vulkan objects",
                    "Memory leak in application code",
                    "Insufficient system RAM",
                ][..],
                &[
                    "Check for memory leaks with a profiler",
                    "Reduce number of simultaneous allocations",
                    "Increase system RAM or add swap space",
                    "Call .destroy() on unused Vulkan objects",
                ][..],
            ),
            vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => (
                "ERROR_OUT_OF_DEVICE_MEMORY",
                "The GPU has run out of video memory (VRAM)",
                &[
                    "Allocating too many buffers or textures",
                    "Creating large render targets",
                    "Not freeing resources",
                    "GPU has limited VRAM",
                ][..],
                &[
                    "Reduce texture sizes or use compression",
                    "Free unused buffers and images",
                    "Use memory budget queries to track usage",
                    "Consider streaming assets instead of loading all at once",
                ][..],
            ),
            vk::Result::ERROR_INITIALIZATION_FAILED => (
                "ERROR_INITIALIZATION_FAILED",
                "Vulkan initialization failed",
                &[
                    "Outdated GPU drivers",
                    "Corrupted Vulkan runtime",
                    "Incompatible hardware",
                    "Missing required extensions",
                ][..],
                &[
                    "Update GPU drivers to latest version",
                    "Reinstall Vulkan runtime (Vulkan SDK)",
                    "Check if GPU supports Vulkan 1.3",
                    "Enable/disable validation layers",
                ][..],
            ),
            vk::Result::ERROR_DEVICE_LOST => (
                "ERROR_DEVICE_LOST",
                "The GPU device has been lost",
                &[
                    "Driver crash or timeout (TDR)",
                    "Infinite loop in shader",
                    "GPU overheating",
                    "Hardware failure",
                    "Invalid command buffer",
                ][..],
                &[
                    "Check for shader infinite loops",
                    "Enable validation layers for detailed diagnostics",
                    "Monitor GPU temperature",
                    "Update GPU drivers",
                    "Rebuild Vulkan objects after device loss",
                ][..],
            ),
            vk::Result::ERROR_MEMORY_MAP_FAILED => (
                "ERROR_MEMORY_MAP_FAILED",
                "Failed to map device memory to host address space",
                &[
                    "Memory not allocated with HOST_VISIBLE flag",
                    "Already mapped",
                    "Invalid memory range",
                ][..],
                &[
                    "Ensure memory has MEMORY_PROPERTY_HOST_VISIBLE_BIT",
                    "Check if memory is already mapped",
                    "Verify offset and size are within bounds",
                ][..],
            ),
            vk::Result::ERROR_LAYER_NOT_PRESENT => (
                "ERROR_LAYER_NOT_PRESENT",
                "Requested validation layer is not available",
                &[
                    "Vulkan SDK not installed",
                    "Validation layers not found in runtime",
                    "Typo in layer name",
                ][..],
                &[
                    "Install Vulkan SDK for validation layers",
                    "Disable validation layers in release builds",
                    "Check layer name spelling",
                ][..],
            ),
            vk::Result::ERROR_EXTENSION_NOT_PRESENT => (
                "ERROR_EXTENSION_NOT_PRESENT",
                "Requested extension is not supported",
                &[
                    "Extension not supported by GPU/driver",
                    "Typo in extension name",
                    "Wrong extension type (instance vs device)",
                ][..],
                &[
                    "Query available extensions before requesting",
                    "Update GPU drivers",
                    "Make extension optional if not critical",
                ][..],
            ),
            vk::Result::ERROR_FEATURE_NOT_PRESENT => (
                "ERROR_FEATURE_NOT_PRESENT",
                "Requested device feature is not supported",
                &[
                    "Hardware doesn't support the feature",
                    "Feature not enabled during device creation",
                ][..],
                &[
                    "Query device features before using them",
                    "Enable required features in DeviceCreateInfo",
                    "Provide fallback for unsupported features",
                ][..],
            ),
            vk::Result::ERROR_INCOMPATIBLE_DRIVER => (
                "ERROR_INCOMPATIBLE_DRIVER",
                "GPU driver is incompatible with Vulkan version",
                &[
                    "Driver too old",
                    "Requesting unsupported Vulkan version",
                ][..],
                &[
                    "Update GPU drivers",
                    "Lower requested Vulkan API version",
                ][..],
            ),
            vk::Result::ERROR_TOO_MANY_OBJECTS => (
                "ERROR_TOO_MANY_OBJECTS",
                "Too many objects of this type have been created",
                &[
                    "Exceeded implementation limit",
                    "Resource leak",
                ][..],
                &[
                    "Check implementation limits",
                    "Reuse objects instead of creating new ones",
                    "Destroy unused objects",
                ][..],
            ),
            vk::Result::ERROR_FORMAT_NOT_SUPPORTED => (
                "ERROR_FORMAT_NOT_SUPPORTED",
                "Requested image/buffer format is not supported",
                &[
                    "Format not supported by GPU",
                    "Format doesn't support required usage",
                ][..],
                &[
                    "Query format properties before use",
                    "Use a fallback format",
                    "Check format support for specific usage flags",
                ][..],
            ),
            vk::Result::ERROR_FRAGMENTED_POOL => (
                "ERROR_FRAGMENTED_POOL",
                "Descriptor pool is too fragmented",
                &[
                    "Too many small allocations",
                    "Pool too small",
                ][..],
                &[
                    "Create larger descriptor pool",
                    "Reset pool and batch allocations",
                    "Use separate pools for different allocation patterns",
                ][..],
            ),
            vk::Result::ERROR_OUT_OF_POOL_MEMORY => (
                "ERROR_OUT_OF_POOL_MEMORY",
                "Descriptor pool has run out of memory",
                &[
                    "Pool created with insufficient capacity",
                    "Allocating more descriptors than specified",
                ][..],
                &[
                    "Increase descriptor pool size",
                    "Free unused descriptor sets",
                    "Create additional descriptor pools",
                ][..],
            ),
            vk::Result::ERROR_INVALID_EXTERNAL_HANDLE => (
                "ERROR_INVALID_EXTERNAL_HANDLE",
                "External handle is invalid",
                &[
                    "Handle has been closed",
                    "Wrong handle type",
                    "Handle from incompatible source",
                ][..],
                &[
                    "Verify handle validity before import",
                    "Check handle type matches expected type",
                ][..],
            ),
            vk::Result::ERROR_SURFACE_LOST_KHR => (
                "ERROR_SURFACE_LOST_KHR",
                "Window surface has been lost",
                &[
                    "Window was destroyed",
                    "Platform-specific surface error",
                ][..],
                &[
                    "Recreate surface",
                    "Check window/platform event handling",
                ][..],
            ),
            vk::Result::ERROR_NATIVE_WINDOW_IN_USE_KHR => (
                "ERROR_NATIVE_WINDOW_IN_USE_KHR",
                "Native window is already in use by another API",
                &[
                    "OpenGL context active on same window",
                    "Surface already created for window",
                ][..],
                &[
                    "Destroy other API contexts",
                    "Use separate windows",
                ][..],
            ),
            vk::Result::ERROR_OUT_OF_DATE_KHR => (
                "ERROR_OUT_OF_DATE_KHR",
                "Swapchain is out of date with surface",
                &[
                    "Window was resized",
                    "Surface properties changed",
                ][..],
                &[
                    "Recreate swapchain",
                    "This is normal during window resize",
                ][..],
            ),
            vk::Result::ERROR_INCOMPATIBLE_DISPLAY_KHR => (
                "ERROR_INCOMPATIBLE_DISPLAY_KHR",
                "Display mode incompatible with surface",
                &[
                    "Display doesn't support required format",
                ][..],
                &[
                    "Query display capabilities",
                    "Use compatible display mode",
                ][..],
            ),
            vk::Result::ERROR_VALIDATION_FAILED_EXT => (
                "ERROR_VALIDATION_FAILED_EXT",
                "Validation layers detected an error",
                &[
                    "Invalid Vulkan API usage",
                    "Validation layer bug",
                ][..],
                &[
                    "Check validation layer output for details",
                    "Fix reported API misuse",
                    "Update validation layers",
                ][..],
            ),
            vk::Result::ERROR_INVALID_SHADER_NV => (
                "ERROR_INVALID_SHADER_NV",
                "Shader code is invalid",
                &[
                    "Malformed SPIR-V",
                    "Shader uses unsupported features",
                ][..],
                &[
                    "Validate SPIR-V with spirv-val",
                    "Check shader compiler output",
                ][..],
            ),
            vk::Result::ERROR_FRAGMENTATION => (
                "ERROR_FRAGMENTATION",
                "Memory allocation failed due to fragmentation",
                &[
                    "Memory heap is fragmented",
                    "Allocation pattern causes fragmentation",
                ][..],
                &[
                    "Use memory allocator (like VMA)",
                    "Batch allocations",
                    "Align allocations properly",
                ][..],
            ),
            vk::Result::ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS => (
                "ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS",
                "Invalid buffer device address",
                &[
                    "Address capture/replay mismatch",
                    "Feature not properly enabled",
                ][..],
                &[
                    "Enable buffer device address features",
                    "Verify address usage",
                ][..],
            ),
            vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => (
                "ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT",
                "Fullscreen exclusive mode was lost",
                &[
                    "User switched windows",
                    "Display configuration changed",
                ][..],
                &[
                    "Recreate swapchain in windowed mode",
                    "Attempt to reacquire exclusive mode",
                ][..],
            ),
            vk::Result::ERROR_UNKNOWN => (
                "ERROR_UNKNOWN",
                "An unknown error occurred",
                &[
                    "Driver bug",
                    "Corrupted state",
                ][..],
                &[
                    "Enable validation layers",
                    "Update drivers",
                    "File bug report with driver vendor",
                ][..],
            ),
            _ => (
                "UNKNOWN_ERROR_CODE",
                "Unrecognized Vulkan error code",
                &["This error code is not documented"][..],
                &["Check Vulkan specification", "Update shdrlib"][..],
            ),
        };

        Self {
            code: result,
            name,
            description,
            causes,
            solutions,
        }
    }
}

impl fmt::Display for VkErrorInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "Vulkan Error: {} ({:?})", self.name, self.code)?;
        writeln!(f, "{}", self.description)?;
        
        if !self.causes.is_empty() {
            writeln!(f, "\nPossible causes:")?;
            for cause in self.causes {
                writeln!(f, "{}", cause)?;
            }
        }
        
        if !self.solutions.is_empty() {
            writeln!(f, "\nSuggested solutions:")?;
            for solution in self.solutions {
                writeln!(f, "{}", solution)?;
            }
        }
        
        Ok(())
    }
}

/// Helper macro for wrapping Vulkan result codes with rich context
///
/// Usage:
/// ```ignore
/// vk_result!(device.create_buffer(&info, None), "creating vertex buffer")?;
/// ```
#[macro_export]
macro_rules! vk_result {
    ($expr:expr, $context:expr) => {
        $expr.map_err(|err| {
            let info = $crate::ex::helpers::vk_result::VkErrorInfo::from_result(err);
            eprintln!("\n╔══════════════════════════════════════════════════════════════╗");
            eprintln!("║ VULKAN ERROR while {}                                        ", $context);
            eprintln!("╠══════════════════════════════════════════════════════════════╣");
            eprintln!("{}", info);
            eprintln!("╚══════════════════════════════════════════════════════════════╝\n");
            err
        })
    };
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_info_display() {
        let info = VkErrorInfo::from_result(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY);
        let output = format!("{}", info);
        
        assert!(output.contains("ERROR_OUT_OF_DEVICE_MEMORY"));
        assert!(output.contains("Possible causes:"));
        assert!(output.contains("Suggested solutions:"));
    }

    #[test]
    fn test_all_common_errors_have_info() {
        let common_errors = [
            vk::Result::ERROR_OUT_OF_HOST_MEMORY,
            vk::Result::ERROR_OUT_OF_DEVICE_MEMORY,
            vk::Result::ERROR_DEVICE_LOST,
            vk::Result::ERROR_OUT_OF_DATE_KHR,
        ];

        for err in common_errors {
            let info = VkErrorInfo::from_result(err);
            assert!(!info.causes.is_empty(), "Error {:?} missing causes", err);
            assert!(!info.solutions.is_empty(), "Error {:?} missing solutions", err);
        }
    }
}