Link

Vulkan 핡심 객체와 ν™œμš© 방법

  • VkInstance : Vulkan λ¬Έλ§₯(context)으둜, GPU λ“œλΌμ΄λ²„μ— μ ‘κ·Όν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
  • VkPhysicalDevice : GPU κ·Έ μžμ²΄μž…λ‹ˆλ‹€. κΈ°λŠ₯, ν˜Έν™˜μ„±, λ©”λͺ¨λ¦¬ 크기, κ·Έ μ΄μ™Έμ˜ μ—¬λŸ¬ GPU μ„ΈλΆ€ 사항을 μ‘°νšŒν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
  • VkDevice : GPUλ₯Ό μ‹€μ œλ‘œ μ‹€ν–‰ν•˜λŠ”λ° μ‚¬μš©ν•  β€œλ…Όλ¦¬μ μΈβ€ GPU λ¬Έλ§₯μž…λ‹ˆλ‹€.
  • VkBuffer : GPUκ°€ μ ‘κ·Όν•  수 μžˆλŠ” λ©”λͺ¨λ¦¬ λ¬ΆμŒμž…λ‹ˆλ‹€.
  • VkImage : 읽고 μ“Έ 수 μžˆλŠ” ν…μŠ€μ³μž…λ‹ˆλ‹€.
  • VkPipeline : 그리기에 ν•„μš”ν•œ GPU μƒνƒœλ₯Ό λ‹΄κ³  μžˆλŠ” κ°œκ²£μž…λ‹ˆλ‹€.(ex. 셰이더, λž˜μŠ€ν„°ν™”, 깊이 μ„€μ •)
  • VkRenderPass : λ Œλ”λ§ν•  μ΄λ―Έμ§€μ˜ 정보λ₯Ό λ‹΄κ³ μžˆμŠ΅λ‹ˆλ‹€. λͺ¨λ“  그리기 λͺ…령은 λ Œλ”νŒ¨μŠ€(renderpass) λ‚΄λΆ€μ—μ„œ μˆ˜ν–‰λ©λ‹ˆλ‹€.
  • VkFrameBuffer : λ Œλ”νŒ¨μŠ€μ˜ νƒ€κ²Ÿ 이미지λ₯Ό λ‹΄μŠ΅λ‹ˆλ‹€. ꡬ버전 vkguideμ—μ„œλ§Œ μ‚¬μš©λ©λ‹ˆλ‹€.
  • VkCommandBuffer : GPU λͺ…령을 κΈ°λ‘ν•©λ‹ˆλ‹€. (λ“œλΌμ΄λ²„κ°€ μ•„λ‹Œ)GPUμ—μ„œ μ‹€ν–‰λ˜λŠ” λͺ¨λ“  λͺ…령은 VkCommandBuffer에 κΈ°λ‘λ©λ‹ˆλ‹€.
  • VkQueue : λͺ…λ Ήμ˜ μ‹€ν–‰ μ§€μ μž…λ‹ˆλ‹€. GPUλŠ” λ‹€λ₯Έ μ†μ„±μ˜ 큐 λͺ¨μŒμ„ κ°–μŠ΅λ‹ˆλ‹€. μ–΄λ–€ νλŠ” κ·Έλž˜ν”½ λͺ…λ Ήλ§Œ λ°›μœΌλ©°, λ‹€λ₯Έ μ–΄λ–€ νλŠ” λ©”λͺ¨λ¦¬ λͺ…λ Ήλ§Œ λ°›μŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ μ»€λ§¨λ“œ λ²„νΌλŠ” 큐에 μ œμΆœλ˜μ–΄ μ‹€ν–‰λ˜λŠ”λ°, μ΄λŠ” GPU둜 λ Œλ”λ§ λͺ…령을 μ „λ‹¬ν•©λ‹ˆλ‹€.
  • VkDescriptorSet : VkBufferμžμ›μ΄λ‚˜ VkImageν…μŠ€μ³μ™€ 같은 데이터λ₯Ό μ…°μ΄λ”λ‘œ μ—°κ²°ν•˜λŠ” 데 ν•„μš”ν•œ 정보λ₯Ό λ‹΄μŠ΅λ‹ˆλ‹€. GPUμ—μ„œμ˜ 포인터라고 생각해도 μ’‹μŠ΅λ‹ˆλ‹€.
  • VkSwapchainKHR : ν™”λ©΄μ˜ 이미지λ₯Ό λ‹΄λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€. λ Œλ”λ§ ν•œ 것을 μ‹œκ°μ μœΌλ‘œ λ³Ό 수 μžˆλŠ” 창에 λ Œλ”λ§ ν•  수 있게 ν•©λ‹ˆλ‹€. KHRμ ‘λ―Έμ‚¬λŠ” Vulkan ν™•μž₯의 κ°μ²΄μž„μ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€. μ—¬κΈ°μ„œλŠ” Vk_KHR_swapchainμž…λ‹ˆλ‹€.
  • VkSemaphore : GPUκ°„ λͺ…λ Ή 동기화에 μ‚¬μš©λ©λ‹ˆλ‹€. μ—¬λŸ¬ μ»€λ§¨λ“œ 버퍼가 μžˆμ„ λ•Œ ν•˜λ‚˜κ°€ μ‹€ν–‰λ˜κ³  κ·Έ λ‹€μŒ 것이 μ‹€ν–‰ν•˜λŠ” 것을 보μž₯ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€.
  • VkFence : GPU와 CPUλ₯Ό λ™κΈ°ν™”ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. μ»€λ§¨λ“œ 버퍼가 GPUμ—μ„œ 싀행이 μ™„λ£Œλ˜μ—ˆλŠ”μ§€λ₯Ό 확인할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

