이번에 회사에서 사용중인 게임엔진에 ASTC 텍스처가 지원되게끔 개선하는 작업을 맡았다!
그래서 이 업무 완료 이후 내용을 공유하고자 작성한 자료를, 공개용으로 간단히 요약해 보았다.
텍스처 압축
텍스처가 무엇인가요?
물체의 시각적인 모든 특성을, 텍스처라고 부른다.
물체의 질감, 모양, 색상, 패턴 등등이 모두 텍스처를 설명하는 단어들이다.
조금 더 게임엔진 쪽으로 설명하자면,
UV매핑할 때 사용되는, 메모리에 로드된 이미지 라고도 표현할 수 있겠다.
위 이미지에서 보이다시피, 마인크래프트의 '텍스처 팩'은 물체가 보이는 모든 특성에 관여하게 된다.
또한, 게임에서 사용중인 2D 이미지들 또한, 모두 다 텍스처라고 말할 수 있을 것이다.
텍스처 압축을 왜 하나요?
이는 텍스처를 떠나, 압축이 가져다 주는 이점과 대체로 유사하다.
텍스처를 압축함으로서, 메모리를 절약할 수 있다.
그래픽 카드의 메모리 (VRAM) 뿐만 아니라,
게임엔진에서 처리중에 사용하는 앱 메모리 또한 줄일 수 있게 된다.
텍스처 크기가 작아지고 효율적이게 된다면, 렌더링 성능은 향상되고, 로딩시간은 감소된다.
텍스처 파일 크기가 작아진다면, 당연히 저장공간 또한 절약할 수 있게 된다.
PNG를 왜 못 쓰나요?
PNG는 훌륭한 이미지 압축 포맷이다.
BMP나 무압축 RGBA8로 저장된 이미지와는, 수십 배까지 차이나기도 한다.
하지만, PNG는 텍스처 압축 포맷으로 사용할 수 없다. 이유가 무엇일까?
같은 1280px X 1280px 크기, 32bpp의 두 이미지가 있다고 해보자.
PNG이미지라면, 이 둘은 크기가 같지 않다.
이 말은, PNG는 '가변 비율 압축' 이라는 의미이다.
aabbbcdddeaaabcccdee 라는 데이터가 a2b3c1d3e1a3b1c3d1e2 로 압축될 때,
aaaabbbbccccddddeeee 라는 데이터는 a4b4c4d4e4 로 짧게 압축될 수 있는,
이런 비슷한 느낌이라고 이해하면 될 것 같다.
그러면 다시, 가변 비율 압축을 왜 못 쓰는 걸까?
텍스처 렌더링
렌더링
렌더링은 아주 깊고 전문적인 영역이지만, 간단히 우리가 보아야 할 부분은,
UV좌표를 사용해서, 텍스처에서 픽셀 색상을 가져오는 과정이다.
이 때, '랜덤 엑세스'가 필요하게 된다.
이 때 랜덤은, 난수 느낌이라기 보다는 순차적이지 않은, 무작위 위치로의 액세스를 의미한다.
따라서, 텍스처 메모리에서 어떠한 픽셀의 데이터를 가져올 때,
'시작 주소 + 오프셋' 으로 바로 접근이 가능해야 하다.
가변 비율로 압축되는 PNG는,
'시작 주소 + 텍스처 사이즈 크기의 반' 위치에 존재하는 데이터가,
이미지의 정 가운데 위치한 픽셀의 데이터가 아니기 때문에, 사용이 불가능한 것이다.
만약 PNG를 그대로 사용하게 된다면.
만약 PNG 이미지들을 그대로 이미지 리소스들로 사용하게 된다면,
다음 과정이 진행되게 된다.
1. 게임 엔진이 PNG 형식에서, 랜덤 엑세스가 가능한 형식 (RGBA8 등)으로 변환한다.
이는 게임 패키징 과정에서 진행될 수도 있을것 같고, 런타임에서 로드 시 진행될 수도 있을 테다.
만일 게임엔진이 런타임에서 리소스 로드 시 이 작업을 수행하게 된다면,
그만큼 CPU 손해가 발생하게 되는 것이다.
2. 변환된 이후의 리소스 크기는 PNG 원본에 비해 매우 커진다.
PNG 원본 이미지를 로드하고 나서 이걸 또 CPU 손해를 보며 압축텍스처로 변환하지 않는 이상,
RGBA8처럼 PNG 디코딩만 수행하게 된다면, 리소스 크기는 매우 커지게 된다.
예를 들어, 671KB였던 PNG 리소스는,
게임엔진에서 다룰 때 6.25MB의 RGBA8 리소스가 되어 버린다.
이는 앱 메모리와 GPU 메모리의 손해로 이어진다.
고정 비율 압축
가변 비율 압축을 사용하지 못하기 때문에,
고정 비율로 리소스 압축을 진행하게 된다.
고정 비율 압축은 달리 말하면, '블럭 단위의 압축'이다.
한 블럭을 몇 바이트로 표현할 지 미리 정해 두는 식이다.
예를 들어, ETC2는 4x4 픽셀을 하나의 블럭 크기로 다룬다.
이 때 하나의 블럭은, 128bit로 표현하기로 정했다.
따라서, ETC2로 이미지를 압축하게 될 경우,
같은 크기의 두 이미지라면, 압축 이후에 두 이미지 크기 또한 같은 크기가 된다.
ASTC
Adaptive and Scaleable Texture Compression!
이라는 이름답게, 원하는 크기의 블럭 기반으로 압축을 지원하는 포맷이다.
ETC2는 4x4px 를 한 블럭으로, 한 블럭은 128bit 로 정해놓았다면,
ASTC는 4x4px ~ 12x12px 중 원하는 크기를 한 블럭으로 정할 수 있고, 이 때 한 블럭은 128bit이다.
위 이미지처럼, 한 블럭이 128bit로 표현될 때, 4x4px (16px)를 128bit로 표현하면 가장 해상도가 좋고,
12x12 (144px) 를 128bit로 표현하면 가장 해상도가 낮은 형태가 되는 식이다.
따라서 원하는 만큼의 해상도-리소스크기의 tradeoff로 조절해서 압축이 가능한 형태이다.
하지만 ASTC도 단점이 있다!
이외에도 내부적으로 알고리즘이 개선되고, 다양한 개선점이 존재하는 ASTC이지만,
단점이 있다.
모바일 기기에서는 OpenGL ES 3.2 부터 지원한다는 점이다.
혹은, OpenGL ES 3.1 + AEP (Android Extension Pack) 이 있어야 한다.
이보다 낮은 사양의 기기들은, ASTC를 그래픽카드로 보여줄 수 없다.
ASTC를 지원하는 사양의 가장 오래된 모바일 기기로는,
갤럭시 S6, iPhone6 정도가 있겠다.
ASTC software decoding
그렇다면, 만약 이미 iPhone5를 지원하게끔 출시된 게임에서는,
눈물을 머금고 iPhone5 사용자를 내치면서 리소스 압축을 ASTC로 변환해야 하는 걸까?
물론 그런 결정이 가능한 상태라면 걱정할 이유 없이 최선이지만,
ASTC를 HW에서 지원하지 않는다면, SW로 디코딩해서 화면에 보여주면 된다!
즉, 리소스가 ASTC라면 이대로 그래픽카드에 넘겼다가는 화면에 출력이 안 될 테니,
리소스를 ASTC에서 해석 가능한 형태로 디코딩해서 그래픽카드로 넘기는 것이다.
이를 위한 라이브러리는 많이 존재한다.
google의 astc-codec 라이브러리,
arm의 astc-encoder 라이브러리,
android의 dEQP(drawElements Quality Program) 기반 라이브러리
이렇게 대표적으로 세 개가 존재하는데,
셋을 '디코딩'으로만 비교해 보았을 때, 마지막 라이브러리가 가장 빠른 디코딩을 보여주었다.
텍스처 압축 샘플 : PNG, ETC2, ASTC
눈으로 비교해보면 가장 잘 와닿는 법!
왼쪽부터 순서대로 PNG 원본 이미지, ETC2 압축, ASTC 압축이다.
가운데 ETC2 압축은 네모네모하게 블럭 단위로 색상 경계들이 깨져 보이지만,
ASTC 압축은 따로 깨짐이 보이지 않고 PNG만큼 깔끔하다.
원본 PNG | ETC2 | ASTC (4x4 block) | |
디스크 크기 | 406KB | 900KB | 900KB |
lz4 이후 디스크 크기 | - | 118KB | 270KB |
텍스처 메모리 크기 | 4500KB (RGBA8 decode) | 900KB | 900KB |
ETC2와 ASTC는 고정비율 텍스처 압축 형식이므로, 별다른 디코딩 없이 그대로 VRAM에 올리면 된다.
이처럼 추가적으로 lz4 압축을 통해, PNG 리소스보다 디스크 용량 크기가 클 수 있는 문제를 해결할 수도 있다.
'연구한 이야기 > 맡은 업무 이야기' 카테고리의 다른 글
드로우콜을 낮춰보자 (0) | 2024.09.01 |
---|---|
게임 서버 로그 처리하기 (0) | 2024.04.14 |
conan 패키지 관리자 도입하기 : 개론 (1) | 2024.02.04 |
ASTC 포맷, 어떤 점이 왜 좋을까? (0) | 2024.01.28 |
Prometheus + Grafana 패키징 (0) | 2024.01.19 |