Link

GPUμ—μ„œ μ½”λ“œ μ‹€ν–‰ν•˜κΈ°

이제 λ Œλ”λ§ 루프가 κ΅¬ν˜„λ˜μ—ˆμœΌλ‹ˆ, λ‹€μŒ λ‹¨κ³„λŠ” μ‹€μ œλ‘œ 무언가λ₯Ό κ·Έλ¦¬λŠ” κ²ƒμž…λ‹ˆλ‹€.

λ„ν˜•μ„ λ°”λ‘œ κ·Έλ¦¬λŠ” κ²ƒλ³΄λ‹€λŠ” μ»΄ν“¨νŠΈ 셰이더λ₯Ό μ‚¬μš©ν•΄ 이미지에 데이터λ₯Ό κΈ°λ‘ν•˜κ³ , 이λ₯Ό 화면에 ν‘œμ‹œν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. 이미지λ₯Ό κ³„μ‚°ν•˜λŠ” μš©λ„μ˜ μ»΄ν“¨νŠΈ μ…°μ΄λ”λŠ” μ—”μ§„μ˜ λ³΅μž‘ν•œ ν›„μ²˜λ¦¬μ—μ„œ ν”νžˆ λ³Ό 수 μžˆλŠ” μ‚¬μš© μ‚¬λ‘€μž…λ‹ˆλ‹€. λ˜ν•œ κ°œλ°œμžκ°€ λ ˆμ΄νŠΈλ ˆμ΄μ‹±μ΄λ‚˜ λ„ν˜•μ΄ μ•„λ‹Œ λ‹€λ₯Έ 것을 그릴 λ•Œμ—λ„ ν”νžˆ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

VkPipeline

Vulkanμ—μ„œλŠ” GPUμ—μ„œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ νŒŒμ΄ν”„λΌμΈμ„ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έλž˜ν”½μŠ€μ™€ μ»΄ν“¨νŠΈ 두 μ’…λ₯˜μ˜ νŒŒμ΄ν”„λΌμΈμ΄ μžˆμŠ΅λ‹ˆλ‹€. μ»΄ν“¨νŠΈ νŒŒμ΄ν”„λΌμΈμ€ κ½€ λ‹¨μˆœν•©λ‹ˆλ‹€. 셰이더 μ½”λ“œμ— 전달할 데이터, 데이터 바인딩에 μ‚¬μš©ν•  λ””μŠ€ν¬λ¦½ν„° λ ˆμ΄μ•„μ›ƒλ§Œ ν•„μš”ν•©λ‹ˆλ‹€. 반면 κ·Έλž˜ν”½μŠ€ νŒŒμ΄ν”„λΌμΈμ€ λΈ”λ Œλ”©, 깊이 ν…ŒμŠ€νŠΈ, λ„ν˜• 포맷 λ“± GPU의 λͺ¨λ“  κ³ μ • κΈ°λŠ₯ ν•˜λ“œμ›¨μ–΄λ₯Ό μœ„ν•œ μƒλ‹Ήν•œ μƒνƒœλ₯Ό ꡬ성해야 ν•©λ‹ˆλ‹€. κ·Έλž˜ν”½μŠ€ νŒŒμ΄ν”„λΌμΈμ€ λ‹€μŒ μž₯μ—μ„œ λ‹€λ£° μ˜ˆμ •μž…λ‹ˆλ‹€.

두 νŒŒμ΄ν”„λΌμΈμ€ 셰이더 λͺ¨λ“ˆκ³Ό λ ˆμ΄μ•„μ›ƒμ„ λ™μΌν•œ λ°©μ‹μœΌλ‘œ μƒμ„±ν•˜λ©°, 이λ₯Ό κ³΅μœ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

VkShaderModule

VkShaderModule은 μ „μ²˜λ¦¬λœ 셰이더 νŒŒμΌμž…λ‹ˆλ‹€. 컴파일된 SpirV νŒŒμΌμ„ 톡해 생성할 수 μžˆμŠ΅λ‹ˆλ‹€. OpenGLκ³Ό 달리 Vulkanμ—μ„œλŠ” GLSL 셰이더 μ½”λ“œλ₯Ό 직접 전달할 수 μ—†μŠ΅λ‹ˆλ‹€. 이λ₯Ό κ°€λŠ₯μΌ€ν•˜λŠ” ν™•μž₯도 μ‘΄μž¬ν•˜κΈ°λŠ” ν•˜μ§€λ§Œ ν‘œμ€€μ€ μ•„λ‹™λ‹ˆλ‹€. λ”°λΌμ„œ GLSL νŒŒμΌμ„ 미리 μ»΄νŒŒμΌν•˜μ—¬ SpirV파일둜 λ§Œλ“€μ–΄μ•Ό ν•©λ‹ˆλ‹€. Vulkan SDKμ—λŠ” glslangValidatorκ°€ ν¬ν•¨λ˜μ–΄ 있으며, 이λ₯Ό μ‚¬μš©ν•΄ GLSL을 SpirV둜 μ»΄νŒŒμΌν•  수 μžˆμŠ΅λ‹ˆλ‹€. vkguide μ½”λ“œμ—μ„œ 메인 ν”„λ‘œμ νŠΈμ˜ CMakeLists.txtλ₯Ό 보면 /shaders/ ν΄λ”μ—μ„œ λͺ¨λ“  셰이더 νŒŒμΌμ„ 가져와 컴파일 νƒ€κ²ŸμœΌλ‘œ μΆ”κ°€ν•˜λŠ” μ»€μŠ€ν…€ λͺ…령을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

이 νŠœν† λ¦¬μ–Όμ—μ„œ 셰이더λ₯Ό μ»΄νŒŒμΌν•˜λ €λ©΄ 셰이더 νƒ€κ²Ÿμ„ λΉŒλ“œν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λŠ” μž‘μ—…μ€ λͺ¨λ“  셰이더 νŒŒμΌμ„ 컴파일 ν•©λ‹ˆλ‹€. CMake의 ν•œκ³„λ‘œ 인해 μƒˆλ‘œμš΄ 셰이더 νŒŒμΌμ„ μΆ”κ°€ν•˜λ©΄ CMake μ„€μ • 단계λ₯Ό μž¬μ‹€ν–‰ν•˜μ—¬ CMakeκ°€ ν•΄λ‹Ή 셰이더 νŒŒμΌμ„ κ°€μ Έμ˜¬ 수 μžˆλ„λ‘ ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ‹€λ₯Έ μ—”μ§„μ—μ„œλŠ” 셰이더 μ»΄νŒŒμΌμ„ μžλ™ν™”ν•˜κΈ° μœ„ν•΄ 엔진이 μ‹œμž‘λ  λ•Œ μžλ™μœΌλ‘œ μ‹€ν–‰κ°€λŠ₯ν•œ 파일 ν˜Ήμ€ .bat, ν˜Ήμ€ λ‹€λ₯Έ 슀크립트 νŒŒμΌμ„ μ‚¬μš©ν•˜λŠ” 것이 μΌλ°˜μ μž…λ‹ˆλ‹€.

Vulkan μ…°μ΄λ”μ˜ μ½”λ“œλ‘œ GLSL λŒ€μ‹  HLSL을 μ‚¬μš©ν•˜λŠ” 것도 κ°€λŠ₯ν•©λ‹ˆλ‹€. λ§Žμ€ ν”„λ‘œμ νŠΈκ°€ μ΄λŸ¬ν•œ 방식을 μ„ ν˜Έν•˜μ§€λ§Œ, 이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ” 닀루지 μ•ŠμŠ΅λ‹ˆλ‹€. λ§Œμ•½ HLSL을 Vulkanμ—μ„œ μ‚¬μš©ν•˜λŠ” 방법에 λŒ€ν•΄ 더 μ•Œκ³  μ‹Άλ‹€λ©΄, HLSL In Vulkanλ₯Ό μ°Έκ³ ν•΄λ³΄μ„Έμš”.

Descriptor sets

