Skip to content

Commit c802535

Browse files
committed
Comments, etc.
1 parent e31ffca commit c802535

1 file changed

Lines changed: 29 additions & 30 deletions

File tree

src/torchcodec/_core/DecodeJpegCuda.cpp

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ namespace facebook::torchcodec {
4545
// API.
4646
// nvjpegDecodeBatch appears to be the only entry-point for the HW path, but
4747
// unclear.
48-
// TODO_IMAGE Should verify on a machien that supports the HW path whether:
49-
// - always calling nvjpegDecodeBatch even on single images is indeed faster than using the 'sw' path
48+
// TODO_IMAGE Should verify on a machine that supports the HW path whether:
49+
// - always calling nvjpegDecodeBatch even on single images is indeed faster
50+
// than using the 'sw' path
5051
// - whether we can just rely on calls to nvjpegDeode to dispatch to the HW path
5152
// and if that's as fast as calling nvjpegDecodeBatch, then maybe we don't
52-
// need to publicly expose an API that accepts batches (decode_jpeg(batch...)).
53+
// need to publicly expose an API that accepts batches
54+
// (decode_jpeg(batch...)).
5355

5456
using namespace exif_private;
5557

@@ -129,8 +131,6 @@ std::unique_ptr<CUDAJpegDecoder> NVJpegCache::get_decoder(
129131
return decoder;
130132
}
131133
}
132-
// Create outside the lock: constructing nvJPEG state is relatively expensive
133-
// and doesn't need the pool.
134134
return std::make_unique<CUDAJpegDecoder>(device);
135135
}
136136

@@ -180,31 +180,27 @@ std::vector<torch::stable::Tensor> decode_jpegs_cuda(
180180
int device_index = get_device_index(device);
181181
StableDeviceGuard device_guard(device_index);
182182

183-
cudaStream_t stream = get_current_cuda_stream(device_index);
183+
cudaStream_t current_stream = get_current_cuda_stream(device_index);
184184

185185
NVJpegCache& cache = NVJpegCache::get_cache(device);
186186
std::unique_ptr<CUDAJpegDecoder> decoder = cache.get_decoder(device);
187187

188-
std::vector<torch::stable::Tensor> result;
188+
std::vector<torch::stable::Tensor> output;
189189
// TODO_IMAGE Do we really need a try/except here?
190190
try {
191-
result = decoder->decode_images(
192-
contig_images, static_cast<ImageReadMode>(mode), stream);
191+
output = decoder->decode_images(
192+
contig_images, static_cast<ImageReadMode>(mode), current_stream);
193193
} catch (const std::exception& e) {
194194
// Return the decoder to the pool even on failure so we don't leak it.
195195
cache.return_decoder(std::move(decoder));
196196
STD_TORCH_CHECK(false, "Error while decoding JPEG images: ", e.what());
197197
}
198198
cache.return_decoder(std::move(decoder));
199199

200-
// decode_images() host-synchronizes the decode stream before returning, so
201-
// the decoded tensors are fully materialized; applying the EXIF transform
202-
// (aten flip/transpose on the current stream) is safe. This matches the CPU
203-
// decoder, which also applies EXIF orientation.
204-
for (size_t i = 0; i < result.size(); ++i) {
205-
result[i] = exif_orientation_transform(result[i], orientations[i]);
200+
for (size_t i = 0; i < output.size(); ++i) {
201+
output[i] = exif_orientation_transform(output[i], orientations[i]);
206202
}
207-
return result;
203+
return output;
208204
}
209205

210206
CUDAJpegDecoder::CUDAJpegDecoder(const torch::stable::Device& target_device)
@@ -241,8 +237,6 @@ CUDAJpegDecoder::CUDAJpegDecoder(const torch::stable::Device& target_device)
241237
status);
242238
}
243239

244-
// Batched (hardware) path state -- only ever used when the HW engine is
245-
// available, so only create it then.
246240
if (hw_decode_available_) {
247241
status = nvjpegJpegStateCreate(nvjpeg_handle_, &nvjpeg_state_hw_);
248242
STD_TORCH_CHECK(
@@ -267,11 +261,11 @@ CUDAJpegDecoder::CUDAJpegDecoder(const torch::stable::Device& target_device)
267261
}
268262

269263
CUDAJpegDecoder::~CUDAJpegDecoder() {
270-
// Unlike torchvision (which leaks these to dodge a Windows atexit-vs-CUDA
271-
// teardown crash), we destroy the nvJPEG handles here. Our decoders are held
272-
// in NVJpegCache, whose per-device instances are intentionally leaked (never
273-
// statically destroyed), so this destructor only runs during normal cache
274-
// eviction while CUDA is alive -- not at process teardown.
264+
// We properly destroy the nvjpeg stuff here. Note that this destructor is
265+
// only called when a decoder cannot return to the cache. This is never
266+
// reached during normal process teardown, because we just leak the entire
267+
// decoder cache, just like we leak the NVDEC cache to avoid weird CUDA
268+
// teardown issues.
275269
nvjpegJpegStateDestroy(nvjpeg_state_sw_);
276270
if (hw_decode_available_) {
277271
nvjpegJpegStreamDestroy(nvjpeg_stream_);
@@ -313,7 +307,8 @@ CUDAJpegDecoder::allocate_output(
313307
output_format = NVJPEG_OUTPUT_RGB;
314308
break;
315309
case ImageReadMode::UNCHANGED:
316-
output_format = source_channels == 1 ? NVJPEG_OUTPUT_Y : NVJPEG_OUTPUT_RGB;
310+
output_format =
311+
source_channels == 1 ? NVJPEG_OUTPUT_Y : NVJPEG_OUTPUT_RGB;
317312
break;
318313
default:
319314
STD_TORCH_CHECK(
@@ -363,7 +358,8 @@ CUDAJpegDecoder::split_images_by_backend(
363358
encoded_images[i].numel(),
364359
nvjpeg_stream_);
365360
int is_supported = -1;
366-
nvjpegDecodeBatchedSupported(nvjpeg_handle_, nvjpeg_stream_, &is_supported);
361+
nvjpegDecodeBatchedSupported(
362+
nvjpeg_handle_, nvjpeg_stream_, &is_supported);
367363
supports_hw = is_supported == 0; // nvJPEG sets 0 when supported
368364
}
369365
(supports_hw ? hw_indices : sw_indices).push_back(i);
@@ -377,7 +373,6 @@ void CUDAJpegDecoder::decode_batched_hardware(
377373
ImageReadMode mode,
378374
cudaStream_t stream,
379375
std::vector<torch::stable::Tensor>& output_tensors) {
380-
381376
std::vector<const unsigned char*> inputs;
382377
std::vector<size_t> sizes;
383378
std::vector<nvjpegImage_t> nvjpeg_images;
@@ -404,7 +399,8 @@ void CUDAJpegDecoder::decode_batched_hardware(
404399
// nvjpegDecodeBatchedInitialize can reconfigure that same state. This is an
405400
// assumption, but the sync point shouldn't hurt perf.
406401
bool needs_sync = false;
407-
for (nvjpegOutputFormat_t group_format : {NVJPEG_OUTPUT_Y, NVJPEG_OUTPUT_RGB}) {
402+
for (nvjpegOutputFormat_t group_format :
403+
{NVJPEG_OUTPUT_Y, NVJPEG_OUTPUT_RGB}) {
408404
std::vector<const unsigned char*> group_inputs;
409405
std::vector<size_t> group_sizes;
410406
std::vector<nvjpegImage_t> group_images;
@@ -429,7 +425,11 @@ void CUDAJpegDecoder::decode_batched_hardware(
429425

430426
// Should we expose max_cpu_threads????
431427
nvjpegStatus_t status = nvjpegDecodeBatchedInitialize(
432-
nvjpeg_handle_, nvjpeg_state_hw_, group_images.size(), /*max_cpu_threads=*/1, group_format);
428+
nvjpeg_handle_,
429+
nvjpeg_state_hw_,
430+
group_images.size(),
431+
/*max_cpu_threads=*/1,
432+
group_format);
433433
STD_TORCH_CHECK(
434434
status == NVJPEG_STATUS_SUCCESS,
435435
"Failed to initialize batch decoding: ",
@@ -486,8 +486,7 @@ std::vector<torch::stable::Tensor> CUDAJpegDecoder::decode_images(
486486
encoded_images, hw_indices, mode, stream, output_tensors);
487487
}
488488
if (!sw_indices.empty()) {
489-
decode_software(
490-
encoded_images, sw_indices, mode, stream, output_tensors);
489+
decode_software(encoded_images, sw_indices, mode, stream, output_tensors);
491490
}
492491

493492
// Host-synchronize before returning: the decoder (and its internal nvJPEG

0 commit comments

Comments
 (0)