OpenGL 에서 GPU 스키닝

1. vertex shader constants
2. vertex shader with bindable uniform
3. vertex shader with VTF (Vertex Texture Fetch)

1. 쉐이더를 지원하는 모든 그래픽 카드에서 가능, SM3 이하 카드에서 상수개수 제한(256 * vec4)이 문제가 됨
2. SM4 이상에서 가능, SM4 에서 이미 충분한 상수 개수(4069 * vec4) 제한
3. SM3 이상에서 가능, 상수개수 제한 없음 (정확히는 텍스쳐 크기로 제한을 받음)

4×3 행렬을 사용했을 때 버텍스 쉐이더 상수 제한이 256 일경우 사용할 수 있는 최대 bone 개수는 80개 정도
(256 / 3 = 85.33333333)… 게다가 다른 상수들을 위한 공간을 확보하고 나면 실제로는 70 개 정도 밖에 여유가 없다.
이를 위해 메쉬를 쪼개든지, 한 메쉬에 영향을 받는 본 개수를 70 개 이하로 줄여야 하는 상황이 된다

VTF 를 이용하면 RGBA_32F_32F_32F_32F 포맷의 1024 x 1 사이즈의 텍스쳐를 이용한다고 할때,
대략 320 개 정도의 bone 을 사용할 수 있다.

실제로 엔진에서는 4096 x 32 (2Mb) 정도 크기의 텍스쳐 잡아놓고(x2 더블 버퍼링), 엔티티 skinning 버퍼로 사용한다
(modular 연산을 피하기 위해 가로로 길게 잡음) 모든 entity 의 skinning 행렬을 가지고 있으므로 instancing 도 가능하다.

PBO(ARB_pixel_buffer_object) 확장을 이용해 버퍼의 내용을 asyncronous 하게 업데이트 가능하고,
TBO(ARB_texture_buffer_object) 확장을 이용하면 좀더 심플하게 메모리 낭비 없이 구현 가능하다

1. direct copy (system memory -> texture)
첫번째 프레임: Copy to Texture1
두번째 프레임: Copy to Texture2

2. PBO
첫번째 프레임: PBO1 -> Texture (DMA copy)
두번째 프레임: PBO2 -> Texture (DMA copy)

3. TBO
첫번째 프레임: TBO1 -> Buffer Texture1
두번째 프레임: TBO2 -> Buffer Texture2

속도는 TBO > PBO > direct copy 순으로 빠름

참고 자료:
http://www.opengl.org/registry/specs/ARB/texture_buffer_object.txt
http://en.wikipedia.org/wiki/Shader_Model_4.0#Shader_model_comparison
http://en.wikipedia.org/wiki/Comparison_of_NVIDIA_GPU
http://en.wikipedia.org/wiki/Comparison_of_ATI_Graphics_Processing_Units

OpenGL Buffer Object

3D Application 은 항상 어떻게 하면 빠르게 그릴까를 연구해왔다.
Geometry 를 좀 더 빠른 속도로 그리기 위해 매번 새로운 기법들이 개발되어 왔는데.. OpenGL 1.1 시절에는 Display List 로 rendering command 를 미리 컴파일하여 binary 버퍼화해버리는 원시적인 방법이 사용됐다. 그러다가 동적인 Geometry 를 일반적인 방법으로 사용하기 위해 Vertex Array 를 사용했고, CVA(Compiled Vertex Array) 나 T&L 이 가능해 지면서 더욱 더 빠른 처리가 가능해졌다.

그 후에는 Vertex Array 자체를 Local(Video)/Non-local(AGP) 메모리에 상주시켜 GPU 가 직접 메모리를 access 하도록 하는 VAR/VAO/VBO 같은 확장이 추가되었다. 이 개념은 OpenGL 이나 DirectX 같은 API 보다는, 그래픽 하드웨어와 더 밀접한 관련이 있다고 볼 수 있는데… 하드웨어 입장에서는 몇가지 operation 으로 나눠볼 수 있겠다.

VBO 기준으로 설명하면..

a. 버퍼를 적당한 새로운 곳에 채운다 – BufferData (Video / AGP / System memory)
b. 버퍼의 내용을 버린다(Discard) – BufferData (data 를 NULL 로 세팅)
c. 버퍼의 장소를 유지한채 전체 버퍼를 synchronous 하게 포인터를 얻어온다 – MapBuffer / UnmapBuffer
d. 버퍼의 장소를 유지한채 일정 부분만 synchronous 하게 업데이트 한다 – BufferSubData

