개발 노트 Vulkan Shader (2) 2017/01/21 07:28 by cagetu

셰이더를 외부에서 작성하고 나면, 프로그램에서 셰이더에 선언된 버텍스 정보들과 변수 정보들을 맞춰서 바인딩해줘야 한다. 이걸 프로그래머가 셰이더를 작성하고, 프로그램에서 어떤 변수를 바인딩할 것이다! 기억했다가 선언하는 방법이 있기는 하지만, 사실 셰이더가 많아지고 수정이 잦아지면 일일이 이렇게 해주기가 어렵기 때문에, 자동으로 셰이더에 작성된 정보를 알아와서 채울 수 있도록 해야 한다.

OpenGL(ES)의 경우에는 셰이더 정보를 제공해주는 함수가 있었기 때문에 조회를 통해서 유연하게 채울 수 있었다.

       _ASSERT(m_Program != 0);
       // 셰이더 링크 (이제 조회가 가능하다.)
       glLinkProgram(m_Program);
       GLint Linked;
       glGetProgramiv(m_Program, GL_LINK_STATUS, &Linked);
       if (!Linked)
       {
              return false;
       }
       // Setup Vertex Format
       {
              attributes[i] = glGetAttribLocation(program, s_FVFPreSet[i].attrib);
       }
       
       // Setup Uniforms
       {
              int uniformCount = 0;
              int uniformMaxLength = 0;
              glGetProgramiv(m_Program, GL_ACTIVE_UNIFORMS, &uniformCount);
              glGetProgramiv(m_Program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformMaxLength);

Vulkan은 spirv에 대한 어떤 내부 정보도 주는 것이 없다. 그래서 셰이더 빌드 후 로딩까지는 했는데, 그 이후 이걸 어떻게 처리해야 할지 고민에 빠졌다. 

언리얼 엔진에서는 어떻게 처리할지 궁금해서 코드를 좀 찾아보니, 언리얼 엔진의 경우는 별도의 ShaderCompiler가 존재하기 때문에, Shader를 컴파일하고 컴파일러가 BindingTable이라는 테이블을 프로그램에 넘겨주도록 되어 있어서 정보를 얻어오고 있었다. 멋지지만, 내가 이걸 따라갈 수 있는 길은 아니다!

자료를 찾아보고 있던 중에, Using Spir-v with spirv-cross 라는 자료를 보게 되었는데, 여기에 내가 원하는 것이 정확하게 있었다!

spirv 를 분석해서, 내부 reflection 정보를 만들어주는 것이다. 사실 glsl를 컴파일하면서 spirv에 대한 내부 정보를 만들어주는 언리얼 방식과 비교하면 컴파일된 spirv를 파싱해서 내부 정보를 얻어오는 방식이 비효율적으로 생각될 수도 있다. 이건 나중에 캐쉬로 사용하면 되지 않을까? 라는 생각이 들기도 하다.

일단 원하는 것을 손에 넣었으니, 이걸 이용해서 셰이더 기반 시스템을 구성할 수 있을 듯 하다. 그럼 코드 붙이러가야지!!


개발 노트 Vulkan Shader 2017/01/12 02:04 by cagetu

Vulkan은 SPIR-V 라는 셰이더 포멧을 사용한다. Spir-v에 대해서는 아직 공부를 하는 중이라 자세한 설명을 하지는 않는다.

SPIR-V는 빌드된 바이너리 형태인데, 일반적으로 glsl로 코드를 작성해서 glslangvalidator 라는 변환 툴을 이용해서 spir-v 파일로 변환한다.

glslangvalidator -V -H default.vert -o default.vert.spv > default.vert.spv.txt
glslangvalidator -V -H default.frag -o default.frag.spv > default.frag.spv.txt

재미있는 것은 지금부터인데, Cross Platform Shaders 처럼 유니티와 언리얼 엔진과 같이 크로스 플랫폼으로 셰이더를 처리하는 녀석들에서는 과연 Vulkan Shader를 어떻게 처리하는가? 이다.

언리얼을 보면 Material Editor에서 노드 그래프를 연결해서 셰이더를 만든다. 그럼 HLSL 코드로 셰이더가 만들어진다. 그리고 이 HLSL 코드를 가지고 플랫폼에 따라 glsl 코드로 변환하여 사용하게 된다. (hlsl2glsl, glsloptimizer)

그럼 Vulkan은 어떻게 될까? HLSL -> GLSL -> (glslangvalidator) -> spirv 코드로 변환되게 된다. 결국 한번의 코드를 이용해서 최종 플랫폼의 셰이더 코드를 자동으로 만들어내는 것이다.

물론 마지막에 기기에 들어갈 때에는 spirv 셰이더 코드만 있으면 된다. 일반적으로 gles와 vulkan을 동시에 사용하기도 하니까, gles shader 코드와 vulkan shader 코드 (spv)가 같이 포함되어 있되록 할 것 같다.

셰이더컴파일 시스템에 대해서는 언리얼이나 유니티 모두 굉장히 시스템이 잘 만들어져 있고 이제는 거의 완성형에 가깝다는 생각이 든다. (이게 여기까지 오는데 엄청난 시간이 걸렸다는 것을 많은 사람들이 모른다.) 공부가 참 많이 된다. 기초가 되는 mesa 라이브러리를 공부해서 한 번의 코드로 다른 플랫폼 코드를 뽑아내면 좋을 것 같은데, 만만치 않다 ;; 나중에 관심 있는 분들에게 도움을 요청해서 한번 같이 해보면 좋을 것 같다.

일단 손으로 glsl 4.5 버전을 작성하고, 그걸로 spv를 만들어내는 것으로 출발해보자!


하루 하루 2016, and 2017 2016/12/30 11:52 by cagetu

2015, and 2016부터 한 해 글들을 쭈욱 훑어보았다. 저 글을 쓸 때와 나는 지금 다른 자리에 앉아 있다. 

[2016]
올 해는 내 인생에서 가장 급격하게 많은 것들이 달라진 한 해였던 것 같다. 연초부터 프로젝트가 깨지고, 회사를 나오고, 수 많은 회사를 다니며 면접을 보면서 그 동안 개발자로의 내 위치가 어느 정도인지, 내가 얼마나 부족한 사람인지에 대한 현실을 직시하게 되었다. 아버지가 아프시고 병원에서 병 간호를 하면서 "내가 할 수 있는 것은 그냥 기다리는 것 뿐이구나!" 라는 사실을 깨우치면서 그 동안 게임을 만들어야 한다는 조급함. 나이를 먹어가면서 무엇인가를 해야만 한다는 조급함. 어떤 자리에 꼭 올라가야 한다는 조급함에 대해서 많은 것을 내려놓을 수 있게 되었다. 가족이 같이 교회를 나가게 된 것도 꽤 큰 이슈이다. 지금은 다행히 주변에 너무 많은 도움으로 정말 좋은 회사에 좋은 자리에서 일할 수 있는 기회가 마련되었고, 정말 가진 것 하나없이 시작해서 10년만에 (어마무시한 대출을 받았지만) 내 집 마련의 꿈을 이룰 수 있게 되었고, 아버지도 회복되셔서 매주 뵐 수 있게 되었다.

연초에 나는 "뭔가 나도 게임을 만들어야 한다는 조급함", "나는 왜 게임을 만들 수 없을까? 라는 패배감 및 자괴감", "내가 과연 프로그래머로 실력이 있을까? 라는 불안감"이 가득했다. 지금은 여러 일을 겪으면서 많이 단단해지고, 길게 볼 수 있는 여유가 생겼으며 내가 할 수 있는 것에 집중하려고 한다.

아마도 40으로 넘어가는 길에서 앞만 보고 달리던 나에게 그 동안의 나를 돌아볼 수 있는 큰 쉼표를 주면서 앞으로를 생각해볼 수 있는 큰 전환점이 되는 한 해가 되었다. 

[2017]

* "다시 초심으로 돌아가보자!"
당분간 게임을 만든다는 욕심을 버리고, 회사 일에 집중하자!

* "인생을 즐기자!"
: 인생을 즐기자! 일도 중요하고, 개발자로의 욕심도 중요하지만, 결국 나와 주변 사람이 모두 즐거워야 하니까! 책도 보고, 게임도 하고, 여행도 다니고, 사람들도 만나고 다니고.. 사진도 좀 찍고... 여유를 가지자!

* "발표하고 싶다!"
: NDC, KGC에 발표할 만한 주제 하나 정도는 공부하자! 집에서 취미 엔진에 집중하면 될 듯!

올 해 잘 견뎌준 나에게, 그리고 우리 가족에게 너무 고맙고 사랑한다!!! 이제 진짜 웃기만 할 수 있는 2017년이 되어보자!!!

새해 복 많이 받아~

개발 노트 [Vulkan일지] 투 두~ 2016/12/29 01:28 by cagetu

1. CommandBuffer 다루는 법 심화 학습
2. CommandBuffer 동기화를 위한 Fence 구현 및 사용 방법 익히기
3. Shader 사용을 위한 SPIR-V 내용 파악
4. Uniform Buffer 익히기
5. Vertex Attribute 익히기
6. Pipeline 익히기

휴~ 꽤 많이 했는데, 아직도 할 게 많네... 쩝...;;;

개발 노트 [Vulkan일지] StagingBuffer 2016/12/27 02:09 by cagetu

Vulkan에서 Buffer를 관리할 때, 최적의 성능을 내기 위해서 디바이스 메모리에 버퍼를 채워야 하고, cpu에서 접근해서 데이터를 사용할 수 있어야 한다. Vulkan에서는 버퍼를 생성할 때 MemoryType를 주어서 어떤 목적으로 사용되는지를 정하게 된다.

VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : GPU Write
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : CPU Write
VK_MEMORY_PROPERTY_HOST_CACHED_BIT : CPU Read/Write

최상에 상태를 위해서, CPU에서 데이터를 채운 버퍼를 GPU에 복사하여 실제 렌더링할 때에는 GPU에 채워진 버퍼만을 사용할 수 있도록 해줘야 하는데, 이를 위해 StagingBuffer 가 필요하다. 즉, CPU에서 버퍼 데이터를 읽고 쓰는 데이터 버퍼이고, StaingBuffer에 버퍼 데이터를 채우고, 이를 DeviceBuffer에 복사해주면 된다.

코드로 보면, 

VertexBuffer::Create
: Device Buffer 생성

VertexBuffer::Lock()
: StagingBuffer 생성 후 StagingBuffer에 데이터 기록

VertexBuffer::Unlock()
: CmdCopyBuffer(StaingBuffer, DeviceBuffer);

이런 형태가 된다.

아마 이전 세대 API들도 드라이버 내부에서는 이렇게 작동하고 있지 않았을까? 생각이 된다. 자세한 설명은 튜토리얼을 읽어보면 좋을 듯 하다..

1 2 3 4 5 6 7 8 9 10 다음



메모장

내가 먼 훗날에 이 글들을 보았을 때, 좋은 추억이 될 수 있기를...

나를 위해... 나에게 쓰는...

msn: cagetu@hotmail.com
mail: cagetu79@gmail.com
twitter: twitter.com/cagetu
facebook: facebook.com/cagetu