Vulkan과 OpenGL ES 게임 렌더링 API 식별

Android 게임을 개발하거나 최적화할 때 엔진에서 사용하는 그래픽 파이프라인을 이해하는 것이 유용한 경우가 많습니다. 엔진은 개발자가 액세스할 수 있는 명확한 신호 없이 Vulkan 또는 GLES를 선택할 수 있습니다. 안타깝게도 게임이 Vulkan에서 실행 중인지 또는 OpenGL ES로 자동 대체되었는지 확인하기 위해 logcat에 의존하는 것이 신뢰할 수 없는 경우가 있습니다.

이 가이드에서는 Android 성능 분석기 (APA)와 맞춤 Perfetto SQL 쿼리를 사용하여 게임의 활성 렌더링 API를 명확하게 식별하는 방법을 보여줍니다.

로그 기반 확인의 제한사항

  • 스트립된 로그: 프로덕션 빌드는 초기화 및 드라이버 로드 흔적을 숨기는 경우가 많습니다.
  • 거짓양성: 일부 게임 엔진은 시작 중에 Vulkan 및 OpenGL ES 라이브러리를 모두 로드하여 둘 다에 대한 초기화 로그를 출력합니다. 결국 하나가 실패하고 대체가 트리거되더라도 마찬가지입니다.
  • 자동 대체: 기기별 드라이버 문제로 인해 Vulkan 초기화가 실패하면 엔진은 심각한 오류를 발생시키지 않고 OpenGL ES로 대체되는 경우가 많습니다.

APA를 사용한 측정

Android Performance Analyzer를 사용하면 시스템 트레이스를 기록하고 SQL 쿼리를 통해 프레임 데이터를 정확하게 분석할 수 있습니다. Vulkan 및 GLES 사용량을 측정하려면 다음 단계를 따르세요.

  1. APA로 트레이스 캡처: 게임을 실행하고 APA를 사용하여 분석하려는 세그먼트(예: 프레임 드롭이 의심되는 게임플레이 중 지점) 중에 시스템 트레이스를 캡처합니다. 기기가 연결되고 트레이스 녹화가 완료되면 트레이스 데이터가 APA 인터페이스에 로드됩니다.

  2. APA에서 SQL 탭 클릭: 추적 분석 화면이 열려 있을 때 UI의 탐색 메뉴에서 SQL 탭을 클릭하여 데이터를 직접 쿼리할 수 있는 추적 프로세서 환경을 엽니다.

  3. APA SQL 탭에 다음 SQL 쿼리 붙여넣기: 다음 SQL 쿼리를 복사하여 쿼리 입력 필드에 붙여넣습니다. 이 쿼리는 eglSwapBuffers, QueuePresentKHR, vkQueuePresentKHR와 같은 그리기 API 이벤트를 트레이스에서 검색합니다. 프로세스 이름별로 그룹화하고 렌더링 API 수와 Vulkan 대 OpenGL ES 렌더링 호출의 비율 분포를 계산합니다.

    WITH raw_slices AS (
      -- 1. Map each slice (API call) to its corresponding process or package via UTID and UPID
      SELECT
        p.name AS process_name,
        s.name AS slice_name
      FROM slice s
      JOIN thread_track tt ON s.track_id = tt.id
      JOIN thread t USING (utid)
      JOIN process p USING (upid)
      WHERE (s.name LIKE 'eglSwapBuffers%'
        OR s.name IN ('QueuePresentKHR', 'vkQueuePresentKHR'))
        -- Optional: To isolate your game, uncomment the following and filter by package name and thread name:
        -- AND p.name = 'com.example.mygame'
        -- AND t.name = 'RenderThread'
    ),
    process_stats AS (
      -- 2. Aggregate counts per individual process
      SELECT
        process_name,
        SUM(CASE WHEN slice_name LIKE 'eglSwapBuffers%' THEN 1 ELSE 0 END) AS egl_count,
        SUM(CASE WHEN slice_name IN ('QueuePresentKHR', 'vkQueuePresentKHR') THEN 1 ELSE 0 END) AS vulkan_count
      FROM raw_slices
      GROUP BY process_name
    ),
    process_totals AS (
      -- 3. Calculate total counts for denominator calculation
      SELECT
        process_name,
        egl_count,
        vulkan_count,
        (egl_count + vulkan_count) AS total_count
      FROM process_stats
    )
    -- 4. Calculate final percentage shares per row, preventing division by zero errors
    SELECT
      process_name,
      egl_count,
      vulkan_count,
      total_count,
      ROUND(CAST(egl_count AS REAL) * 100 / NULLIF(total_count, 0), 2) AS "egl_percentage(%)",
      ROUND(CAST(vulkan_count AS REAL) * 100 / NULLIF(total_count, 0), 2) AS "vulkan_percentage(%)"
    FROM process_totals
    ORDER BY total_count DESC;
    
  4. 쿼리 실행: 입력 필드 근처에 있는 쿼리 실행 버튼을 클릭합니다. 쿼리가 실행되면 결과 창에 프로세스 이름 (process_name), OpenGL ES 프레젠테이션 수 (egl_count), Vulkan 프레젠테이션 수 (vulkan_count), 총 프레젠테이션 수(total_count), 사용된 각 API의 비율 (egl_percentage(%)vulkan_percentage(%))을 보여주는 표가 표시됩니다.

    실행된 SQL 쿼리와 결과 렌더링 API 측정항목이 표에 표시된 화면

일반적인 문제

  • SurfaceFlinger 혼동 방지: 항상 게임의 애플리케이션 스레드 (RenderThread)로 검색 범위를 지정하세요. SurfaceFlinger와 같은 시스템 전체 컴포지터 프로세스는 게임의 내부 렌더링 파이프라인이 아닌 시스템 UI 렌더링을 나타내는 Vulkan 또는 EGL 트랙을 표시할 수 있습니다. 게임을 격리하려면 기본 SQL 쿼리의 WHERE 절에서 선택적 필터의 주석을 해제하고 'com.example.mygame'을 게임의 패키지 이름으로 바꿉니다.

    -- AND p.name = 'com.example.mygame'
    -- AND t.name = 'RenderThread'
    
  • 워밍업 기간: 게임의 기본 3D 장면이 완전히 로드된 후에 항상 트레이스를 기록합니다. 스플래시 화면이나 로드 전환 중에 캡처하면 초기화가 잘못될 수 있습니다.

LevelUp 예외 추적 요구사항

Android Performance Analyzer (APA)를 사용하여 Google Play 게임즈 LevelUp 면제를 받기 위한 트레이스를 캡처하는 경우 트레이스가 유효하려면 특정 하드웨어 요구사항을 충족해야 합니다.

타겟 Level Up 참조 기기 중 하나를 사용하여 트레이스를 캡처하는 것이 좋습니다.

또한 정확한 유효성 검사를 위해 ANGLE GPU를 사용하는 기기에서 트레이스를 가져오면 안 됩니다. 특히 다음 기기를 사용하여 LevelUp 면제 트레이스를 캡처할 수 없습니다.

  • GPU 공급업체가 AMD Xclipse GPU를 사용하는 SLSI (Samsung)인 기기
  • Pixel 11