j2k_cuda/encode/
facade.rs1use core::{cell::Cell, marker::PhantomData};
4
5use j2k::{EncodeBackendPreference, EncodedJ2k, J2kLosslessEncodeOptions, J2kLosslessSamples};
6use j2k_core::BackendKind;
7
8use super::CudaEncodeStageAccelerator;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12#[non_exhaustive]
13pub enum CudaEncodeFallbackReason {
14 DeviceUnavailable,
16 DeviceRouteIncomplete,
18}
19
20#[derive(Clone, Debug, Eq, PartialEq)]
26pub struct CudaLosslessEncodeResult {
27 requested_backend: EncodeBackendPreference,
28 fallback_reason: Option<CudaEncodeFallbackReason>,
29 encoded: EncodedJ2k,
30}
31
32impl CudaLosslessEncodeResult {
33 fn new(
34 requested_backend: EncodeBackendPreference,
35 device_unavailable: bool,
36 encoded: EncodedJ2k,
37 ) -> Self {
38 let fallback_reason = if requested_backend == EncodeBackendPreference::Auto
39 && encoded.backend == BackendKind::Cpu
40 {
41 Some(if device_unavailable {
42 CudaEncodeFallbackReason::DeviceUnavailable
43 } else {
44 CudaEncodeFallbackReason::DeviceRouteIncomplete
45 })
46 } else {
47 None
48 };
49 Self {
50 requested_backend,
51 fallback_reason,
52 encoded,
53 }
54 }
55
56 #[must_use]
58 pub const fn requested_backend(&self) -> EncodeBackendPreference {
59 self.requested_backend
60 }
61
62 #[must_use]
64 pub const fn actual_backend(&self) -> BackendKind {
65 self.encoded.backend
66 }
67
68 #[must_use]
70 pub const fn fallback_reason(&self) -> Option<CudaEncodeFallbackReason> {
71 self.fallback_reason
72 }
73
74 #[must_use]
76 pub const fn dispatch_report(&self) -> j2k::J2kEncodeDispatchReport {
77 self.encoded.dispatch_report
78 }
79
80 #[must_use]
82 pub const fn encoded(&self) -> &EncodedJ2k {
83 &self.encoded
84 }
85
86 #[must_use]
88 pub fn into_encoded(self) -> EncodedJ2k {
89 self.encoded
90 }
91}
92
93#[derive(Debug)]
116pub struct CudaLosslessEncoder {
117 accelerator: CudaEncodeStageAccelerator,
118 not_sync: PhantomData<Cell<()>>,
119}
120
121impl Default for CudaLosslessEncoder {
122 fn default() -> Self {
123 Self::new()
124 }
125}
126
127impl CudaLosslessEncoder {
128 #[must_use]
130 pub fn new() -> Self {
131 Self {
132 accelerator: CudaEncodeStageAccelerator::default(),
133 not_sync: PhantomData,
134 }
135 }
136
137 pub fn encode(
143 &mut self,
144 samples: J2kLosslessSamples<'_>,
145 options: &J2kLosslessEncodeOptions,
146 ) -> Result<CudaLosslessEncodeResult, crate::Error> {
147 self.encode_with_options(samples, *options)
148 }
149
150 pub fn encode_strict_cuda(
157 &mut self,
158 samples: J2kLosslessSamples<'_>,
159 options: &J2kLosslessEncodeOptions,
160 ) -> Result<CudaLosslessEncodeResult, crate::Error> {
161 self.encode_with_options(
162 samples,
163 options.with_backend(EncodeBackendPreference::RequireDevice),
164 )
165 }
166
167 fn encode_with_options(
168 &mut self,
169 samples: J2kLosslessSamples<'_>,
170 options: J2kLosslessEncodeOptions,
171 ) -> Result<CudaLosslessEncodeResult, crate::Error> {
172 self.accelerator.begin_encode_attempt();
173 let requested_backend = options.backend;
174 let encoded = if requested_backend == EncodeBackendPreference::CpuOnly {
175 j2k::encode_j2k_lossless(samples, &options)
176 } else {
177 j2k::encode_j2k_lossless_with_accelerator(
178 samples,
179 &options,
180 BackendKind::Cuda,
181 &mut self.accelerator,
182 )
183 };
184
185 match encoded {
186 Ok(encoded) => Ok(CudaLosslessEncodeResult::new(
187 requested_backend,
188 self.accelerator.device_unavailable_observed(),
189 encoded,
190 )),
191 Err(error) => {
192 self.accelerator = CudaEncodeStageAccelerator::default();
193 Err(error.into())
194 }
195 }
196 }
197}