꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by ozlael


들어가며

요즘에는 스마트폰을 사용하지 않는 사람을 찾아보기가 힘들지요. 저 역시도 한시도 스마트폰을 품에서 떠나보내지 않고 생활하고 있습니다. 정말이지 스마트폰이 없던 시절에는 도대체 어떤 폰을 들고다녔나 싶습니다. 아마 다시 일반 피쳐폰 쓰라고 하면 돌아버릴거예요. WM6 폰을 쓰라고 해도 목매달아버릴지도 모르지요. 아마 다들 마찬가지이실거라 생각합니다. 그런데 지금으로부터 10년전인 2002년에는 어떤 핸드폰을 사용했는지 기억나시나요? 그 당시는 폴더형의 핸드폰이 주류를 이루고 있었고 LCD가  흑백에서 컬러로 바뀌는 과도기적인 시절이였습니다. 안테나도 당연히 존재하고 있었고 1년뒤인 2003년이 되서야 안테나가 없는 핸드폰이 나오기 시작하면서 '인테나'라는 용어가 생겨나기 시작했지요. MP3 재생같은 것도 상상조차 할 수 없었고 16poly가 지원되는 것도 놀라웠던 시절이지요. 참 그시절의 핸드폰은 어떻게 사용했나 싶습니다. 정말로 긴 세월이 흘렀네요.

이미지 출처 : http://tomorrowhistory.tistory.com/

하라는 게임 이야기는 안하고 갑자기 웬 뜬금없는 핸드폰 이야기냐구요? 실은 DirectX9에 대한 이야기를 하려고 합니다. 바로 이 놀라운 핸드폰들을 사용하던 2002년에 DirectX9가 처음 배포되었지요. 그러고서는 10년이 지난 지금까지도 계속 사용되고 있는 것입니다. 지속적인 업데이트가 되면서 마이너 버젼들이 바뀌긴 했지만 DirectX9의 목숨이 계속 유지되고 있는것입니다. 게다가 여전히 대부분의 게임들이 DirectX9으로 제작되고 있습니다. 핸드폰은 흑백에서 컬러로, 16화음에서 자연음으로, 폴더폰에서 스마트폰으로 바뀌는 긴 세월 동안 DirectX9이 사용되고있는 것입니다. DirectX10이 나오고 이제 11까지도 나왔지만 여전히 DirectX9을 버릴 수가 없습니다. 아직은 대부분의 가정에 있는 PC들이 여전히 windows XP를 이용고 있고 심지어 PC방조차 마찬가지입니다. 앞으로도 당분간은 DirectX10,11로는 개발할 일이 없어보입니다. 

하지만 10년 사이에 그래픽카드 H/W는 엄청난 속도로 발전을 해왔습니다. 2002년에 3dfx의 voodoo가 단종되고 그 10년뒤인 현재는 라데온 HD 7xxx, 지포스 GTX 6xx등이 나오고 있습니다. 메모리클럭은 166Mhz에서 6000hz로 30배 이상의 성능으로 발전했습니다. 

이 부두 카드에는 슬픈 전설이 있지. 하지만 난 전설따위 믿지 않아

이토록 긴 세월동안 동일한 DirectX9 사용해왔지만 GPU는 엄청난 변화를 겪어왔습니다. 따라서 DirectX9를 사용함에 있어서도 시간이 지나면서 그 효율도 변화하게 됩니다. 예전에는 당연시 되어 왔던 최적화 방법이 현대의 그래픽카드에서는 비효율적인 최적화 방법이 될 수도 있는것이지요. 이번 포스팅에서는 DirectX9에서  최적화 시, 예전과는 다른 현대의 GPU를 위한 주의점을 정리해보고자 합니다. 사실, 별건 없고 nVIDIA, ATi, Intel의 그래픽 프로그래밍 가이드 문서의 내용들 중에서 주관적인 견해로 정리한 것입니다. 제가 자의적으로 해석하고 받아들인 내용도 있으므로 시간 되신다면 원문을 모두 읽어보시길 권장합니다. 혹시라도 틀린 내용이 있다면 지적 부탁드립니다.

