use ash::vk;
use std::fmt;
#[derive(Debug, Clone)]
pub struct VkErrorInfo {
pub code: vk::Result,
pub name: &'static str,
pub description: &'static str,
pub causes: &'static [&'static str],
pub solutions: &'static [&'static str],
}
impl VkErrorInfo {
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(())
}
}
#[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);
}
}
}