Skip to main content

j2k_transcode/jpeg_to_htj2k/
batch.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use super::{
4    component_sampling_for_jpeg, dct_blocks_to_8x8_f64, decompose_97_from_first_level,
5    decomposition_levels_for_components,
6    encode_precomputed_htj2k_53_with_accelerator_and_max_host_bytes,
7    encode_precomputed_htj2k_97_batch_owned_with_accelerator_and_max_host_bytes,
8    encode_precomputed_htj2k_97_with_accelerator_and_max_host_bytes,
9    encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes,
10    encode_preencoded_htj2k_97_compact_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
11    encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes,
12    encode_preencoded_htj2k_97_owned_with_accelerator_and_max_host_bytes_and_required_magnitude_bound,
13    encode_prequantized_htj2k_97_with_accelerator_and_max_host_bytes,
14    encoded_transcode_retained_bytes, error_metrics_i32_with_live_budget, extract_dct_blocks,
15    flatten_integer_wavelet, float97_reference_coefficients,
16    float_direct_97_wavelet_from_component, float_reference_coefficients,
17    integer_dct_job_for_component, integer_direct_wavelet_from_component,
18    integer_reference_coefficients, integer_wavelet_from_first_level, j2k_dwt97_from_wavelet,
19    j2k_dwt_from_integer_wavelet, jpeg_to_htj2k_with_scratch, map_encode_error,
20    rounded_wavelet97_i32, transcode_path_name, validate_component_block_grid,
21    validate_jpeg_transcode_workspace, validate_transcode_options, BatchTranscodeReport,
22    ComponentWavelet97, CpuOnlyJ2kEncodeStageAccelerator, DctExtractOptions,
23    DctGridI16ToHtj2k97CodeBlockBatch, DctGridI16ToHtj2k97CodeBlockJob, DctGridToDwt97Job,
24    DctGridToHtj2k97CodeBlockJob, DctToWaveletStageAccelerator, Dwt97BatchStageTimings,
25    EncodedTranscode, EncodedTranscodeBatch, HostLiveBudget, Htj2k97CodeBlockOptions,
26    IndexedParallelIterator, Instant, IntegerWavelet, IntoParallelIterator,
27    IntoParallelRefIterator, J2kEncodeDispatchReport, J2kEncodeStageAccelerator, JpegDctImage,
28    JpegTileBatchInput, JpegToHtj2kCoefficientPath, JpegToHtj2kEncodeOptions, JpegToHtj2kError,
29    JpegToHtj2kOptions, JpegToHtj2kScratch, JpegToHtj2kTranscoder, ParallelIterator,
30    PrecomputedHtj2k53Component, PrecomputedHtj2k53Image, PrecomputedHtj2k97Component,
31    PrecomputedHtj2k97Image, PreencodedHtj2k97CompactComponent, PreencodedHtj2k97CompactImage,
32    PreencodedHtj2k97Component, PreencodedHtj2k97Image, PrequantizedHtj2k97Component,
33    PrequantizedHtj2k97Image, TranscodeComponentReport, TranscodeReport, TranscodeTimingReport,
34    TranscodeValidationClassification,
35};
36use crate::allocation::try_vec_with_capacity;
37mod group_budget;
38mod workspace;
39use self::workspace::{validate_batch_workspace, BatchWorkspaceKind};
40mod result_slots;
41use self::result_slots::BatchResultSlots;
42mod prepare;
43pub(super) use self::prepare::{
44    batch_component_groups, float97_batch_component_groups, prepare_float97_batch_tile,
45    prepare_integer_batch_tile, BatchComponentRef, Float97BatchTile, Float97PrecomputedBatchRecord,
46    IntegerBatchTile,
47};
48mod transform;
49pub(super) use self::transform::{
50    add_dwt97_batch_stage_timings, htj2k97_codeblock_options, i16_htj2k97_jobs_for_batch_group,
51    record_accelerator_attempt, record_accelerator_dispatch, record_batch_attempt,
52    record_batch_dispatch, record_cpu_fallback, transform_float97_batch_tiles,
53    transform_integer_batch_tiles,
54};
55mod accelerated_storage;
56#[cfg(test)]
57pub(super) use self::accelerated_storage::store_compact_preencoded_component;
58pub(super) use self::accelerated_storage::{
59    try_store_grouped_i16_preencoded_float97_batches, try_store_prequantized_float97_batch_group,
60};
61mod storage;
62pub(super) use self::storage::{store_float97_batch_wavelet, store_integer_batch_wavelet};
63mod encode;
64pub(super) use self::encode::{
65    add_encode_timing_counters_from_result, encode_float97_prepared_tiles,
66    encode_integer_prepared_tiles, record_encode_dispatch_delta,
67};
68mod actual_live;
69use self::actual_live::{
70    validate_float97_prepared_collection, validate_integer_prepared_collection,
71};
72mod individual;
73use self::individual::transcode_tile_batch_individually;
74
75/// Transcode many JPEG tiles into HTJ2K codestreams.
76pub fn jpeg_to_htj2k_batch(
77    tiles: &[JpegTileBatchInput<'_>],
78    options: &JpegToHtj2kOptions,
79) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
80    JpegToHtj2kTranscoder::default().transcode_batch(tiles, options)
81}
82
83enum BatchExecutionRoute {
84    Integer53,
85    AcceleratedFloat97,
86    Individual,
87}
88
89fn validate_batch_route(
90    tiles: &[JpegTileBatchInput<'_>],
91    options: &JpegToHtj2kOptions,
92    accelerator: &impl DctToWaveletStageAccelerator,
93) -> Result<BatchExecutionRoute, JpegToHtj2kError> {
94    validate_transcode_options(options)?;
95    let (route, workspace_kind) = match options.coefficient_path {
96        JpegToHtj2kCoefficientPath::IntegerDirect53 => {
97            (BatchExecutionRoute::Integer53, BatchWorkspaceKind::Integer)
98        }
99        JpegToHtj2kCoefficientPath::FloatDirectLinear97
100            if accelerator.supports_dwt97_batch()
101                || accelerator.supports_htj2k97_codeblock_batch() =>
102        {
103            (
104                BatchExecutionRoute::AcceleratedFloat97,
105                BatchWorkspaceKind::AcceleratedFloat97,
106            )
107        }
108        JpegToHtj2kCoefficientPath::FloatDirectLinear53
109        | JpegToHtj2kCoefficientPath::FloatDirectLinear97 => (
110            BatchExecutionRoute::Individual,
111            BatchWorkspaceKind::Individual,
112        ),
113    };
114    validate_batch_workspace(tiles, options, workspace_kind)?;
115    Ok(route)
116}
117
118pub(super) fn jpeg_tile_batch_to_htj2k_with_scratch<
119    A: DctToWaveletStageAccelerator,
120    E: J2kEncodeStageAccelerator,
121>(
122    tiles: &[JpegTileBatchInput<'_>],
123    options: &JpegToHtj2kOptions,
124    scratch: &mut JpegToHtj2kScratch,
125    accelerator: &mut A,
126    encode_accelerator: &mut E,
127) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
128    match validate_batch_route(tiles, options, accelerator)? {
129        BatchExecutionRoute::Integer53 => {}
130        BatchExecutionRoute::AcceleratedFloat97 => {
131            return jpeg_float97_tile_batch_to_htj2k_with_scratch(
132                tiles,
133                options,
134                scratch,
135                accelerator,
136                encode_accelerator,
137            );
138        }
139        BatchExecutionRoute::Individual => {
140            return transcode_tile_batch_individually(
141                tiles,
142                options,
143                scratch,
144                accelerator,
145                encode_accelerator,
146            );
147        }
148    }
149
150    let extract_start = Instant::now();
151    let mut prepared_results = try_vec_with_capacity(tiles.len())?;
152    tiles
153        .par_iter()
154        .enumerate()
155        .map(|(tile_index, tile)| {
156            (
157                tile_index,
158                prepare_integer_batch_tile(tile_index, tile.bytes, options),
159            )
160        })
161        .collect_into_vec(&mut prepared_results);
162    validate_integer_prepared_collection(&prepared_results, prepared_results.capacity(), scratch)?;
163    let extract_us = extract_start.elapsed().as_micros();
164    let mut tile_results = BatchResultSlots::try_new(tiles.len())?;
165    let mut prepared_tiles = try_vec_with_capacity(tiles.len())?;
166    for (tile_index, result) in prepared_results {
167        match result {
168            Ok(prepared) => prepared_tiles.push(prepared),
169            Err(error) => tile_results.insert(tile_index, Err(error))?,
170        }
171    }
172
173    let transform_start = Instant::now();
174    let mut timings = TranscodeTimingReport::default();
175    let (reversible_dwt53_batches, reversible_dwt53_batch_jobs) = transform_integer_batch_tiles(
176        &mut prepared_tiles,
177        options,
178        scratch,
179        accelerator,
180        &mut timings,
181    )?;
182    let transform_us = transform_start.elapsed().as_micros();
183    timings.jpeg_dct_extract_us = extract_us;
184    timings.dct_to_wavelet_total_us = transform_us;
185    timings.tile_count = prepared_tiles.len();
186
187    let encode_start = Instant::now();
188    let mut encode_external = HostLiveBudget::process_cap();
189    encode_external.add_bytes(scratch.retained_bytes()?)?;
190    encode_external.add_bytes(tile_results.retained_slot_bytes()?)?;
191    let encoded_tiles = encode_integer_prepared_tiles(
192        prepared_tiles,
193        options,
194        encode_accelerator,
195        encode_external.live_bytes(),
196    )?;
197    for (tile_index, encoded) in encoded_tiles {
198        add_encode_timing_counters_from_result(&mut timings, &encoded);
199        tile_results.insert(tile_index, encoded)?;
200    }
201    let encode_us = encode_start.elapsed().as_micros();
202    timings.htj2k_encode_us = encode_us;
203
204    let output_tiles =
205        tile_results.into_results_with_live_budget(scratch.retained_bytes()?, |result| {
206            result
207                .as_ref()
208                .map_or(Ok(0), encoded_transcode_retained_bytes)
209        })?;
210    Ok(batch_output(
211        output_tiles,
212        BatchTranscodeReport {
213            tile_count: tiles.len(),
214            successful_tiles: 0,
215            failed_tiles: 0,
216            transformed_components: reversible_dwt53_batch_jobs,
217            reversible_dwt53_batches,
218            reversible_dwt53_batch_jobs,
219            extract_us,
220            transform_us,
221            encode_us,
222            timings,
223            coefficient_path: options.coefficient_path,
224        },
225    ))
226}
227
228pub(super) fn jpeg_float97_tile_batch_to_htj2k_with_scratch<
229    A: DctToWaveletStageAccelerator,
230    E: J2kEncodeStageAccelerator,
231>(
232    tiles: &[JpegTileBatchInput<'_>],
233    options: &JpegToHtj2kOptions,
234    scratch: &mut JpegToHtj2kScratch,
235    accelerator: &mut A,
236    encode_accelerator: &mut E,
237) -> Result<EncodedTranscodeBatch, JpegToHtj2kError> {
238    let extract_start = Instant::now();
239    let mut prepared_results = try_vec_with_capacity(tiles.len())?;
240    tiles
241        .par_iter()
242        .enumerate()
243        .map(|(tile_index, tile)| {
244            (
245                tile_index,
246                prepare_float97_batch_tile(tile_index, tile.bytes, options),
247            )
248        })
249        .collect_into_vec(&mut prepared_results);
250    validate_float97_prepared_collection(&prepared_results, prepared_results.capacity(), scratch)?;
251    let extract_us = extract_start.elapsed().as_micros();
252    let mut tile_results = BatchResultSlots::try_new(tiles.len())?;
253    let mut prepared_tiles = try_vec_with_capacity(tiles.len())?;
254    for (tile_index, result) in prepared_results {
255        match result {
256            Ok(prepared) => prepared_tiles.push(prepared),
257            Err(error) => tile_results.insert(tile_index, Err(error))?,
258        }
259    }
260
261    let transform_start = Instant::now();
262    let mut timings = TranscodeTimingReport::default();
263    let (_dwt97_batches, dwt97_batch_jobs) = transform_float97_batch_tiles(
264        &mut prepared_tiles,
265        options,
266        scratch,
267        accelerator,
268        &mut timings,
269    )?;
270    let transform_us = transform_start.elapsed().as_micros();
271    timings.jpeg_dct_extract_us = extract_us;
272    timings.dct_to_wavelet_total_us = transform_us;
273    timings.tile_count = prepared_tiles.len();
274
275    let encode_start = Instant::now();
276    let mut encode_external = HostLiveBudget::process_cap();
277    encode_external.add_bytes(scratch.retained_bytes()?)?;
278    encode_external.add_bytes(tile_results.retained_slot_bytes()?)?;
279    let encoded_tiles = encode_float97_prepared_tiles(
280        prepared_tiles,
281        options,
282        encode_accelerator,
283        encode_external.live_bytes(),
284    )?;
285    for (tile_index, encoded) in encoded_tiles {
286        add_encode_timing_counters_from_result(&mut timings, &encoded);
287        tile_results.insert(tile_index, encoded)?;
288    }
289    let encode_us = encode_start.elapsed().as_micros();
290    timings.htj2k_encode_us = encode_us;
291
292    let output_tiles =
293        tile_results.into_results_with_live_budget(scratch.retained_bytes()?, |result| {
294            result
295                .as_ref()
296                .map_or(Ok(0), encoded_transcode_retained_bytes)
297        })?;
298    Ok(batch_output(
299        output_tiles,
300        BatchTranscodeReport {
301            tile_count: tiles.len(),
302            successful_tiles: 0,
303            failed_tiles: 0,
304            transformed_components: dwt97_batch_jobs,
305            reversible_dwt53_batches: 0,
306            reversible_dwt53_batch_jobs: 0,
307            extract_us,
308            transform_us,
309            encode_us,
310            timings,
311            coefficient_path: options.coefficient_path,
312        },
313    ))
314}
315
316pub(super) fn batch_output(
317    tiles: Vec<Result<EncodedTranscode, JpegToHtj2kError>>,
318    mut report: BatchTranscodeReport,
319) -> EncodedTranscodeBatch {
320    report.successful_tiles = tiles.iter().filter(|tile| tile.is_ok()).count();
321    report.failed_tiles = tiles.len().saturating_sub(report.successful_tiles);
322    EncodedTranscodeBatch { tiles, report }
323}