2008년에 와서 map_buffer_range 확장으로 좀 더 세밀하게 제어가 가능해졌는데..

버퍼의 일정 부분만 sync or async 하게 Map 이 가능하고,
업데이트시 flush 되는 sub range 를 별도로 지정이 가능해졌다.

보통 GPU 쪽에서 사용할땐 Video > AGP > System memory 순으로 빠르고,
CPU 를 경유한 write 는 System > AGP > Video memory 순으로 빠른데…
용도에 따라서 static 버퍼는 한번만 write 하면 그만이지만, dynamic 버퍼는 여러번 write 하게된다.
GPU 에서 버퍼를 점유중일때, 사용이 끝날때까지 기다리지 않고 asynchronous 하게 write 를 해야 속도를 올릴 수 있다.

※ 필자의 경험을 바탕으로 적은거라 일부 틀린 내용이 있을 수도 있음

다양한 extension 들이 추가되면서 이 buffer 를 아래와 같은 여러가지 데이터 형태로서 사용 할수 있게 되었다. (점점 범용적이 되어가고 있다.)

버퍼를 버텍스 데이터로 사용 — VertexArrayPointer – ARB_vertex_buffer_object (NV10, R100)
프레임버퍼로/프레임버퍼로부터 버퍼를 쓰기 — DrawPixels/ReadPixels – ARB_pixel_buffer_object (NV40, R300)
버퍼로부터 텍스쳐를 업데이트 — TexSubImage – ARB_pixel_buffer_object ( ” )
버퍼로부터 쉐이더 상수를 지정 — UniformBuffer – EXT_bindable_uniform (G80, R600)
버퍼를 커다란 1D 텍스쳐로 사용 — TexBuffer – EXT_texture_buffer_object (G80, R600)

이정도만으로도 활용범위는 뭔가 굉장히 많아 보인다.

몇가지 예를 들면…

ex 1. Render To VertexBuffer

fragment shader 를 거쳐 나온 프레임버퍼를 비동기로 읽어 vertex array 로 재사용 가능

ex 2. 텍스쳐 스트리밍

텍스쳐를 화면에 그리면서 다음 프레임의 내용을 비동기로 업데이트 가능

ex 3. 하드웨어 스키닝에 활용

a) VTF(Vertex Texture Fetch) 를 이용해 텍스쳐 데이터로 전달시 PBO 를 이용해 텍스쳐를 업데이트
b) a 와 같은 방식으로 texture buffer object 를 이용해 버퍼를 텍스쳐 데이터로 직접 사용
c) bindable uniform 을 이용해 쉐이더 상수 버퍼를 구성후 쉐이더에 버퍼 형식으로 전달

참고 자료:
http://developer.nvidia.com/object/using_VBOs.html
http://www.songho.ca/opengl/gl_vbo.html
http://www.songho.ca/opengl/gl_pbo.html
http://winnwe.com/pda/perma/1115/
http://winnwe.com/pda/perma/1116/
http://www.nvidia.com/dev_content/nvopenglspecs/GL_NV_vertex_array_range.txt
http://www.nvidia.com/dev_content/nvopenglspecs/GL_NV_vertex_array_range2.txt
http://www.nvidia.com/dev_content/nvopenglspecs/GL_NV_pixel_data_range.txt
http://developer.download.nvidia.com/opengl/specs/GL_NV_transform_feedback.txt
http://www.nvidia.com/dev_content/nvopenglspecs/GL_ARB_vertex_buffer_object.txt
http://www.nvidia.com/dev_content/nvopenglspecs/GL_ARB_pixel_buffer_object.txt
http://developer.download.nvidia.com/opengl/specs/GL_EXT_bindable_uniform.txt
http://developer.download.nvidia.com/opengl/specs/GL_EXT_texture_buffer_object.txt
http://www.opengl.org/registry/specs/APPLE/flush_buffer_range.txt
http://www.opengl.org/registry/specs/ARB/map_buffer_range.txt
http://www.opengl.org/registry/specs/ARB/copy_buffer.txt
http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt