Files
video-v1/vav2/docs/working/NVDECAV1Decoder_Sync_Stabilization_Design.md

126 lines
5.5 KiB
Markdown
Raw Normal View History

2025-10-07 00:52:35 +09:00
# NVDECAV1Decoder 동기화 로직 안정화 설계
- **문서 작성일:** 2025년 10월 6일
- **작성자:** Gemini
## 1. 문제 정의
현재 `NVDECAV1Decoder::DecodeToSurface` 함수는 디코딩 완료 및 프레임 반환 순서를 보장하기 위해 `sleep_for`를 사용하는 폴링(polling) 및 busy-wait 방식에 크게 의존하고 있다.
1. **FIFO 순서 보장 로직:** `while (m_returnCounter.load() != my_submission_id)` 루프 내 `sleep_for(1ms)`는 불필요한 지연을 유발하며, 특정 조건에서 데드락(무한 루프)을 발생시킬 수 있는 위험이 있다.
2. **디코딩 완료 대기 로직:** `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`를 추가한다.
**기존:**
```cpp
struct DecodeSlot {
std::atomic<bool> in_use{false};
std::atomic<bool> is_ready{false};
// ...
};
```
**변경 후:**
```cpp
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`에만 직접 신호를 보낸다.
**기존 (개념):**
```cpp
// PollingThreadFunc
if (decode_complete) {
slot.is_ready.store(true);
// 전역 CV에 신호를 보냄
global_cv.notify_all();
}
```
**변경 후:**
```cpp
// 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`만 직접 기다린다.
**기존:**
```cpp
// 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), ...)) {
// 매우 긴 타임아웃 처리
// ...
}
```
**변경 후:**
```cpp
// 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. 기대 효과
1. **안정성 대폭 향상:** `sleep`과 긴 타임아웃으로 인한 불확실성을 제거하고, 특정 프레임의 처리가 멈추더라도 다른 프레임의 대기 로직에 영향을 주지 않아 데드락 위험이 크게 감소한다.
2. **지연 시간(Latency) 감소:** 디코딩 완료 즉시 해당 프레임을 기다리던 스레드가 깨어나므로, 불필요한 `sleep` 지연이 사라지고 시스템 응답성이 향상된다.
3. **코드 단순화 및 유지보수성 향상:** 복잡한 동적 타임아웃 계산 로직이 사라지고, "신호-대기" 관계가 명확해져 코드 이해가 쉬워진다.
## 5. 작업 범위
- **포함:**
- `NVDECAV1Decoder.h``DecodeSlot` 구조체 수정
- `NVDECAV1Decoder.cpp``PollingThreadFunc`, `DecodeToSurface` 함수 내부 로직 수정
- **미포함:**
- `NVDECAV1Decoder` 클래스의 공개(public) 인터페이스 변경
- `Player` 또는 `Renderer` 등 외부 클래스의 아키텍처 변경