κ³ μˆ˜μ€€ Vulkan μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 흐름

μ—”μ§„ μ΄ˆκΈ°ν™”

λ¨Όμ €, λͺ¨λ“  것을 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€. Vulkan을 μ΄ˆκΈ°ν™”ν•˜κΈ° μœ„ν•΄μ„  VkInstanceλ₯Ό μƒμ„±ν•˜λŠ” 것뢀터 μ‹œμž‘ν•©λ‹ˆλ‹€. VkInstanceλ‘œλΆ€ν„° μ»΄ν“¨ν„°μ˜ μ‚¬μš© κ°€λŠ₯ν•œ VkPhysicalDevice ν•Έλ“€μ˜ λͺ©λ‘μ„ μ‘°νšŒν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, 컴퓨터가 μ „μš© GPU와 톡합 κ·Έλž˜ν”½μŠ€λ₯Ό κ°–λŠ”λ‹€λ©΄, 각각 VkPhysicalDeviceκ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€. VkPhysicalDeviceλ‘œλΆ€ν„° μ‚¬μš© κ°€λŠ₯ν•œ κΈ°λŠ₯κ³Ό μ œν•œ 사항을 μ‘°νšŒν•œ ν›„ VkDeviceλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. VkDeviceμ—μ„œλŠ” μ»€λ§¨λ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ ν•΄μ£ΌλŠ” VkQueue 핸듀을 얻을 수 μžˆμŠ΅λ‹ˆλ‹€. κ·Έ ν›„, VkSwapchainKHR을 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€. VkQueue ν•Έλ“€λ‘œ μ»€λ§¨λ“œ 버퍼λ₯Ό ν• λ‹Ήν•  수 μžˆλŠ” 객체인 VkCommandPool을 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

에셋 μ΄ˆκΈ°ν™”

