use core::{cell::Cell, marker::PhantomData};
use j2k::{EncodeBackendPreference, EncodedJ2k, J2kLosslessEncodeOptions, J2kLosslessSamples};
use j2k_core::BackendKind;
use super::CudaEncodeStageAccelerator;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum CudaEncodeFallbackReason {
DeviceUnavailable,
DeviceRouteIncomplete,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CudaLosslessEncodeResult {
requested_backend: EncodeBackendPreference,
fallback_reason: Option<CudaEncodeFallbackReason>,
encoded: EncodedJ2k,
}
impl CudaLosslessEncodeResult {
fn new(
requested_backend: EncodeBackendPreference,
device_unavailable: bool,
encoded: EncodedJ2k,
) -> Self {
let fallback_reason = if requested_backend == EncodeBackendPreference::Auto
&& encoded.backend == BackendKind::Cpu
{
Some(if device_unavailable {
CudaEncodeFallbackReason::DeviceUnavailable
} else {
CudaEncodeFallbackReason::DeviceRouteIncomplete
})
} else {
None
};
Self {
requested_backend,
fallback_reason,
encoded,
}
}
#[must_use]
pub const fn requested_backend(&self) -> EncodeBackendPreference {
self.requested_backend
}
#[must_use]
pub const fn actual_backend(&self) -> BackendKind {
self.encoded.backend
}
#[must_use]
pub const fn fallback_reason(&self) -> Option<CudaEncodeFallbackReason> {
self.fallback_reason
}
#[must_use]
pub const fn dispatch_report(&self) -> j2k::J2kEncodeDispatchReport {
self.encoded.dispatch_report
}
#[must_use]
pub const fn encoded(&self) -> &EncodedJ2k {
&self.encoded
}
#[must_use]
pub fn into_encoded(self) -> EncodedJ2k {
self.encoded
}
}
#[derive(Debug)]
pub struct CudaLosslessEncoder {
accelerator: CudaEncodeStageAccelerator,
not_sync: PhantomData<Cell<()>>,
}
impl Default for CudaLosslessEncoder {
fn default() -> Self {
Self::new()
}
}
impl CudaLosslessEncoder {
#[must_use]
pub fn new() -> Self {
Self {
accelerator: CudaEncodeStageAccelerator::default(),
not_sync: PhantomData,
}
}
pub fn encode(
&mut self,
samples: J2kLosslessSamples<'_>,
options: &J2kLosslessEncodeOptions,
) -> Result<CudaLosslessEncodeResult, crate::Error> {
self.encode_with_options(samples, *options)
}
pub fn encode_strict_cuda(
&mut self,
samples: J2kLosslessSamples<'_>,
options: &J2kLosslessEncodeOptions,
) -> Result<CudaLosslessEncodeResult, crate::Error> {
self.encode_with_options(
samples,
options.with_backend(EncodeBackendPreference::RequireDevice),
)
}
fn encode_with_options(
&mut self,
samples: J2kLosslessSamples<'_>,
options: J2kLosslessEncodeOptions,
) -> Result<CudaLosslessEncodeResult, crate::Error> {
self.accelerator.begin_encode_attempt();
let requested_backend = options.backend;
let encoded = if requested_backend == EncodeBackendPreference::CpuOnly {
j2k::encode_j2k_lossless(samples, &options)
} else {
j2k::encode_j2k_lossless_with_accelerator(
samples,
&options,
BackendKind::Cuda,
&mut self.accelerator,
)
};
match encoded {
Ok(encoded) => Ok(CudaLosslessEncodeResult::new(
requested_backend,
self.accelerator.device_unavailable_observed(),
encoded,
)),
Err(error) => {
self.accelerator = CudaEncodeStageAccelerator::default();
Err(error.into())
}
}
}
}