셰이더에 데이터λ₯Ό μ „λ‹¬ν•˜λ €λ©΄ 바인딩을 μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. Vulkanμ—μ„œλŠ” μ΄λ―Έμ§€λ‚˜ 버퍼 같은 객체λ₯Ό μ…°μ΄λ”λ‘œ λ°”μΈλ”©ν•˜κΈ° μœ„ν•΄ λ””μŠ€ν¬λ¦½ν„° μ…‹(DescriptorSet)이 ν•„μš”ν•©λ‹ˆλ‹€. ν•˜λ‚˜μ˜ λ””μŠ€ν¬λ¦½ν„°λŠ” μžμ›(λ²„νΌλ‚˜ 이미지 λ“±)에 λŒ€ν•œ ν•Έλ“€ ν˜Ήμ€ 포인터라고 μƒκ°ν•˜λ©΄ λ©λ‹ˆλ‹€. μ΄λŠ” λ²„νΌμ˜ ν¬κΈ°λ‚˜ μ΄λ―Έμ§€μ˜ μƒ˜ν”ŒλŸ¬(sampler) νƒ€μž…κ³Ό 같은 정보도 λ‹΄κ³  μžˆμŠ΅λ‹ˆλ‹€. VkDescriptorSet은 μ΄λŸ¬ν•œ 포인터듀을 묢어놓은 μ§‘ν•©μž…λ‹ˆλ‹€. Vulkan은 μ…°μ΄λ”λ‘œ κ°œλ³„ λ¦¬μ†ŒμŠ€λ₯Ό 직접 λ°”μΈλ”©ν•˜λŠ” 것을 ν—ˆμš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ””μŠ€ν¬λ¦½ν„° 셋은 VkDescriptorSetLayout을 기반으둜 VkDescriptorPoolμ—μ„œ ν• λ‹Ήλ©λ‹ˆλ‹€. VkDescriptorSetLayout은 λ””μŠ€ν¬λ¦½ν„° 셋이 μ–΄λ–€ μžμ›μ„ λ‹΄λŠ”μ§€λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.(예 : 이미지 2개) λ””μŠ€ν¬λ¦½ν„° 셋이 ν• λ‹Ήλ˜λ©΄ vkUpdateDescriptorSetsλ₯Ό μ‚¬μš©ν•΄ 데이터λ₯Ό κ°±μ‹  ν•  수 있으며, μ΄λ•Œ VkWriteDescriptorSet의 배열을 λ„˜κΉλ‹ˆλ‹€. λ””μŠ€ν¬λ¦½ν„° 셋을 ꡬ성이 μ™„λ£Œλ˜λ©΄ νŒŒμ΄ν”„λΌμΈμ— VkBindDescriptorSetsλ₯Ό μ‚¬μš©ν•΄ 바인딩할 수 있으며 μ…°μ΄λ”μ—μ„œ 데이터λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이 μ±•ν„°μ—μ„œλŠ” λ Œλ”λ§ν•  이미지λ₯Ό μ»΄ν“¨νŠΈ 셰이더에 μ—°κ²°ν•˜μ—¬ 셰이더가 ν•΄λ‹Ή 이미지에 직접 μ“Έ 수 μžˆλ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€. νŠœν† λ¦¬μ–Ό μ „λ°˜μ— 걸쳐 μ΄λŸ¬ν•œ 흐름을 λ‹¨μˆœν™”ν•˜λŠ” 좔상화λ₯Ό μž‘μ„±ν•  κ²ƒμž…λ‹ˆλ‹€. νŒŒμ΄ν”„λΌμΈμ€ μ—¬λŸ¬ 개의 λ””μŠ€ν¬λ¦½ν„° 셋을 바인딩할 수 μžˆλŠ” μŠ¬λ‘―μ„ κ°–μŠ΅λ‹ˆλ‹€. Vulkan λͺ…μ„Έμ—μ„œλŠ” 적어도 4개의 λ””μŠ€ν¬λ¦½ν„° 셋을 보μž₯ν•˜λ©°, 이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ” 이 수λ₯Ό κΈ°μ€€μœΌλ‘œ μ‚Όκ² μŠ΅λ‹ˆλ‹€. GPU μ œμ‘°μ‚¬μ— λ”°λ₯΄λ©΄ 각 λ””μŠ€ν¬λ¦½ν„° μ…‹ μŠ¬λ‘―μ€ μΌμ •ν•œ λΉ„μš©μ΄ λ“€ 수 μžˆμœΌλ―€λ‘œ, κ°€λŠ₯ν•œ ν•œ 적게 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. μ΄ν›„μ˜ νŠœν† λ¦¬μ–Όμ—μ„œλŠ” λ””μŠ€ν¬λ¦½ν„° μ…‹ 0λ²ˆμ—λŠ” μœ λ‹ˆνΌ 버퍼와 일뢀 특수 ν…μŠ€μ³λ₯Ό ν¬ν•¨ν•˜λŠ” μ „μ—­ 씬 데이터λ₯Ό 바인딩할 것이고, λ””μŠ€ν¬λ¦½ν„° μ…‹ 1λ²ˆμ—λŠ” κ°œλ³„ 였브젝트 데이터λ₯Ό λ°”μΈλ”©ν•˜λŠ” μš©λ„λ‘œ μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€.