핡심 ꡬ쑰체듀이 μ΄ˆκΈ°ν™” 되고 λ‚˜λ©΄, λ Œλ”λ§ν•  λŒ€μƒμ— ν•„μš”ν•œ μžμ›μ„ μ΄ˆκΈ°ν™”ν•΄μ•Όν•©λ‹ˆλ‹€. λ¨Έν…Œλ¦¬μ–Ό(Material)이 λ‘œλ”©λ˜λ©΄ VkPipeline의 집합을 μƒμ„±ν•©λ‹ˆλ‹€. μ΄λŠ” λ¨Έν…Œλ¦¬μ–Όμ„ λ Œλ”λ§ν•˜λŠ” 데 ν•„μš”ν•œ 셰이더 μ‘°ν•©κ³Ό νŒŒλΌλ―Έν„°λ₯Ό λ‹΄μŠ΅λ‹ˆλ‹€. λ©”μ‹œ(Mesh)λ₯Ό λ Œλ”λ§ ν•˜κΈ° μœ„ν•΄ λ©”μ‹œμ˜ 정점 데이터λ₯Ό VkBuffer μžμ›μœΌλ‘œ, 그리고 λ©”μ‹œμ˜ ν…μŠ€μ³λ₯Ό VkImage μžμ›μœΌλ‘œ μ—…λ‘œλ“œν•΄μ•Ό ν•©λ‹ˆλ‹€. 이 λ•Œ 이미지가 β€œμ½μ„ 수 μžˆλŠ”β€ λ ˆμ΄μ•„μ›ƒμž„μ„ λΆ„λͺ…νžˆ ν•΄μ•Όν•©λ‹ˆλ‹€. λ˜ν•œ μ£Όμš” λ Œλ”λ§ νŒ¨μŠ€μ— λŒ€ν•΄ VkRenderPass 객체λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ£Όμš” λ Œλ”λ§ν•˜λŠ”λ° μ‚¬μš©ν•  VkRenderPassκ°€ μžˆμ„ 수 있고, 그림자 νŒ¨μŠ€μ— μ‚¬μš©ν•  VkRenderPassκ°€ μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 특히 νŒŒμ΄ν”„λΌμΈ 생성은 κ½€ λΉ„μ‹Ό 편이기 λ•Œλ¬Έμ—, μ‹€μ œ μ—”μ§„μ—μ„œλŠ” μ΄λŸ¬ν•œ μž‘μ—…μ€ λ°±κ·ΈλΌμš΄λ“œ μ“°λ ˆλ“œμ—μ„œ 병렬화 λ˜μ–΄ 처리될 수 μžˆμŠ΅λ‹ˆλ‹€.

λ Œλ”λ§ 루프

이제 λ Œλ”λ§ν•  μ€€λΉ„κ°€ λλ‚¬μŠ΅λ‹ˆλ‹€. λ¨Όμ € VkSwapchainKHR에 λ Œλ”λ§ν•  이미지λ₯Ό μš”μ²­ν•΄μ•Ό ν•©λ‹ˆλ‹€. 그리고 VkCommandPoolλ‘œλΆ€ν„° VkCommandBufferλ₯Ό ν• λ‹Ήν•˜κ±°λ‚˜ 이미 ν• λ‹Ήλœ μ»€λ§¨λ“œ 버퍼λ₯Ό μž¬μ‚¬μš©ν•΄ μ»€λ§¨λ“œλ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€. λ‹€μŒμœΌλ‘œλŠ” VkRenderPassλ₯Ό μ‹œμž‘ν•΄μ„œ λ Œλ”λ§μ„ μ‹œμž‘ν•©λ‹ˆλ‹€. μ΄λŠ” 일반적인 VkRenderPass ν˜Ήμ€ 동적 λ Œλ”λ§μ„ μ‚¬μš©ν•΄ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ Œλ”νŒ¨μŠ€λŠ” μŠ€μ™‘μ²΄μΈμœΌλ‘œλΆ€ν„° λ°›μ•„μ˜¨ 이미지에 λ Œλ”λ§μ„ μˆ˜ν–‰ν•˜λ„λ‘ μ§€μ •ν•©λ‹ˆλ‹€. 그리고 VkPipeline, (셰이더 νŒŒλΌλ―Έν„°μ— ν•„μš”ν•œ)VkDescriptorSet, 정점 버퍼λ₯Ό λ°”μΈλ”©ν•˜κ³  그리기 ν˜ΈμΆœμ„ μ‹€ν–‰ν•  루프λ₯Ό λ§Œλ“­λ‹ˆλ‹€. λ Œλ”νŒ¨μŠ€μ—μ„œ 그리기가 λλ‚¬μœΌλ©΄ VkRenderPassλ₯Ό λλƒ…λ‹ˆλ‹€. λ Œλ”λ§ ν•  것이 λ‚¨μ•„μžˆμ§€ μ•Šλ‹€λ©΄ VkCommandBuffer도 λλƒ…λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ λ Œλ”λ§ν•˜κΈ° μœ„ν•΄ μ»€λ§¨λ“œ 버퍼λ₯Ό 큐둜 μ œμΆœν•©λ‹ˆλ‹€. 그러면 GPUμ—μ„œ μ»€λ§¨λ“œ λ²„νΌμ˜ λͺ…λ Ήλ“€μ˜ 싀행이 μ‹œμž‘λ  κ²ƒμž…λ‹ˆλ‹€. λ§Œμ•½ λ Œλ”λ§ν•œ κ²°κ³Όλ₯Ό 화면에 ν‘œμ‹œν•˜κ³  μ‹Άλ‹€λ©΄, λ Œλ”λ§ν•œ 이미지λ₯Ό 화면에 β€œν‘œμ‹œ(present)β€ν•΄μ•Όν•©λ‹ˆλ‹€. 아직 λ Œλ”λ§μ΄ λλ‚˜μ§€ μ•Šμ•˜μ„ 수 있기 λ•Œλ¬Έμ—, μ„Έλ§ˆν¬μ–΄(semaphore)λ₯Ό μ‚¬μš©ν•΄ λ Œλ”λ§μ΄ 끝날 λ•Œ κΉŒμ§€ 화면에 이미지λ₯Ό ν‘œμ‹œν•˜λŠ” μž‘μ—…μ„ λŒ€κΈ°μ‹œμΌœμ•Ό ν•©λ‹ˆλ‹€.

