311 lines
8.6 KiB
Markdown
311 lines
8.6 KiB
Markdown
# Red Surface NVDEC 테스트 프로젝트 명세서
|
|
|
|
## 프로젝트 개요
|
|
|
|
**프로젝트 명:** red-surface-nvdec
|
|
**위치:** `vav2/platforms/windows/tests/red-surface-nvdec/`
|
|
**타입:** Console Application (Headless Test)
|
|
**목적:** NVDEC AV1 디코더의 D3D12 surface 렌더링을 픽셀 단위 stripe 패턴 검증을 통해 확인
|
|
|
|
## 목표
|
|
|
|
1. **NVDEC AV1 Decoder 테스트** - 명시적으로 D3D12 surface 출력 사용
|
|
2. **NV12 Texture 생성 검증** - CUDA-D3D12 interop 확인
|
|
3. **픽셀 정확도 검증** - D3D12 surface 데이터를 CPU로 읽어서 확인
|
|
4. **Stripe 문제 탐지** - 모든 프레임에서 8픽셀 단위 빨강/검정 패턴 체크
|
|
|
|
## 테스트 입력 파일
|
|
|
|
### 테스트 비디오 (위치: `D:/Project/video-av1/sample/`)
|
|
|
|
1. **test_720p_stripe.webm**
|
|
- 해상도: 1280x720
|
|
- 코덱: AV1
|
|
- 패턴: 8픽셀 세로 줄무늬 (검정-빨강-검정-빨강...)
|
|
- 길이: 1초 (25 프레임)
|
|
|
|
2. **test_1080p_stripe.webm**
|
|
- 해상도: 1920x1080
|
|
- 코덱: AV1
|
|
- 패턴: 8픽셀 세로 줄무늬 (검정-빨강-검정-빨강...)
|
|
- 길이: 1초 (25 프레임)
|
|
|
|
### 예상 패턴
|
|
|
|
```
|
|
X 좌표: 0-7 8-15 16-23 24-31 ...
|
|
픽셀 색상: 검정 빨강 검정 빨강 ...
|
|
RGB 값: (0,0,0) (255,0,0) (0,0,0) (255,0,0)
|
|
```
|
|
|
|
## 아키텍처
|
|
|
|
### 컴포넌트
|
|
|
|
```
|
|
RedSurfaceNVDECTest (Console App)
|
|
├── D3D12 Device Manager
|
|
│ ├── D3D12 device 생성 (headless)
|
|
│ ├── Command queue/list 생성
|
|
│ └── CUDA interop이 가능한 NV12 texture 생성
|
|
├── VavCore 통합
|
|
│ ├── VavCore 초기화
|
|
│ ├── 테스트 비디오 파일 열기
|
|
│ ├── Decoder 타입을 NVDEC으로 명시적 설정
|
|
│ └── Zero-copy를 위한 D3D12 device 설정
|
|
├── 프레임 디코딩 루프
|
|
│ ├── D3D12 surface로 프레임 디코딩
|
|
│ ├── NV12 texture를 CPU 메모리로 Readback
|
|
│ └── Stripe 패턴 검증
|
|
└── 픽셀 검증
|
|
├── NV12 → RGB 변환
|
|
├── 8픽셀 stripe 패턴 체크
|
|
└── 프레임별 pass/fail 리포트
|
|
```
|
|
|
|
## 테스트 플로우
|
|
|
|
### 1. 초기화 단계
|
|
|
|
```cpp
|
|
// Step 1: D3D12 device 생성 (headless)
|
|
ID3D12Device* device = CreateD3D12Device();
|
|
|
|
// Step 2: VavCore 초기화
|
|
vavcore_initialize();
|
|
|
|
// Step 3: VavCore player 생성
|
|
VavCorePlayer* player = vavcore_create_player();
|
|
|
|
// Step 4: Decoder 타입을 NVDEC으로 명시적 설정
|
|
vavcore_set_decoder_type(player, VAVCORE_DECODER_NVDEC);
|
|
|
|
// Step 5: Zero-copy를 위한 D3D12 device 설정
|
|
vavcore_set_d3d_device(player, device, VAVCORE_SURFACE_D3D12);
|
|
|
|
// Step 6: 테스트 비디오 열기
|
|
vavcore_open_file(player, "test_720p_stripe.webm");
|
|
```
|
|
|
|
### 2. 디코딩 단계
|
|
|
|
```cpp
|
|
for each frame:
|
|
// Step 1: 이 프레임을 위한 NV12 texture 생성
|
|
ID3D12Resource* nv12Texture = CreateNV12Texture(width, height);
|
|
|
|
// Step 2: D3D12 surface로 프레임 디코딩
|
|
result = vavcore_decode_to_surface(
|
|
player,
|
|
VAVCORE_SURFACE_D3D12,
|
|
nv12Texture
|
|
);
|
|
|
|
// Step 3: D3D12 texture를 CPU로 Readback
|
|
uint8_t* cpuBuffer = ReadbackD3D12Texture(nv12Texture);
|
|
|
|
// Step 4: Stripe 패턴 검증
|
|
bool passed = VerifyStripePattern(cpuBuffer, width, height);
|
|
|
|
printf("Frame %d: %s\n", frameIndex, passed ? "PASS" : "FAIL");
|
|
```
|
|
|
|
### 3. 검증 로직
|
|
|
|
```cpp
|
|
bool VerifyStripePattern(uint8_t* nv12Data, int width, int height) {
|
|
// NV12를 RGB로 변환
|
|
RGB* rgbData = ConvertNV12ToRGB(nv12Data, width, height);
|
|
|
|
// 첫 번째 행 체크 (대표값)
|
|
for (int x = 0; x < width; x++) {
|
|
int stripeIndex = x / 8; // 8픽셀 stripe 폭
|
|
bool shouldBeRed = (stripeIndex % 2) == 1;
|
|
|
|
RGB pixel = rgbData[x];
|
|
|
|
if (shouldBeRed) {
|
|
// 빨강 기대: R=255, G=0, B=0 (오차 허용)
|
|
if (pixel.r < 200 || pixel.g > 50 || pixel.b > 50) {
|
|
printf("FAIL at X=%d: Expected red, got RGB(%d,%d,%d)\n",
|
|
x, pixel.r, pixel.g, pixel.b);
|
|
return false;
|
|
}
|
|
} else {
|
|
// 검정 기대: R=0, G=0, B=0 (오차 허용)
|
|
if (pixel.r > 50 || pixel.g > 50 || pixel.b > 50) {
|
|
printf("FAIL at X=%d: Expected black, got RGB(%d,%d,%d)\n",
|
|
x, pixel.r, pixel.g, pixel.b);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
```
|
|
|
|
## 기술 요구사항
|
|
|
|
### 의존성
|
|
|
|
1. **VavCore Library**
|
|
- Link: `VavCore-debug.lib`
|
|
- Include: `VavCore/VavCore.h`
|
|
|
|
2. **D3D12**
|
|
- Headers: `d3d12.h`, `dxgi1_6.h`
|
|
- Libraries: `d3d12.lib`, `dxgi.lib`
|
|
|
|
3. **CUDA** (interop 검증용)
|
|
- Headers: `cuda.h`, `cuda_d3d12_interop.h`
|
|
- Libraries: `cuda.lib`
|
|
|
|
### 빌드 설정
|
|
|
|
- **플랫폼:** x64
|
|
- **구성:** Debug
|
|
- **C++ 표준:** C++17
|
|
- **문자 집합:** Unicode
|
|
- **하위 시스템:** Console
|
|
|
|
### Include 디렉토리
|
|
|
|
```
|
|
$(ProjectDir)..\..\vavcore\include
|
|
$(CUDA_PATH)\include
|
|
```
|
|
|
|
### Library 디렉토리
|
|
|
|
```
|
|
$(ProjectDir)..\..\vavcore\lib
|
|
$(CUDA_PATH)\lib\x64
|
|
```
|
|
|
|
### 추가 종속성
|
|
|
|
```
|
|
VavCore-debug.lib
|
|
d3d12.lib
|
|
dxgi.lib
|
|
cuda.lib
|
|
kernel32.lib
|
|
user32.lib
|
|
```
|
|
|
|
## 성공 기준
|
|
|
|
### Pass 조건
|
|
|
|
1. **모든 프레임이 에러 없이 디코딩 성공**
|
|
2. **모든 프레임에서 stripe 패턴 검증 성공**
|
|
3. **UV plane copy 에러 없음** (이전 문제 해결 확인)
|
|
4. **시각적 artifact 없음** (stripe 정렬이 정확함)
|
|
|
|
### 출력 포맷
|
|
|
|
```
|
|
[RedSurfaceNVDECTest] 테스트 시작...
|
|
[RedSurfaceNVDECTest] 비디오: test_720p_stripe.webm (1280x720)
|
|
[RedSurfaceNVDECTest] 디코더: NVDEC (명시적)
|
|
[RedSurfaceNVDECTest] Surface: D3D12
|
|
|
|
Frame 0: PASS (25/25 stripe checks passed)
|
|
Frame 1: PASS (25/25 stripe checks passed)
|
|
Frame 2: PASS (25/25 stripe checks passed)
|
|
...
|
|
Frame 24: PASS (25/25 stripe checks passed)
|
|
|
|
[RedSurfaceNVDECTest] 결과: 25/25 프레임 PASSED
|
|
[RedSurfaceNVDECTest] 테스트 성공
|
|
```
|
|
|
|
### 실패 출력
|
|
|
|
```
|
|
Frame 5: FAIL at X=64: Expected red, got RGB(128,0,0)
|
|
Frame 5: FAIL at X=72: Expected red, got RGB(64,0,0)
|
|
Frame 5: Stripe pattern verification failed (2 errors)
|
|
|
|
[RedSurfaceNVDECTest] 결과: 24/25 프레임 PASSED
|
|
[RedSurfaceNVDECTest] 테스트 실패
|
|
```
|
|
|
|
## 테스트 대상 알려진 문제
|
|
|
|
### 현재 문제 (이전 분석에서)
|
|
|
|
1. **UV plane copy failed: invalid argument**
|
|
- 에러 코드: 1 (cudaMemcpy2D)
|
|
- 원인: 불명 (texture height padding 불일치?)
|
|
|
|
2. **렌더링된 비디오의 Stripe artifacts**
|
|
- 증상: 세로 줄무늬 또는 패턴 왜곡
|
|
- 원인: 불명 (pitch 불일치? UV offset 잘못됨?)
|
|
|
|
3. **D3D12 texture 할당 padding**
|
|
- 할당된 높이: 2288
|
|
- 논리적 높이: 2160
|
|
- NVDEC과 D3D12 간의 UV offset 불일치
|
|
|
|
### 예상 결과
|
|
|
|
- **테스트 통과 시:** NVDEC decoder와 D3D12 interop이 정상 작동
|
|
- **테스트 실패 시:** 정확히 어느 프레임/픽셀에서 손상이 발생하는지 식별
|
|
- **진단 가치:** 픽셀 레벨 검증으로 근본 원인 파악
|
|
|
|
## 파일 구조
|
|
|
|
```
|
|
vav2/platforms/windows/tests/red-surface-nvdec/
|
|
├── RedSurfaceNVDECTest.vcxproj # MSBuild 프로젝트 파일
|
|
├── src/
|
|
│ ├── main.cpp # Entry point
|
|
│ ├── D3D12Manager.h # D3D12 device/resource 관리
|
|
│ ├── D3D12Manager.cpp
|
|
│ ├── PixelVerifier.h # NV12→RGB + stripe 검증
|
|
│ ├── PixelVerifier.cpp
|
|
│ └── VavCoreWrapper.h # VavCore 통합
|
|
│ VavCoreWrapper.cpp
|
|
├── bin/Debug/ # 출력 디렉토리
|
|
└── obj/Debug/ # 중간 파일
|
|
```
|
|
|
|
## 구현 계획
|
|
|
|
### Phase 1: 프로젝트 설정
|
|
- .vcxproj 파일 생성
|
|
- 빌드 설정 구성
|
|
- VavCore 및 D3D12 라이브러리 링크
|
|
|
|
### Phase 2: D3D12 인프라
|
|
- D3D12Manager 클래스 구현
|
|
- Device 생성 (headless)
|
|
- NV12 texture 생성
|
|
- Texture readback (D3D12 → CPU)
|
|
|
|
### Phase 3: VavCore 통합
|
|
- VavCoreWrapper 클래스 구현
|
|
- VavCore 초기화
|
|
- 비디오 파일 열기
|
|
- D3D12 surface로 프레임 디코딩
|
|
|
|
### Phase 4: 픽셀 검증
|
|
- PixelVerifier 클래스 구현
|
|
- NV12 → RGB 변환
|
|
- 8픽셀 stripe 패턴 체커
|
|
- 프레임별 리포팅
|
|
|
|
### Phase 5: 테스트
|
|
- test_720p_stripe.webm으로 테스트
|
|
- test_1080p_stripe.webm으로 테스트
|
|
- 모든 프레임 통과 확인
|
|
|
|
## 참조
|
|
|
|
- VavCore API: `vav2/platforms/windows/vavcore/include/VavCore/VavCore.h`
|
|
- D3D12 문서: Microsoft Docs
|
|
- NVDEC 구현: `vav2/platforms/windows/vavcore/src/Decoder/NVDECAV1Decoder.cpp`
|
|
- 기존 테스트: `vav2/platforms/windows/tests/headless/SimpleVavCoreTest.vcxproj`
|