use crate::{
error::CudaError, execution::CudaExecutionStats, htj2k_decode::HTJ2K_STATUS_OK,
memory::CudaDeviceBuffer,
};
use j2k_core::host_capacity_bytes;
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct CudaHtj2kEncodeTables<'a> {
pub vlc_table0: &'a [u16; 2048],
pub vlc_table1: &'a [u16; 2048],
pub uvlc_table: &'a [u8],
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaHtj2kEncodeResources {
pub(crate) vlc_table0: CudaDeviceBuffer,
pub(crate) vlc_table1: CudaDeviceBuffer,
pub(crate) uvlc_table: CudaDeviceBuffer,
}
impl CudaHtj2kEncodeResources {
pub(super) fn launch_tables(&self) -> CudaHtj2kEncodeLaunchTables<'_> {
CudaHtj2kEncodeLaunchTables {
vlc_table0: &self.vlc_table0,
vlc_table1: &self.vlc_table1,
uvlc_table: &self.uvlc_table,
}
}
}
#[derive(Clone, Copy)]
pub(super) struct CudaHtj2kEncodeLaunchTables<'a> {
pub(super) vlc_table0: &'a CudaDeviceBuffer,
pub(super) vlc_table1: &'a CudaDeviceBuffer,
pub(super) uvlc_table: &'a CudaDeviceBuffer,
}
pub(super) struct CudaHtj2kEncodeCodeblocksLaunch<'a> {
pub(super) coefficients: &'a CudaDeviceBuffer,
pub(super) output: &'a CudaDeviceBuffer,
pub(super) jobs: &'a CudaDeviceBuffer,
pub(super) tables: CudaHtj2kEncodeLaunchTables<'a>,
pub(super) statuses: &'a CudaDeviceBuffer,
pub(super) job_count: usize,
}
pub(super) struct CudaHtj2kEncodeMultiInputLaunch<'a> {
pub(super) output: &'a CudaDeviceBuffer,
pub(super) jobs: &'a CudaDeviceBuffer,
pub(super) tables: CudaHtj2kEncodeLaunchTables<'a>,
pub(super) statuses: &'a CudaDeviceBuffer,
pub(super) job_count: usize,
}
pub(crate) const HTJ2K_ENCODE_MAX_CODEBLOCK_WIDTH: u32 = 1024;
pub(crate) const HTJ2K_ENCODE_MAX_CODEBLOCK_SAMPLES: usize = 4096;
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CudaHtj2kEncodeCodeBlockJob {
pub coefficient_offset: u32,
pub width: u32,
pub height: u32,
pub total_bitplanes: u8,
pub target_coding_passes: u8,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CudaHtj2kEncodeCodeBlockRegionJob {
pub coefficient_offset: u32,
pub coefficient_stride: u32,
pub width: u32,
pub height: u32,
pub total_bitplanes: u8,
pub target_coding_passes: u8,
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct CudaHtj2kEncodeResidentTarget<'a> {
pub coefficients: &'a CudaDeviceBuffer,
pub coefficient_count: usize,
pub jobs: &'a [CudaHtj2kEncodeCodeBlockJob],
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub(crate) struct CudaHtj2kEncodeKernelJob {
pub(crate) coefficient_offset: u32,
pub(crate) coefficient_stride: u32,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) total_bitplanes: u32,
pub(crate) output_offset: u32,
pub(crate) output_capacity: u32,
pub(crate) target_coding_passes: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub(crate) struct CudaHtj2kEncodeMultiInputKernelJob {
pub(crate) coefficient_ptr: u64,
pub(crate) coefficient_offset: u32,
pub(crate) coefficient_stride: u32,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) total_bitplanes: u32,
pub(crate) output_offset: u32,
pub(crate) output_capacity: u32,
pub(crate) target_coding_passes: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct CudaHtj2kEncodeCompactJob {
pub(crate) source_offset: u32,
pub(crate) compact_offset: u32,
pub(crate) data_len: u32,
pub(crate) reserved: u32,
}
#[doc(hidden)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CudaHtj2kEncodeStatus {
pub code: u32,
pub detail: u32,
pub data_len: u32,
pub number_of_coding_passes: u32,
pub missing_bit_planes: u32,
pub reserved0: u32,
pub reserved1: u32,
pub reserved2: u32,
}
impl CudaHtj2kEncodeStatus {
pub fn is_ok(self) -> bool {
self.code == HTJ2K_STATUS_OK
}
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CudaHtj2kEncodeStageTimings {
pub ht_encode_us: u128,
pub ht_kernel_us: u128,
pub ht_status_readback_us: u128,
pub ht_compact_us: u128,
pub ht_output_readback_us: u128,
}
impl CudaHtj2kEncodeStageTimings {
pub(super) fn from_parts(
ht_kernel_us: u128,
ht_status_readback_us: u128,
ht_compact_us: u128,
ht_output_readback_us: u128,
) -> Self {
Self {
ht_encode_us: ht_kernel_us
.saturating_add(ht_status_readback_us)
.saturating_add(ht_compact_us)
.saturating_add(ht_output_readback_us),
ht_kernel_us,
ht_status_readback_us,
ht_compact_us,
ht_output_readback_us,
}
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaHtj2kEncodedCodeBlock {
pub(crate) data: Vec<u8>,
pub(crate) status: CudaHtj2kEncodeStatus,
pub(crate) execution: CudaExecutionStats,
pub(crate) stage_timings: CudaHtj2kEncodeStageTimings,
}
impl CudaHtj2kEncodedCodeBlock {
pub fn data(&self) -> &[u8] {
&self.data
}
impl_cuda_htj2k_encoded_status_accessors!();
pub fn into_parts(self) -> (Vec<u8>, u32, u32, u8, u8) {
(
self.data,
htj2k_encoded_cleanup_length(self.status),
htj2k_encoded_refinement_length(self.status),
htj2k_encoded_num_coding_passes(self.status),
htj2k_encoded_num_zero_bitplanes(self.status),
)
}
}
pub(crate) fn htj2k_encoded_cleanup_length(status: CudaHtj2kEncodeStatus) -> u32 {
if status.number_of_coding_passes <= 1 {
status.data_len
} else {
status.reserved0
}
}
pub(crate) fn htj2k_encoded_refinement_length(status: CudaHtj2kEncodeStatus) -> u32 {
if status.number_of_coding_passes <= 1 {
0
} else {
status.reserved1
}
}
pub(crate) fn htj2k_encoded_num_coding_passes(status: CudaHtj2kEncodeStatus) -> u8 {
u8::try_from(status.number_of_coding_passes).unwrap_or(u8::MAX)
}
pub(crate) fn htj2k_encoded_num_zero_bitplanes(status: CudaHtj2kEncodeStatus) -> u8 {
u8::try_from(status.missing_bit_planes).unwrap_or(u8::MAX)
}
pub(super) fn empty_htj2k_encoded_code_blocks() -> CudaHtj2kEncodedCodeBlocks {
CudaHtj2kEncodedCodeBlocks {
code_blocks: Vec::new(),
execution: CudaExecutionStats::default(),
stage_timings: CudaHtj2kEncodeStageTimings::default(),
}
}
pub(super) fn validate_resident_coefficient_capacity(
coefficients: &CudaDeviceBuffer,
coefficient_count: usize,
) -> Result<(), CudaError> {
let available_coefficients = coefficients.typed_view::<i32>()?.len();
if available_coefficients < coefficient_count {
return Err(CudaError::OutputTooSmall {
required: coefficient_count
.checked_mul(std::mem::size_of::<i32>())
.ok_or(CudaError::LengthTooLarge {
len: coefficient_count,
})?,
have: coefficients.byte_len(),
});
}
Ok(())
}
#[doc(hidden)]
#[derive(Debug)]
pub struct CudaHtj2kEncodedCodeBlocks {
pub(crate) code_blocks: Vec<CudaHtj2kEncodedCodeBlock>,
pub(crate) execution: CudaExecutionStats,
pub(crate) stage_timings: CudaHtj2kEncodeStageTimings,
}
impl CudaHtj2kEncodedCodeBlocks {
pub fn code_blocks(&self) -> &[CudaHtj2kEncodedCodeBlock] {
&self.code_blocks
}
pub fn into_code_blocks(self) -> Vec<CudaHtj2kEncodedCodeBlock> {
self.code_blocks
}
#[doc(hidden)]
pub fn host_capacity_bytes(&self) -> usize {
self.code_blocks.iter().fold(
host_capacity_bytes::<CudaHtj2kEncodedCodeBlock>(self.code_blocks.capacity()),
|bytes, block| bytes.saturating_add(host_capacity_bytes::<u8>(block.data.capacity())),
)
}
pub fn execution(&self) -> CudaExecutionStats {
self.execution
}
pub fn stage_timings(&self) -> CudaHtj2kEncodeStageTimings {
self.stage_timings
}
}