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