codec/render: silence hw-decode failures, vram-aware dynamic worker pool
- open_hw_accel marks the device unavailable when the decoder OPEN fails (cuvidCreateDecoder OOM at 4K) too, not just device-context creation: without it every subsequent decoder session retried CUDA and flooded the log per open. - Decoder gains hardware_decoding(); the oak-render decode-session LRU evicts hardware sessions first (each pins a GPU surface pool — ~100 MB at 4K), so a full cache cannot exhaust video memory before the next open. - Worker pool count now factors GPU vram: per-worker budget = 1 GiB (1080p peak) scaled by pixel ratio + 256 MiB idle floor, 10% reserve of free vram; applied when hardware decoding is on (nvidia-smi query, None otherwise falls back to the RAM/CPU policy). - Dynamic pool resize: ProcessDispatcher::set_target_workers grows or retires workers; retiring ones stop claiming, drain their in-flight batch (future playback frames included), then exit naturally on the shutdown signal — no mid-work kill (30 s deadline only as a hung- decoder last resort). A retiring worker that dies re-queues its frames to surviving workers. Resizes are throttled to 2 s (a resolution burst merges; only the latest target applies) so 1080p<->4K flaps cannot thrash process spawns. - RenderManager::set_workspace_size announces the sequence resolution; RealEngine calls it from refresh_sequence_info. - Integration test: shrink 3->1 mid-wave (all frames complete, retired workers exit naturally) then regrow 1->3 and render a fresh wave.
This commit is contained in:
@@ -320,6 +320,15 @@ pub trait Decoder: Send + Sync {
|
||||
cancelled: Option<&CancelAtom>,
|
||||
) -> crate::error::Result<()>;
|
||||
|
||||
/// Unless the media requires it, prefer hardware over software (e.g. the
|
||||
/// FFmpeg decoder's platform hwaccel). The decode-session cache evicts
|
||||
/// hardware sessions first: each one pins GPU memory (an NVDEC 4K
|
||||
/// decoder holds ~10 surface frames of `4096×2160×1.5` ≈ 100+ MB of
|
||||
/// CUDA memory), while software sessions pin only system RAM.
|
||||
fn hardware_decoding(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Offset of the audio start relative to the video (rational seconds).
|
||||
fn get_audio_start_offset(&self) -> Rational {
|
||||
// C++ default `virtual Rational get_audio_start_offset() const { return 0; }`
|
||||
|
||||
@@ -331,6 +331,11 @@ impl Decoder for FFmpegDecoder {
|
||||
.unwrap_or_else(CodecStream::new)
|
||||
}
|
||||
|
||||
fn hardware_decoding(&self) -> bool {
|
||||
let state = self.state.lock().unwrap_or_else(|e| e.into_inner());
|
||||
state.as_ref().is_some_and(|s| s.hw_device.is_some())
|
||||
}
|
||||
|
||||
fn retrieve_video_frame(&self, p: &RetrieveVideoParams) -> crate::error::Result<Arc<Frame>> {
|
||||
ffmpeg_init()?;
|
||||
let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
|
||||
|
||||
@@ -203,11 +203,23 @@ pub fn open_hw_accel(
|
||||
unsafe { (*context.as_mut_ptr()).hw_device_ctx = device };
|
||||
let mut opts = Dictionary::new();
|
||||
opts.set("threads", &crate::ffmpeg::decoder_threads());
|
||||
context
|
||||
.decoder()
|
||||
.open_as_with(codec, opts)
|
||||
.ok()
|
||||
.map(|opened| (opened, device_type))
|
||||
let opened = match context.decoder().open_as_with(codec, opts) {
|
||||
Ok(opened) => opened,
|
||||
Err(_) => {
|
||||
// The device context was created but the hardware DECODER
|
||||
// could not be created for this stream (e.g. NVDEC
|
||||
// `cuvidCreateDecoder` out-of-memory at 4K — 4K surface pools
|
||||
// need tens of MB of video memory, and a full/too-small GPU
|
||||
// would otherwise spam its error on EVERY decoder open for
|
||||
// the life of the process). Same treatment as a failed
|
||||
// device-context creation: mark the type so later opens skip
|
||||
// the attempt and its log noise; the caller falls back to the
|
||||
// next candidate / software decode.
|
||||
mark_device_unavailable(device_type);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some((opened, device_type))
|
||||
}
|
||||
|
||||
/// Whether a decoded frame's pixel format is a hardware surface that
|
||||
|
||||
Reference in New Issue
Block a user