j2k_native/j2c/decode/
workspace.rs1use super::{DecompositionStorage, OutputRegion, TileDecodeContext};
6#[cfg(feature = "parallel")]
7use crate::{HtCodeBlockDecodeWorkspace, J2kCodeBlockDecodeWorkspace};
8#[cfg(feature = "parallel")]
9use alloc::vec::Vec;
10
11#[cfg(feature = "parallel")]
13pub(in crate::j2c::decode) struct TaskWorkspace<W> {
14 pub(in crate::j2c::decode) workspace: W,
15 pub(in crate::j2c::decode) prepared_width: u32,
17 pub(in crate::j2c::decode) prepared_height: u32,
18}
19
20#[cfg(feature = "parallel")]
21pub(in crate::j2c::decode) type HtTaskWorkspace = TaskWorkspace<HtCodeBlockDecodeWorkspace>;
22
23#[cfg(feature = "parallel")]
25pub(in crate::j2c::decode) trait TaskScratch: Default + Send {
26 fn planned_bytes(width: u32, height: u32) -> crate::Result<usize>;
28 fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()>;
29 fn owned_bytes(&self) -> crate::Result<usize>;
30}
31
32#[cfg(feature = "parallel")]
33impl TaskScratch for HtCodeBlockDecodeWorkspace {
34 fn planned_bytes(width: u32, height: u32) -> crate::Result<usize> {
35 crate::j2c::ht_block_decode::ht_decode_workspace_bytes(width, height)
36 }
37
38 fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()> {
39 self.reserve(width, height)
40 }
41
42 fn owned_bytes(&self) -> crate::Result<usize> {
43 self.allocated_bytes()
44 }
45}
46
47#[cfg(feature = "parallel")]
48impl TaskScratch for J2kCodeBlockDecodeWorkspace {
49 fn planned_bytes(width: u32, height: u32) -> crate::Result<usize> {
50 crate::j2c::bitplane::classic_decode_workspace_bytes(width, height)
51 }
52
53 fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()> {
54 self.prepare(width, height)
55 }
56
57 fn owned_bytes(&self) -> crate::Result<usize> {
58 self.allocated_bytes()
59 }
60}
61
62#[cfg(feature = "parallel")]
63pub(in crate::j2c::decode) fn task_workspace_bytes<W: TaskScratch>(
64 slots: &Vec<TaskWorkspace<W>>,
65) -> crate::Result<usize> {
66 let mut bytes = slots
67 .capacity()
68 .checked_mul(core::mem::size_of::<TaskWorkspace<W>>())
69 .ok_or(crate::ValidationError::ImageTooLarge)?;
70 for slot in slots {
71 bytes = bytes
72 .checked_add(slot.workspace.owned_bytes()?)
73 .ok_or(crate::ValidationError::ImageTooLarge)?;
74 }
75 Ok(bytes)
76}
77
78#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
80pub enum CpuDecodeParallelism {
81 #[default]
83 Auto,
84 Serial,
86}
87
88#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
90pub struct DecoderWorkspaceStats {
91 decode_calls: u64,
92 component_owner_reuses: u64,
93 tier1_owner_reuses: u64,
94 idwt_owner_reuses: u64,
95 scratch_capacity_retries: u64,
96 retained_component_bytes: usize,
97 retained_tier1_bytes: usize,
98 retained_idwt_bytes: usize,
99}
100
101impl DecoderWorkspaceStats {
102 #[must_use]
104 pub const fn decode_calls(self) -> u64 {
105 self.decode_calls
106 }
107
108 #[must_use]
110 pub const fn component_owner_reuses(self) -> u64 {
111 self.component_owner_reuses
112 }
113
114 #[must_use]
116 pub const fn tier1_owner_reuses(self) -> u64 {
117 self.tier1_owner_reuses
118 }
119
120 #[must_use]
122 pub const fn idwt_owner_reuses(self) -> u64 {
123 self.idwt_owner_reuses
124 }
125
126 #[must_use]
128 pub const fn scratch_capacity_retries(self) -> u64 {
129 self.scratch_capacity_retries
130 }
131
132 #[must_use]
134 pub const fn retained_component_bytes(self) -> usize {
135 self.retained_component_bytes
136 }
137
138 #[must_use]
140 pub const fn retained_tier1_bytes(self) -> usize {
141 self.retained_tier1_bytes
142 }
143
144 #[must_use]
146 pub const fn retained_idwt_bytes(self) -> usize {
147 self.retained_idwt_bytes
148 }
149
150 #[must_use]
152 pub const fn retained_scratch_bytes(self) -> usize {
153 self.retained_tier1_bytes
154 .saturating_add(self.retained_idwt_bytes)
155 }
156}
157
158#[derive(Default)]
164pub struct DecoderWorkspace {
165 tile_decode_context: TileDecodeContext,
166 pub(super) cpu_decode_parallelism: CpuDecodeParallelism,
167 stats: DecoderWorkspaceStats,
168}
169
170impl DecoderWorkspace {
171 #[must_use]
173 pub const fn stats(&self) -> DecoderWorkspaceStats {
174 self.stats
175 }
176}
177
178pub struct DecoderContext<'a> {
180 pub(crate) tile_decode_context: TileDecodeContext,
181 pub(crate) storage: DecompositionStorage<'a>,
182 #[cfg(test)]
183 pub(crate) direct_plan_code_blocks: usize,
184 pub(super) cpu_decode_parallelism: CpuDecodeParallelism,
185 pub(crate) line_region_idwt: bool,
190 workspace_stats: DecoderWorkspaceStats,
191}
192
193impl Default for DecoderContext<'_> {
194 fn default() -> Self {
195 Self {
196 tile_decode_context: TileDecodeContext::default(),
197 storage: DecompositionStorage::default(),
198 #[cfg(test)]
199 direct_plan_code_blocks: 0,
200 cpu_decode_parallelism: CpuDecodeParallelism::Auto,
201 line_region_idwt: true,
202 workspace_stats: DecoderWorkspaceStats::default(),
203 }
204 }
205}
206
207impl DecoderContext<'_> {
208 #[must_use]
210 pub fn from_workspace(workspace: DecoderWorkspace) -> Self {
211 Self {
212 tile_decode_context: workspace.tile_decode_context,
213 storage: DecompositionStorage::default(),
214 #[cfg(test)]
215 direct_plan_code_blocks: 0,
216 cpu_decode_parallelism: workspace.cpu_decode_parallelism,
217 line_region_idwt: true,
218 workspace_stats: workspace.stats,
219 }
220 }
221
222 #[must_use]
224 pub fn into_workspace(mut self) -> DecoderWorkspace {
225 self.storage.release_all_allocations();
226 DecoderWorkspace {
227 tile_decode_context: self.tile_decode_context,
228 cpu_decode_parallelism: self.cpu_decode_parallelism,
229 stats: self.workspace_stats,
230 }
231 }
232
233 #[must_use]
235 pub const fn workspace_stats(&self) -> DecoderWorkspaceStats {
236 self.workspace_stats
237 }
238
239 pub(super) fn record_decode_start(
240 &mut self,
241 retained_component_bytes: usize,
242 retained_tier1_bytes: usize,
243 retained_idwt_bytes: usize,
244 ) {
245 self.workspace_stats.decode_calls = self.workspace_stats.decode_calls.saturating_add(1);
246 if retained_component_bytes != 0 {
247 self.workspace_stats.component_owner_reuses = self
248 .workspace_stats
249 .component_owner_reuses
250 .saturating_add(1);
251 }
252 if retained_tier1_bytes != 0 {
253 self.workspace_stats.tier1_owner_reuses =
254 self.workspace_stats.tier1_owner_reuses.saturating_add(1);
255 }
256 if retained_idwt_bytes != 0 {
257 self.workspace_stats.idwt_owner_reuses =
258 self.workspace_stats.idwt_owner_reuses.saturating_add(1);
259 }
260 }
261
262 pub(super) fn record_scratch_capacity_retry(&mut self) {
263 self.workspace_stats.scratch_capacity_retries = self
264 .workspace_stats
265 .scratch_capacity_retries
266 .saturating_add(1);
267 }
268
269 pub(super) fn record_decode_complete(
270 &mut self,
271 retained_component_bytes: usize,
272 retained_tier1_bytes: usize,
273 retained_idwt_bytes: usize,
274 ) {
275 self.workspace_stats.retained_component_bytes = retained_component_bytes;
276 self.workspace_stats.retained_tier1_bytes = retained_tier1_bytes;
277 self.workspace_stats.retained_idwt_bytes = retained_idwt_bytes;
278 }
279
280 pub(crate) fn release_reusable_allocations(&mut self) {
281 self.tile_decode_context.release_all_allocations();
282 self.storage.release_all_allocations();
283 }
284
285 pub(crate) fn set_output_region(&mut self, output_region: Option<(u32, u32, u32, u32)>) {
286 self.tile_decode_context.output_region = output_region.map(OutputRegion::from_tuple);
287 }
288
289 pub(crate) fn set_round_irreversible_output(&mut self, enabled: bool) {
290 self.tile_decode_context.round_irreversible_output = enabled;
291 }
292
293 pub(crate) fn set_defer_mct(&mut self, enabled: bool) {
294 self.tile_decode_context.defer_mct = enabled;
295 }
296
297 #[must_use]
299 pub fn cpu_decode_parallelism(&self) -> CpuDecodeParallelism {
300 self.cpu_decode_parallelism
301 }
302
303 pub fn set_cpu_decode_parallelism(&mut self, parallelism: CpuDecodeParallelism) {
305 self.cpu_decode_parallelism = parallelism;
306 }
307
308 #[cfg(test)]
311 pub(crate) fn set_line_region_idwt(&mut self, enabled: bool) {
312 self.line_region_idwt = enabled;
313 }
314
315 pub(crate) fn permit_line_region_idwt(&mut self, permitted: bool) {
317 self.tile_decode_context.line_region_idwt_permitted = permitted;
318 }
319}