use crate::gfx::error::{DeviceLostReason, RenderError};
use objc2_foundation::NSError;
use objc2_metal::MTLCommandBufferError;
pub(super) fn classify_command_buffer_error(code: usize, detail: String) -> RenderError {
match MTLCommandBufferError(code) {
MTLCommandBufferError::DeviceRemoved => RenderError::DeviceLost {
reason: DeviceLostReason::Removed,
detail,
},
MTLCommandBufferError::Timeout => RenderError::DeviceLost {
reason: DeviceLostReason::Hung,
detail,
},
MTLCommandBufferError::PageFault | MTLCommandBufferError::StackOverflow => {
RenderError::DeviceLost {
reason: DeviceLostReason::Hung,
detail,
}
}
MTLCommandBufferError::AccessRevoked => RenderError::DeviceLost {
reason: DeviceLostReason::Reset,
detail,
},
MTLCommandBufferError::OutOfMemory => RenderError::OutOfDeviceMemory(detail),
_ => RenderError::Other(detail),
}
}
pub(super) fn classify_ns_error(error: &NSError) -> RenderError {
let detail = error.localizedDescription().to_string();
let in_metal_domain = &*error.domain() == unsafe { objc2_metal::MTLCommandBufferErrorDomain };
if in_metal_domain {
classify_command_buffer_error(error.code() as usize, detail)
} else {
RenderError::Other(detail)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn classify(code: MTLCommandBufferError) -> RenderError {
classify_command_buffer_error(code.0, "gpu fault".to_string())
}
#[test]
fn device_removed_is_device_lost_removed() {
assert_eq!(
classify(MTLCommandBufferError::DeviceRemoved),
RenderError::DeviceLost {
reason: DeviceLostReason::Removed,
detail: "gpu fault".to_string(),
}
);
}
#[test]
fn timeout_and_page_fault_are_device_lost_hung() {
for code in [
MTLCommandBufferError::Timeout,
MTLCommandBufferError::PageFault,
MTLCommandBufferError::StackOverflow,
] {
assert_eq!(
classify(code),
RenderError::DeviceLost {
reason: DeviceLostReason::Hung,
detail: "gpu fault".to_string(),
}
);
}
}
#[test]
fn access_revoked_is_device_lost_reset() {
assert_eq!(
classify(MTLCommandBufferError::AccessRevoked),
RenderError::DeviceLost {
reason: DeviceLostReason::Reset,
detail: "gpu fault".to_string(),
}
);
}
#[test]
fn out_of_memory_is_typed_oom() {
assert_eq!(
classify(MTLCommandBufferError::OutOfMemory),
RenderError::OutOfDeviceMemory("gpu fault".to_string())
);
}
#[test]
fn unrecognized_codes_stay_other() {
for code in [
MTLCommandBufferError::None,
MTLCommandBufferError::Internal,
MTLCommandBufferError::InvalidResource,
MTLCommandBufferError::Memoryless,
MTLCommandBufferError::NotPermitted,
] {
assert_eq!(classify(code), RenderError::Other("gpu fault".to_string()));
}
}
}