WIP
This commit is contained in:
388
vav2/notes/gemini-anal1.txt
Normal file
388
vav2/notes/gemini-anal1.txt
Normal file
@@ -0,0 +1,388 @@
|
||||
✦ 알겠습니다. Vav2Player에서 화면이 검게 나오는 문제를 진단해 보겠습니다. 이 문제는 비디오 처리 파이프라인의 여러 단계에서 발생할 수 있습니다.
|
||||
단계별로 나누어 원인을 좁혀가겠습니다.
|
||||
|
||||
진단 계획:
|
||||
|
||||
1. `VavCore` 디코딩 확인: VavCore가 비디오 파일을 성공적으로 열고, 디코더를 선택하고, 비디오 프레임을 디코딩하여 Vav2Player로 전달하는지
|
||||
확인합니다.
|
||||
2. `Vav2Player` 렌더링 확인: Vav2Player가 VavCore로부터 받은 프레임 데이터를 D3D12 텍스처로 올바르게 변환하고, 셰이더를 통해 색상 변환(YUV to
|
||||
RGB)을 수행한 후 화면에 정상적으로 렌더링하는지 확인합니다.
|
||||
|
||||
예상 원인:
|
||||
|
||||
* 디코딩 실패: 적절한 디코더를 찾지 못하거나, 파일이 손상되었거나, 디코딩 과정에서 오류가 발생하여 유효한 프레임이 생성되지 않음.
|
||||
* 데이터 전달 오류: 디코딩된 프레임 데이터가 VavCore에서 Vav2Player로 전달되지 않거나, 데이터 포인터가 유효하지 않음.
|
||||
* 렌더링 파이프라인 문제:
|
||||
* D3D12 리소스(텍스처) 생성 실패.
|
||||
* CPU-GPU 데이터 복사 실패.
|
||||
* YUV to RGB 변환 픽셀 셰이더 오류 (예: 항상 검은색을 출력).
|
||||
* 렌더링 상태 또는 파이프라인 설정 오류.
|
||||
|
||||
첫 번째 단계: `VavCore`와 `Vav2Player`의 연동 지점 확인
|
||||
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ x SearchText {"path":"D:\\Project\\video-av1\\vav2","pattern":"IVideoDecoder"} │
|
||||
│ │
|
||||
│ Path validation failed: Attempted path "D:\Project\video-av1\vav2" resolves outside the allowed workspace directories: │
|
||||
│ D:\Project\video-av1\vav2\platforms\android │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
먼저 Vav2Player 코드에서 VavCore의 핵심 인터페이스인 IVideoDecoder 또는 VideoDecoderFactory를 사용하는 부분을 찾아, 디코딩 요청 및 결과 수신
|
||||
과정에 로그를 추가하여 문제를 확인해 보겠습니다.
|
||||
|
||||
관련 파일을 찾기 위해 프로젝트 전체에서 IVideoDecoder를 검색하겠습니다.
|
||||
|
||||
✦ 경로 문제로 이전 명령이 실패했습니다. 현재 작업 디렉토리(D:\Project\video-av1\vav2\platforms\android)를 기준으로 다시 시도하겠습니다.
|
||||
|
||||
사용자께서 언급하신 vav2player는 WinUI3 애플리케이션이 아닌, 현재 경로에 있는 Android 애플리케이션(applications\vav2player)으로 보입니다.
|
||||
따라서 Android 환경에 맞춰 진단을 진행하겠습니다.
|
||||
|
||||
Android 앱의 렌더링 파이프라인을 이해하기 위해, 관련 설계 문서를 먼저 읽어보겠습니다. 이 문서는 검은 화면 문제의 원인을 파악하는 데 중요한
|
||||
단서가 될 수 있습니다.
|
||||
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile \Android_GPU_Surface_Pipeline_Design.md │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 디자인 문서를 잘 읽어보았습니다. 문서에 따르면, 이 앱은 MediaCodec 디코더가 GPU 메모리(Vulkan Image)에 직접 프레임을 출력하고, Vulkan 렌더러가
|
||||
이 메모리를 복사 없이 바로 화면에 그리는 'Zero-Copy' 파이프라인을 목표로 합니다.
|
||||
|
||||
핵심 진단 포인트:
|
||||
|
||||
이 방식은 매우 효율적이지만, 한 단계라도 실패하면 화면이 검게 나올 수밖에 없는 구조입니다. CPU를 이용한 예비 경로(Fallback)가 의도적으로
|
||||
배제되었기 때문입니다.
|
||||
|
||||
문서에 명시된 몇 가지 잠재적인 실패 지점이 있습니다.
|
||||
|
||||
1. 하드웨어/드라이버 미지원: 기기가 MediaCodec에서 Vulkan 표면으로 직접 디코딩하는 것을 지원하지 않을 수 있습니다.
|
||||
2. 초기화 실패: Vulkan 디바이스를 VavCore에 등록하는 과정(vavcore_set_vulkan_device)이 실패했을 수 있습니다.
|
||||
3. 잘못된 프레임 타입: VavCore가 디코딩은 했지만, 예상된 Vulkan 표면 타입이 아닌 다른 포맷(예: CPU 메모리 버퍼)으로 프레임을 반환했을 수
|
||||
있습니다.
|
||||
4. Vulkan 렌더링 오류: RenderVulkanImage 함수에서 전달받은 Vulkan 이미지를 화면에 그리는 데 실패했을 수 있습니다.
|
||||
|
||||
다음 단계: 로그 확인을 위한 소스 코드 탐색
|
||||
|
||||
가장 확실한 방법은 문서에 언급된 오류 로그가 실제 로그캣(logcat)에 출력되는지 확인하는 것입니다. 그전에, 해당 로직을 담고 있는 핵심 소스 파일인
|
||||
vavcore_vulkan_bridge.cpp가 어디에 있는지 찾아보겠습니다. 이 파일의 내용을 보면 현재 구현 상태를 더 정확히 파악할 수 있습니다.
|
||||
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ FindFiles '**/vavcore_vulkan_bridge.cpp' within . │
|
||||
│ │
|
||||
│ Found 1 matching file(s) │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 좋습니다. vavcore_vulkan_bridge.cpp 파일의 위치를 찾았습니다. 이제 이 파일의 내용을 읽어서 디자인 문서에 기술된 대로 오류 처리 및 로깅 로직이
|
||||
실제로 구현되어 있는지 확인하겠습니다.
|
||||
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...ons\main\cpp\vavcore_vulkan_bridge.cpp │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ vavcore_vulkan_bridge.cpp 파일의 내용을 확인했습니다. 소스 코드는 이전에 검토한 디자인 문서와 정확히 일치하며, 파이프라인의 각 단계마다 상세한
|
||||
로그를 기록하고 있습니다.
|
||||
|
||||
이제 검은 화면의 원인은 다음 중 하나일 가능성이 매우 높으며, 이는 Android의 logcat을 통해 확인할 수 있습니다.
|
||||
|
||||
가장 유력한 시나리오:
|
||||
|
||||
1. `GPU SURFACE NOT SUPPORTED` 오류: LoadVideoFile 함수에서 이 로그가 발견된다면, 현재 기기의 하드웨어나 드라이버가 VavCore가 요구하는
|
||||
제로-카피(zero-copy) 디코딩을 지원하지 않는다는 의미입니다. 이것이 가장 가능성 있는 원인입니다.
|
||||
2. `UNSUPPORTED SURFACE TYPE` 오류: ConvertVavCoreFrameToVulkan 함수에서 이 로그가 나온다면, 디코더는 생성되었지만 예상과 다른 종류의 비디오
|
||||
프레임(예: CPU 메모리)을 반환하고 있다는 뜻입니다.
|
||||
3. `Failed to render GPU surface frame` 오류: ProcessNextFrame 함수에서 이 로그가 보인다면, 디코딩은 성공했지만 Vulkan 렌더러가 프레임을
|
||||
화면에 그리는 과정에서 실패했다는 의미입니다.
|
||||
4. `Failed to register Vulkan device` 오류: InitializeVulkanRenderer 함수에서 이 로그가 보인다면, 초기화 단계에서 Vulkan과 VavCore 연동에
|
||||
실패한 것입니다.
|
||||
|
||||
사용자께 요청:
|
||||
|
||||
이제 실제 기기에서 어떤 오류가 발생하는지 확인해야 합니다.
|
||||
|
||||
1. Android 기기를 PC에 연결하고, vav2player 앱을 실행하여 검은 화면이 나타나도록 하십시오.
|
||||
2. 그 상태에서 터미널 또는 명령 프롬프트를 열고 아래 명령어를 실행하여 로그를 캡처해 주십시오. 이 명령어는 이번 문제와 관련된 세 가지 주요
|
||||
태그(VavCoreVulkanBridge, VulkanRenderer, VavCore)의 로그만 필터링합니다.
|
||||
|
||||
1 adb logcat -s VavCoreVulkanBridge VulkanRenderer VavCore
|
||||
|
||||
3. 위 명령어를 실행한 후 출력되는 로그를 복사하여 여기에 붙여넣어 주십시오. 로그를 분석하여 정확한 원인을 진단하겠습니다.
|
||||
|
||||
> D:\Project\video-av1\time.log 파일로 테스트 로그를 기록해두었다. 로그를 분석해보고 원인을 찾아봐줘.
|
||||
|
||||
✦ 알겠습니다. D:\Project\video-av1\time.log 에 저장된 로그를 분석해달라고 하셨군요.
|
||||
|
||||
하지만 보안상의 이유로 제 작업 공간은 D:\Project\video-av1\vav2\platforms\android 디렉토리로 제한되어 있어, 상위 폴더에 있는 파일을 직접
|
||||
읽을 수 없습니다.
|
||||
|
||||
번거로우시겠지만, D:\Project\video-av1\time.log 파일의 전체 내용을 복사하여 여기에 붙여넣어 주시겠어요? 로그 내용을 받으면 즉시 분석하여
|
||||
문제의 원인을 찾아드리겠습니다.
|
||||
|
||||
> D:\Project\video-av1\vav2\platforms\android\time.log 로 옮겨두었다
|
||||
|
||||
✦ 알겠습니다. 파일을 작업 공간으로 옮겨주셔서 감사합니다. 이제 로그 파일을 읽고 분석하여 원인을 찾아보겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile time.log │
|
||||
│ │
|
||||
│ Read lines 1-2000 of 5603 from time.log │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 로그 파일을 성공적으로 읽고 분석했습니다.
|
||||
|
||||
로그 분석 결과:
|
||||
|
||||
놀랍게도, 로그 자체는 오류 없이 전체 파이프라인이 성공적으로 실행되고 있음을 보여줍니다.
|
||||
|
||||
1. 디코더 초기화 성공: c2.qti.av1.decoder (Qualcomm AV1 하드웨어 디코더)가 성공적으로 로드되고, Vulkan 표면(GPU 메모리) 출력을 지원하는 것으로
|
||||
확인됩니다.
|
||||
|
||||
1 I/VavCoreVulkanBridge: GPU SURFACE SUPPORT VERIFIED
|
||||
2 I/VavCoreVulkanBridge: Zero-copy MediaCodec → Vulkan pipeline ENABLED
|
||||
2. 프레임 디코딩 성공: vavcore_decode_to_surface가 호출되어 프레임을 성공적으로 디코딩하고, surface_type: 7 (VULKAN_IMAGE)을 반환합니다. 이는
|
||||
디코더가 GPU 메모리에 직접 프레임을 쓰고 있음을 의미합니다.
|
||||
1 I/VavCore-MediaCodec: DecodeToSurface [Vulkan]: Frame 1 decoded via ImageReader → VkImage
|
||||
3. Vulkan 렌더링 성공: 디코딩된 VkImage가 VulkanRenderer로 전달되고, 렌더러는 vkQueuePresentKHR 호출에 성공하며 프레임을 화면에 표시했다고
|
||||
로그를 남깁니다.
|
||||
|
||||
1 I/VulkanRenderer: Frame presented successfully to screen
|
||||
2 I/VulkanRenderer: RenderVulkanImage completed successfully
|
||||
|
||||
이 로그대로라면 비디오가 정상적으로 보여야 합니다. 하지만 화면이 검게 나온다는 것은, API 호출은 성공했지만 처리 과정의 데이터에 근본적인
|
||||
문제가 있음을 시사합니다.
|
||||
|
||||
---
|
||||
|
||||
결정적 단서 및 원인 진단
|
||||
|
||||
로그에서 결정적인 단서를 찾았습니다.
|
||||
|
||||
1 I/VavCore-SurfaceManager: AHardwareBuffer Vulkan properties:
|
||||
2 I/VavCore-SurfaceManager: format: 0
|
||||
3 I/VavCore-SurfaceManager: CRITICAL FIX: Overriding VK_FORMAT_UNDEFINED to VK_FORMAT_G8_B8R8_2PLANE_420_UNORM (NV12)
|
||||
|
||||
이것이 문제의 핵심 원인입니다.
|
||||
|
||||
1. MediaCodec 디코더가 AHardwareBuffer(하드웨어 버퍼)에 프레임을 출력했습니다.
|
||||
2. VavCore가 이 하드웨어 버퍼의 Vulkan 속성을 질의(vkGetAndroidHardwareBufferPropertiesANDROID)했을 때, Vulkan 드라이버가 이미지의 픽셀 포맷을
|
||||
알려주지 않고 format: 0 (VK_FORMAT_UNDEFINED)을 반환했습니다.
|
||||
3. 코드에서는 이 문제를 해결하기 위해 포맷을 VK_FORMAT_G8_B8R8_2PLANE_420_UNORM (일반적인 NV12 포맷)으로 강제로 가정하고 있습니다. (CRITICAL
|
||||
FIX 로그)
|
||||
|
||||
결론:
|
||||
Vulkan 드라이버가 픽셀 포맷 정보를 제대로 제공하지 않고 있으며, 코드에서는 이를 NV12 포맷일 것이라고 "추측"하고 있습니다. 하지만 실제
|
||||
디코더가 출력한 포맷이 NV12가 아니거나, 드라이버와의 호환성 문제로 인해 데이터에 접근할 수 없는 상태일 가능성이 매우 높습니다.
|
||||
|
||||
결과적으로 Vulkan 렌더러는 유효하지 않거나 잘못된 형식의 데이터를 가지고 YUV->RGB 색상 변환을 시도하게 되고, 그 결과는 검은색 화면으로
|
||||
나타나는 것입니다. 모든 API 호출이 성공했음에도 불구하고 내용물 없는 '빈 껍데기'만 렌더링되고 있는 상황입니다.
|
||||
|
||||
다음 단계:
|
||||
|
||||
이 문제는 VavCore의 VavCore-SurfaceManager가 MediaCodec의 출력 버퍼(AHardwareBuffer)를 처리하는 방식과 Vulkan 드라이버의 특정 동작 방식
|
||||
사이의 호환성 문제입니다.
|
||||
|
||||
이 문제를 해결하려면 AHardwareBuffer의 실제 포맷을 알아내거나, ImageReader를 설정할 때 특정 포맷을 강제해야 합니다. 관련 코드를 좀 더 자세히
|
||||
살펴보겠습니다. VavCore-SurfaceManager 관련 소스 파일을 찾아보겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ FindFiles '**/VavCore-SurfaceManager.*' within . │
|
||||
│ │
|
||||
│ No files found │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ VavCore-SurfaceManager라는 이름의 파일을 직접 찾는 데는 실패했습니다. 로그 태그와 실제 파일명이 다를 수 있습니다.
|
||||
|
||||
대신, 로그에 기록된 "VavCore-SurfaceManager"라는 문자열이 포함된 소스 파일을 직접 검색하여 이 로직이 어느 파일에 구현되어 있는지
|
||||
찾아보겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ SearchText 'VavCore-SurfaceManager' in **/*.{cpp,h} within ./ │
|
||||
│ │
|
||||
│ No matches found │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 검색 결과가 나오지 않는 것이 이상하네요. 로그에는 분명히 VavCore-SurfaceManager 태그가 존재하는데 소스 코드에서 해당 문자열을 찾을 수 없다는
|
||||
것은, 이 로직이 현재 우리가 보고 있는 vav2player 애플리케이션 소스 코드 바깥에 존재할 가능성을 시사합니다.
|
||||
|
||||
가장 유력한 시나리오는 VavCore가 별도의 라이브러리로 빌드되고, vav2player는 이 라이브러리를 가져다 사용만 하는 구조라는 것입니다. 즉, 문제의
|
||||
핵심인 VavCore-SurfaceManager의 소스 코드는 이 디렉토리에 없고, 미리 빌드된 libVavCore.so 파일 안에 포함되어 있을 것입니다.
|
||||
|
||||
가설 변경 및 새로운 접근:
|
||||
|
||||
문제의 원인이 VK_FORMAT_UNDEFINED를 NV12로 강제 오버라이드하는 부분이라는 점은 거의 확실합니다. VavCore 라이브러리의 소스 코드를 직접 수정할
|
||||
수 없는 현 상황에서, 우리는 VavCore 라이브러리가 올바른 포맷을 인지하도록 다른 방법을 찾아야 합니다.
|
||||
|
||||
VavCore가 MediaCodec의 출력 포맷을 결정하는 데 영향을 줄 수 있는 부분은 vav2player 애플리케이션에서 MediaCodec을 설정하는 부분일 것입니다.
|
||||
|
||||
vavcore_vulkan_bridge.cpp 로그를 다시 살펴보면, MediaCodec을 설정하고 ImageReader에 연결하는 과정이 보입니다. 이 ImageReader를 생성할 때
|
||||
출력 이미지 포맷을 명시적으로 지정할 수 있습니다. 만약 현재 ImageReader가 특정 포맷 지정 없이 생성되고 있다면, 드라이버가 기본 포맷(그리고
|
||||
VK_FORMAT_UNDEFINED를 반환하는)을 사용하게 될 수 있습니다.
|
||||
|
||||
다음 단계:
|
||||
|
||||
vavcore_vulkan_bridge.cpp 또는 관련 JNI 코드에서 ImageReader를 생성하거나 MediaCodec을 설정하는 부분을 찾아, 이미지 포맷을 어떻게 설정하고
|
||||
있는지 확인해야 합니다.
|
||||
|
||||
vavcore_vulkan_bridge.cpp 파일에서 ImageReader 또는 MediaCodec.configure와 관련된 부분을 찾아 분석해 보겠습니다. 다시 한번
|
||||
vavcore_vulkan_bridge.cpp 파일을 읽고 해당 부분을 집중적으로 확인하겠습니다.
|
||||
|
||||
╭────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...ons\main\cpp\vavcore_vulkan_bridge.cpp │
|
||||
╰────────────────────────────────────────────────────────╯
|
||||
✦ vavcore_vulkan_bridge.cpp 파일을 다시 검토했지만, 이 파일은 VavCore 라이브러리의 공개 API를 호출하는 역할만 할 뿐, ImageReader의 포맷을 직접
|
||||
설정하는 부분은 포함하고 있지 않습니다. ImageReader 생성 및 설정은 libVavCore.so 라이브러리 내부에 캡슐화되어 있는 것으로 보입니다.
|
||||
|
||||
따라서 VavCore 라이브러리 소스 코드 없이는 ImageReader가 어떤 포맷으로 생성되는지 직접 수정하기는 어렵습니다.
|
||||
|
||||
새로운 가설: 셰이더 문제
|
||||
|
||||
문제의 원인이 VK_FORMAT_UNDEFINED를 NV12로 강제 변환하는 부분이라는 것은 거의 확실합니다. 그렇다면, 반대로 생각해볼 수 있습니다.
|
||||
VulkanRenderer의 픽셀 셰이더가 NV12 포맷을 올바르게 처리하고 있는지 확인하는 것입니다.
|
||||
|
||||
만약 셰이더 코드에 버그가 있거나, NV12가 아닌 다른 포맷(예: RGBA)을 기대하고 있다면, NV12 데이터가 입력되었을 때 화면이 검게 나올 수
|
||||
있습니다.
|
||||
|
||||
다음 단계: 렌더러 및 셰이더 코드 확인
|
||||
|
||||
VulkanRenderer의 소스 코드와 YUV를 RGB로 변환하는 GLSL 셰이더 코드를 찾아서 분석해 보겠습니다. 이를 통해 렌더러가 어떤 색상 포맷을 기대하고
|
||||
있는지, 그리고 색상 변환 로직이 올바른지 확인할 수 있습니다.
|
||||
|
||||
먼저 VulkanRenderer의 소스 파일을 찾아보겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ FindFiles '**/vulkan_renderer.*' within . │
|
||||
│ │
|
||||
│ Found 2 matching file(s) │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 좋습니다. vulkan_renderer 소스 파일들을 찾았습니다.
|
||||
|
||||
먼저 헤더 파일(vulkan_renderer.h)을 읽어서 RenderVulkanImage 함수의 인터페이스와 클래스의 전반적인 구조를 파악하겠습니다.
|
||||
|
||||
╭────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...ons\app\src\main\cpp\vulkan_renderer.h │
|
||||
╰────────────────────────────────────────────────────────╯
|
||||
✦ 헤더 파일을 잘 분석했습니다. RenderVulkanImage라는 이름의 제로-카피(zero-copy) 렌더링 함수와, 별도의 Y, U, V 평면을 처리하는 CPU 기반 렌더링
|
||||
함수들이 모두 선언되어 있군요.
|
||||
|
||||
이제 가장 중요한 vulkan_renderer.cpp 구현 파일을 읽어서, 셰이더 코드가 어떻게 작성되어 있고 RenderVulkanImage 함수가 외부 VkImage를 어떻게
|
||||
처리하는지 직접 확인해 보겠습니다. 특히 YUV를 RGB로 변환하는 셰이더 코드와 vkCreateImageView 함수 호출 부분을 집중적으로 분석하겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...tions\src\main\cpp\vulkan_renderer.cpp │
|
||||
│ │
|
||||
│ Read lines 1-2000 of 2577 from applications/vav2player/app/src/main/cpp/vulkan_renderer.cpp │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ vulkan_renderer.cpp 파일의 첫 부분을 분석한 결과, 흥미로운 점을 발견했습니다.
|
||||
|
||||
1. `#include "yuv_shaders.h"`: 셰이더 코드가 별도의 yuv_shaders.h 파일에 포함되어 있습니다. 이 파일을 분석하면 색상 변환 로직을 정확히 파악할
|
||||
수 있습니다.
|
||||
2. `RenderVulkanImage` 함수의 부재: 로그에서는 분명히 RenderVulkanImage 함수가 호출되고 있지만, 제가 읽은 파일의 첫 2000줄에는 해당 함수의
|
||||
구현이 없습니다. 파일의 뒷부분에 있을 것으로 보입니다.
|
||||
|
||||
계속해서 vulkan_renderer.cpp 파일의 나머지 부분을 읽어서 RenderVulkanImage 함수의 실제 구현과, 비디오 렌더링에 사용되는 그래픽스 파이프라인
|
||||
및 디스크립터 셋(descriptor set)이 어떻게 설정되는지 확인하겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...tions\src\main\cpp\vulkan_renderer.cpp │
|
||||
│ │
|
||||
│ Read lines 2001-2577 of 2577 from applications/vav2player/app/src/main/cpp/vulkan_renderer.cpp │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ vulkan_renderer.cpp 파일의 나머지 부분을 모두 분석했으며, 결정적인 단서를 찾았습니다.
|
||||
|
||||
분석 결과
|
||||
|
||||
1. `RenderVulkanImage` 함수 구현 확인
|
||||
파일 끝에서 RenderVulkanImage 함수의 구현을 찾았습니다. 이 함수는 입력받은 VkImage를 NV12 포맷(2-plane YUV)으로 간주하고, Y 평면과 UV
|
||||
평면에 대해 각각 별도의 VkImageView를 생성합니다.
|
||||
* Y 평면 (Luminance): VK_FORMAT_R8_UNORM 포맷의 뷰 생성
|
||||
* UV 평면 (Chrominance): VK_FORMAT_R8G8_UNORM 포맷의 뷰 생성
|
||||
|
||||
이것은 NV12 포맷을 처리하는 표준적인 접근 방식이며, 이 부분의 코드는 올바르게 작성된 것으로 보입니다.
|
||||
|
||||
2. 치명적인 모순 발견
|
||||
하지만 심각한 문제점을 발견했습니다. RenderVulkanImage 함수는 Y 평면과 UV 평면, 이렇게 2개의 텍스처를 셰이더에 전달하기 위해 디스크립터
|
||||
셋(descriptor set)의 바인딩 0번과 1번을 업데이트합니다.
|
||||
|
||||
그러나 파이프라인을 생성하는 CreateDescriptorSetLayout 함수는 다음과 같이 바인딩을 전혀 만들지 않도록 코딩되어 있습니다.
|
||||
|
||||
1 // vulkan_renderer.cpp 내의 CreateDescriptorSetLayout 함수
|
||||
2 bool VulkanVideoRenderer::CreateDescriptorSetLayout() {
|
||||
3 LOGI("Creating descriptor set layout (no bindings - Hello Triangle style)...");
|
||||
4
|
||||
5 // No bindings for hardcoded triangle shader
|
||||
6 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
|
||||
7 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
8 layoutInfo.bindingCount = 0; // <-- 바인딩 개수가 0입니다!
|
||||
9 layoutInfo.pBindings = nullptr;
|
||||
10 // ...
|
||||
11 }
|
||||
|
||||
결론: 문제의 원인
|
||||
|
||||
이것이 바로 검은 화면의 원인입니다.
|
||||
|
||||
Vulkan 렌더링 파이프라인이 텍스처를 받을 준비가 전혀 되어 있지 않은 상태(바인딩 0개)에서, 렌더링 루프는 디코딩된 비디오 텍스처를
|
||||
파이프라인에 전달하려고 시도(바인딩 2개 업데이트)하고 있습니다.
|
||||
|
||||
Vulkan의 유효성 검사 계층(validation layer)이 비활성화되어 있다면 이 API 호출이 오류를 뿜지 않고 조용히 실패할 수 있습니다. 결과적으로
|
||||
셰이더는 샘플링할 텍스처를 전혀 받지 못하므로, 최종적으로 검은색만 출력하게 됩니다.
|
||||
|
||||
다음 단계: 셰이더 확인 및 수정 제안
|
||||
|
||||
이제 마지막 확인 단계로, 셰이더 코드가 실제로 2개의 텍스처(Y, UV)를 필요로 하는지 yuv_shaders.h 파일을 읽어 확인하겠습니다. 이 확인이
|
||||
끝나면, CreateDescriptorSetLayout 함수를 수정하여 문제를 해결하는 코드를 제안해 드릴 수 있습니다.
|
||||
|
||||
yuv_shaders.h 파일을 읽겠습니다.
|
||||
|
||||
╭────────────────────────────────────────────────────────╮
|
||||
│ ✓ ReadFile ...cations\app\src\main\cpp\yuv_shaders.h │
|
||||
╰────────────────────────────────────────────────────────╯
|
||||
✦ yuv_shaders.h 파일을 분석한 결과, 마지막 퍼즐 조각이 맞춰졌고, 동시에 또 다른 모순점을 발견했습니다.
|
||||
|
||||
최종 분석 및 원인 확정
|
||||
|
||||
1. 셰이더의 요구사항: 주석으로 포함된 GLSL 프래그먼트 셰이더 코드는 3개의 분리된 텍스처(yTexture, uTexture, vTexture)를 바인딩 0, 1, 2번에서
|
||||
받을 것으로 기대하고 있습니다. 이는 Y, U, V 평면이 완전히 분리된 YUV420p 포맷을 위한 코드입니다.
|
||||
|
||||
2. 드러난 모순점 종합:
|
||||
* 파이프라인 vs 렌더러: CreateDescriptorSetLayout 함수는 0개의 텍스처 바인딩을 설정합니다. 하지만 RenderVulkanImage 함수는 2개의
|
||||
바인딩(Y, UV)을 사용하려고 합니다.
|
||||
* 렌더러 vs 셰이더: RenderVulkanImage 함수는 2-plane NV12 포맷(Y 평면 1개, UV 평면 1개)에 맞춰 2개의 텍스처 뷰를 준비합니다. 하지만
|
||||
프래그먼트 셰이더는 3-plane YUV420p 포맷을 위한 3개의 텍스처를 기대합니다.
|
||||
|
||||
결론: 현재 코드는 렌더링 파이프라인의 여러 단계에 걸쳐 서로 맞지 않는 약속을 하고 있습니다. 파이프라인은 텍스처를 받을 준비가 안 됐고, 설령
|
||||
받는다고 해도 렌더러가 준비한 데이터와 셰이더가 기대하는 데이터의 형식이 다릅니다. 이로 인해 GPU는 아무것도 그리지 못하고 검은 화면만
|
||||
출력하게 됩니다.
|
||||
|
||||
---
|
||||
|
||||
해결 방안
|
||||
|
||||
이 문제를 해결하려면 이 세 가지 구성요소(파이프라인, 렌더러, 셰이더)가 모두 NV12 포맷을 사용하도록 통일해야 합니다. RenderVulkanImage 함수가
|
||||
이미 NV12를 기준으로 작성되어 있으므로, 나머지 두 부분을 여기에 맞추는 것이 가장 효율적입니다.
|
||||
|
||||
수정 계획:
|
||||
|
||||
1. `CreateDescriptorSetLayout` 수정: 2개의 텍스처(Y, UV)를 받을 수 있도록 바인딩 2개를 설정하는 코드로 교체합니다.
|
||||
2. 프래그먼트 셰이더 교체: 3개의 텍스처(Y, U, V) 대신 2개의 텍스처(Y, UV)를 입력받아 색상 변환을 수행하는 올바른 NV12용 GLSL 셰이더 코드로
|
||||
교체합니다.
|
||||
|
||||
먼저, 셰이더 코드가 들어있는 파일을 찾아야 합니다. yuv_shaders.h에 선언된 vertex_shader_spirv 변수가 정의된 파일을 검색하겠습니다.
|
||||
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ✓ SearchText 'vertex_shader_spirv' in **/*.cpp within ./ │
|
||||
│ │
|
||||
│ Found 4 matches │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
✦ 좋습니다. 셰이더 코드가 yuv_shaders.cpp 파일에 정의되어 있는 것을 확인했습니다.
|
||||
|
||||
분석 결과, 문제는 세 부분에 걸쳐 있습니다.
|
||||
1. 파이프라인 설정(`CreateDescriptorSetLayout`): 텍스처를 받을 준비(바인딩)가 전혀 되어있지 않습니다.
|
||||
2. 렌더러-셰이더 바인딩 불일치: RenderVulkanImage 함수는 Y, UV 텍스처를 바인딩 0, 1번에 전달하려 하지만, 셰이더는 Y, U, V 텍스처를 바인딩 0,
|
||||
1, 2번에서 기대합니다.
|
||||
3. 셰이더 포맷 불일치: 렌더러는 2-plane NV12 포맷을 준비하지만, 셰이더는 3-plane YUV420p 포맷을 기대합니다.
|
||||
|
||||
이 문제들을 한 번에 해결하기 위해, NV12 포맷을 기준으로 코드를 통일하겠습니다.
|
||||
|
||||
수정 작업 계획:
|
||||
|
||||
1. `vulkan_renderer.cpp` 수정:
|
||||
* CreateDescriptorSetLayout 함수를 3개의 바인딩(UBO, Y-sampler, UV-sampler)을 갖도록 수정합니다.
|
||||
* CreateGraphicsPipeline 함수가 텍스처 좌표를 사용하는 정점(vertex) 속성을 올바르게 사용하도록 수정합니다.
|
||||
* RenderVulkanImage 함수가 Y와 UV 텍스처를 올바른 바인딩 번호(1번, 2번)에 전달하도록 수정합니다.
|
||||
2. `yuv_shaders.cpp` 수정:
|
||||
* 프래그먼트 셰이더 코드를 2개의 텍스처(Y, UV)를 입력받아 NV12 포맷을 올바르게 처리하는 코드로 교체합니다.
|
||||
|
||||
먼저 1단계인 vulkan_renderer.cpp 파일 수정을 시작하겠습니다. 여러 부분을 수정해야 하므로, 가장 핵심적인 CreateDescriptorSetLayout 함수부터
|
||||
교체하겠습니다.
|
||||
Reference in New Issue
Block a user