j2k_transcode_metal/
lib.rs1#[cfg(target_os = "macos")]
19mod metal;
20
21#[doc(hidden)]
22pub mod weights;
23
24mod accelerator;
25mod error;
26mod route;
27
28pub use accelerator::MetalDctToWaveletStageAccelerator;
29pub use error::{MetalRuntimeFailure, MetalTranscodeError};
30#[cfg(target_os = "macos")]
31pub use route::resident_codestream_buffer_from_metal_encoded_j2k;
32pub use route::{
33 jpeg_to_htj2k_batch_with_metal_route, jpeg_to_htj2k_with_metal_route, MetalEncodedTranscode,
34 MetalEncodedTranscodeBatch, MetalTranscodeFallbackReason, MetalTranscodeRouteReport,
35};
36
37#[cfg(target_os = "macos")]
38pub use metal::MetalTranscodeSession;
39
40pub const METAL_UNAVAILABLE: &str = "Metal is unavailable on this host";
42
43#[cfg(not(target_os = "macos"))]
44#[derive(Clone, Copy, Debug, Default)]
45pub struct MetalTranscodeSession {
47 _private: (),
48}
49
50#[cfg(not(target_os = "macos"))]
51impl MetalTranscodeSession {
52 pub const fn system_default() -> Result<Self, MetalTranscodeError> {
54 Err(MetalTranscodeError::MetalUnavailable)
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::route::{
62 ensure_strict_metal_batch_dispatched, ensure_strict_metal_dispatched, route_report,
63 };
64 use j2k_core::{BackendKind, BackendRequest};
65 use j2k_transcode::{BatchTranscodeReport, JpegToHtj2kCoefficientPath, TranscodeTimingReport};
66
67 #[cfg(target_os = "macos")]
68 #[derive(Debug)]
69 struct TestRuntimeError;
70
71 #[cfg(target_os = "macos")]
72 impl core::fmt::Display for TestRuntimeError {
73 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 formatter.write_str("driver rejected execution")
75 }
76 }
77
78 #[cfg(target_os = "macos")]
79 impl std::error::Error for TestRuntimeError {}
80
81 #[cfg(target_os = "macos")]
82 #[test]
83 fn runtime_failure_retains_operation_detail_and_source() {
84 let error = MetalTranscodeError::runtime("Metal test command buffer", TestRuntimeError);
85
86 assert_eq!(
87 error.to_string(),
88 "Metal test command buffer: driver rejected execution"
89 );
90 let runtime = std::error::Error::source(&error)
91 .and_then(|source| source.downcast_ref::<MetalRuntimeFailure>())
92 .expect("runtime failure wrapper");
93 let source = std::error::Error::source(runtime).expect("concrete runtime source");
94 assert!(source.downcast_ref::<TestRuntimeError>().is_some());
95 assert!(!error.is_recoverable());
96 }
97
98 #[test]
99 fn allocation_failures_are_hard_in_auto_mode() {
100 let error = MetalTranscodeError::HostAllocationTooLarge {
101 requested: usize::MAX,
102 cap: j2k_core::DEFAULT_MAX_HOST_ALLOCATION_BYTES,
103 what: "test output",
104 };
105 assert!(!error.is_recoverable());
106 assert!(MetalTranscodeError::UnsupportedJob("test decline").is_recoverable());
107 }
108
109 #[test]
110 fn route_report_uses_shared_accelerator_work_classifier() {
111 let timings = TranscodeTimingReport {
112 dwt97_batch_readback_bytes: 128,
113 ..TranscodeTimingReport::default()
114 };
115 let route = route_report(BackendRequest::Auto, &timings);
116 assert_eq!(route.selected_transform_backend, BackendKind::Metal);
117 assert_eq!(route.fallback_reason, None);
118 }
119
120 #[test]
121 fn strict_metal_accepts_shared_accelerator_work_evidence() {
122 let timings = TranscodeTimingReport {
123 dwt97_batch_pack_upload_transfers: 1,
124 ..TranscodeTimingReport::default()
125 };
126 let batch_report = BatchTranscodeReport {
127 tile_count: 1,
128 successful_tiles: 1,
129 failed_tiles: 0,
130 transformed_components: 1,
131 reversible_dwt53_batches: 0,
132 reversible_dwt53_batch_jobs: 0,
133 extract_us: 0,
134 transform_us: 0,
135 encode_us: 0,
136 timings,
137 coefficient_path: JpegToHtj2kCoefficientPath::FloatDirectLinear53,
138 };
139
140 ensure_strict_metal_dispatched(&timings).expect("shared classifier marks Metal work");
141 ensure_strict_metal_batch_dispatched(&batch_report)
142 .expect("batch strict route uses shared classifier");
143 }
144}