1#[cfg(feature = "cuda-runtime")]
4use super::grayscale_batch::decode_grayscale_cuda_resident_batch_into_with_profile;
5#[cfg(feature = "cuda-runtime")]
6use super::resident::{
7 decode_batch_to_cuda_resident_surface_with_profile_control,
8 decode_region_scaled_to_cuda_resident_surface_impl,
9 decode_region_to_cuda_resident_surface_impl, decode_scaled_to_cuda_resident_surface_impl,
10 decode_to_cuda_resident_surface_impl, decode_to_cuda_resident_surface_with_profile_impl,
11};
12use super::{
13 checked_surface_len, submit_ready_device, validate_surface_request,
14 wrap_cpu_staged_cuda_surface, wrap_surface, BackendRequest, CpuBackedImageDecode, CpuDecoder,
15 CpuJ2kScratchPool, CudaHtj2kProfileReport, CudaSession, DecodeOutcome, DeviceDecodePlan,
16 DeviceDecodeRequest, Downscale, Error, ImageCodec, ImageDecodeDevice, ImageDecodeSubmit,
17 J2kDecodeWarning, J2kDecoder, J2kView, PixelFormat, ReadySubmission, Rect, Surface,
18 DEFAULT_MAX_HOST_ALLOCATION_BYTES,
19};
20use crate::{
21 allocation::try_vec_filled,
22 routing::{auto_cuda_available, auto_decode_uses_cuda, AutoDecodeOperation},
23};
24
25impl<'a> J2kDecoder<'a> {
26 pub fn new(input: &'a [u8]) -> Result<Self, Error> {
28 let view = J2kView::parse(input)?;
29 let (transfer_syntax, payload_kind) = view.support_info().map_or((None, None), |support| {
30 (Some(support.transfer_syntax), Some(support.payload_kind))
31 });
32 Ok(Self {
33 bytes: input,
34 inner: CpuDecoder::from_view(view)?,
35 transfer_syntax,
36 payload_kind,
37 pool: CpuJ2kScratchPool::new(),
38 })
39 }
40
41 fn auto_decode_uses_cuda(
42 &self,
43 work_dimensions: (u32, u32),
44 fmt: PixelFormat,
45 operation: AutoDecodeOperation,
46 ) -> bool {
47 match (self.transfer_syntax, self.payload_kind) {
48 (Some(transfer_syntax), Some(payload_kind)) => auto_decode_uses_cuda(
49 work_dimensions,
50 self.inner.info().components,
51 fmt,
52 transfer_syntax,
53 payload_kind,
54 operation,
55 ),
56 _ => false,
57 }
58 }
59
60 fn decode_to_surface_impl(
61 &mut self,
62 session: &mut CudaSession,
63 fmt: PixelFormat,
64 backend: BackendRequest,
65 ) -> Result<Surface, Error> {
66 validate_surface_request(backend)?;
67 if matches!(backend, BackendRequest::Cuda)
68 || (backend == BackendRequest::Auto
69 && self.auto_decode_uses_cuda(
70 self.inner.info().dimensions,
71 fmt,
72 AutoDecodeOperation::Full,
73 )
74 && auto_cuda_available(session)?)
75 {
76 return decode_to_cuda_resident_surface_impl(self, session, fmt);
77 }
78 let dims = self.inner.info().dimensions;
79 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
80 j2k_profile::emit_gpu_route_surface_profile(
81 ("j2k", "cuda"),
82 (
83 "full",
84 format_args!("{backend:?}"),
85 format_args!("{fmt:?}"),
86 "cpu_decode_then_wrap",
87 ),
88 dims,
89 [],
90 );
91 self.inner
92 .decode_into_with_scratch(&mut self.pool, &mut out, stride, fmt)?;
93 wrap_surface(out, dims, fmt, backend, session)
94 }
95
96 fn decode_region_to_surface_impl(
97 &mut self,
98 session: &mut CudaSession,
99 fmt: PixelFormat,
100 roi: Rect,
101 backend: BackendRequest,
102 ) -> Result<Surface, Error> {
103 validate_surface_request(backend)?;
104 if matches!(backend, BackendRequest::Cuda) {
105 return decode_region_to_cuda_resident_surface_impl(self, session, fmt, roi);
106 }
107 let plan = DeviceDecodePlan::for_image(
108 self.inner.info().dimensions,
109 DeviceDecodeRequest::Region { roi },
110 )?;
111 let dims = plan.output_dims();
112 if backend == BackendRequest::Auto
113 && self.auto_decode_uses_cuda(dims, fmt, AutoDecodeOperation::Region)
114 && auto_cuda_available(session)?
115 {
116 return decode_region_to_cuda_resident_surface_impl(self, session, fmt, roi);
117 }
118 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
119 self.inner
120 .decode_region_into(&mut self.pool, &mut out, stride, fmt, plan.source_rect())?;
121 wrap_surface(out, dims, fmt, backend, session)
122 }
123
124 fn decode_scaled_to_surface_impl(
125 &mut self,
126 session: &mut CudaSession,
127 fmt: PixelFormat,
128 scale: Downscale,
129 backend: BackendRequest,
130 ) -> Result<Surface, Error> {
131 validate_surface_request(backend)?;
132 if matches!(backend, BackendRequest::Cuda) {
133 return decode_scaled_to_cuda_resident_surface_impl(self, session, fmt, scale);
134 }
135 let plan = DeviceDecodePlan::for_image(
136 self.inner.info().dimensions,
137 DeviceDecodeRequest::Scaled { scale },
138 )?;
139 let dims = plan.output_dims();
140 if backend == BackendRequest::Auto
141 && scale == Downscale::Half
142 && self.auto_decode_uses_cuda(dims, fmt, AutoDecodeOperation::ScaledHalf)
143 && auto_cuda_available(session)?
144 {
145 return decode_scaled_to_cuda_resident_surface_impl(self, session, fmt, scale);
146 }
147 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
148 self.inner
149 .decode_scaled_into(&mut self.pool, &mut out, stride, fmt, scale)?;
150 wrap_surface(out, dims, fmt, backend, session)
151 }
152
153 fn decode_region_scaled_to_surface_impl(
154 &mut self,
155 session: &mut CudaSession,
156 fmt: PixelFormat,
157 roi: Rect,
158 scale: Downscale,
159 backend: BackendRequest,
160 ) -> Result<Surface, Error> {
161 validate_surface_request(backend)?;
162 if matches!(backend, BackendRequest::Cuda) {
163 return decode_region_scaled_to_cuda_resident_surface_impl(
164 self, session, fmt, roi, scale,
165 );
166 }
167 let plan = DeviceDecodePlan::for_image(
168 self.inner.info().dimensions,
169 DeviceDecodeRequest::RegionScaled { roi, scale },
170 )?;
171 let dims = plan.output_dims();
172 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
173 self.inner.decode_region_scaled_into(
174 &mut self.pool,
175 &mut out,
176 stride,
177 fmt,
178 plan.source_rect(),
179 scale,
180 )?;
181 wrap_surface(out, dims, fmt, backend, session)
182 }
183
184 pub fn decode_to_device_with_session(
187 &mut self,
188 fmt: PixelFormat,
189 session: &mut CudaSession,
190 ) -> Result<Surface, Error> {
191 self.decode_to_surface_impl(session, fmt, BackendRequest::Cuda)
192 }
193
194 #[doc(hidden)]
197 pub fn decode_request_to_device_with_session(
198 &mut self,
199 fmt: PixelFormat,
200 request: DeviceDecodeRequest,
201 session: &mut CudaSession,
202 ) -> Result<Surface, Error> {
203 match request {
204 DeviceDecodeRequest::Full => self.decode_to_device_with_session(fmt, session),
205 DeviceDecodeRequest::Region { roi } => {
206 self.decode_region_to_device_with_session(fmt, roi, session)
207 }
208 DeviceDecodeRequest::Scaled { scale } => {
209 self.decode_scaled_to_device_with_session(fmt, scale, session)
210 }
211 DeviceDecodeRequest::RegionScaled { roi, scale } => {
212 self.decode_region_scaled_to_device_with_session(fmt, roi, scale, session)
213 }
214 }
215 }
216
217 #[doc(hidden)]
220 pub fn decode_to_device_with_session_and_profile(
221 &mut self,
222 fmt: PixelFormat,
223 session: &mut CudaSession,
224 ) -> Result<(Surface, CudaHtj2kProfileReport), Error> {
225 decode_to_cuda_resident_surface_with_profile_impl(self, session, fmt)
226 }
227
228 pub fn decode_batch_to_device_with_session(
231 inputs: &[&[u8]],
232 fmt: PixelFormat,
233 session: &mut CudaSession,
234 ) -> Result<Vec<Surface>, Error> {
235 decode_batch_to_cuda_resident_surface_with_profile_control(inputs, session, fmt, false)
236 .map(|(surfaces, _report)| surfaces)
237 }
238
239 #[doc(hidden)]
242 pub fn decode_batch_to_device_with_session_and_profile(
243 inputs: &[&[u8]],
244 fmt: PixelFormat,
245 session: &mut CudaSession,
246 ) -> Result<(Vec<Surface>, CudaHtj2kProfileReport), Error> {
247 decode_batch_to_cuda_resident_surface_with_profile_control(inputs, session, fmt, true)
248 }
249
250 #[cfg(feature = "cuda-runtime")]
253 #[doc(hidden)]
254 pub fn decode_batch_into_external_device_with_session(
255 inputs: &[&[u8]],
256 fmt: PixelFormat,
257 destination: &mut j2k_cuda_runtime::CudaExternalDeviceBufferViewMut<'_>,
258 session: &mut CudaSession,
259 ) -> Result<
260 (
261 Vec<j2k_cuda_runtime::CudaDeviceBufferRange>,
262 CudaHtj2kProfileReport,
263 ),
264 Error,
265 > {
266 decode_grayscale_cuda_resident_batch_into_with_profile(
267 inputs,
268 session,
269 fmt,
270 destination,
271 false,
272 )
273 }
274
275 pub(crate) fn decode_region_to_device_with_session(
278 &mut self,
279 fmt: PixelFormat,
280 roi: Rect,
281 session: &mut CudaSession,
282 ) -> Result<Surface, Error> {
283 self.decode_region_to_surface_impl(session, fmt, roi, BackendRequest::Cuda)
284 }
285
286 pub(crate) fn decode_scaled_to_device_with_session(
289 &mut self,
290 fmt: PixelFormat,
291 scale: Downscale,
292 session: &mut CudaSession,
293 ) -> Result<Surface, Error> {
294 self.decode_scaled_to_surface_impl(session, fmt, scale, BackendRequest::Cuda)
295 }
296
297 pub(crate) fn decode_region_scaled_to_device_with_session(
300 &mut self,
301 fmt: PixelFormat,
302 roi: Rect,
303 scale: Downscale,
304 session: &mut CudaSession,
305 ) -> Result<Surface, Error> {
306 self.decode_region_scaled_to_surface_impl(session, fmt, roi, scale, BackendRequest::Cuda)
307 }
308
309 pub fn decode_to_host_surface(&mut self, fmt: PixelFormat) -> Result<Surface, Error> {
311 let mut session = CudaSession::default();
312 self.decode_to_surface_impl(&mut session, fmt, BackendRequest::Cpu)
313 }
314
315 pub fn decode_to_cpu_staged_cuda_surface_with_session(
318 &mut self,
319 fmt: PixelFormat,
320 session: &mut CudaSession,
321 ) -> Result<Surface, Error> {
322 let dims = self.inner.info().dimensions;
323 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
324 self.inner
325 .decode_into_with_scratch(&mut self.pool, &mut out, stride, fmt)?;
326 wrap_cpu_staged_cuda_surface(&out, dims, fmt, session)
327 }
328
329 pub fn decode_region_to_cpu_staged_cuda_surface_with_session(
332 &mut self,
333 fmt: PixelFormat,
334 roi: Rect,
335 session: &mut CudaSession,
336 ) -> Result<Surface, Error> {
337 let plan = DeviceDecodePlan::for_image(
338 self.inner.info().dimensions,
339 DeviceDecodeRequest::Region { roi },
340 )?;
341 let dims = plan.output_dims();
342 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
343 self.inner
344 .decode_region_into(&mut self.pool, &mut out, stride, fmt, plan.source_rect())?;
345 wrap_cpu_staged_cuda_surface(&out, dims, fmt, session)
346 }
347
348 pub fn decode_scaled_to_cpu_staged_cuda_surface_with_session(
351 &mut self,
352 fmt: PixelFormat,
353 scale: Downscale,
354 session: &mut CudaSession,
355 ) -> Result<Surface, Error> {
356 let dims = DeviceDecodePlan::for_image(
357 self.inner.info().dimensions,
358 DeviceDecodeRequest::Scaled { scale },
359 )?
360 .output_dims();
361 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
362 self.inner
363 .decode_scaled_into(&mut self.pool, &mut out, stride, fmt, scale)?;
364 wrap_cpu_staged_cuda_surface(&out, dims, fmt, session)
365 }
366
367 pub fn decode_region_scaled_to_cpu_staged_cuda_surface_with_session(
370 &mut self,
371 fmt: PixelFormat,
372 roi: Rect,
373 scale: Downscale,
374 session: &mut CudaSession,
375 ) -> Result<Surface, Error> {
376 let plan = DeviceDecodePlan::for_image(
377 self.inner.info().dimensions,
378 DeviceDecodeRequest::RegionScaled { roi, scale },
379 )?;
380 let dims = plan.output_dims();
381 let (mut out, stride) = allocate_cpu_surface(dims, fmt)?;
382 self.inner.decode_region_scaled_into(
383 &mut self.pool,
384 &mut out,
385 stride,
386 fmt,
387 plan.source_rect(),
388 scale,
389 )?;
390 wrap_cpu_staged_cuda_surface(&out, dims, fmt, session)
391 }
392}
393
394fn allocate_cpu_surface(dims: (u32, u32), fmt: PixelFormat) -> Result<(Vec<u8>, usize), Error> {
395 let (stride, len) = checked_surface_len(
396 dims,
397 fmt.bytes_per_pixel(),
398 DEFAULT_MAX_HOST_ALLOCATION_BYTES,
399 "j2k CUDA CPU-staged surface",
400 )?;
401 Ok((
402 try_vec_filled(len, 0u8, "j2k CUDA CPU-staged surface")?,
403 stride,
404 ))
405}
406
407#[cfg(not(feature = "cuda-runtime"))]
408fn decode_to_cuda_resident_surface_impl(
409 _decoder: &mut J2kDecoder<'_>,
410 _session: &mut CudaSession,
411 _fmt: PixelFormat,
412) -> Result<Surface, Error> {
413 Err(Error::CudaUnavailable)
414}
415
416#[cfg(not(feature = "cuda-runtime"))]
417fn decode_to_cuda_resident_surface_with_profile_impl(
418 _decoder: &mut J2kDecoder<'_>,
419 _session: &mut CudaSession,
420 _fmt: PixelFormat,
421) -> Result<(Surface, CudaHtj2kProfileReport), Error> {
422 Err(Error::CudaUnavailable)
423}
424
425#[cfg(not(feature = "cuda-runtime"))]
426fn decode_region_to_cuda_resident_surface_impl(
427 _decoder: &mut J2kDecoder<'_>,
428 _session: &mut CudaSession,
429 _fmt: PixelFormat,
430 _roi: Rect,
431) -> Result<Surface, Error> {
432 Err(Error::CudaUnavailable)
433}
434
435#[cfg(not(feature = "cuda-runtime"))]
436fn decode_scaled_to_cuda_resident_surface_impl(
437 _decoder: &mut J2kDecoder<'_>,
438 _session: &mut CudaSession,
439 _fmt: PixelFormat,
440 _scale: Downscale,
441) -> Result<Surface, Error> {
442 Err(Error::CudaUnavailable)
443}
444
445#[cfg(not(feature = "cuda-runtime"))]
446fn decode_region_scaled_to_cuda_resident_surface_impl(
447 _decoder: &mut J2kDecoder<'_>,
448 _session: &mut CudaSession,
449 _fmt: PixelFormat,
450 _roi: Rect,
451 _scale: Downscale,
452) -> Result<Surface, Error> {
453 Err(Error::CudaUnavailable)
454}
455
456#[cfg(not(feature = "cuda-runtime"))]
457fn decode_batch_to_cuda_resident_surface_with_profile_control(
458 _inputs: &[&[u8]],
459 _session: &mut CudaSession,
460 _fmt: PixelFormat,
461 _collect_stage_timings: bool,
462) -> Result<(Vec<Surface>, CudaHtj2kProfileReport), Error> {
463 Err(Error::CudaUnavailable)
464}
465
466#[doc(hidden)]
467impl ImageCodec for J2kDecoder<'_> {
468 type Error = Error;
469 type Warning = J2kDecodeWarning;
470 type Pool = crate::J2kScratchPool;
471}
472
473impl<'a> CpuBackedImageDecode<'a> for J2kDecoder<'a> {
474 type Cpu = CpuDecoder<'a>;
475 type View = J2kView<'a>;
476
477 fn inspect_cpu(input: &'a [u8]) -> Result<j2k_core::Info, Self::Error> {
478 Ok(CpuDecoder::inspect(input)?)
479 }
480
481 fn parse_cpu(input: &'a [u8]) -> Result<Self::View, Self::Error> {
482 Ok(J2kView::parse(input)?)
483 }
484
485 fn from_cpu_view(view: Self::View) -> Result<Self, Self::Error> {
486 let bytes = view.bytes();
487 let (transfer_syntax, payload_kind) = view.support_info().map_or((None, None), |support| {
488 (Some(support.transfer_syntax), Some(support.payload_kind))
489 });
490 Ok(Self {
491 bytes,
492 inner: CpuDecoder::from_view(view)?,
493 transfer_syntax,
494 payload_kind,
495 pool: CpuJ2kScratchPool::new(),
496 })
497 }
498
499 fn cpu_decoder_mut(&mut self) -> &mut Self::Cpu {
500 &mut self.inner
501 }
502
503 fn map_cpu_outcome(
504 outcome: DecodeOutcome<<Self::Cpu as ImageCodec>::Warning>,
505 ) -> DecodeOutcome<Self::Warning> {
506 outcome
507 }
508}
509
510#[doc(hidden)]
511impl<'a> ImageDecodeDevice<'a> for J2kDecoder<'a> {
512 type DeviceSurface = Surface;
513}
514
515#[doc(hidden)]
516impl<'a> ImageDecodeSubmit<'a> for J2kDecoder<'a> {
517 type Session = CudaSession;
518 type DeviceSurface = Surface;
519 type SubmittedSurface = ReadySubmission<Surface, Error>;
520
521 fn submit_to_device(
522 &mut self,
523 session: &mut Self::Session,
524 fmt: PixelFormat,
525 backend: BackendRequest,
526 ) -> Result<Self::SubmittedSurface, Self::Error> {
527 validate_surface_request(backend)?;
528 Ok(submit_ready_device(session, |session| {
529 self.decode_to_surface_impl(session, fmt, backend)
530 }))
531 }
532
533 fn submit_region_to_device(
534 &mut self,
535 session: &mut Self::Session,
536 fmt: PixelFormat,
537 roi: Rect,
538 backend: BackendRequest,
539 ) -> Result<Self::SubmittedSurface, Self::Error> {
540 validate_surface_request(backend)?;
541 Ok(submit_ready_device(session, |session| {
542 self.decode_region_to_surface_impl(session, fmt, roi, backend)
543 }))
544 }
545
546 fn submit_scaled_to_device(
547 &mut self,
548 session: &mut Self::Session,
549 fmt: PixelFormat,
550 scale: Downscale,
551 backend: BackendRequest,
552 ) -> Result<Self::SubmittedSurface, Self::Error> {
553 validate_surface_request(backend)?;
554 Ok(submit_ready_device(session, |session| {
555 self.decode_scaled_to_surface_impl(session, fmt, scale, backend)
556 }))
557 }
558
559 fn submit_region_scaled_to_device(
560 &mut self,
561 session: &mut Self::Session,
562 fmt: PixelFormat,
563 roi: Rect,
564 scale: Downscale,
565 backend: BackendRequest,
566 ) -> Result<Self::SubmittedSurface, Self::Error> {
567 validate_surface_request(backend)?;
568 Ok(submit_ready_device(session, |session| {
569 self.decode_region_scaled_to_surface_impl(session, fmt, roi, scale, backend)
570 }))
571 }
572}