diff --git a/vav2/notes/av1-dpb.txt b/vav2/notes/av1-dpb.txt new file mode 100644 index 0000000..66bea6b --- /dev/null +++ b/vav2/notes/av1-dpb.txt @@ -0,0 +1,132 @@ + +╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ > NVDEC API 에 관해서 질문이 있다. 각 프레임을 디코딩해서 Video Frame 으로 얻어 와서 cuvidMapVideoFrame() 를 통해서 실제 │ +│ frame data 를 얻어온다. │ +│ 그런데 AV1 디코딩은 최대 16개 이상의 프레임을 보관했다가, frame reordering 에 의해서 이전에 디코딩했던 프레임을 화면에 │ +│ 렌더링할 경우가 있다. NVDEC 에서 video frame 에 대해서 캐싱을 지원하고 있는지 궁금하다. 현재까지의 실험으로는 캐싱을 하지 │ +│ 않는 것으로 밝혀졌는데, 사실인지 검증이 필요하다. 필요하면 웹서칭을 통해서 사실 검증을 진행해도 좋다. │ +│ 만약, video frame 캐싱을 지원하지 않는다면 NVDEC API 로 비디오 렌더링을 구현할 때 사용자가 직접 16개의 프레임을 직접 │ +│ 캐싱해서 보관해야 하는지 궁금하고, D3D12 texture 에 렌더링까지 해서 캐싱하는 것이 유리할지 (메모리나 연산 측면에서), │ +│ 아니면 CUDA video frame 으로 캐싱을 가지고 있다가, 나중에 D3D12 texture 로 렌더링할 때 그 때 데이터 변환을 수행하는 것이 │ +│ 좋은지도 궁금하다. │ +│ 결론으로 AV1 frame format 의 순서와 렌더링 순서는 서로 뒤바뀌는 현상이 있어서 16개의 frame 정도는 캐싱할 필요가 있는데, │ +│ NVDEC CUDA layer 에서 이를 지원해주는지, 지원해주지 않는다면 application 에서 어떻게 캐싱을 구현하는 것이 좋은지 │ +│ 메모리/연산 측면에서 효율적인 방안에 대해서 검토를 부탁한다. │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +✦ 훌륭한 질문입니다. AV1 디코딩과 NVDEC API의 동작 방식, 그리고 애플리케이션 레벨에서의 효율적인 렌더링 전략에 대한 핵심적인 + 내용이 모두 포함되어 있습니다. 질문을 세 부분으로 나누어 상세히 답변해 드리겠습니다. + + + 1. NVDEC API는 프레임 캐싱/재정렬을 지원하는가? + - NVDEC의 역할: NVDEC는 저수준(low-level) 하드웨어 가속 디코더입니다. 주된 역할은 압축된 비디오 비트스트림(AV1, HEVC 등)을 + 입력받아 디코딩된 원본 비디오 프레임(YUV 포맷)을 최대한 빨리 GPU 메모리에 출력하는 것입니다. 즉, 디코딩 순서(Decode + Order)대로 동작합니다. + 따라서 AV1처럼 프레임 재정렬이 필수적인 코덱을 사용하려면, 사용자가 말씀하신 대로 애플리케이션에서 직접 최대 16개(또는 그 + 이상)의 프레임을 보관할 캐시(버퍼)를 구현해야 합니다. + + --- + + + 2. 프레임 캐싱 구현 방안: CUDA Frame vs D3D12 Texture + 방안 A: CUDA 비디오 프레임(CUvideoptr)으로 캐싱 + - 동작 방식: + - 장점: + - 단점: + - 동작 방식: + - 단점: + 압도적으로 `방안 A`, 즉 CUDA 비디오 프레임 형태로 캐싱하는 것을 권장합니다. + + 현대적인 고성능 비디오 플레이어는 모두 이 방식을 사용합니다. 이유는 다음과 같습니다. + + + 1. 메모리가 핵심입니다: 비디오 재생, 특히 4K/8K 고해상도 비디오에서 가장 큰 병목은 VRAM입니다. CUDA 프레임(NV12)으로 캐싱하는 + 것은 D3D12 RGB 텍스처로 캐싱하는 것에 비해 VRAM을 절반 이하로 사용하므로, 더 높은 해상도와 더 많은 캐시 프레임을 + 안정적으로 유지할 수 있습니다. + - NVDEC는 프레임 재정렬을 해주지 않으므로, 애플리케이션에서 직접 프레임 캐시(DPB)를 구현해야 합니다. + +╭──────────────╮ +│ > continue │ +╰──────────────╯ + +✦ 알겠습니다. 이어서 NVDEC와 D3D12를 사용한 고성능 비디오 렌더링 파이프라인을 구축할 때 고려해야 할 추가적인 기술적 세부사항과 + 구현 팁에 대해 설명하겠습니다. + + + 4. 애플리케이션 DPB(Decoded Picture Buffer) 구현 전략 + CUDA 프레임 인덱스를 캐싱하기로 결정했으니, 이제 이 DPB를 어떻게 효율적으로 관리할지에 대한 구체적인 전략이 필요합니다. + + + - 자료구조 선택: + - 버퍼 관리 로직: + 5. CUDA-D3D12 Interoperability 심화 + 1. 외부 메모리 할당: cuvidCtxLock으로 컨텍스트를 잠근 후, cuMemCreate를 사용하여 외부 메모리 핸들(External Memory Handle)을 + 생성할 D3D12 리소스(ID3D12Resource)를 준비합니다. 이 리소스는 디코더의 출력 포맷(예: DXGI_FORMAT_NV12)과 일치해야 합니다. + + 이 방식은 NVDEC가 직접 D3D12 리소스에 디코딩 결과를 쓰는 가장 효율적인 경로입니다. cuvidMapVideoFrame을 사용하는 것보다 한 + 단계 더 최적화된 방법이라고 할 수 있습니다. + + ### 6. 동기화 문제 (Synchronization) + + CUDA와 D3D12는 서로 다른 비동기식 커맨드 큐를 사용하므로, 둘 사이의 작업 순서를 보장하기 위한 명시적인 동기화가 필수적입니다. + + + - `cudaExternalSemaphore` 사용: + 이러한 동기화는 파이프라인의 안정성을 보장하고, 리소스 접근 충돌로 인한 크래시나 깨진 화면을 방지하는 데 매우 중요합니다. + + ### 최종 요약 및 제언 + + + - DPB 관리: PTS를 키로 사용하는 맵을 구현하고, pfnDisplayPicture 콜백을 통해 표시할 프레임을 식별하며, 더 이상 필요 없는 + 프레임은 즉시 해제하여 디코더 버퍼 고갈을 방지하세요. + 이러한 개념들을 VavCore 프로젝트에 적용한다면, CPU 부하를 최소화하면서 매우 높은 성능을 내는 하드웨어 가속 비디오 플레이어를 + 구현할 수 있을 것입니다. 현재 VavCore의 아키텍처는 이러한 저수준 최적화를 적용하기에 매우 적합한 구조로 보입니다. + +╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ > NVDEC API 에 관해서 질문이 있다. 각 프레임을 디코딩해서 Video Frame 으로 얻어 와서 cuvidMapVideoFrame() 를 통해서 실제 │ +│ frame data 를 얻어온다. │ +│ 그런데 AV1 디코딩은 최대 16개 이상의 프레임을 보관했다가, frame reordering 에 의해서 이전에 디코딩했던 프레임을 화면에 │ +│ 렌더링할 경우가 있다. NVDEC 에서 video frame 에 대해서 캐싱을 지원하고 있는지 궁금하다. 현재까지의 실험으로는 캐싱을 하지 │ +│ 않는 것으로 밝혀졌는데, 사실인지 검증이 필요하다. 필요하면 웹서칭을 통해서 사실 검증을 진행해도 좋다. │ +│ 만약, video frame 캐싱을 지원하지 않는다면 NVDEC API 로 비디오 렌더링을 구현할 때 사용자가 직접 16개의 프레임을 직접 │ +│ 캐싱해서 보관해야 하는지 궁금하고, D3D12 texture 에 렌더링까지 해서 캐싱하는 것이 유리할지 (메모리나 연산 측면에서), │ +│ 아니면 CUDA video frame 으로 캐싱을 가지고 있다가, 나중에 D3D12 texture 로 렌더링할 때 그 때 데이터 변환을 수행하는 것이 │ +│ 좋은지도 궁금하다. │ +│ 결론으로 AV1 frame format 의 순서와 렌더링 순서는 서로 뒤바뀌는 현상이 있어서 16개의 frame 정도는 캐싱할 필요가 있는데, │ +│ NVDEC CUDA layer 에서 이를 지원해주는지, 지원해주지 않는다면 application 에서 어떻게 캐싱을 구현하는 것이 좋은지 │ +│ 메모리/연산 측면에서 효율적인 방안에 대해서 검토를 부탁한다. │ +╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +✦ 네, 다시 한번 질문의 핵심을 요약하고 명확히 답변해 드리겠습니다. 이전에 답변 드린 내용의 핵심을 정리하는 방식으로 + 설명하겠습니다. + + ### 질문 요약 + + + 1. NVDEC의 자동 캐싱/재정렬 지원 여부: AV1의 프레임 재정렬에 필요한 캐싱(버퍼링)을 NVDEC가 자동으로 처리해주는가? + 1. NVDEC의 자동 캐싱 지원 여부: 지원하지 않음 + - 사용자의 실험 결과가 정확합니다. NVDEC는 디코딩 순서(Decode Order)에 따라 비디오 프레임을 생성할 뿐, 표시 + 순서(Presentation Order)에 맞춰 프레임을 캐싱하거나 재정렬하는 기능은 제공하지 않습니다. + 메모리와 연산 효율성을 종합적으로 고려했을 때, 디코딩된 CUDA 비디오 프레임(`CUvideoptr` 또는 관련 인덱스) 형태로 캐싱하는 + 것이 압도적으로 유리합니다. + + + | 항목 | 방안 A: CUDA 프레임으로 캐싱 (권장) | 방안 B: D3D12 텍스처로 캐싱 | + | :--- | :--- | :--- | + | 메모리 효율성 | 매우 높음. YUV(NV12 등) 포맷으로 저장하여 VRAM 사용량 최소화. (RGB 대비 1/2 ~ 1/3 수준) | 매우 낮음. 비압축 + RGB 포맷으로 저장하여 VRAM 사용량 극대화. 고해상도에서 치명적. | + | 렌더링 시 연산 | YUV->RGB 색상 변환 셰이더 실행 필요. (현대 GPU에서 부담이 거의 없는 저비용 연산) | 이미 변환된 텍스처를 + 복사(Blit)만 하면 됨. (연산 비용 가장 낮음) | + | 구현 복잡도 | 렌더링 시 색상 변환 로직이 추가되나, 표준적인 방식. | 렌더링 로직은 단순해지나, 디코딩 직후 변환 과정이 + 추가됨. | + | 유연성 | 렌더링 시점에 색 공간, 톤 매핑 등을 유연하게 변경 가능. | 모든 효과가 미리 적용된 상태로 '구워져' 유연성 낮음. | + + ### 최종 결론 + + + AV1의 프레임 재정렬 문제를 해결하기 위해 애플리케이션에서 직접 프레임 버퍼를 구현해야 하며, VRAM을 효율적으로 사용하기 위해 + 디코딩된 프레임을 원본 YUV 포맷(CUDA 프레임) 그대로 버퍼에 저장하고, 실제 화면에 렌더링하는 시점에 셰이더를 통해 실시간으로 + YUV->RGB 색상 변환을 수행하는 것이 최적의 방안입니다. + + + 이 방식은 현대적인 고성능 비디오 플레이어의 표준적인 아키텍처입니다. \ No newline at end of file diff --git a/vav2/diag1.txt b/vav2/notes/diag1.txt similarity index 100% rename from vav2/diag1.txt rename to vav2/notes/diag1.txt diff --git a/vav2/optimization.txt b/vav2/notes/optimization.txt similarity index 100% rename from vav2/optimization.txt rename to vav2/notes/optimization.txt diff --git a/vav2/optimization2.txt b/vav2/notes/optimization2.txt similarity index 100% rename from vav2/optimization2.txt rename to vav2/notes/optimization2.txt diff --git a/vav2/optimization3-hwcodec.txt b/vav2/notes/optimization3-hwcodec.txt similarity index 100% rename from vav2/optimization3-hwcodec.txt rename to vav2/notes/optimization3-hwcodec.txt diff --git a/vav2/optimization4.txt b/vav2/notes/optimization4.txt similarity index 100% rename from vav2/optimization4.txt rename to vav2/notes/optimization4.txt diff --git a/prompt-redesign-nvdec.txt b/vav2/notes/prompt-redesign-nvdec.txt similarity index 100% rename from prompt-redesign-nvdec.txt rename to vav2/notes/prompt-redesign-nvdec.txt diff --git a/prompt-redesign-ringbuffer.txt b/vav2/notes/prompt-redesign-ringbuffer.txt similarity index 100% rename from prompt-redesign-ringbuffer.txt rename to vav2/notes/prompt-redesign-ringbuffer.txt diff --git a/prompt.txt b/vav2/notes/prompt.txt similarity index 100% rename from prompt.txt rename to vav2/notes/prompt.txt diff --git a/prompt2.txt b/vav2/notes/prompt2.txt similarity index 100% rename from prompt2.txt rename to vav2/notes/prompt2.txt diff --git a/prompt3.txt b/vav2/notes/prompt3.txt similarity index 100% rename from prompt3.txt rename to vav2/notes/prompt3.txt diff --git a/todo.txt b/vav2/notes/todo.txt similarity index 100% rename from todo.txt rename to vav2/notes/todo.txt diff --git a/vav2/todo10.txt b/vav2/notes/todo10.txt similarity index 100% rename from vav2/todo10.txt rename to vav2/notes/todo10.txt diff --git a/vav2/todo11.txt b/vav2/notes/todo11.txt similarity index 100% rename from vav2/todo11.txt rename to vav2/notes/todo11.txt diff --git a/vav2/todo12.txt b/vav2/notes/todo12.txt similarity index 100% rename from vav2/todo12.txt rename to vav2/notes/todo12.txt diff --git a/vav2/todo13.txt b/vav2/notes/todo13.txt similarity index 100% rename from vav2/todo13.txt rename to vav2/notes/todo13.txt diff --git a/vav2/todo14.txt b/vav2/notes/todo14.txt similarity index 100% rename from vav2/todo14.txt rename to vav2/notes/todo14.txt diff --git a/vav2/todo15.txt b/vav2/notes/todo15.txt similarity index 100% rename from vav2/todo15.txt rename to vav2/notes/todo15.txt diff --git a/todo16.txt b/vav2/notes/todo16.txt similarity index 100% rename from todo16.txt rename to vav2/notes/todo16.txt diff --git a/todo17.txt b/vav2/notes/todo17.txt similarity index 100% rename from todo17.txt rename to vav2/notes/todo17.txt diff --git a/todo18.txt b/vav2/notes/todo18.txt similarity index 100% rename from todo18.txt rename to vav2/notes/todo18.txt diff --git a/todo19.txt b/vav2/notes/todo19.txt similarity index 100% rename from todo19.txt rename to vav2/notes/todo19.txt diff --git a/vav2/todo2.txt b/vav2/notes/todo2.txt similarity index 100% rename from vav2/todo2.txt rename to vav2/notes/todo2.txt diff --git a/todo20.txt b/vav2/notes/todo20.txt similarity index 100% rename from todo20.txt rename to vav2/notes/todo20.txt diff --git a/todo21.txt b/vav2/notes/todo21.txt similarity index 100% rename from todo21.txt rename to vav2/notes/todo21.txt diff --git a/todo22.txt b/vav2/notes/todo22.txt similarity index 100% rename from todo22.txt rename to vav2/notes/todo22.txt diff --git a/todo23.txt b/vav2/notes/todo23.txt similarity index 100% rename from todo23.txt rename to vav2/notes/todo23.txt diff --git a/todo24.txt b/vav2/notes/todo24.txt similarity index 100% rename from todo24.txt rename to vav2/notes/todo24.txt diff --git a/vav2/todo3.txt b/vav2/notes/todo3.txt similarity index 100% rename from vav2/todo3.txt rename to vav2/notes/todo3.txt diff --git a/vav2/todo5.txt b/vav2/notes/todo5.txt similarity index 100% rename from vav2/todo5.txt rename to vav2/notes/todo5.txt diff --git a/vav2/todo6.txt b/vav2/notes/todo6.txt similarity index 100% rename from vav2/todo6.txt rename to vav2/notes/todo6.txt diff --git a/vav2/todo7.txt b/vav2/notes/todo7.txt similarity index 100% rename from vav2/todo7.txt rename to vav2/notes/todo7.txt diff --git a/vav2/todo8.txt b/vav2/notes/todo8.txt similarity index 100% rename from vav2/todo8.txt rename to vav2/notes/todo8.txt diff --git a/vav2/todo9.txt b/vav2/notes/todo9.txt similarity index 100% rename from vav2/todo9.txt rename to vav2/notes/todo9.txt