use super::{DecompositionStorage, OutputRegion, TileDecodeContext};
#[cfg(feature = "parallel")]
use crate::{HtCodeBlockDecodeWorkspace, J2kCodeBlockDecodeWorkspace};
#[cfg(feature = "parallel")]
use alloc::vec::Vec;
#[cfg(feature = "parallel")]
pub(in crate::j2c::decode) struct TaskWorkspace<W> {
pub(in crate::j2c::decode) workspace: W,
pub(in crate::j2c::decode) prepared_width: u32,
pub(in crate::j2c::decode) prepared_height: u32,
}
#[cfg(feature = "parallel")]
pub(in crate::j2c::decode) type HtTaskWorkspace = TaskWorkspace<HtCodeBlockDecodeWorkspace>;
#[cfg(feature = "parallel")]
pub(in crate::j2c::decode) trait TaskScratch: Default + Send {
fn planned_bytes(width: u32, height: u32) -> crate::Result<usize>;
fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()>;
fn owned_bytes(&self) -> crate::Result<usize>;
}
#[cfg(feature = "parallel")]
impl TaskScratch for HtCodeBlockDecodeWorkspace {
fn planned_bytes(width: u32, height: u32) -> crate::Result<usize> {
crate::j2c::ht_block_decode::ht_decode_workspace_bytes(width, height)
}
fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()> {
self.reserve(width, height)
}
fn owned_bytes(&self) -> crate::Result<usize> {
self.allocated_bytes()
}
}
#[cfg(feature = "parallel")]
impl TaskScratch for J2kCodeBlockDecodeWorkspace {
fn planned_bytes(width: u32, height: u32) -> crate::Result<usize> {
crate::j2c::bitplane::classic_decode_workspace_bytes(width, height)
}
fn prepare_for(&mut self, width: u32, height: u32) -> crate::Result<()> {
self.prepare(width, height)
}
fn owned_bytes(&self) -> crate::Result<usize> {
self.allocated_bytes()
}
}
#[cfg(feature = "parallel")]
pub(in crate::j2c::decode) fn task_workspace_bytes<W: TaskScratch>(
slots: &Vec<TaskWorkspace<W>>,
) -> crate::Result<usize> {
let mut bytes = slots
.capacity()
.checked_mul(core::mem::size_of::<TaskWorkspace<W>>())
.ok_or(crate::ValidationError::ImageTooLarge)?;
for slot in slots {
bytes = bytes
.checked_add(slot.workspace.owned_bytes()?)
.ok_or(crate::ValidationError::ImageTooLarge)?;
}
Ok(bytes)
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum CpuDecodeParallelism {
#[default]
Auto,
Serial,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DecoderWorkspaceStats {
decode_calls: u64,
component_owner_reuses: u64,
tier1_owner_reuses: u64,
idwt_owner_reuses: u64,
scratch_capacity_retries: u64,
retained_component_bytes: usize,
retained_tier1_bytes: usize,
retained_idwt_bytes: usize,
}
impl DecoderWorkspaceStats {
#[must_use]
pub const fn decode_calls(self) -> u64 {
self.decode_calls
}
#[must_use]
pub const fn component_owner_reuses(self) -> u64 {
self.component_owner_reuses
}
#[must_use]
pub const fn tier1_owner_reuses(self) -> u64 {
self.tier1_owner_reuses
}
#[must_use]
pub const fn idwt_owner_reuses(self) -> u64 {
self.idwt_owner_reuses
}
#[must_use]
pub const fn scratch_capacity_retries(self) -> u64 {
self.scratch_capacity_retries
}
#[must_use]
pub const fn retained_component_bytes(self) -> usize {
self.retained_component_bytes
}
#[must_use]
pub const fn retained_tier1_bytes(self) -> usize {
self.retained_tier1_bytes
}
#[must_use]
pub const fn retained_idwt_bytes(self) -> usize {
self.retained_idwt_bytes
}
#[must_use]
pub const fn retained_scratch_bytes(self) -> usize {
self.retained_tier1_bytes
.saturating_add(self.retained_idwt_bytes)
}
}
#[derive(Default)]
pub struct DecoderWorkspace {
tile_decode_context: TileDecodeContext,
pub(super) cpu_decode_parallelism: CpuDecodeParallelism,
stats: DecoderWorkspaceStats,
}
impl DecoderWorkspace {
#[must_use]
pub const fn stats(&self) -> DecoderWorkspaceStats {
self.stats
}
}
pub struct DecoderContext<'a> {
pub(crate) tile_decode_context: TileDecodeContext,
pub(crate) storage: DecompositionStorage<'a>,
#[cfg(test)]
pub(crate) direct_plan_code_blocks: usize,
pub(super) cpu_decode_parallelism: CpuDecodeParallelism,
pub(crate) line_region_idwt: bool,
workspace_stats: DecoderWorkspaceStats,
}
impl Default for DecoderContext<'_> {
fn default() -> Self {
Self {
tile_decode_context: TileDecodeContext::default(),
storage: DecompositionStorage::default(),
#[cfg(test)]
direct_plan_code_blocks: 0,
cpu_decode_parallelism: CpuDecodeParallelism::Auto,
line_region_idwt: true,
workspace_stats: DecoderWorkspaceStats::default(),
}
}
}
impl DecoderContext<'_> {
#[must_use]
pub fn from_workspace(workspace: DecoderWorkspace) -> Self {
Self {
tile_decode_context: workspace.tile_decode_context,
storage: DecompositionStorage::default(),
#[cfg(test)]
direct_plan_code_blocks: 0,
cpu_decode_parallelism: workspace.cpu_decode_parallelism,
line_region_idwt: true,
workspace_stats: workspace.stats,
}
}
#[must_use]
pub fn into_workspace(mut self) -> DecoderWorkspace {
self.storage.release_all_allocations();
DecoderWorkspace {
tile_decode_context: self.tile_decode_context,
cpu_decode_parallelism: self.cpu_decode_parallelism,
stats: self.workspace_stats,
}
}
#[must_use]
pub const fn workspace_stats(&self) -> DecoderWorkspaceStats {
self.workspace_stats
}
pub(super) fn record_decode_start(
&mut self,
retained_component_bytes: usize,
retained_tier1_bytes: usize,
retained_idwt_bytes: usize,
) {
self.workspace_stats.decode_calls = self.workspace_stats.decode_calls.saturating_add(1);
if retained_component_bytes != 0 {
self.workspace_stats.component_owner_reuses = self
.workspace_stats
.component_owner_reuses
.saturating_add(1);
}
if retained_tier1_bytes != 0 {
self.workspace_stats.tier1_owner_reuses =
self.workspace_stats.tier1_owner_reuses.saturating_add(1);
}
if retained_idwt_bytes != 0 {
self.workspace_stats.idwt_owner_reuses =
self.workspace_stats.idwt_owner_reuses.saturating_add(1);
}
}
pub(super) fn record_scratch_capacity_retry(&mut self) {
self.workspace_stats.scratch_capacity_retries = self
.workspace_stats
.scratch_capacity_retries
.saturating_add(1);
}
pub(super) fn record_decode_complete(
&mut self,
retained_component_bytes: usize,
retained_tier1_bytes: usize,
retained_idwt_bytes: usize,
) {
self.workspace_stats.retained_component_bytes = retained_component_bytes;
self.workspace_stats.retained_tier1_bytes = retained_tier1_bytes;
self.workspace_stats.retained_idwt_bytes = retained_idwt_bytes;
}
pub(crate) fn release_reusable_allocations(&mut self) {
self.tile_decode_context.release_all_allocations();
self.storage.release_all_allocations();
}
pub(crate) fn set_output_region(&mut self, output_region: Option<(u32, u32, u32, u32)>) {
self.tile_decode_context.output_region = output_region.map(OutputRegion::from_tuple);
}
pub(crate) fn set_round_irreversible_output(&mut self, enabled: bool) {
self.tile_decode_context.round_irreversible_output = enabled;
}
pub(crate) fn set_defer_mct(&mut self, enabled: bool) {
self.tile_decode_context.defer_mct = enabled;
}
#[must_use]
pub fn cpu_decode_parallelism(&self) -> CpuDecodeParallelism {
self.cpu_decode_parallelism
}
pub fn set_cpu_decode_parallelism(&mut self, parallelism: CpuDecodeParallelism) {
self.cpu_decode_parallelism = parallelism;
}
#[cfg(test)]
pub(crate) fn set_line_region_idwt(&mut self, enabled: bool) {
self.line_region_idwt = enabled;
}
pub(crate) fn permit_line_region_idwt(&mut self, permitted: bool) {
self.tile_decode_context.line_region_idwt_permitted = permitted;
}
}