ν‘Έμ‹œ μƒμˆ˜

셰이더에 버퍼와 이미지λ₯Ό μ „λ‹¬ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” λ””μŠ€ν¬λ¦½ν„° μ…‹ 외에도, Vulkan은 μ»€λ§¨λ“œ 버퍼λ₯Ό κΈ°λ‘ν•˜λŠ” κ³Όμ • 쀑에 직접 λͺ‡ λ°”μ΄νŠΈμ˜ 데이터λ₯Ό μ“Έ 수 μžˆλŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. μ΄λŠ” ν‘Έμ‹œ μƒμˆ˜(Push Constants)라 뢈리며, Vulkanμ—λ§Œ μ‘΄μž¬ν•˜λŠ” λ…νŠΉν•œ λ°©μ‹μž…λ‹ˆλ‹€. 셰이더에 직접 λ°”μΈλ”©λ˜λŠ” μ•„μ£Ό μ†ŒλŸ‰μ˜ λ©”λͺ¨λ¦¬ 곡간을 μ˜ˆμ•½ν•˜λŠ” ꡬ쑰둜, μ†λ„λŠ” λΉ λ₯΄μ§€λ§Œ 맀우 μ œν•œλœ μš©λŸ‰λ§Œ μ‚¬μš©ν•  수 있으며, λͺ…령을 μΈμ½”λ”©ν•˜λŠ” μ‹œμ μ— μž‘μ„±λ˜μ–΄μ•Όλ§Œ ν•©λ‹ˆλ‹€. 주둜 객체가 λ§Žμ€ 데이터λ₯Ό μš”κ΅¬ν•˜μ§€μ•Šμ„ λ•Œ, 객체별 데이터 전달에 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. GPU μ œμ‘°μ‚¬μ— λ”°λ₯΄λ©΄ 이상적인 μš©λ‘€λŠ” μ…°μ΄λ”λ‘œ 인덱슀λ₯Ό 전달해 더 큰 데이터 버퍼에 μ ‘κ·Όν•  λ•Œ μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒ

μ…°μ΄λ”λŠ” λ‹€μ–‘ν•œ μž…λ ₯이 ν•„μš”ν•˜λ©°, μ΄λŸ¬ν•œ μž…λ ₯ κ΅¬μ‘°λŠ” VkPipelineLayout이 μ •μ˜ν•©λ‹ˆλ‹€. νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ„ μƒμ„±ν•˜κΈ° μœ„ν•΄ VkDescriptorSetLayoutκ³Ό ν‘Έμ‹œ μƒμˆ˜μ— μ‚¬μš©λ˜λŠ” PushConstantRange μ •μ˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€. κ·Έλž˜ν”½μŠ€ νŒŒμ΄ν”„λΌμΈκ³Ό μ»΄ν“¨νŠΈ νŒŒμ΄ν”„λΌμΈμ˜ νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ€ 같은 λ°©μ‹μœΌλ‘œ λ ˆμ΄μ•„μ›ƒμ„ μƒμ„±ν•˜λ©°, μ‹€μ œ νŒŒμ΄ν”„λΌμΈμ΄ μƒμ„±λ˜κΈ° 전에 λ¨Όμ € μƒμ„±λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