Vulkanμ—μ„œμ˜ λ Œλ”λ§ 루프 μ˜μ‚¬μ½”λ“œμž…λ‹ˆλ‹€.

// μŠ€μ™‘μ²΄μΈμ— λ Œλ”λ§ν•  μ΄λ―Έμ§€μ˜ 인덱슀λ₯Ό μš”μ²­ν•©λ‹ˆλ‹€.
int image_index = request_image(mySwapchain);

// μ»€λ§¨λ“œ 버퍼λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
VkCommandBuffer cmd = allocate_command_buffer();

// μ»€λ§¨λ“œ 버퍼λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
vkBeginCommandBuffer(cmd, ... );

// μŠ€μ™‘μ²΄μΈμœΌλ‘œλΆ€ν„° λ°›μ•„μ˜¨ 이미지 인덱슀둜 μƒˆλ‘œμš΄ λ Œλ”νŒ¨μŠ€λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
// 각 ν”„λ ˆμž„ λ²„νΌλŠ” μŠ€μ™‘μ²΄μΈμ˜ 이미지λ₯Ό μ°Έμ‘°ν•©λ‹ˆλ‹€.
vkCmdBeginRenderPass(cmd, main_render_pass, framebuffers[image_index] );

// λͺ¨λ“  객체λ₯Ό λ Œλ”λ§ν•©λ‹ˆλ‹€.
for(object in PassObjects){

    // 객체λ₯Ό λ Œλ”λ§ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” ꡬ성과 셰이더λ₯Ό λ°”μΈλ”©ν•©λ‹ˆλ‹€.
    vkCmdBindPipeline(cmd, object.pipeline);
    
    // 객체λ₯Ό λ Œλ”λ§ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” 정점과 인덱슀 버퍼λ₯Ό λ°”μΈλ”©ν•©λ‹ˆλ‹€.
    vkCmdBindVertexBuffers(cmd, object.VertexBuffer,...);
    vkCmdBindIndexBuffer(cmd, object.IndexBuffer,...);

    // 셰이더 μž…λ ₯으둜 μ‚¬μš©λ˜λŠ” 객체의 λ””μŠ€ν¬λ¦½ν„° 셋을 λ°”μΈλ”©ν•©λ‹ˆλ‹€.
    vkCmdBindDescriptorSets(cmd, object.textureDescriptorSet);
    vkCmdBindDescriptorSets(cmd, object.parametersDescriptorSet);

    // κ·Έλ¦½λ‹ˆλ‹€.
    vkCmdDraw(cmd,...);
}

// λ Œλ”νŒ¨μŠ€μ™€ μ»€λ§¨λ“œ 버퍼λ₯Ό λλƒ…λ‹ˆλ‹€.
vkCmdEndRenderPass(cmd);
vkEndCommandBuffer(cmd);


// μ»€λ§¨λ“œ 버퍼λ₯Ό μ œμΆœν•˜κ³  GPUμ—μ„œ μ‹€ν–‰ν•©λ‹ˆλ‹€.
vkQueueSubmit(graphicsQueue, cmd, ...);

// λ Œλ”λ§ν•œ 이미지λ₯Ό 화면에 ν‘œμ‹œν•©λ‹ˆλ‹€.
// renderSemaphoreλŠ” `cmd`κ°€ 싀행될 λ•Œ κΉŒμ§€ 이미지λ₯Ό ν‘œμ‹œν•˜μ§€ μ•ŠλŠ” 것을 보μž₯ν•©λ‹ˆλ‹€.
vkQueuePresent(graphicsQueue, renderSemaphore);

Next: Project files and libraries