1use super::{ErrorMetrics, JpegToHtj2kCoefficientPath};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct BatchTranscodeReport {
9 pub tile_count: usize,
11 pub successful_tiles: usize,
13 pub failed_tiles: usize,
15 pub transformed_components: usize,
17 pub reversible_dwt53_batches: usize,
19 pub reversible_dwt53_batch_jobs: usize,
21 pub extract_us: u128,
23 pub transform_us: u128,
25 pub encode_us: u128,
27 pub timings: TranscodeTimingReport,
30 pub coefficient_path: JpegToHtj2kCoefficientPath,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum TranscodeBatchProfileRequest {
37 Cpu,
39 MetalAuto,
41 MetalExplicit,
43}
44
45impl TranscodeBatchProfileRequest {
46 #[must_use]
48 pub const fn as_str(self) -> &'static str {
49 match self {
50 Self::Cpu => "cpu",
51 Self::MetalAuto => "metal_auto",
52 Self::MetalExplicit => "metal_explicit",
53 }
54 }
55
56 #[must_use]
58 pub fn transform_processor(self, timings: &TranscodeTimingReport) -> &'static str {
59 if matches!(self, Self::MetalAuto | Self::MetalExplicit)
60 && timings.accelerator_work_observed()
61 {
62 "metal"
63 } else {
64 "cpu"
65 }
66 }
67
68 #[must_use]
70 pub fn profile_path(self, timings: &TranscodeTimingReport) -> &'static str {
71 if self.transform_processor(timings) != "metal" {
72 return "cpu";
73 }
74 match self {
75 Self::Cpu => "cpu",
76 Self::MetalAuto => "auto",
77 Self::MetalExplicit => "metal",
78 }
79 }
80}
81
82const TRANSCODE_PROFILE_FIELD_COUNT: usize = 65;
83
84macro_rules! push_labels {
85 ($builder:ident; $($key:literal => $value:expr),+ $(,)?) => {
86 $($builder.label($key, $value)?;)+
87 };
88}
89
90macro_rules! push_metrics {
91 ($builder:ident; $($key:literal => $value:expr),+ $(,)?) => {
92 $($builder.metric($key, $value)?;)+
93 };
94}
95
96#[derive(Debug, PartialEq, Eq)]
98pub struct TranscodeBatchProfileRow {
99 fields: Vec<(&'static str, String)>,
100 path: &'static str,
101}
102
103pub(super) type TranscodeBatchProfileFields = Vec<(&'static str, String)>;
104
105impl TranscodeBatchProfileRow {
106 pub fn new(
108 report: &BatchTranscodeReport,
109 context: impl AsRef<str>,
110 request: TranscodeBatchProfileRequest,
111 ) -> j2k_profile::ProfileResult<Self> {
112 let timings = report.timings;
113 let total_us = report
114 .extract_us
115 .checked_add(report.transform_us)
116 .and_then(|value| value.checked_add(report.encode_us))
117 .ok_or(j2k_profile::ProfileError::SizeOverflow {
118 what: "transcode batch total profile time",
119 })?;
120 let transform_processor = request.transform_processor(&timings);
121 let path = request.profile_path(&timings);
122 let mut builder = TranscodeProfileRowBuilder::new()?;
123
124 push_labels!(builder;
125 "codec" => "transcode",
126 "op" => "transcode_batch",
127 "request" => request.as_str(),
128 "path" => path,
129 "pipeline" => "jpeg_to_htj2k",
130 "context" => SanitizedContext(context.as_ref()),
131 "coefficient_path" => coefficient_path_label(report.coefficient_path),
132 "extract_processor" => "cpu",
133 "transform_processor" => transform_processor,
134 "encode_processor" => "cpu",
135 );
136 Self::push_batch_fields(&mut builder, report, total_us)?;
137 Self::push_input_timing_fields(&mut builder, &timings)?;
138 Self::push_dwt97_timing_fields(&mut builder, &timings)?;
139 Self::push_transfer_fields(&mut builder, &timings)?;
140 Self::push_encode_timing_fields(&mut builder, &timings)?;
141 Self::push_accelerator_fields(&mut builder, &timings)?;
142
143 Ok(Self {
144 fields: builder.finish()?,
145 path,
146 })
147 }
148
149 fn push_batch_fields(
150 builder: &mut TranscodeProfileRowBuilder,
151 report: &BatchTranscodeReport,
152 total_us: u128,
153 ) -> j2k_profile::ProfileResult<()> {
154 push_metrics!(builder;
155 "tile_count" => report.tile_count,
156 "successful_tiles" => report.successful_tiles,
157 "failed_tiles" => report.failed_tiles,
158 "transformed_components" => report.transformed_components,
159 "reversible_dwt53_batches" => report.reversible_dwt53_batches,
160 "reversible_dwt53_batch_jobs" => report.reversible_dwt53_batch_jobs,
161 "extract_us" => report.extract_us,
162 "transform_us" => report.transform_us,
163 "encode_us" => report.encode_us,
164 "total_us" => total_us,
165 );
166 Ok(())
167 }
168
169 fn push_input_timing_fields(
170 builder: &mut TranscodeProfileRowBuilder,
171 timings: &TranscodeTimingReport,
172 ) -> j2k_profile::ProfileResult<()> {
173 push_metrics!(builder;
174 "source_raw_probe_us" => timings.source_raw_probe_us,
175 "read_region_decode_us" => timings.read_region_decode_us,
176 "compose_pad_us" => timings.compose_pad_us,
177 "generated_jpeg_encode_us" => timings.generated_jpeg_encode_us,
178 "jpeg_dct_extract_us" => timings.jpeg_dct_extract_us,
179 "jpeg_dct_repack_us" => timings.jpeg_dct_repack_us,
180 "dct_to_wavelet_total_us" => timings.dct_to_wavelet_total_us,
181 "dct_to_wavelet_accelerator_us" => timings.dct_to_wavelet_accelerator_us,
182 "dct_to_wavelet_cpu_fallback_us" => timings.dct_to_wavelet_cpu_fallback_us,
183 "dwt_decompose_us" => timings.dwt_decompose_us,
184 );
185 Ok(())
186 }
187
188 fn push_dwt97_timing_fields(
189 builder: &mut TranscodeProfileRowBuilder,
190 timings: &TranscodeTimingReport,
191 ) -> j2k_profile::ProfileResult<()> {
192 push_metrics!(builder;
193 "dwt97_batch_pack_upload_us" => timings.dwt97_batch_pack_upload_us,
194 "dwt97_batch_pack_upload_transfers" => timings.dwt97_batch_pack_upload_transfers,
195 "dwt97_batch_pack_upload_bytes" => timings.dwt97_batch_pack_upload_bytes,
196 "dwt97_batch_resident_dct_handoff_count" => timings.dwt97_batch_resident_dct_handoff_count,
197 "dwt97_batch_idct_row_lift_us" => timings.dwt97_batch_idct_row_lift_us,
198 "dwt97_batch_column_lift_us" => timings.dwt97_batch_column_lift_us,
199 "dwt97_batch_resident_dwt_handoff_count" => timings.dwt97_batch_resident_dwt_handoff_count,
200 "dwt97_batch_quantize_codeblock_us" => timings.dwt97_batch_quantize_codeblock_us,
201 "dwt97_batch_ht_encode_us" => timings.dwt97_batch_ht_encode_us,
202 "dwt97_batch_ht_codeblock_dispatches" => timings.dwt97_batch_ht_codeblock_dispatches,
203 );
204 Ok(())
205 }
206
207 fn push_transfer_fields(
208 builder: &mut TranscodeProfileRowBuilder,
209 timings: &TranscodeTimingReport,
210 ) -> j2k_profile::ProfileResult<()> {
211 let device_to_host_transfer_count = timings
212 .dwt97_batch_readback_transfers
213 .checked_add(timings.dwt97_batch_ht_status_readback_transfers)
214 .and_then(|value| value.checked_add(timings.dwt97_batch_ht_output_readback_transfers))
215 .ok_or(j2k_profile::ProfileError::SizeOverflow {
216 what: "device-to-host profile transfer count",
217 })?;
218 let device_to_host_transfer_bytes = timings
219 .dwt97_batch_readback_bytes
220 .checked_add(timings.dwt97_batch_ht_status_readback_bytes)
221 .and_then(|value| value.checked_add(timings.dwt97_batch_ht_output_readback_bytes))
222 .ok_or(j2k_profile::ProfileError::SizeOverflow {
223 what: "device-to-host profile transfer bytes",
224 })?;
225 push_metrics!(builder;
226 "dwt97_batch_ht_status_readback_us" => timings.dwt97_batch_ht_status_readback_us,
227 "dwt97_batch_ht_status_readback_transfers" => timings.dwt97_batch_ht_status_readback_transfers,
228 "dwt97_batch_ht_status_readback_bytes" => timings.dwt97_batch_ht_status_readback_bytes,
229 "dwt97_batch_ht_output_readback_us" => timings.dwt97_batch_ht_output_readback_us,
230 "dwt97_batch_ht_output_readback_transfers" => timings.dwt97_batch_ht_output_readback_transfers,
231 "dwt97_batch_ht_output_readback_bytes" => timings.dwt97_batch_ht_output_readback_bytes,
232 "dwt97_batch_readback_us" => timings.dwt97_batch_readback_us,
233 "dwt97_batch_readback_transfers" => timings.dwt97_batch_readback_transfers,
234 "dwt97_batch_readback_bytes" => timings.dwt97_batch_readback_bytes,
235 "host_to_device_transfer_count" => timings.dwt97_batch_pack_upload_transfers,
236 "host_to_device_transfer_bytes" => timings.dwt97_batch_pack_upload_bytes,
237 "device_to_host_transfer_count" => device_to_host_transfer_count,
238 "device_to_host_transfer_bytes" => device_to_host_transfer_bytes,
239 );
240 Ok(())
241 }
242
243 fn push_encode_timing_fields(
244 builder: &mut TranscodeProfileRowBuilder,
245 timings: &TranscodeTimingReport,
246 ) -> j2k_profile::ProfileResult<()> {
247 push_metrics!(builder;
248 "htj2k_encode_us" => timings.htj2k_encode_us,
249 "htj2k_encode_accelerator_dispatches" => timings.htj2k_encode_accelerator_dispatches,
250 "htj2k_encode_ht_code_block_dispatches" => timings.htj2k_encode_ht_code_block_dispatches,
251 "htj2k_encode_packetization_dispatches" => timings.htj2k_encode_packetization_dispatches,
252 "component_count" => timings.component_count,
253 "batch_count" => timings.batch_count,
254 "batch_jobs" => timings.batch_jobs,
255 );
256 Ok(())
257 }
258
259 fn push_accelerator_fields(
260 builder: &mut TranscodeProfileRowBuilder,
261 timings: &TranscodeTimingReport,
262 ) -> j2k_profile::ProfileResult<()> {
263 push_metrics!(builder;
264 "accelerator_attempts" => timings.accelerator_attempts,
265 "accelerator_jobs" => timings.accelerator_jobs,
266 "accelerator_dispatches" => timings.accelerator_dispatches,
267 "accelerator_dispatched_jobs" => timings.accelerator_dispatched_jobs,
268 "cpu_fallback_jobs" => timings.cpu_fallback_jobs,
269 );
270 Ok(())
271 }
272
273 #[must_use]
275 pub fn fields(&self) -> &[(&'static str, String)] {
276 &self.fields
277 }
278
279 #[must_use]
281 pub const fn codec(&self) -> &'static str {
282 "transcode"
283 }
284
285 #[must_use]
287 pub const fn op(&self) -> &'static str {
288 "transcode_batch"
289 }
290
291 #[must_use]
293 pub const fn path(&self) -> &'static str {
294 self.path
295 }
296}
297
298struct TranscodeProfileRowBuilder {
299 fields: TranscodeBatchProfileFields,
300 limits: j2k_profile::ProfileLimits,
301 input_bytes: usize,
302 retained_bytes: usize,
303}
304
305impl TranscodeProfileRowBuilder {
306 fn new() -> j2k_profile::ProfileResult<Self> {
307 let limits = j2k_profile::ProfileLimits::default();
308 if TRANSCODE_PROFILE_FIELD_COUNT > limits.max_fields() {
309 return Err(j2k_profile::ProfileError::LimitExceeded {
310 what: "transcode profile field count",
311 requested: TRANSCODE_PROFILE_FIELD_COUNT,
312 limit: limits.max_fields(),
313 });
314 }
315 let mut fields = Vec::new();
316 fields
317 .try_reserve_exact(TRANSCODE_PROFILE_FIELD_COUNT)
318 .map_err(|_| j2k_profile::ProfileError::AllocationFailed {
319 what: "transcode profile field storage",
320 requested: TRANSCODE_PROFILE_FIELD_COUNT,
321 })?;
322 let retained_bytes = fields
323 .capacity()
324 .checked_mul(core::mem::size_of::<(&'static str, String)>())
325 .ok_or(j2k_profile::ProfileError::SizeOverflow {
326 what: "transcode profile field storage",
327 })?;
328 if retained_bytes > limits.max_retained_bytes() {
329 return Err(j2k_profile::ProfileError::LimitExceeded {
330 what: "transcode profile retained bytes",
331 requested: retained_bytes,
332 limit: limits.max_retained_bytes(),
333 });
334 }
335 Ok(Self {
336 fields,
337 limits,
338 input_bytes: 0,
339 retained_bytes,
340 })
341 }
342
343 fn label(
344 &mut self,
345 key: &'static str,
346 value: impl core::fmt::Display,
347 ) -> j2k_profile::ProfileResult<()> {
348 let field = j2k_profile::ProfileField::label_with_limits(key, value, self.limits)?;
349 self.push(key, field.into_value())
350 }
351
352 fn metric(
353 &mut self,
354 key: &'static str,
355 value: impl core::fmt::Display,
356 ) -> j2k_profile::ProfileResult<()> {
357 let field = j2k_profile::ProfileField::metric_with_limits(key, value, self.limits)?;
358 self.push(key, field.into_value())
359 }
360
361 fn push(&mut self, key: &'static str, value: String) -> j2k_profile::ProfileResult<()> {
362 let field_count =
363 self.fields
364 .len()
365 .checked_add(1)
366 .ok_or(j2k_profile::ProfileError::SizeOverflow {
367 what: "transcode profile field count",
368 })?;
369 if field_count > self.limits.max_fields() {
370 return Err(j2k_profile::ProfileError::LimitExceeded {
371 what: "transcode profile field count",
372 requested: field_count,
373 limit: self.limits.max_fields(),
374 });
375 }
376 let input_bytes = self
377 .input_bytes
378 .checked_add(key.len())
379 .and_then(|bytes| bytes.checked_add(value.len()))
380 .ok_or(j2k_profile::ProfileError::SizeOverflow {
381 what: "transcode profile input bytes",
382 })?;
383 if input_bytes > self.limits.max_input_bytes() {
384 return Err(j2k_profile::ProfileError::LimitExceeded {
385 what: "transcode profile input bytes",
386 requested: input_bytes,
387 limit: self.limits.max_input_bytes(),
388 });
389 }
390 let retained_bytes = self.retained_bytes.checked_add(value.capacity()).ok_or(
391 j2k_profile::ProfileError::SizeOverflow {
392 what: "transcode profile retained bytes",
393 },
394 )?;
395 if retained_bytes > self.limits.max_retained_bytes() {
396 return Err(j2k_profile::ProfileError::LimitExceeded {
397 what: "transcode profile retained bytes",
398 requested: retained_bytes,
399 limit: self.limits.max_retained_bytes(),
400 });
401 }
402 if self.fields.len() == self.fields.capacity() {
403 return Err(j2k_profile::ProfileError::InvalidInput {
404 what: "transcode profile field reservation exhausted",
405 });
406 }
407 self.fields.push((key, value));
408 self.input_bytes = input_bytes;
409 self.retained_bytes = retained_bytes;
410 Ok(())
411 }
412
413 fn finish(self) -> j2k_profile::ProfileResult<TranscodeBatchProfileFields> {
414 if self.fields.len() != TRANSCODE_PROFILE_FIELD_COUNT {
415 return Err(j2k_profile::ProfileError::InvalidInput {
416 what: "transcode profile field schema is incomplete",
417 });
418 }
419 Ok(self.fields)
420 }
421}
422
423struct SanitizedContext<'a>(&'a str);
424
425impl core::fmt::Display for SanitizedContext<'_> {
426 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
427 let mut parts = self.0.split(' ');
428 if let Some(first) = parts.next() {
429 formatter.write_str(first)?;
430 }
431 for part in parts {
432 formatter.write_str("_")?;
433 formatter.write_str(part)?;
434 }
435 Ok(())
436 }
437}
438
439const fn coefficient_path_label(path: JpegToHtj2kCoefficientPath) -> &'static str {
440 match path {
441 JpegToHtj2kCoefficientPath::IntegerDirect53 => "IntegerDirect53",
442 JpegToHtj2kCoefficientPath::FloatDirectLinear53 => "FloatDirectLinear53",
443 JpegToHtj2kCoefficientPath::FloatDirectLinear97 => "FloatDirectLinear97",
444 }
445}
446
447impl BatchTranscodeReport {
448 pub fn profile_row(
450 &self,
451 context: impl AsRef<str>,
452 request: TranscodeBatchProfileRequest,
453 ) -> j2k_profile::ProfileResult<TranscodeBatchProfileRow> {
454 TranscodeBatchProfileRow::new(self, context, request)
455 }
456}
457
458#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
465pub struct TranscodeTimingReport {
466 pub source_raw_probe_us: u128,
468 pub read_region_decode_us: u128,
470 pub compose_pad_us: u128,
472 pub generated_jpeg_encode_us: u128,
474 pub jpeg_dct_extract_us: u128,
476 pub jpeg_dct_repack_us: u128,
478 pub dct_to_wavelet_total_us: u128,
480 pub dct_to_wavelet_accelerator_us: u128,
482 pub dct_to_wavelet_cpu_fallback_us: u128,
484 pub dwt_decompose_us: u128,
486 pub dwt97_batch_pack_upload_us: u128,
488 pub dwt97_batch_pack_upload_transfers: usize,
490 pub dwt97_batch_pack_upload_bytes: u64,
492 pub dwt97_batch_resident_dct_handoff_count: usize,
494 pub dwt97_batch_idct_row_lift_us: u128,
496 pub dwt97_batch_column_lift_us: u128,
498 pub dwt97_batch_resident_dwt_handoff_count: usize,
500 pub dwt97_batch_quantize_codeblock_us: u128,
502 pub dwt97_batch_ht_encode_us: u128,
504 pub dwt97_batch_ht_kernel_us: u128,
506 pub dwt97_batch_ht_status_readback_us: u128,
508 pub dwt97_batch_ht_status_readback_transfers: usize,
510 pub dwt97_batch_ht_status_readback_bytes: u64,
512 pub dwt97_batch_ht_compact_us: u128,
514 pub dwt97_batch_ht_output_readback_us: u128,
516 pub dwt97_batch_ht_output_readback_transfers: usize,
518 pub dwt97_batch_ht_output_readback_bytes: u64,
520 pub dwt97_batch_ht_codeblock_dispatches: usize,
522 pub dwt97_batch_readback_us: u128,
524 pub dwt97_batch_readback_transfers: usize,
526 pub dwt97_batch_readback_bytes: u64,
528 pub htj2k_encode_us: u128,
530 pub htj2k_encode_accelerator_dispatches: usize,
532 pub htj2k_encode_ht_code_block_dispatches: usize,
534 pub htj2k_encode_packetization_dispatches: usize,
536 pub dicom_spool_write_us: u128,
538 pub dicom_final_write_us: u128,
540 pub tile_count: usize,
542 pub component_count: usize,
544 pub batch_count: usize,
546 pub batch_jobs: usize,
548 pub accelerator_attempts: usize,
550 pub accelerator_jobs: usize,
552 pub accelerator_dispatches: usize,
554 pub accelerator_dispatched_jobs: usize,
556 pub cpu_fallback_jobs: usize,
558}
559
560impl TranscodeTimingReport {
561 pub fn accelerator_work_observed(&self) -> bool {
564 self.accelerator_dispatches > 0
565 || self.dwt97_batch_pack_upload_transfers > 0
566 || self.dwt97_batch_pack_upload_bytes > 0
567 || self.dwt97_batch_resident_dct_handoff_count > 0
568 || self.dwt97_batch_idct_row_lift_us > 0
569 || self.dwt97_batch_column_lift_us > 0
570 || self.dwt97_batch_resident_dwt_handoff_count > 0
571 || self.dwt97_batch_quantize_codeblock_us > 0
572 || self.dwt97_batch_ht_encode_us > 0
573 || self.dwt97_batch_ht_kernel_us > 0
574 || self.dwt97_batch_ht_compact_us > 0
575 || self.dwt97_batch_ht_codeblock_dispatches > 0
576 || self.dwt97_batch_readback_transfers > 0
577 || self.dwt97_batch_readback_bytes > 0
578 || self.dwt97_batch_ht_status_readback_transfers > 0
579 || self.dwt97_batch_ht_status_readback_bytes > 0
580 || self.dwt97_batch_ht_output_readback_transfers > 0
581 || self.dwt97_batch_ht_output_readback_bytes > 0
582 }
583
584 pub(super) fn add_assign(&mut self, other: Self) {
585 macro_rules! saturating_add_fields {
586 ($($field:ident),+ $(,)?) => {
587 $(
588 self.$field = self.$field.saturating_add(other.$field);
589 )+
590 };
591 }
592
593 saturating_add_fields!(
594 source_raw_probe_us,
595 read_region_decode_us,
596 compose_pad_us,
597 generated_jpeg_encode_us,
598 jpeg_dct_extract_us,
599 jpeg_dct_repack_us,
600 dct_to_wavelet_total_us,
601 dct_to_wavelet_accelerator_us,
602 dct_to_wavelet_cpu_fallback_us,
603 dwt_decompose_us,
604 dwt97_batch_pack_upload_us,
605 dwt97_batch_pack_upload_transfers,
606 dwt97_batch_pack_upload_bytes,
607 dwt97_batch_resident_dct_handoff_count,
608 dwt97_batch_idct_row_lift_us,
609 dwt97_batch_column_lift_us,
610 dwt97_batch_resident_dwt_handoff_count,
611 dwt97_batch_quantize_codeblock_us,
612 dwt97_batch_ht_encode_us,
613 dwt97_batch_ht_kernel_us,
614 dwt97_batch_ht_status_readback_us,
615 dwt97_batch_ht_status_readback_transfers,
616 dwt97_batch_ht_status_readback_bytes,
617 dwt97_batch_ht_compact_us,
618 dwt97_batch_ht_output_readback_us,
619 dwt97_batch_ht_output_readback_transfers,
620 dwt97_batch_ht_output_readback_bytes,
621 dwt97_batch_ht_codeblock_dispatches,
622 dwt97_batch_readback_us,
623 dwt97_batch_readback_transfers,
624 dwt97_batch_readback_bytes,
625 htj2k_encode_us,
626 htj2k_encode_accelerator_dispatches,
627 htj2k_encode_ht_code_block_dispatches,
628 htj2k_encode_packetization_dispatches,
629 dicom_spool_write_us,
630 dicom_final_write_us,
631 tile_count,
632 component_count,
633 batch_count,
634 batch_jobs,
635 accelerator_attempts,
636 accelerator_jobs,
637 accelerator_dispatches,
638 accelerator_dispatched_jobs,
639 cpu_fallback_jobs,
640 );
641 }
642}
643
644#[derive(Debug, Clone, PartialEq, Eq)]
646pub struct TranscodeComponentReport {
647 pub component_index: usize,
649 pub width: u32,
651 pub height: u32,
653 pub block_cols: u32,
655 pub block_rows: u32,
657 pub x_rsiz: u8,
659 pub y_rsiz: u8,
661}
662
663pub type TranscodeValidationMetrics = ErrorMetrics;
665
666#[derive(Debug, Clone, Copy, PartialEq, Eq)]
668pub enum TranscodeValidationClassification {
669 Exact,
671 OneLsbBounded,
675 OutsideThreshold,
677}
678
679impl TranscodeValidationClassification {
680 #[must_use]
683 pub fn classify_metrics(metrics: &TranscodeValidationMetrics) -> Self {
684 if metrics.exact_matches == metrics.total && metrics.max_abs_error == 0 {
685 Self::Exact
686 } else if metrics.is_one_lsb_bounded(0.999) {
687 Self::OneLsbBounded
688 } else {
689 Self::OutsideThreshold
690 }
691 }
692}
693
694#[derive(Debug, PartialEq, Eq)]
696pub struct TranscodeReport {
697 pub width: u32,
699 pub height: u32,
701 pub component_count: usize,
703 pub components: Vec<TranscodeComponentReport>,
705 pub float_reference_metrics: Option<TranscodeValidationMetrics>,
708 pub float_reference_classification: Option<TranscodeValidationClassification>,
710 pub integer_reference_metrics: Option<TranscodeValidationMetrics>,
713 pub integer_reference_classification: Option<TranscodeValidationClassification>,
715 pub decomposition_levels: u8,
717 pub coefficient_path: JpegToHtj2kCoefficientPath,
719 pub path: &'static str,
721 pub extract_us: u128,
723 pub transform_us: u128,
725 pub encode_us: u128,
727 pub timings: TranscodeTimingReport,
729}