#[cfg(feature = "cuda-oxide-jpeg-decode")]
use crate::kernels::CudaLaunchGeometry;
use crate::{error::CudaError, execution::CudaExecutionStats, memory::CudaDeviceBuffer};
macro_rules! define_cuda_jpeg_rgb8_decode_plan {
(
$(#[$meta:meta])*
pub struct $name:ident <'a> {
$($prefix:tt)*
}
) => {
$(#[$meta])*
pub struct $name<'a> {
$($prefix)*
pub dimensions: (u32, u32),
pub mcus_per_row: u32,
pub mcu_rows: u32,
pub entropy_bytes: &'a [u8],
pub entropy_checkpoints: &'a [CudaJpegEntropyCheckpoint],
pub y_quant: [u16; 64],
pub cb_quant: [u16; 64],
pub cr_quant: [u16; 64],
pub y_dc_table: CudaJpegHuffmanTable,
pub y_ac_table: CudaJpegHuffmanTable,
pub cb_dc_table: CudaJpegHuffmanTable,
pub cb_ac_table: CudaJpegHuffmanTable,
pub cr_dc_table: CudaJpegHuffmanTable,
pub cr_ac_table: CudaJpegHuffmanTable,
}
};
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct CudaJpegHuffmanTable {
pub(crate) max_code: [i32; 17],
pub(crate) val_offset: [i32; 17],
pub(crate) values: [u8; 256],
pub(crate) values_len: u32,
}
impl CudaJpegHuffmanTable {
#[doc(hidden)]
pub fn from_jpeg_bits_values(
bits: [u8; 16],
values_len: u16,
values: [u8; 256],
) -> Result<Self, CudaError> {
let values_len_usize = usize::from(values_len);
let canonical = j2k_codec_math::jpeg::derive_canonical_huffman(&bits, values_len_usize)
.map_err(|error| CudaError::InvalidArgument {
message: format!("JPEG Huffman {error}"),
})?;
for len in 1usize..=16 {
let all_ones = (1i32 << len) - 1;
if canonical.max_code[len] == all_ones {
return Err(CudaError::InvalidArgument {
message: format!(
"JPEG Huffman length {len} assigns the forbidden all-ones code"
),
});
}
}
Ok(Self {
max_code: canonical.max_code,
val_offset: canonical.val_offset,
values,
values_len: u32::from(values_len),
})
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[doc(hidden)]
pub struct CudaJpegEntropyCheckpoint {
pub mcu_index: u32,
pub entropy_pos: u32,
pub bit_acc: u64,
pub bit_count: u32,
pub y_prev_dc: i32,
pub cb_prev_dc: i32,
pub cr_prev_dc: i32,
pub reserved: u32,
pub reserved_tail: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum CudaJpegRgb8Sampling {
Fast420,
Fast422,
Fast444,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CudaJpegChunkedEntropyConfig {
pub subsequence_words: u32,
pub sequence_len: u32,
pub max_overflow_subsequences: u32,
}
impl Default for CudaJpegChunkedEntropyConfig {
fn default() -> Self {
Self {
subsequence_words: 1024,
sequence_len: 128,
max_overflow_subsequences: 4,
}
}
}
impl CudaJpegChunkedEntropyConfig {
pub fn subsequence_bits(self) -> u32 {
self.subsequence_words.saturating_mul(32)
}
pub fn validate(self) -> Result<(), CudaError> {
if self.subsequence_words == 0 {
return Err(CudaError::InvalidArgument {
message: "JPEG entropy subsequence_words must be nonzero".to_string(),
});
}
if self.subsequence_words.checked_mul(32).is_none() {
return Err(CudaError::InvalidArgument {
message: "JPEG entropy subsequence_words bit size exceeds u32".to_string(),
});
}
if self.sequence_len == 0 {
return Err(CudaError::InvalidArgument {
message: "JPEG entropy sequence_len must be nonzero".to_string(),
});
}
Ok(())
}
pub fn subsequence_count_for_entropy_bytes(
self,
entropy_len: usize,
) -> Result<usize, CudaError> {
self.validate()?;
let entropy_bits = entropy_len
.checked_mul(8)
.ok_or(CudaError::LengthTooLarge { len: entropy_len })?;
let bits = self.subsequence_bits() as usize;
Ok(entropy_bits.div_ceil(bits))
}
}
#[doc(hidden)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CudaJpegEntropySyncState {
pub code: u32,
pub start_bit: u32,
pub end_bit: u32,
pub bit_pos: u32,
pub symbol_count: u32,
pub block_phase: u32,
pub zigzag_index: u32,
pub reserved: u32,
}
#[doc(hidden)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CudaJpegEntropyOverflowState {
pub code: u32,
pub from_subsequence: u32,
pub to_subsequence: u32,
pub overflow_bits: u32,
pub synchronized: u32,
pub reserved: [u32; 3],
}
#[doc(hidden)]
#[derive(Debug, Eq, PartialEq)]
pub struct CudaJpegChunkedEntropyReport {
pub config: CudaJpegChunkedEntropyConfig,
pub entropy_bytes: usize,
pub states: Vec<CudaJpegEntropySyncState>,
pub overflows: Vec<CudaJpegEntropyOverflowState>,
pub execution: CudaExecutionStats,
}
impl CudaJpegChunkedEntropyReport {
#[doc(hidden)]
pub fn retained_host_bytes(&self) -> Result<usize, CudaError> {
self.states
.capacity()
.checked_mul(core::mem::size_of::<CudaJpegEntropySyncState>())
.and_then(|states| {
self.overflows
.capacity()
.checked_mul(core::mem::size_of::<CudaJpegEntropyOverflowState>())
.and_then(|overflows| states.checked_add(overflows))
})
.ok_or(CudaError::HostAllocationTooLarge {
requested: usize::MAX,
cap: j2k_core::DEFAULT_MAX_HOST_ALLOCATION_BYTES,
what: "CUDA JPEG entropy diagnostic report",
})
}
pub fn subsequence_count(&self) -> usize {
self.states.len()
}
pub fn synchronized_overflow_count(&self) -> usize {
self.overflows
.iter()
.filter(|overflow| overflow.synchronized != 0)
.count()
}
pub fn max_overflow_bits(&self) -> Option<u32> {
self.overflows
.iter()
.map(|overflow| overflow.overflow_bits)
.max()
}
pub fn failed_state_count(&self) -> usize {
self.states.iter().filter(|state| state.code != 0).count()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum CudaJpegBaselineEncodeFormat {
Gray8,
Rgb8,
}
impl CudaJpegBaselineEncodeFormat {
#[doc(hidden)]
pub fn abi(self) -> u32 {
match self {
Self::Gray8 => JPEG_BASELINE_ENCODE_FORMAT_GRAY8,
Self::Rgb8 => JPEG_BASELINE_ENCODE_FORMAT_RGB8,
}
}
}
const JPEG_BASELINE_ENCODE_FORMAT_GRAY8: u32 = 0;
const JPEG_BASELINE_ENCODE_FORMAT_RGB8: u32 = 1;
#[cfg(feature = "cuda-oxide-jpeg-encode")]
pub(super) const JPEG_BASELINE_ENCODE_STATUS_OK: u32 = 0;
#[cfg(feature = "cuda-oxide-jpeg-encode")]
pub(super) const JPEG_BASELINE_ENCODE_STATUS_OVERFLOW: u32 = 1;
#[cfg(feature = "cuda-oxide-jpeg-encode")]
pub(super) const JPEG_BASELINE_ENCODE_STATUS_MISSING_HUFFMAN: u32 = 2;
#[cfg(feature = "cuda-oxide-jpeg-encode")]
pub(super) const JPEG_BASELINE_ENCODE_STATUS_INVALID_PARAMS: u32 = 3;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[doc(hidden)]
pub struct CudaJpegBaselineEncodeParams {
pub input_offset_bytes: u32,
pub input_width: u32,
pub input_height: u32,
pub output_width: u32,
pub output_height: u32,
pub pitch_bytes: u32,
pub mcus_per_row: u32,
pub mcu_rows: u32,
pub restart_interval_mcus: u32,
pub format: u32,
pub components: u32,
pub max_h: u32,
pub max_v: u32,
pub h0: u32,
pub v0: u32,
pub h1: u32,
pub v1: u32,
pub h2: u32,
pub v2: u32,
pub entropy_offset_bytes: u32,
pub entropy_capacity: u32,
}
unsafe impl crate::execution::CudaKernelParam for CudaJpegBaselineEncodeParams {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct CudaJpegBaselineEncodeHuffmanTable {
pub codes: [u16; 256],
pub lens: [u8; 256],
}
impl Default for CudaJpegBaselineEncodeHuffmanTable {
fn default() -> Self {
Self {
codes: [0; 256],
lens: [0; 256],
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(crate) struct CudaJpegBaselineEncodeStatus {
pub(crate) code: u32,
pub(crate) entropy_len: u32,
pub(crate) detail: u32,
pub(crate) reserved: u32,
}
#[derive(Debug)]
#[doc(hidden)]
pub struct CudaJpegBaselineEntropyEncodeJob<'a> {
pub input: &'a CudaDeviceBuffer,
pub input_offset: usize,
pub params: CudaJpegBaselineEncodeParams,
pub q_luma: [u8; 64],
pub q_chroma: [u8; 64],
pub huff_dc_luma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_ac_luma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_dc_chroma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_ac_chroma: CudaJpegBaselineEncodeHuffmanTable,
pub entropy_capacity: usize,
}
#[derive(Debug)]
#[doc(hidden)]
pub struct CudaJpegBaselineEntropyEncodeBatchJob<'a> {
pub input: &'a CudaDeviceBuffer,
pub params: Vec<CudaJpegBaselineEncodeParams>,
pub q_luma: [u8; 64],
pub q_chroma: [u8; 64],
pub huff_dc_luma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_ac_luma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_dc_chroma: CudaJpegBaselineEncodeHuffmanTable,
pub huff_ac_chroma: CudaJpegBaselineEncodeHuffmanTable,
pub entropy_capacity: usize,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaJpegChunkedEntropyPlan<'a> {
pub config: CudaJpegChunkedEntropyConfig,
pub entropy_bytes: &'a [u8],
pub y_dc_table: CudaJpegHuffmanTable,
pub y_ac_table: CudaJpegHuffmanTable,
pub cb_dc_table: CudaJpegHuffmanTable,
pub cb_ac_table: CudaJpegHuffmanTable,
pub cr_dc_table: CudaJpegHuffmanTable,
pub cr_ac_table: CudaJpegHuffmanTable,
}
define_cuda_jpeg_rgb8_decode_plan! {
#[derive(Debug)]
#[doc(hidden)]
pub struct CudaJpegRgb8DecodePlan<'a> {
pub sampling: CudaJpegRgb8Sampling,
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(crate) struct CudaJpeg420Params {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) mcus_per_row: u32,
pub(crate) mcu_rows: u32,
pub(crate) entropy_len: u32,
pub(crate) checkpoint_count: u32,
pub(crate) out_stride: u32,
pub(crate) reserved: u32,
}
unsafe impl crate::execution::CudaKernelParam for CudaJpeg420Params {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(crate) struct CudaJpegEntropyChunkParams {
pub(crate) entropy_len: u32,
pub(crate) entropy_bits: u32,
pub(crate) subsequence_bits: u32,
pub(crate) subsequence_count: u32,
pub(crate) sequence_len: u32,
pub(crate) max_overflow_subsequences: u32,
pub(crate) reserved0: u32,
pub(crate) reserved1: u32,
}
unsafe impl crate::execution::CudaKernelParam for CudaJpegEntropyChunkParams {}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(crate) struct CudaJpegDecodeStatus {
pub(crate) code: u32,
pub(crate) detail: u32,
pub(crate) position: u32,
pub(crate) reserved: u32,
}
#[cfg(feature = "cuda-oxide-jpeg-decode")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct CudaJpegRgb8ValidatedPlan {
pub(crate) params: CudaJpeg420Params,
pub(crate) output_len: usize,
pub(crate) geometry: CudaLaunchGeometry,
}