j2k_cuda/batch/
external.rs1use super::{BatchGroupInfo, CudaBatchError, Error, J2kDecodeWarning, Rect};
6
7#[cfg(feature = "cuda-runtime")]
10#[derive(Debug)]
11pub struct CudaExternalBatchGroup {
12 pub(super) info: BatchGroupInfo,
13 pub(super) source_indices: Vec<usize>,
14 pub(super) decoded_rects: Vec<Rect>,
15 pub(super) warnings: Vec<Vec<J2kDecodeWarning>>,
16 pub(super) ranges: Vec<j2k_cuda_runtime::CudaDeviceBufferRange>,
17}
18
19#[cfg(feature = "cuda-runtime")]
25#[must_use = "submitted CUDA decode must be retained or waited"]
26pub struct SubmittedCudaExternalBatch {
27 pub(super) group: CudaExternalBatchGroup,
28 pub(super) pending: SubmittedCudaCodecBatch,
29}
30
31#[cfg(feature = "cuda-runtime")]
32pub(super) enum SubmittedCudaCodecBatch {
33 Grayscale(crate::decoder::grayscale_batch::SubmittedGrayscaleExternalBatch),
34 Color(crate::decoder::SubmittedNativeColorExternalBatch),
35}
36
37#[cfg(feature = "cuda-runtime")]
38impl SubmittedCudaCodecBatch {
39 pub(super) fn ranges(&self) -> &[j2k_cuda_runtime::CudaDeviceBufferRange] {
40 match self {
41 Self::Grayscale(pending) => pending.ranges(),
42 Self::Color(pending) => pending.ranges(),
43 }
44 }
45
46 fn is_complete(&self) -> Result<bool, Error> {
47 match self {
48 Self::Grayscale(pending) => pending.is_complete(),
49 Self::Color(pending) => pending.is_complete(),
50 }
51 }
52
53 fn finish(
54 self,
55 ) -> Result<
56 (
57 Vec<j2k_cuda_runtime::CudaDeviceBufferRange>,
58 crate::CudaHtj2kProfileReport,
59 ),
60 Error,
61 > {
62 match self {
63 Self::Grayscale(pending) => pending.finish(),
64 Self::Color(pending) => pending.finish(),
65 }
66 }
67}
68
69#[cfg(feature = "cuda-runtime")]
71#[derive(Debug)]
72#[must_use = "pending CUDA work must remain retained until it completes"]
73#[expect(
74 clippy::large_enum_variant,
75 reason = "boxing would allocate on every incomplete retirement poll in the throughput path"
76)]
77pub enum CudaExternalBatchTryFinish {
78 Pending(SubmittedCudaExternalBatch),
80 Complete(CudaExternalBatchGroup),
82}
83
84#[cfg(feature = "cuda-runtime")]
85impl core::fmt::Debug for SubmittedCudaExternalBatch {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 f.debug_struct("SubmittedCudaExternalBatch")
88 .field("group", &self.group)
89 .field("pending", &true)
90 .finish()
91 }
92}
93
94#[cfg(feature = "cuda-runtime")]
95impl SubmittedCudaExternalBatch {
96 #[must_use]
98 pub fn group(&self) -> &CudaExternalBatchGroup {
99 &self.group
100 }
101
102 pub fn is_complete(&self) -> Result<bool, CudaBatchError> {
104 let source_indices = self.group().source_indices.clone();
105 self.pending
106 .is_complete()
107 .map_err(|source| CudaBatchError::GroupExecution {
108 source_indices,
109 source: Box::new(source),
110 })
111 }
112
113 pub fn try_finish(self) -> Result<CudaExternalBatchTryFinish, CudaBatchError> {
116 if self.is_complete()? {
117 self.wait().map(CudaExternalBatchTryFinish::Complete)
118 } else {
119 Ok(CudaExternalBatchTryFinish::Pending(self))
120 }
121 }
122
123 pub fn wait(self) -> Result<CudaExternalBatchGroup, CudaBatchError> {
125 let Self { mut group, pending } = self;
126 let source_indices = group.source_indices.clone();
127 let (ranges, _report) =
128 pending
129 .finish()
130 .map_err(|source| CudaBatchError::GroupExecution {
131 source_indices,
132 source: Box::new(source),
133 })?;
134 group.ranges = ranges;
135 Ok(group)
136 }
137}
138
139#[cfg(feature = "cuda-runtime")]
140impl CudaExternalBatchGroup {
141 #[must_use]
143 pub const fn info(&self) -> &BatchGroupInfo {
144 &self.info
145 }
146
147 #[must_use]
149 pub fn source_indices(&self) -> &[usize] {
150 &self.source_indices
151 }
152
153 #[must_use]
155 pub fn decoded_rects(&self) -> &[Rect] {
156 &self.decoded_rects
157 }
158
159 #[must_use]
161 pub fn warnings(&self) -> &[Vec<J2kDecodeWarning>] {
162 &self.warnings
163 }
164
165 #[must_use]
167 pub fn ranges(&self) -> &[j2k_cuda_runtime::CudaDeviceBufferRange] {
168 &self.ranges
169 }
170}