j2k_transcode_metal/
accelerator.rs1use j2k_transcode::{
4 DctGridToReversibleDwt53Job, DctToWaveletStageCounterEvent as CounterEvent,
5 DctToWaveletStageCounters, Dwt97BatchStageTimings, ReversibleDwt53FirstLevel,
6 TranscodeStageDispatchMode, TranscodeStageError,
7};
8
9#[cfg(target_os = "macos")]
10use crate::metal;
11#[cfg(target_os = "macos")]
12use crate::MetalTranscodeError;
13#[cfg(target_os = "macos")]
14use crate::MetalTranscodeSession;
15
16mod dispatch;
17
18const DEFAULT_AUTO_MIN_SAMPLES: usize = 224 * 224;
19const DEFAULT_AUTO_DWT97_MIN_SAMPLES: usize = usize::MAX;
23const DEFAULT_AUTO_REVERSIBLE_MIN_SAMPLES: usize = usize::MAX;
24const DEFAULT_AUTO_REVERSIBLE_BATCH_MIN_JOBS: usize = 32;
25const DEFAULT_AUTO_REVERSIBLE_BATCH_MIN_SAMPLES: usize = 224 * 224 * 32;
26const DEFAULT_AUTO_DWT97_BATCH_MIN_JOBS: usize = 32;
27const DEFAULT_AUTO_DWT97_BATCH_MIN_SAMPLES: usize = 224 * 224 * 32;
28const MAX_AUTO_DWT97_STAGED_BATCH_AXIS: usize = 1024;
31
32#[derive(Debug, Clone)]
34pub struct MetalDctToWaveletStageAccelerator {
35 mode: TranscodeStageDispatchMode,
36 min_auto_samples: usize,
37 min_auto_dwt97_samples: usize,
38 min_auto_reversible_samples: usize,
39 min_auto_reversible_batch_jobs: usize,
40 min_auto_reversible_batch_samples: usize,
41 counters: DctToWaveletStageCounters,
42 last_dwt97_batch_stage_timings: Option<Dwt97BatchStageTimings>,
43 min_auto_dwt97_batch_jobs: usize,
44 min_auto_dwt97_batch_samples: usize,
45 #[cfg(target_os = "macos")]
46 session: Option<MetalTranscodeSession>,
47}
48
49impl MetalDctToWaveletStageAccelerator {
50 #[must_use]
52 pub const fn new_explicit() -> Self {
53 Self {
54 mode: TranscodeStageDispatchMode::Explicit,
55 min_auto_samples: 0,
56 min_auto_dwt97_samples: 0,
57 min_auto_reversible_samples: 0,
58 min_auto_reversible_batch_jobs: 0,
59 min_auto_reversible_batch_samples: 0,
60 counters: DctToWaveletStageCounters::new(),
61 last_dwt97_batch_stage_timings: None,
62 min_auto_dwt97_batch_jobs: 0,
63 min_auto_dwt97_batch_samples: 0,
64 #[cfg(target_os = "macos")]
65 session: None,
66 }
67 }
68
69 #[must_use]
71 pub const fn for_auto() -> Self {
72 Self {
73 mode: TranscodeStageDispatchMode::Auto,
74 min_auto_samples: DEFAULT_AUTO_MIN_SAMPLES,
75 min_auto_dwt97_samples: DEFAULT_AUTO_DWT97_MIN_SAMPLES,
76 min_auto_reversible_samples: DEFAULT_AUTO_REVERSIBLE_MIN_SAMPLES,
77 min_auto_reversible_batch_jobs: DEFAULT_AUTO_REVERSIBLE_BATCH_MIN_JOBS,
78 min_auto_reversible_batch_samples: DEFAULT_AUTO_REVERSIBLE_BATCH_MIN_SAMPLES,
79 counters: DctToWaveletStageCounters::new(),
80 last_dwt97_batch_stage_timings: None,
81 min_auto_dwt97_batch_jobs: DEFAULT_AUTO_DWT97_BATCH_MIN_JOBS,
82 min_auto_dwt97_batch_samples: DEFAULT_AUTO_DWT97_BATCH_MIN_SAMPLES,
83 #[cfg(target_os = "macos")]
84 session: None,
85 }
86 }
87
88 #[cfg(target_os = "macos")]
90 #[must_use]
91 pub fn new_explicit_with_session(session: MetalTranscodeSession) -> Self {
92 Self::new_explicit().with_session(session)
93 }
94
95 #[cfg(target_os = "macos")]
97 #[must_use]
98 pub fn for_auto_with_session(session: MetalTranscodeSession) -> Self {
99 Self::for_auto().with_session(session)
100 }
101
102 #[cfg(target_os = "macos")]
104 #[must_use]
105 pub fn new_explicit_with_device(device: ::metal::Device) -> Self {
106 Self::new_explicit_with_session(MetalTranscodeSession::new(device))
107 }
108
109 #[cfg(target_os = "macos")]
111 #[must_use]
112 pub fn for_auto_with_device(device: ::metal::Device) -> Self {
113 Self::for_auto_with_session(MetalTranscodeSession::new(device))
114 }
115
116 #[cfg(target_os = "macos")]
118 #[must_use]
119 pub fn with_session(mut self, session: MetalTranscodeSession) -> Self {
120 self.session = Some(session);
121 self
122 }
123
124 #[cfg(target_os = "macos")]
126 #[must_use]
127 pub fn with_device(self, device: ::metal::Device) -> Self {
128 self.with_session(MetalTranscodeSession::new(device))
129 }
130
131 #[cfg(target_os = "macos")]
132 fn metal_session(&mut self) -> &mut MetalTranscodeSession {
133 self.session
134 .get_or_insert_with(MetalTranscodeSession::default)
135 }
136
137 #[must_use]
139 pub const fn with_auto_min_samples(mut self, min_samples: usize) -> Self {
140 self.min_auto_samples = min_samples;
141 self.min_auto_dwt97_samples = min_samples;
142 self
143 }
144
145 #[must_use]
147 pub const fn with_auto_dwt97_min_samples(mut self, min_samples: usize) -> Self {
148 self.min_auto_dwt97_samples = min_samples;
149 self
150 }
151
152 #[must_use]
154 pub const fn with_auto_dwt97_batch_thresholds(
155 mut self,
156 min_jobs: usize,
157 min_samples: usize,
158 ) -> Self {
159 self.min_auto_dwt97_batch_jobs = min_jobs;
160 self.min_auto_dwt97_batch_samples = min_samples;
161 self
162 }
163
164 #[must_use]
166 pub const fn with_auto_reversible_min_samples(mut self, min_samples: usize) -> Self {
167 self.min_auto_reversible_samples = min_samples;
168 self
169 }
170
171 #[must_use]
173 pub const fn with_auto_reversible_batch_thresholds(
174 mut self,
175 min_jobs: usize,
176 min_samples: usize,
177 ) -> Self {
178 self.min_auto_reversible_batch_jobs = min_jobs;
179 self.min_auto_reversible_batch_samples = min_samples;
180 self
181 }
182
183 #[must_use]
185 pub const fn reversible_dwt53_attempts(&self) -> usize {
186 self.counters.reversible_dwt53_attempts()
187 }
188
189 #[must_use]
191 pub const fn reversible_dwt53_dispatches(&self) -> usize {
192 self.counters.reversible_dwt53_dispatches()
193 }
194
195 #[must_use]
197 pub const fn reversible_dwt53_batch_attempts(&self) -> usize {
198 self.counters.reversible_dwt53_batch_attempts()
199 }
200
201 #[must_use]
203 pub const fn reversible_dwt53_batch_dispatches(&self) -> usize {
204 self.counters.reversible_dwt53_batch_dispatches()
205 }
206
207 #[must_use]
209 pub const fn dwt53_attempts(&self) -> usize {
210 self.counters.dwt53_attempts()
211 }
212
213 #[must_use]
215 pub const fn dwt53_dispatches(&self) -> usize {
216 self.counters.dwt53_dispatches()
217 }
218
219 #[must_use]
221 pub const fn dwt97_attempts(&self) -> usize {
222 self.counters.dwt97_attempts()
223 }
224
225 #[must_use]
227 pub const fn dwt97_dispatches(&self) -> usize {
228 self.counters.dwt97_dispatches()
229 }
230
231 #[must_use]
233 pub const fn dwt97_batch_attempts(&self) -> usize {
234 self.counters.dwt97_batch_attempts()
235 }
236
237 #[must_use]
239 pub const fn dwt97_batch_dispatches(&self) -> usize {
240 self.counters.dwt97_batch_dispatches()
241 }
242
243 #[must_use]
245 pub const fn htj2k97_codeblock_batch_attempts(&self) -> usize {
246 self.counters.htj2k97_codeblock_batch_attempts()
247 }
248
249 #[must_use]
251 pub const fn htj2k97_codeblock_batch_dispatches(&self) -> usize {
252 self.counters.htj2k97_codeblock_batch_dispatches()
253 }
254
255 #[must_use]
257 pub const fn last_dwt97_batch_stage_timings(&self) -> Option<Dwt97BatchStageTimings> {
258 self.last_dwt97_batch_stage_timings
259 }
260
261 #[cfg(target_os = "macos")]
262 fn recover<T>(&self, error: MetalTranscodeError) -> Result<Option<T>, TranscodeStageError> {
263 self.mode
264 .recover(error, MetalTranscodeError::is_recoverable)
265 }
266
267 pub fn dct_grid_to_reversible_dwt53_batch(
269 &mut self,
270 jobs: &[DctGridToReversibleDwt53Job<'_>],
271 ) -> Result<Option<Vec<ReversibleDwt53FirstLevel>>, TranscodeStageError> {
272 self.dispatch_reversible_dwt53_batch(jobs)
273 }
274
275 fn dispatch_reversible_dwt53_batch(
276 &mut self,
277 jobs: &[DctGridToReversibleDwt53Job<'_>],
278 ) -> Result<Option<Vec<ReversibleDwt53FirstLevel>>, TranscodeStageError> {
279 self.counters
280 .record(CounterEvent::ReversibleDwt53BatchAttempt, 1);
281 if jobs.is_empty() {
282 return Ok(Some(Vec::new()));
283 }
284 let total_samples = jobs.iter().fold(0usize, |total, job| {
285 total.saturating_add(job.width.saturating_mul(job.height))
286 });
287 if self.mode.is_auto()
288 && (jobs.len() < self.min_auto_reversible_batch_jobs
289 || total_samples < self.min_auto_reversible_batch_samples)
290 {
291 return Ok(None);
292 }
293
294 #[cfg(not(target_os = "macos"))]
295 {
296 self.mode.unavailable()
297 }
298 #[cfg(target_os = "macos")]
299 {
300 match metal::dispatch_dct_grid_to_reversible_dwt53_batch(self.metal_session(), jobs) {
301 Ok(output) => {
302 self.counters
303 .record(CounterEvent::ReversibleDwt53BatchDispatch, 1);
304 Ok(Some(output))
305 }
306 Err(error) => self.recover(error),
307 }
308 }
309 }
310}
311
312impl Default for MetalDctToWaveletStageAccelerator {
313 fn default() -> Self {
314 Self::for_auto()
315 }
316}