diff --git a/vav2/D3D_Surface_Direct_Decoding_Design.md b/vav2/D3D_Surface_Direct_Decoding_Design.md new file mode 100644 index 0000000..bb487cf --- /dev/null +++ b/vav2/D3D_Surface_Direct_Decoding_Design.md @@ -0,0 +1,483 @@ +# D3D Surface 직접 디코딩 아키텍처 설계 + +## 개요 + +이 문서는 VavCore에서 D3D surface 직접 디코딩을 구현하여 CPU 메모리 복사를 제거하고 고성능 GPU-to-GPU 렌더링을 가능하게 하는 아키텍처를 제시합니다. 이 설계는 CPU 전용 디코딩과의 호환성을 유지하면서 모든 주요 하드웨어 가속 SDK를 지원합니다. + +## 현재 상태 분석 + +### VavCoreVideoFrame (CPU 전용) +```c +typedef struct { + uint8_t* y_plane; // Y 평면 데이터 (CPU 메모리) + uint8_t* u_plane; // U 평면 데이터 (CPU 메모리) + uint8_t* v_plane; // V 평면 데이터 (CPU 메모리) + + int y_stride; // Y 평면 stride + int u_stride; // U 평면 stride + int v_stride; // V 평면 stride + + int width; // 프레임 너비 + int height; // 프레임 높이 + + uint64_t timestamp_us; // 타임스탬프 (마이크로초) + uint64_t frame_number; // 프레임 시퀀스 번호 +} VavCoreVideoFrame; +``` + +**제한사항:** +- CPU 메모리 포인터만 제공 +- 렌더링을 위해 GPU → CPU → GPU 메모리 복사 필요 +- 고해상도 콘텐츠에서 성능 병목 발생 + +## SDK D3D Surface 지원 분석 + +### 1. AMD AMF (Advanced Media Framework) + +**D3D Surface 지원:** ✅ 완전한 D3D11/D3D12 텍스처 지원 + +**핵심 컴포넌트:** +- `AMFSurface` - 범용 surface 추상화 +- `AMFContext::CreateSurfaceFromDX11Native()` - D3D11 텍스처 래퍼 +- `AMFContext::CreateSurfaceFromDX12Native()` - D3D12 리소스 래퍼 + +**사용 패턴:** +```cpp +// D3D11 텍스처 surface 생성 +ID3D11Texture2D* d3d11Texture; +AMFSurfacePtr amfSurface; +amfContext->CreateSurfaceFromDX11Native(d3d11Texture, &amfSurface, nullptr); + +// AMF surface에 직접 디코딩 +amfDecoder->SubmitInput(amfSurface); +amfDecoder->QueryOutput(&outputSurface); +``` + +### 2. Intel VPL (Video Processing Library) + +**D3D Surface 지원:** ✅ mfxFrameSurface1을 통한 D3D11/D3D12 지원 + +**핵심 컴포넌트:** +- `mfxFrameSurface1` - D3D 핸들이 포함된 surface 디스크립터 +- `mfxHandleType` - D3D11/D3D12 핸들 타입 지정 +- 외부 할당자 통합 + +**사용 패턴:** +```cpp +// D3D11 surface 할당자 설정 +mfxFrameSurface1 surface = {}; +surface.Info = videoParams.mfx.FrameInfo; +surface.Data.MemId = d3d11Texture; // 직접 D3D11 텍스처 할당 + +// D3D surface에 디코딩 +MFXVideoDECODE_DecodeFrameAsync(session, nullptr, &surface, &outputSurface, &sync); +``` + +### 3. NVIDIA NVDEC + +**D3D Surface 지원:** ✅ D3D interop을 통한 CUDA 디바이스 포인터 + +**핵심 컴포넌트:** +- `cuvidMapVideoFrame()` - 디코딩된 프레임을 CUDA 디바이스 포인터로 매핑 +- `CUdeviceptr` - CUDA 디바이스 메모리 포인터 +- D3D-CUDA 상호 운용성 API + +**사용 패턴:** +```cpp +// 디코딩된 프레임을 CUDA 디바이스 메모리로 매핑 +CUdeviceptr devicePtr; +unsigned int pitch; +cuvidMapVideoFrame(decoder, picIdx, &devicePtr, &pitch, ¶ms); + +// D3D 텍스처를 CUDA에 등록 +CUgraphicsResource cudaResource; +cuGraphicsD3D11RegisterResource(&cudaResource, d3d11Texture, CU_GRAPHICS_REGISTER_FLAGS_NONE); +``` + +### 4. dav1d (소프트웨어 디코더) + +**D3D Surface 지원:** ❌ CPU 전용 디코더 + +**특징:** +- 순수 소프트웨어 구현 +- CPU 메모리 포인터만 제공 +- GPU surface 통합 없음 +- 렌더링을 위해 CPU → GPU 업로드 필요 + +## 제안된 아키텍처 + +### 1. 확장된 Surface 타입 + +**새로운 VavCoreSurfaceType 열거형:** +```c +typedef enum { + VAVCORE_SURFACE_CPU = 0, // 기존 CPU 메모리 + VAVCORE_SURFACE_D3D11_TEXTURE = 1, // D3D11 텍스처 + VAVCORE_SURFACE_D3D12_RESOURCE = 2,// D3D12 리소스 + VAVCORE_SURFACE_CUDA_DEVICE = 3, // CUDA 디바이스 포인터 + VAVCORE_SURFACE_AMF_SURFACE = 4 // AMF surface 래퍼 +} VavCoreSurfaceType; +``` + +**확장된 VavCoreVideoFrame:** +```c +typedef struct { + // 기존 CPU 필드들 (호환성 유지) + uint8_t* y_plane; + uint8_t* u_plane; + uint8_t* v_plane; + int y_stride; + int u_stride; + int v_stride; + + // 프레임 메타데이터 + int width; + int height; + uint64_t timestamp_us; + uint64_t frame_number; + + // 새로운 D3D surface 필드들 + VavCoreSurfaceType surface_type; + union { + struct { + // CPU 메모리 (기존) + uint8_t* planes[3]; + int strides[3]; + } cpu; + + struct { + // D3D11 텍스처 + void* d3d11_texture; // ID3D11Texture2D* + void* d3d11_device; // ID3D11Device* + uint32_t subresource_index; + } d3d11; + + struct { + // D3D12 리소스 + void* d3d12_resource; // ID3D12Resource* + void* d3d12_device; // ID3D12Device* + uint32_t subresource_index; + } d3d12; + + struct { + // CUDA 디바이스 포인터 + uint64_t device_ptr; // CUdeviceptr + uint32_t pitch; + void* cuda_context; // CUcontext + } cuda; + + struct { + // AMF surface + void* amf_surface; // AMFSurface* + void* amf_context; // AMFContext* + } amf; + } surface_data; +} VavCoreVideoFrame; +``` + +### 2. 디코더 인터페이스 확장 + +**향상된 디코더 인터페이스:** +```cpp +class IVideoDecoder { +public: + // 기존 메서드들 + virtual bool DecodeFrame(const uint8_t* packet_data, size_t packet_size, + VavCoreVideoFrame& frame) = 0; + + // 새로운 D3D surface 메서드들 + virtual bool SupportsSurfaceType(VavCoreSurfaceType type) = 0; + virtual bool DecodeToSurface(const uint8_t* packet_data, size_t packet_size, + VavCoreSurfaceType target_type, + void* target_surface, + VavCoreVideoFrame& frame) = 0; + virtual bool SetD3DDevice(void* d3d_device, VavCoreSurfaceType type) = 0; +}; +``` + +### 3. 하드웨어별 구현 + +#### AMD AMF 디코더 구현 +```cpp +class AMFDecoder : public IVideoDecoder { +private: + AMFContextPtr m_amfContext; + AMFComponentPtr m_amfDecoder; + ID3D11Device* m_d3d11Device; + +public: + bool SupportsSurfaceType(VavCoreSurfaceType type) override { + return (type == VAVCORE_SURFACE_D3D11_TEXTURE || + type == VAVCORE_SURFACE_D3D12_RESOURCE || + type == VAVCORE_SURFACE_AMF_SURFACE); + } + + bool DecodeToSurface(const uint8_t* packet_data, size_t packet_size, + VavCoreSurfaceType target_type, + void* target_surface, + VavCoreVideoFrame& frame) override { + // D3D 텍스처로부터 AMF surface 생성 + AMFSurfacePtr inputSurface; + if (target_type == VAVCORE_SURFACE_D3D11_TEXTURE) { + m_amfContext->CreateSurfaceFromDX11Native( + static_cast(target_surface), + &inputSurface, nullptr); + } + + // AMF surface에 직접 디코딩 + m_amfDecoder->SubmitInput(inputSurface); + + AMFDataPtr outputData; + m_amfDecoder->QueryOutput(&outputData); + + // 프레임 메타데이터 채움 + frame.surface_type = VAVCORE_SURFACE_AMF_SURFACE; + frame.surface_data.amf.amf_surface = outputData.GetPtr(); + frame.surface_data.amf.amf_context = m_amfContext.GetPtr(); + + return true; + } +}; +``` + +#### Intel VPL 디코더 구현 +```cpp +class VPLDecoder : public IVideoDecoder { +private: + mfxSession m_session; + ID3D11Device* m_d3d11Device; + +public: + bool DecodeToSurface(const uint8_t* packet_data, size_t packet_size, + VavCoreSurfaceType target_type, + void* target_surface, + VavCoreVideoFrame& frame) override { + mfxFrameSurface1 surface = {}; + surface.Info = m_videoParams.mfx.FrameInfo; + + if (target_type == VAVCORE_SURFACE_D3D11_TEXTURE) { + surface.Data.MemId = target_surface; // 직접 D3D11 텍스처 + + mfxSyncPoint sync; + mfxFrameSurface1* outputSurface; + + MFXVideoDECODE_DecodeFrameAsync(m_session, nullptr, &surface, + &outputSurface, &sync); + MFXVideoCORE_SyncOperation(m_session, sync, MFX_INFINITE); + + // 프레임 메타데이터 채움 + frame.surface_type = VAVCORE_SURFACE_D3D11_TEXTURE; + frame.surface_data.d3d11.d3d11_texture = outputSurface->Data.MemId; + frame.surface_data.d3d11.d3d11_device = m_d3d11Device; + } + + return true; + } +}; +``` + +#### NVIDIA NVDEC 구현 +```cpp +class NVDECDecoder : public IVideoDecoder { +private: + CUvideodecoder m_decoder; + CUcontext m_cudaContext; + +public: + bool DecodeToSurface(const uint8_t* packet_data, size_t packet_size, + VavCoreSurfaceType target_type, + void* target_surface, + VavCoreVideoFrame& frame) override { + // 프레임 디코딩 + CUVIDPICPARAMS picParams = {}; + // ... packet_data로부터 picParams 설정 + + cuvidDecodePicture(m_decoder, &picParams); + + // CUDA 디바이스 포인터로 매핑 + CUdeviceptr devicePtr; + unsigned int pitch; + CUVIDPROCPARAMS procParams = {}; + + cuvidMapVideoFrame(m_decoder, picParams.CurrPicIdx, + &devicePtr, &pitch, &procParams); + + // 프레임 메타데이터 채움 + frame.surface_type = VAVCORE_SURFACE_CUDA_DEVICE; + frame.surface_data.cuda.device_ptr = devicePtr; + frame.surface_data.cuda.pitch = pitch; + frame.surface_data.cuda.cuda_context = m_cudaContext; + + return true; + } +}; +``` + +### 4. 렌더러 통합 + +**D3D Surface 인식 렌더러:** +```cpp +class D3DSurfaceRenderer { +public: + bool RenderFrame(const VavCoreVideoFrame& frame) { + switch (frame.surface_type) { + case VAVCORE_SURFACE_D3D11_TEXTURE: + return RenderD3D11Texture(frame.surface_data.d3d11); + + case VAVCORE_SURFACE_D3D12_RESOURCE: + return RenderD3D12Resource(frame.surface_data.d3d12); + + case VAVCORE_SURFACE_CUDA_DEVICE: + return RenderCudaDevicePtr(frame.surface_data.cuda); + + case VAVCORE_SURFACE_AMF_SURFACE: + return RenderAMFSurface(frame.surface_data.amf); + + case VAVCORE_SURFACE_CPU: + default: + return RenderCPUFrame(frame); + } + } + +private: + bool RenderD3D11Texture(const auto& d3d11_data) { + auto texture = static_cast(d3d11_data.d3d11_texture); + // 직접 텍스처-to-백버퍼 복사 또는 셰이더 렌더링 + // CPU 메모리 복사 불필요 + return true; + } +}; +``` + +### 5. 폴백 전략 + +**자동 Surface 타입 선택:** +```cpp +class AdaptiveDecoder { +public: + VavCoreSurfaceType SelectOptimalSurfaceType(VavCoreDecoderType decoder_type) { + switch (decoder_type) { + case VAVCORE_DECODER_AMF: + if (m_d3d11Device) return VAVCORE_SURFACE_D3D11_TEXTURE; + if (m_d3d12Device) return VAVCORE_SURFACE_D3D12_RESOURCE; + break; + + case VAVCORE_DECODER_VPL: + if (m_d3d11Device) return VAVCORE_SURFACE_D3D11_TEXTURE; + break; + + case VAVCORE_DECODER_NVDEC: + return VAVCORE_SURFACE_CUDA_DEVICE; + + case VAVCORE_DECODER_DAV1D: + case VAVCORE_DECODER_MEDIA_FOUNDATION: + default: + return VAVCORE_SURFACE_CPU; + } + + return VAVCORE_SURFACE_CPU; // 폴백 + } +}; +``` + +## 성능 향상 효과 + +### 예상 성능 개선 + +**4K AV1 디코딩 + 렌더링 파이프라인:** + +| 구성 요소 | 현재 (CPU) | D3D Surface 사용 | 개선도 | +|-----------|------------|------------------|--------| +| 디코딩 | 15-25ms | 10-20ms | 1.2-1.5배 | +| GPU 업로드 | 5-10ms | 0ms | ∞ | +| 렌더링 | 1-3ms | 0.5-1ms | 2-3배 | +| **총합** | **21-38ms** | **10.5-21ms** | **2-3.6배** | + +**메모리 대역폭 절약:** +- 4K YUV420: 프레임당 ~12MB +- 60fps: ~720MB/s 메모리 대역폭 절약 +- 메모리 압박 및 캐시 오염 감소 + +### 사용 사례 및 이점 + +1. **고해상도 콘텐츠 (4K+)** + - GPU → CPU → GPU 병목 제거 + - 실시간 4K60 디코딩 + 렌더링 가능 + +2. **멀티 스트림 시나리오** + - CPU 메모리 복사 없이 여러 비디오 스트림 처리 + - 효율적인 GPU 메모리 공유 + +3. **실시간 애플리케이션** + - 라이브 스트리밍 지연 시간 감소 + - 시스템 반응성을 위한 CPU 사용률 감소 + +## 구현 단계 + +### 1단계: 핵심 인프라 +- [ ] Surface union을 포함한 VavCoreVideoFrame 확장 +- [ ] Surface 메서드가 포함된 IVideoDecoder 인터페이스 업데이트 +- [ ] Surface 타입 기능 감지 구현 + +### 2단계: 하드웨어 디코더 통합 +- [ ] AMD AMF surface 디코딩 구현 +- [ ] Intel VPL surface 디코딩 구현 +- [ ] NVIDIA NVDEC CUDA 통합 + +### 3단계: 렌더러 업데이트 +- [ ] D3D11/D3D12 surface 렌더링 +- [ ] CUDA-D3D 상호 운용성 +- [ ] AMF surface 렌더링 + +### 4단계: 최적화 및 테스트 +- [ ] 성능 벤치마킹 +- [ ] 폴백 메커니즘 개선 +- [ ] 멀티 GPU 지원 + +## 호환성 고려사항 + +### 하위 호환성 +- 기존 CPU 기반 코드는 변경 없이 계속 작동 +- VavCoreVideoFrame이 기존 CPU 필드 유지 +- D3D 불가능 시 CPU 디코딩으로 자동 폴백 + +### 플랫폼 지원 +- **Windows 10/11**: 완전한 D3D11/D3D12 지원 +- **구형 Windows**: CPU 디코딩으로 폴백 +- **비Windows**: CPU 전용 (향후: Vulkan/OpenGL) + +### 하드웨어 요구사항 +- **AMD**: AV1 하드웨어 디코딩을 위한 RX 6000+ 시리즈 +- **Intel**: Arc 시리즈 또는 11세대+ 내장 그래픽 +- **NVIDIA**: AV1 하드웨어 디코딩을 위한 RTX 30 시리즈+ + +## 위험 평가 + +### 기술적 위험 +1. **드라이버 호환성**: 하드웨어별 드라이버 문제 + - **완화**: CPU 디코딩으로 완전한 폴백 + +2. **메모리 관리**: D3D surface 생명주기 관리 + - **완화**: RAII 래퍼 및 참조 카운팅 + +3. **동기화**: GPU-GPU 동기화 복잡성 + - **완화**: 명시적 동기화 프리미티브 + +### 성능 위험 +1. **초기 구현**: 최적화된 CPU 경로보다 느릴 수 있음 + - **완화**: 성능 게이트가 포함된 단계적 롤아웃 + +2. **메모리 오버헤드**: 추가 surface 메타데이터 + - **완화**: Union 기반 저장소, 최소 오버헤드 + +## 결론 + +D3D surface 직접 디코딩은 고해상도 AV1 콘텐츠에 대한 중요한 성능 기회를 제공합니다. 제안된 아키텍처는 하드웨어 가속 시나리오에서 상당한 성능 향상을 가능하게 하면서 하위 호환성을 유지합니다. + +구현은 다음을 우선시합니다: +1. **호환성**: 기존 코드가 계속 작동 +2. **성능**: 불필요한 메모리 복사 제거 +3. **유연성**: 여러 하드웨어 벤더 지원 +4. **유지보수성**: 명확한 추상화 계층 + +적절한 구현을 통해 이 아키텍처는 시스템 안정성과 호환성을 유지하면서 4K+ 콘텐츠에 대해 2-3배의 성능 향상을 제공할 수 있습니다. \ No newline at end of file diff --git a/vav2/GEMINI.md b/vav2/GEMINI.md new file mode 100644 index 0000000..c771caf --- /dev/null +++ b/vav2/GEMINI.md @@ -0,0 +1,71 @@ +# Vav2Player 프로젝트 분석 요약 + +## 1. 프로젝트 아키텍처 + +Vav2Player 프로젝트는 크게 두 개의 주요 구성 요소로 나뉩니다. + +- **`VavCore` (정적 라이브러리):** + - 순수 C++로 작성된 비디오 디코딩 핵심 라이브러리입니다. + - 특정 UI 프레임워크에 대한 의존성이 없으며, 이식성을 목표로 설계되었습니다. + - 최종적으로는 Godot 엔진 등 다른 애플리케이션에서 사용할 수 있도록 C API를 제공하는 것을 목표로 합니다. + +- **`Vav2Player` (WinUI 3 애플리케이션):** + - `VavCore` 라이브러리를 사용하는 클라이언트 애플리케이션입니다. + - C++/WinRT 및 WinUI 3를 사용하여 현대적인 Windows UI를 구현합니다. + - D3D12를 이용한 고성능 비디오 렌더링을 담당합니다. + +## 2. VavCore 라이브러리 상세 + +### 목적 +`VavCore`는 다양한 디코딩 백엔드를 지원하는 고성능, 확장 가능, 이식성 있는 비디오 디코딩 엔진을 제공하는 것을 목표로 합니다. + +### 핵심 인터페이스 (`IVideoDecoder`) +모든 디코더 클래스의 기반이 되는 추상 인터페이스입니다. `Initialize`, `DecodeFrame`, `Cleanup` 등의 메서드를 정의하여 디코더의 동작을 표준화합니다. + +### 디코더 팩토리 (`VideoDecoderFactory`) +"등록 기반 팩토리(Registration-Based Factory)" 패턴을 사용하여 디코더 객체를 생성합니다. + +- **동작 방식:** 각 디코더 구현 파일(`.cpp`)은 자신의 생성자 함수와 가용성 체크 함수를 `VideoDecoderFactory`에 정적으로 등록합니다. 팩토리는 이 등록 정보를 기반으로 요청된 타입의 디코더를 생성합니다. +- **장점:** + - **확장성:** 새로운 디코더를 추가할 때 팩토리 코드를 수정할 필요가 없습니다. (개방-폐쇄 원칙) + - **의존성 분리:** 팩토리가 특정 디코더의 헤더 파일을 포함하지 않아 SDK 간의 헤더 충돌 문제를 원천적으로 방지합니다. + - **자동 선택:** 시스템 환경(예: GPU 종류)에 따라 우선순위가 가장 높은 최적의 디코더를 자동으로 선택할 수 있습니다. +- **지원 디코더:** + - `dav1d` (소프트웨어) + - `AMF` (AMD 하드웨어 가속) + - `NVDEC` (NVIDIA 하드웨어 가속) + - `VPL` (Intel 하드웨어 가속) + - `Media Foundation` (Windows 내장 디코더) + +## 3. Vav2Player 애플리케이션 상세 + +### 역할 +`Vav2Player`는 `VavCore`를 사용하여 비디오 프레임을 디코딩하고, D3D12를 사용하여 화면에 렌더링하는 GUI 프론트엔드입니다. + +### 렌더링 파이프라인 +- **목표:** GPU 내에서 모든 처리를 완료하여 CPU-GPU 간 데이터 전송을 최소화하는 고성능 렌더링을 목표로 합니다. +- **이상적인 경로:** + 1. `VavCore`의 하드웨어 디코더가 GPU 메모리에 `NV12` 포맷의 서피스를 출력합니다. + 2. 렌더러는 이 GPU 서피스를 직접 텍스처로 참조합니다. + 3. 픽셀 셰이더를 사용하여 실시간으로 YUV를 RGB로 색상 변환합니다. + 4. 변환된 RGB 이미지를 `SwapChainPanel`에 최종 출력합니다. +- **현재 상태:** 현재는 호환성을 위해 GPU에서 디코딩된 프레임을 CPU로 읽어온 후, 다시 렌더링을 위해 GPU로 업로드하는 단순화된 경로를 사용할 수 있습니다. (성능 최적화를 위해 이상적인 경로로의 전환이 필요합니다.) + +## 4. 주요 의존성 + +- **UI 및 애플리케이션 모델:** Windows App SDK (WinUI 3), C++/WinRT +- **렌더링:** Direct3D 12, DXGI +- **디코더 SDK:** + - `dav1d` + - `AMD AMF` + - `NVIDIA CUDA / NVDEC` + - `Intel oneVPL` + - `Windows Media Foundation` +- **컨테이너:** `libwebm` (WebM 파일 파싱용) + +## 5. 빌드 시스템 + +- Visual Studio 2022 솔루션(`.sln`)을 통해 관리됩니다. +- `VavCore`는 정적 라이브러리(`.lib`)로 빌드됩니다. +- `Vav2Player` 실행 파일은 `VavCore.lib`를 링크하여 디코딩 기능을 사용합니다. +- 각 하드웨어 가속 디코더는 해당 SDK의 라이브러리 및 헤더 파일에 대한 의존성을 가집니다. diff --git a/vav2/Vav2Player/VavCore/src/VavCore.cpp b/vav2/Vav2Player/VavCore/src/VavCore.cpp index 323dd69..3863bb3 100644 --- a/vav2/Vav2Player/VavCore/src/VavCore.cpp +++ b/vav2/Vav2Player/VavCore/src/VavCore.cpp @@ -105,6 +105,8 @@ static VavCore::VideoDecoderFactory::DecoderType to_decoder_type(VavCoreDecoderT case VAVCORE_DECODER_DAV1D: return VavCore::VideoDecoderFactory::DecoderType::DAV1D; case VAVCORE_DECODER_NVDEC: return VavCore::VideoDecoderFactory::DecoderType::NVDEC; case VAVCORE_DECODER_MEDIA_FOUNDATION: return VavCore::VideoDecoderFactory::DecoderType::MEDIA_FOUNDATION; + case VAVCORE_DECODER_VPL: return VavCore::VideoDecoderFactory::DecoderType::VPL; + case VAVCORE_DECODER_AMF: return VavCore::VideoDecoderFactory::DecoderType::AMF; default: return VavCore::VideoDecoderFactory::DecoderType::AUTO; } } diff --git a/vav2/todo8.txt b/vav2/todo8.txt index 3402e96..b6288f8 100644 --- a/vav2/todo8.txt +++ b/vav2/todo8.txt @@ -7,6 +7,11 @@ CLAUDE.md 파일을 확인하여 현재 작업 상황을 점검하고 완료된 VavCoreVideoFrame 에는 color_space 변수가 없다. 차후에 이것을 사용할 기능이 들어가게 될까? +이제 vav2/ 경로 하위에 Android 버전의 VavCore 를 만들고, iOS 버전의 VavCore 를 만들것이다. +그리고 VavCore 를 Godot Engine 4.4.1 의 C# 언어로 플러그인(Extension)을 만들어서 이것을 활용하여 게임 엔진내에서 동영상을 +렌더링하고자 한다. 이 과정에서 프로젝트 구성 디렉토리 구조를 제안해줘봐. Vav2Player 프로젝트는 그대로 둔 상태로 말이다. + + ------------ VavCoreVideoFrame 에는 현재 cpu data 만 제공하고 있다. d3d surface 에 직접 av1 프레임을 디코딩해주는 SDK 도 있다.