nVIDIA : http://developer.download.nvidia.com/GPU_Programming_Guide/GPU_Programming_Guide_G80.pdf

ATi : http://developer.amd.com/media/gpu_assets/ATI_Radeon_HD_2000_programming_guide.pdf

Intel : http://software.intel.com/file/34436


통합셰이더모델 (unified shader model)

여기서 말하는 "현대의 GPU"의 기준을 콕 찝어 이야기 하자면 DirectX 10 이상 지원 모델으로 삼으면 될 것 같습니다. nVIDIA Geforce 8xxx 시리즈 이상, ATi Radeon HD 2xxx 시리즈 이상, Intel GMA 3xxx 시리즈 이상부터 지원되는 것으로 알고있습니다. 시기적으로는 2008년 이후정도가 되지 않을까 싶습니다. 이 때부터 DirectX10 지원 카드가 본격적으로 출시되기 시작했지요. 

이 DirectX 10 지원 여부가 그래픽 카드 아키텍쳐의 큰 변화를 가져오게 되었습니다. 바로 통합 셰이더 모델( Unified Shader Model)이지요. 예전에는 버텍스 셰이더 (vertex shader) 처리 유닛과 픽셀 셰이더 (pixel shader) 처리 유닛이 물리적으로 따로 존재했었습니다. 하지만 DirectX 10부터는 아키텍쳐가 통합 셰이더 모델로 변경되었습니다. 버텍스든 픽셀이든 지오메트리든 같은 셰이더 처리 유닛을 사용하게 되는 것이지요.  

이미지 출처 : http://vlsi2.kaist.ac.kr/research/multimedia-processor/unified-shader

예전에는 버텍스들을 VS 전용 유닛에서 처리하고 그 결과물을 여차저차해서 PS 전용 유닛에서 처리하고 래스터라이징을 처리가 되었습니다만 통합 셰이더 아키텍쳐에서는 이러한 VS와 PS의 처리 유닛이 물리적으로 구분되어 있지 않습니다.

 아~ 그렇구나~ 근데 이게 뭐 어쨌다구? 아까부터 왜 쓸데없는 소리만 지껄임? 님 장난함?

이러한 통합 셰이더 모델은 DirectX 10, 11을 지원하기 위한 아키텍쳐지만 DirectX9에서도 물리적으로는 이 통합 셰이더 모델로 작동할 수 밖에 없습니다. 그렇게 되면서 엄청난 변화가 생기게 되었고 그 변화를 적극 활용하는 것이 현대 GPU를 위한 최적화의 중요한 포인트가 되는 것이지요.  


버텍스와 픽셀의 셰이더 로드 밸런싱

통합 셰이더 (Unified Shader) 이전의 기존 모델에서는 픽셀 세이더 처리가 간단하더라도 버텍스 셰이더 처리가 무거우면 버텍스 셰이더가 병목이되었고, 반대로 픽셀 셰이더 처리가 무거우면 버텍스 세이더가 간단하더라도 픽셀 세이더가 그 발목을 잡아 병목이 일어나는 시나리오가 발생을 합니다. 일반적으로 픽셀 셰이더 처리 능력이 버텍스 셰이더 처리 능력보다 허접했기 때문에 최대한 많은 연산을 버텍스 셰이더에서 처리하고 그 결과를 픽셀 셰이더에 념겨주도록 만드는 것이 미덕이였습니다. 

하지만, 통합 셰이더 모델에서는 이야기가 달라집니다. 버텍스 셰이더든 픽셀 셰이더든 동일한 처리 유닛이 사용 되기 때문에 누가 누구의 발목을 잡는 상황이 일어나는 걱정은 하지 않아도 됩니다. GPU가 적절히 셰이더 로드 밸런싱을 조절하게 됩니다. 오히려 버텍스 세이더에서 계산해서 픽셀 세이더로 넘겨주는 정보가 너무 많으면 오히려 캐시 퍼포먼스나 대역폭 병목 문제등이 발생 할 수 있으므로 확인해가면서 적절한 밸런스를 잘 판단하고 사용해야 합니다.