μ»΄ν“¨νŠΈ νŒŒμ΄ν”„λΌμΈ

μ»΄ν“¨νŠΈ νŒŒμ΄ν”„λΌμΈμ„ κ΅¬μ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” νŒŒμ΄ν”„λΌμΈ λ ˆμ΄μ•„μ›ƒμ„ λ¨Όμ € μƒμ„±ν•˜κ³ , 셰이더 λͺ¨λ“ˆμ„ μ—°κ²°ν•΄μ•Ό ν•©λ‹ˆλ‹€. νŒŒμ΄ν”„λΌμΈμ΄ λ§Œλ“€μ–΄μ§€λ©΄ μ»΄ν“¨νŠΈ 셰이더λ₯Ό VkCmdBindPipelineλ₯Ό ν˜ΈμΆœν•΄ λ°”μΈλ”©ν•˜κ³  VkCmdDispatchλ₯Ό ν˜ΈμΆœν•΄ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ»΄ν“¨νŠΈ μ…°μ΄λ”λŠ” νŠΉμˆ˜ν•œ ν”„λ‘œκ·Έλž˜λ° λͺ¨λΈμ„ λ”°λ¦…λ‹ˆλ‹€. VkCmdDispatch 호좜 μ‹œ, Vulkanμ—κ²Œ X, Y, Z ν˜•νƒœλ‘œ μž‘μ—… 그룹의 수λ₯Ό μ§€μ •ν•©λ‹ˆλ‹€. 이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ” 이미지λ₯Ό μ²˜λ¦¬ν•  κ²ƒμ΄λ―€λ‘œ 2개의 μ°¨μ›λ§Œ μ‚¬μš©ν•©λ‹ˆλ‹€. μ΄λ―Έμ§€μ˜ 각 ν”½μ…€ κ·Έλ£Ήλ§ˆλ‹€ ν•˜λ‚˜μ˜ μž‘μ—… 그룹이 μ‹€ν–‰λ˜λ„λ‘ μ„€μ •ν•©λ‹ˆλ‹€.

셰이더 λ‚΄λΆ€μ—μ„œλŠ” λ‹€μŒκ³Ό 같은 μ½”λ“œκ°€ λ³΄μž…λ‹ˆλ‹€. layout (local_size_x = 16, local_size_y = 16) in;. μ΄λŠ” 각 μž‘μ—… 그룹이 16 * 16 μ“°λ ˆλ“œλ‘œ ꡬ성됨을 μ˜λ―Έν•©λ‹ˆλ‹€. 즉, vkCmdDispatch둜 ν•œ μž‘μ—… 그룹이 호좜되면, 16 * 16개의 μ“°λ ˆλ“œκ°€ λ³‘λ ¬λ‘œ μ‹€ν–‰λ˜μ–΄ 16 * 16 ν”½μ…€ μ‚¬κ°ν˜•μ„ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

셰이더 μ½”λ“œμ—μ„œλŠ” gl_LocalInvocationID λ³€μˆ˜λ₯Ό 톡해 ν˜„μž¬ μ“°λ ˆλ“œ μΈλ±μŠ€μ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. gl_GlobalInvocationID와 gl_WorkGroupID도 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ³€μˆ˜λ“€μ„ μ‚¬μš©ν•˜μ—¬ 각 μ“°λ ˆλ“œκ°€ μ–΄λ–€ 픽셀을 μ²˜λ¦¬ν•  μ§€ μ •ν™•νžˆ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

이제 μ‹€μ œ μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©° μ–΄λ–»κ²Œ 셰이더가 μž‘λ™ν•˜λŠ” μ§€ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

Next: Vulkan Shaders - Code