5.5 KiB
NVDECAV1Decoder 동기화 로직 안정화 설계
- 문서 작성일: 2025년 10월 6일
- 작성자: Gemini
1. 문제 정의
현재 NVDECAV1Decoder::DecodeToSurface 함수는 디코딩 완료 및 프레임 반환 순서를 보장하기 위해 sleep_for를 사용하는 폴링(polling) 및 busy-wait 방식에 크게 의존하고 있다.
- FIFO 순서 보장 로직:
while (m_returnCounter.load() != my_submission_id)루프 내sleep_for(1ms)는 불필요한 지연을 유발하며, 특정 조건에서 데드락(무한 루프)을 발생시킬 수 있는 위험이 있다. - 디코딩 완료 대기 로직:
frame_ready.wait_for는 해상도에 따라 수 초에 달하는 매우 긴 타임아웃을 사용한다. 이는 시스템의 잠재적 불안정성을 회피하기 위한 방어 코드(고육지책)로 보이며, 실제 문제 발생 시 애플리케이션이 장시간 멈추는 결과를 초래한다.
이 두 가지 sleep 기반 메커니즘은 시스템의 안정성을 저해하고, 예측 불가능한 성능 저하를 유발하는 핵심 원인이다.
2. 목표
대규모 아키텍처 변경 없이, DecodeToSurface 함수 내부의 동기화 로직을 안정적이고 효율적인 이벤트 기반 방식으로 수정한다.
sleep기반 대기를 제거하여 데드락 위험을 최소화하고 안정성을 향상시킨다.- 불필요한 지연 시간을 제거하여 디코딩 파이프라인의 응답성을 높인다.
- 함수의 외부 인터페이스는 변경하지 않고 내부 구현만 개선하여 하위 호환성을 유지한다.
3. 제안 설계
핵심 아이디어는 "전역적인 신호(global notification)를 개별적인 신호(per-slot notification)로" 변경하는 것이다.
3.1. DecodeSlot 구조체 수정
NVDECAV1Decoder.h 파일의 DecodeSlot 구조체에 각 슬롯별 동기화를 위한 std::condition_variable과 std::mutex를 추가한다.
기존:
struct DecodeSlot {
std::atomic<bool> in_use{false};
std::atomic<bool> is_ready{false};
// ...
};
변경 후:
struct DecodeSlot {
std::atomic<bool> in_use{false};
std::atomic<bool> is_ready{false};
std::mutex slot_mutex;
std::condition_variable slot_cv;
// ...
};
3.2. PollingThreadFunc 수정 (신호 전송부)
폴링 스레드는 특정 슬롯(slot[i])의 디코딩 완료(cuvidGetDecodeStatus)를 감지하면, 해당 슬롯의 condition_variable에만 직접 신호를 보낸다.
기존 (개념):
// PollingThreadFunc
if (decode_complete) {
slot.is_ready.store(true);
// 전역 CV에 신호를 보냄
global_cv.notify_all();
}
변경 후:
// PollingThreadFunc
if (decode_complete) {
{
std::lock_guard<std::mutex> lock(slot.slot_mutex);
slot.is_ready.store(true);
} // lock 해제
slot.slot_cv.notify_one(); // 해당 슬롯을 기다리는 스레드만 깨움
}
3.3. DecodeToSurface 수정 (신호 수신부)
DecodeToSurface 함수는 더 이상 해상도 기반의 긴 타임아웃을 사용하지 않는다. 대신, 자신이 기다려야 할 슬롯(my_slot)을 특정한 후, 해당 슬롯의 condition_variable만 직접 기다린다.
기존:
// 7. Wait for decode to complete with adaptive timeout based on resolution
// ...
if (!my_slot.frame_ready.wait_for(lock, std::chrono::milliseconds(timeout_ms), ...)) {
// 매우 긴 타임아웃 처리
// ...
}
변경 후:
// 7. Wait for decode to complete by waiting on the specific slot's condition variable
DecodeSlot& my_slot = m_ringBuffer[my_slot_idx];
{
std::unique_lock<std::mutex> lock(my_slot.slot_mutex);
// is_ready 플래그를 확인하며 신호가 올 때까지 대기
if (!my_slot.slot_cv.wait_for(lock, std::chrono::seconds(2), [&my_slot]{ return my_slot.is_ready.load(); })) {
// 안전장치: 2초의 고정 타임아웃. 드라이버 멈춤 등 예외 상황 방지
LOGF_ERROR("[DecodeToSurface] Decode timeout for slot %d after 2 seconds", my_slot_idx);
my_slot.in_use.store(false);
m_returnCounter.fetch_add(1); // 데드락 방지를 위해 카운터 증가
return false;
}
}
// is_ready가 true임이 보장됨
참고: FIFO 순서 보장을 위한 while 루프는 이 설계 변경의 범위에 포함되지 않으나, 디코딩 완료 대기 로직이 안정화되면 해당 루프의 데드락 위험도 간접적으로 감소한다.
4. 기대 효과
- 안정성 대폭 향상:
sleep과 긴 타임아웃으로 인한 불확실성을 제거하고, 특정 프레임의 처리가 멈추더라도 다른 프레임의 대기 로직에 영향을 주지 않아 데드락 위험이 크게 감소한다. - 지연 시간(Latency) 감소: 디코딩 완료 즉시 해당 프레임을 기다리던 스레드가 깨어나므로, 불필요한
sleep지연이 사라지고 시스템 응답성이 향상된다. - 코드 단순화 및 유지보수성 향상: 복잡한 동적 타임아웃 계산 로직이 사라지고, "신호-대기" 관계가 명확해져 코드 이해가 쉬워진다.
5. 작업 범위
- 포함:
NVDECAV1Decoder.h의DecodeSlot구조체 수정NVDECAV1Decoder.cpp의PollingThreadFunc,DecodeToSurface함수 내부 로직 수정
- 미포함:
NVDECAV1Decoder클래스의 공개(public) 인터페이스 변경Player또는Renderer등 외부 클래스의 아키텍처 변경