이미지 출처 : http://www.mips.com/Customer_newsletter_0908/partnerShowcase.htm


VTF (Vertex Texture Fetch)

버텍스 세이더에서 텍스쳐를 샘플링 할 수 있는 기능인 VTF는 계륵같은 존재였습니다. 막상 기능은 존재하지만 버텍스 세이더에서 텍스쳐 샘플링을 담당하는 유닛이 너무 느렸기 때문에 마음놓고 사용하기가 번거로운 기능이였지요. 하지만 통합 셰이더 모델에서는 텍스쳐 샘플링 유닛이 따로 구분되어 있지 않고 사용하기 때문에 버텍스 셰이더에서 텍스쳐를 샘플링 하는 것이 부담스럽지가 않습니다.

이미지 출처 : http://developer.nvidia.com/sites/default/files/akamai/tools/files/PerfHUD6-UserGuide.pdf

 GPU Gems 3권에서는 이를 이용하여 인스턴싱을 구현하는 방법이 소개되고 있고, 마비노기2에서는 이를 이용하여 스키닝을 처리하고 있는 등 통합 셰이더 아키텍쳐의 수혜를 적극적으로 받고있는 능력 중 하나가 되었습니다.

이미지 출처 : 마비노기2 랜더링 기술, 전형규


셰이더 모델 버젼

전통적으로는 가급적이면 셰이더 모델 버젼을 낮은 것을 사용하는 것이 권장되어 왔습니다. 비록 Shader Model 3.0이 지원되는 하드웨어일지라도 가급적이면 Shader Model 1.1을 사용하거나 여력이 되지 않는다면 Shader Model 2.0을 사용하는 것이 권장되어 왔습니다. 하지만 이제는 가급적이면 높은 버젼의 모델을 사용하는 것이 권장되고 있습니다. 통합 셰이더 아키텍쳐는 높은 버젼에 적합하게 만들어져 있는 것이지요. DirectX 9에서는 4.0 사용이 불가하니 3.0을 사용하는 것이 좋겠지요. 특히 SM 3.0에서는 VPOS같은 훌륭한(?) 시멘틱을 사용 할 수 있어 연산도 줄일 수 있지요. 또한 최신버젼으 셰이더 컴파일러가 최적화가 잘 되어 있으므로 fxc 역시 최신 버젼을 사용하는 것이 좋습니다.


동적 분기 ( Dynamic branch)

전통적으로는 if 분기문을 사용하는 것은 죄악으로 여겨져 왔습니다만 현대의 GPU에서는 다이나믹 브랜치의 퍼포먼스가 훌륭하기때문에 if 분기를 적절히 잘 사용하면 오히려 퍼포먼스 향상을 꾀할 수가 있습니다. 예를들면 디렉셔널 라이팅의 연산이 필요 없는 그림자 영역은 연산을 건너 뛴다거나, N dot L 연산이 0 이하면 스페큘라 연산을 건너 뛴다거나 하는 식으로 사용을 할 수도 있겠지요.  

KILLZONE2는 정적 그림자 영역은 라이팅 연산을 건너뜁니다. 

 다만 주의할 점은, 픽셀이면 8x8=64픽셀, 버텍스면 연속적으로 64개가 분기 조건이 같아야 한다는 점입니다. 그렇지 않을 경우 캐시 퍼포먼스가 떨어져서 오히려 성능 저하를 초래할 수 있습니다. 앞서 예시를 든 N dot L 결과로 스페큘라를 건너뛰는 경우는 고주파 노말맵으로만 이루어진 장면인 경우는 부적합한 상황이 될 수도 있겠지요. 

이러한 동적분기의 성능을 활용하여 우버 셰이더로 활용 할 수도 있습니다. 여러 매터리얼을 한 셰이더로 몰아넣어 셰이더 설정 비용을 줄이는 것이죠. 다만 셰이더가 너무 길어지면 셰이더 캐시 문제가 발생 할 수도 있습니다.


64비트 텍스쳐 

