use core::num::NonZeroUsize;
use crate::{backend::BackendRequest, pixel::PixelFormat, scale::Downscale, types::Rect};
mod allocation;
mod collection;
#[doc(hidden)]
pub use allocation::{
checked_batch_count_product, checked_batch_count_sum, try_batch_reserve_for_push,
try_batch_reserve_to, BatchAllocationBudget, BatchAllocationRequest,
};
pub use collection::{
try_collect_indexed_batch_results, try_collect_ordered_batch_results,
try_collect_ordered_batch_results_with_limits,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct TileBatchOptions {
pub workers: Option<NonZeroUsize>,
}
impl TileBatchOptions {
#[must_use]
pub const fn new(workers: Option<NonZeroUsize>) -> Self {
Self { workers }
}
}
#[doc(hidden)]
pub type IndexedBatchResult<T, E> = (usize, Result<T, E>);
#[doc(hidden)]
pub type BatchResultSlot<T, E> = Option<Result<T, E>>;
pub struct TileDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
}
pub struct TileRegionDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub roi: Rect,
}
pub struct TileScaledDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub scale: Downscale,
}
pub struct TileRegionScaledDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub roi: Rect,
pub scale: Downscale,
}
pub struct TileRegionScaledDeviceDecodeRequest<'i> {
pub input: &'i [u8],
pub fmt: PixelFormat,
pub roi: Rect,
pub scale: Downscale,
pub backend: BackendRequest,
}
#[derive(Debug)]
pub struct TileBatchError<E> {
pub index: usize,
pub source: E,
}
impl<E: core::fmt::Display> core::fmt::Display for TileBatchError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "tile {} decode failed: {}", self.index, self.source)
}
}
impl<E: core::error::Error + 'static> core::error::Error for TileBatchError<E> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.source)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum BatchInfrastructureError {
#[error("batch plan requires at least one job")]
EmptyBatchPlan,
#[error("{what} is too large: requested {requested} bytes, cap {cap}")]
AllocationTooLarge {
what: &'static str,
requested: usize,
cap: usize,
},
#[error("host allocation failed for {bytes} bytes while allocating {what}")]
HostAllocationFailed {
what: &'static str,
bytes: usize,
},
#[error("failed to spawn batch worker {worker}")]
WorkerSpawnFailed {
worker: usize,
},
#[error("batch worker {worker} panicked")]
WorkerPanicked {
worker: usize,
},
#[error("batch worker slot {worker} is outside retained slot count {available}")]
WorkerSlotMissing {
worker: usize,
available: usize,
},
#[error("parallel batch worker panicked")]
ParallelWorkerPanicked,
#[error("batch scheduler state was poisoned")]
SchedulerPoisoned,
#[error("batch result index {index} is outside job count {job_count}")]
ResultIndexOutOfBounds {
index: usize,
job_count: usize,
},
#[error("batch result index {index} was reported more than once")]
DuplicateResult {
index: usize,
},
#[error("batch worker result missing for job {index}")]
MissingResult {
index: usize,
},
#[error("batch result {index} changed kind during ordered collection")]
ResultKindMismatch {
index: usize,
},
}
#[derive(Debug)]
#[non_exhaustive]
pub enum BatchDecodeError<E> {
Tile(TileBatchError<E>),
Infrastructure(BatchInfrastructureError),
}
impl<E> BatchDecodeError<E> {
#[must_use]
pub const fn tile_error(&self) -> Option<&TileBatchError<E>> {
match self {
Self::Tile(error) => Some(error),
Self::Infrastructure(_) => None,
}
}
#[must_use]
pub const fn infrastructure_error(&self) -> Option<&BatchInfrastructureError> {
match self {
Self::Tile(_) => None,
Self::Infrastructure(error) => Some(error),
}
}
}
impl<E: core::fmt::Display> core::fmt::Display for BatchDecodeError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Tile(error) => error.fmt(f),
Self::Infrastructure(error) => error.fmt(f),
}
}
}
impl<E: core::error::Error + 'static> core::error::Error for BatchDecodeError<E> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Tile(error) => Some(error),
Self::Infrastructure(error) => Some(error),
}
}
}
impl<E> From<BatchInfrastructureError> for BatchDecodeError<E> {
fn from(error: BatchInfrastructureError) -> Self {
Self::Infrastructure(error)
}
}
impl<E> From<TileBatchError<E>> for BatchDecodeError<E> {
fn from(error: TileBatchError<E>) -> Self {
Self::Tile(error)
}
}
#[doc(hidden)]
pub fn tile_batch_worker_count(
batch_size: usize,
options: TileBatchOptions,
available_workers: usize,
) -> usize {
if batch_size <= 1 {
return 1;
}
let workers = options.workers.map_or(available_workers, NonZeroUsize::get);
workers.max(1).min(batch_size)
}