1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use ;
use Error as ThisError;
/// Errors from opening the CUDA device context.
/// The one CUDA device context every CUDA element in a pipeline shares.
///
/// This is the exact analog of the single `ID3D11Device` the D3D11 stack
/// requires (see [`crate::elements::D3d11Renderer`]'s docs on why that stack
/// needs one shared device rather than merely one adapter): a CUDA frame
/// allocated against one context is not readable from another, so
/// [`crate::elements::CudaDecoder`] and [`crate::elements::CudaRenderer`]
/// must be constructed from the *same* `CudaDevice`. Create one per stack
/// and hand it to every CUDA element.
///
/// Each element takes its own FFmpeg reference at construction, so this
/// value only has to outlive the constructor calls — not the pipeline. The
/// underlying context stays alive as long as any element, or any frame
/// still in flight downstream, references it.
///
/// # Create it once, up front
///
/// Because this is the device's *primary* context (see below), constructing
/// and dropping a `CudaDevice` retains and releases process-wide driver
/// state. Doing that on one thread while another thread has NVDEC or NVENC
/// work in flight has been observed to segfault inside `libnvcuvid`, on a
/// thread the driver itself owns — nothing in this crate can catch or
/// recover that. Build one per process before the pipelines start, hand it
/// to every CUDA element, and keep it until they are gone.
///
/// Opens the default CUDA device. There is deliberately no ordinal
/// parameter: a `CudaRenderer`'s graphics device has to be on the same
/// physical GPU, and this crate has no way to verify that pairing, so
/// offering a choice here would only make a mismatch expressible without
/// making it detectable.
///
/// # Why the primary context
///
/// This opens the device's **primary** CUDA context rather than letting
/// FFmpeg create a private one, and that choice is what makes
/// [`crate::elements::CudaFrameRenderer`] implementable at all. Importing a
/// Vulkan/D3D image into CUDA means calling `cuImportExternalMemory` on the
/// *same* context the frames live on — and the only other way to reach
/// FFmpeg's private context is to hand-mirror `AVCUDADeviceContext`, which
/// `ffmpeg-sys-next` does not bind. Reconstructing FFmpeg hwcontext structs
/// by hand is exactly the practice that corrupted memory in this crate's
/// D3D11VA history (see `d3d11va_decoder`'s own notes). With the primary
/// context, an interop implementation calls `cuDevicePrimaryCtxRetain` and
/// provably gets the same `CUcontext` these frames were decoded on, with no
/// struct layout guessed anywhere.
/// `AV_CUDA_USE_PRIMARY_CONTEXT` from `libavutil/hwcontext_cuda.h` — passed
/// as `av_hwdevice_ctx_create`'s `flags`, which is a plain documented `int`
/// parameter, not a struct layout this crate has to mirror.
const AV_CUDA_USE_PRIMARY_CONTEXT: i32 = 1 << 0;