현대의 GPU는 64bit 텍스쳐도 한 싸이클에 처리합니다. 메모리 용량 문제만 제외한다면 64bit HDR 텍스쳐를 사용 시 성능 저하를 걱정할 필요는 없는 것이지요. 하지만 무조건 그런 것은 아니고 fixed point 64bit는 여전히 두 싸이클이 걸립니다. floating point 64bit 텍스쳐를 사용해야 한 싸이클 내 처리가 되는 것이지요. 또한 필터링 역시 Point와 Bilinear인 경우엔 한 싸이클이지만 Trilinear인 경우는 두 싸이클이 소모됩니다. 


깊이 테스트 (Z-test)

현대의 그래픽 카드는 깊이 테스트의 성능이 매우 탁월합니다. 드라이버 자체적으로 H/W Depth stencil을 축소한 버퍼를 따로 가지고 있어 이를 통해 사전 테스트하여 테스트 비용을 줄이는 방법을 사용하기 때문이지요. nVIDIA는 "Hyper Z"라는 이름이고 ATi는 "Fine-grained Z"라는 이름으로 사용하고 있던데 결국 같은 기법인데 이름만 다르게 사용하는 것으로 보입니다.

이미지 출처 : Z-Buffer Optimizations, Patrick Cozzi

Early-Z 패스를 추가하여 Z-test를 활용할 수도 있습니다. 랜더링 패스 이전에 한 단계 추가하여 컬러 출력 없이 깊이만을 그려서 Z버퍼를 채워서 비싼 은면의 연산을 초기에 차단하는 것이지요. 오브젝트를 깊이순으로 정렬하여 앞의 오브젝트를 먼저 그려 뒤에 가려지는 오브젝트의 오버드로우를 방지할 수도 있습니다. 

이와 비슷한 컨셉으로 스카이박스(Sky Box)를 맨 마지막에 그리는 것도 생각해볼만합니다. 전통적으로는 스카이 박스를 Z-test를 비활성화하여 맨 처음 랜더링하고 그 뒤에 터레인이나 오브젝트등을 랜더링해왔습니다. 하늘이야 항상 배경으로 그려지는 것이고 굳이 쓸데없이 Z-test 처리하며 그릴 필요가 없던 것이지요. 하지만 이제는 Z-test의 처리가 고속으로 이루어지므로 이에 대한 큰 부담이 없으며 오히려 화면에 보이지 않게 될 픽셀을 일찌감치 걸러내어 픽셀 연산 비용을 아끼기 위해 하늘을 맨 마지막에 그리는 것도 좋은 방법이 될 수 있습니다.

이러한 고속의 Z-test를 위해서는 clear()가 필수적으로 따라줘야 합니다. 예전에는 화면의 모든 픽셀이 갱신되게 될 것이므로 화면의 clear를 건너 뛰는 방법도 사용해왔습니다. 하지만 현대의 하드웨어에서는 clear시 고속 처리를 위한 정보들이 초기화 되므로 씬 랜더 시작 전에 반드시 항상 clear 수행되어야 합니다. 풀 스크린 쿼드를 그리는 경우에는 해당되지 않습니다.


마치며

기존의 하드웨어와 현대의 하드웨어의 차이만을 초점으로 맞춰서 말씀드렸습니다만 좀 더 큰 시각으로 최적화에 대한 내용을 알고 싶으시면 제 블로그의 글을 참고 바랍니다. (블로그 홍보 죄송합니다 꾸벅) 근데 사실 좀 더 많은 PC를 커버하기 위해서는 이대로 하는 것에는 좀 무리가 따릅니다. 국내야 이 글이 대상으로 삼는 하드웨어가 대부분인 것으로 판단해도 무리가 없겠지만 해외 특히 동남아의 경우는 더 낮은 사양도 많을 것으로 판단됩니다. 이래도 고민 저래도 고민이죠. 최적화는 하면 할수록 골치가 아픈 것 같습니다. 모두들 스트레스 받지 마시고 멘탈 보호해가면서 개발하세요. 꾸벅

제 블로그의 예전 자료 :  http://ozlael.egloos.com/3671648  


반응형
,