패킷으로 전송하기 위한 복셀 오브젝트의 복셀 데이터 압축. 2월말에서 3월 초까지 작업내용이다.
나중에 블로그에 포스팅 하려고 페이스북 타임라인에 대충 메모를 해놨었다. 잊고 있었는데 오늘에서야 정리해서 포스팅한다.
Voxel Horizon에서 플레이어가 게임에 접속하면 현재 위치 기준으로 주변의 복셀 오브젝트들을 긁어다가 네트워크를 통해 플레이어의 디바이스에 전달한다.
복셀 데이터는 일단 컬러값(인덱스)를 빼고 순수 지오메트리 데이터는 복셀 한칸당 1bit다. 8x8x8짜리 복셀 오브젝트는 정확히 64bytes를 사용한다.
맵 위치에 따라 다르지만 초기 로그인 시에 140KB정도나 전송해야하는 경우도 있다. 물론 이 경우 압축하지 않으면 280KB정도의 패킷을 전송한다. 이 경우 유저측 버퍼 사이즈를 256KB로 잡아놨기 때문에 ‘버퍼부족-패킷 수신에 문제있음’ 으로 간주하여 네트워크 라이브러리(socket api아님. 자체 라이브러리)에서 접속을 끊어버리는 결과로 이어진다.
따라서 실서비스에선 결국 버퍼 사이즈도 늘릴 필요가 있을것이다. 1MB정도는 잡아줘야할텐데 동접 4000이면 버퍼 메모리만 4GB이므로 지금처럼 램8GB짜리 VM으로 빈곤하게 서버를 운영한다면 무리가 따른다. 동접수에 제한을 걸던지 서버사양을 높여야 한다.
하여간 1차적으로 해야할 일은 복셀 데이터를 압축하는 것이다.
이미 복셀 한칸당 1bit만 사용하고 있으므로 크게 줄일 가능성은 없어보인다. 실제로 zlib같은걸로 압축해봐야 거의 줄지 않는다. 거의 줄지 않는데 비해 얼타임으로 사용하기엔 문제가 있을만큼 느리다.
결국 내가 만든 복셀 데이터의 자료구조를 제일 잘 아는 내가 압축을 직접 구현할 수 밖에 없다.
압축은 다음과 같이 구현했다.
- 8x8x8 복셀 오브젝트에서 2x2x2블럭으로 쪼개면 2x2x2블럭의 패턴은 8 bits, 최대 256가지.
- 최대 패턴 개수 16개로 제한을 건다. 오브젝트 한개당 16개 패턴을 초과할 경우 압축불가로 처리.
- 8 bits x 16 = 128 bits = 16 bytes, 이것이 패턴 팔레트 사이즈
- 2x2x2블럭 하나는 16가지중 하나의 패턴을 가지게 되므로 각 블럭은 4 bits로 표현 가능.
- 8x8x8오브젝트에서 2x2x2블럭의 개수는 64개. 4 bits x 64 = 256 bits = 32 bytes. 이것이 압축된 복셀 데이터 바디 사이즈
- 패턴 팔레트16 bytes + 바디 32 bytes = 48 bytes.
- 압축하지 않은 8x8x8복셀오브젝트의 복셀 데이터 사이즈는 64 bytes.
- 패턴이 16개인 오브젝트는 64bytes -> 48 bytes 로 25% 사이즈를 줄일 수 있음.
테스트중인 맵을 보면…
패턴개수 | 오브젝트 개수
3 | 8417
4 | 8682
5 | 2370
6 | 3498
7 | 1902
8 | 1686
9 | 1481
10 | 1175
11 | 586
12 | 403
13 | 272
14 | 205
15 | 153
16 | 111
압축 가능한 오브젝트 수 : 30941
8x8x8오브젝트 개수 : 31206
총 오브젝트 개수 : 50562
스트리밍할때 평균적으로 61%정도의 오브젝트에 대해 25% 패킷사이즈 감소.
패턴이 8개 이하일때는 2x2x2블럭 하나를 3 bits로 표현 가능. 이 경우 패턴 팔레트 사이즈 8 bytes , 바디 사이즈 3 bits x 64 = 192 bits = 24 bytes , 합쳐서 32 bytes로 압축가능.
패턴이 4개 이하일때 2x2x2블럭 하나를 2 bits로 표현 가능. 패턴 팔레트 사이즈 4 bytes , 바디 사이즈 2 bits x 64 = 128 bits = 16 bytes , 합쳐서 20 bytes로 압축가능.
3월말에 작업완료했고 실제로 패킷 사이즈를 크~~~게 줄였다. 패킷 버퍼도 256KB그대로 놔두고 있다. 언젠간 늘리긴 해야겠지.