λλ¬λ³΄κΈ°
μ΄λ² μ±ν°μμλ μ΄λ―Έ μμ±λ μ€μΌλ ν€ μ½λλ₯Ό μ΄ν΄λ³΄λ©° ν΄λΉ μ½λκ° μ€μ λ‘ μ΄λ€ μν μ νλ μ§ νμΈν΄λ³΄κ² μ΅λλ€.
νμΌμ project/src/ ν΄λμ μμ΅λλ€.
- vk_engine.h/cpp : μ΄λ μμ§μ ν΅μ¬ ν΄λμ€μ λλ€. νν 리μΌμμ λ€λ£° λλΆλΆμ μ½λκ° μλ κ³³μ λλ€.
 - main.cpp : μ½λμ μ§μ μ μ λλ€. vk_engine νΈμΆ μΈμλ μ무 μ½λλ μμ΅λλ€.
 - vk_initializers.h/cpp : Vulkan ꡬ쑰체λ₯Ό μμ±νλ κ²μ λλ ν¨μκ° ν¬ν¨λμ΄ μμ΅λλ€.
 - vk_images.h/cpp : μ΄λ―Έμ§μ κ΄λ ¨λ ν¨μλ€μ ν¬ν¨λμ΄ μμ΅λλ€.
 - vk_pipelines.h/cpp : νμ΄νλΌμΈμ μν μΆμνκ° ν¬ν¨λμ΄ μμ΅λλ€.
 - vk_descriptors.h/cpp : λμ€ν¬λ¦½ν° μ (descriptor set) μΆμνκ° ν¬ν¨λμ΄ μμ΅λλ€.
 - vk_loader.h/cpp : GLTF νμΌμ λΆλ¬μ€κΈ° μν ν¨μκ° ν¬ν¨λμ΄ μμ΅λλ€.
 - vk_types.h : μ 체 μ½λ λ² μ΄μ€λ μ΄ ν€λλ₯Ό ν¬ν¨ν©λλ€. μ΄λ λ리 μ¬μ©λλ κΈ°λ³Έ ꡬ쑰체μ ν¬ν¨ νμΌμ μ 곡ν©λλ€.
 
vk_engineμ νλ‘μ νΈμ μ€μ¬μ΄μ μ£Όμ μμ§ ν΄λμ€κ° λ  κ²μ
λλ€. vk_loaderλ GLTF νμΌμ λΆλ¬μ¬ λ vk_engineκ³Ό μνΈμμ©ν΄μΌ νλ―λ‘, μ΄μ ν΅ν©λ  μμ μ
λλ€. κ·Έ μΈμ λ€λ₯Έ νμΌλ€μ νν λ¦¬μΌ μ§νμ λ°λΌ λ§λ€μ΄μ§λ μΌλ°μ μΈ Vulkan μΆμν κ³μΈ΅μΌλ‘, Vulkan μΈμλ λ³λ€λ₯Έ μμ‘΄μ±μ΄ μμ΅λλ€. λ°λΌμ μ΄λ¬ν μΆμν νμΌλ€μ μ¬λ¬λΆμ λ€λ₯Έ νλ‘μ νΈμμλ μμ λ‘κ² νμ©ν  μ μμ΅λλ€.
μ½λ
#include <vk_engine.h>
int main(int argc, char* argv[])
{
	VulkanEngine engine;
	engine.init();	
	
	engine.run();	
	engine.cleanup();	
	return 0;
}
λ¨μν main.cppλ‘ μμν©λλ€. VulkanEngine ν¨μλ₯Ό νΈμΆνλ κ²μΈμλ μ무κ²λ νμ§ μμ΅λλ€.
ν₯νμλ argc/argvμμ μ λ¬λ λͺ
λ Ήμ€ μΈμλ μ€μ νμΌμ μ΄μ©ν΄ νλΌλ―Έν°λ₯Ό ꡬμ±νλ μ©λλ‘ μ¬μ©ν  μλ μμ΅λλ€.
vk_types.hμ λ€μμ ν¬ν¨ν©λλ€.
#pragma once
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <span>
#include <array>
#include <functional>
#include <deque>
#include <vulkan/vulkan.h>
#include <vulkan/vk_enum_string_helper.h>
#include <vk_mem_alloc.h>
#include <fmt/core.h>
#include <glm/mat4x4.hpp>
#include <glm/vec4.hpp>
#define VK_CHECK(x)                                                     \
    do {                                                                \
        VkResult err = x;                                               \
        if (err) {                                                      \
             fmt::print("Detected Vulkan error: {}", string_VkResult(err)); \
            abort();                                                    \
        }                                                               \
    } while (0)
#pragrma onceλ μ»΄νμΌλ¬μκ² κ°μ νμΌμ λ λ² ν¬ν¨νμ§ λ§λΌκ³  μλ €μ£Όλ μ μ²λ¦¬κΈ° μ§μλ¬Έμ
λλ€. μ΄λ ν€λκ°λμ λμΌν μν μ νμ§λ§ λ κ°κ²°ν©λλ€.
<vulkan/vulkan.h>λΌλ Vulkan ν΅μ¬ ν€λλ₯Ό ν¬ν¨ν©λλ€. μ΄λ μ°λ¦¬κ° νμλ‘ νλ λͺ¨λ  ꡬ쑰체μ Vulkan ν¨μ μ μλ₯Ό λ΄κ³  μμ΅λλ€. λν μ½λ μ λ°μ μ¬μ©ν  fmt λΌμ΄λΈλ¬λ¦¬μ ν΅μ¬ ν€λλ₯Ό ν¬ν¨νκ³  VK_CHECK λ§€ν¬λ‘λ₯Ό λ§λ€μ΄ Vulkan νΈμΆ μ μλ¬λ₯Ό κ΄λ¦¬ν  κ²μ
λλ€. νν λ¦¬μΌμμλ vk_enum_string_helper.hλ₯Ό μ¬μ©ν  κ²μ
λλ€. μ΄λ Vulkan SDKκ° μ κ³΅νλ ν€λλ‘, μ£Όμ΄μ§ Vulkan μ΄κ±°νμ λν λ¬Έμμ΄μ κ°μ Έμ¬ μ μκ² ν΄μ€λλ€. μ΄λ¬ν λ°©μμΌλ‘ λ‘κ·Έλ₯Ό μμ±ν  λ λ§€μ° μ μ©ν©λλ€.
μ΄ νν λ¦¬μΌμ μΆλ ₯μ μν΄ νμ€ std::coutμ μ¬μ©νμ§ μμ κ²μ
λλ€. λμ  {fmt} λΌμ΄λΈλ¬λ¦¬λ₯Ό λμ  μ¬μ©ν  κ²μ
λλ€. μ΄λ λ¬Έμμ΄ ν¬λ§·ν
κ³Ό μΆλ ₯μ μν κ³ νμ§ λΌμ΄λΈλ¬λ¦¬μ
λλ€. C++20μ std::formatμ μ΄ λΌμ΄λΈλ¬λ¦¬λ₯Ό κΈ°λ°μΌλ‘ μμ±λμμ§λ§, λ λ§μ κΈ°λ₯κ³Ό μ§μμ μν΄ ν΄λΉ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νκ² μ΅λλ€. μ¬κΈ°μλ Vulkan μλ¬κ° λ°μν κ²½μ° fmt::printlnμ μ¬μ©ν΄ μ½μμ°½μ μλ¬λ₯Ό μΆλ ₯νκ² μ΅λλ€.
vk_initializers.hλ μ¬μ  μμ±λ νμΌμ
λλ€. μ΄λ λλΆλΆμ Vulkan info ꡬ쑰체μ λ€λ₯Έ μ μ¬ν κ²λ€μ μ΄λμ
λΌμ΄μ λ₯Ό λ΄μ΅λλ€. μ΄λ μ΄λ¬ν ꡬ쑰체λ€μ μ½κ° μΆμννμ¬ μ΄λ₯Ό μ¬μ©ν  λ λ§λ€ μ½λμ μΆμνμ λν΄ μ€λͺ
ν  κ²μ
λλ€.
Vulkan κ·Έ μ체λ₯Ό λ΄λ vk_types ν€λλ₯Ό ν¬ν¨νκ³ , μ΄ν μ΄κ³³μ μΆκ°ν  ν¨μλ₯Ό λ΄μ λ€μμ€νμ΄μ€λ₯Ό μ μΈν©λλ€.
λ§μ§λ§μΌλ‘ ν΅μ¬ ν΄λμ€μΈ vk_engine.hλ₯Ό μ΄ν΄λ΄
μλ€.
#pragma once
#include <vk_types.h>
class VulkanEngine {
public:
	bool _isInitialized{ false };
	int _frameNumber {0};
	bool stop_rendering{ false };
	VkExtent2D _windowExtent{ 1700 , 900 };
	struct SDL_Window* _window{ nullptr };
	static VulkanEngine& Get();
	//initializes everything in the engine
	void init();
	//shuts down the engine
	void cleanup();
	//draw loop
	void draw();
	//run main loop
	void run();
};
vk_initializersκ³Ό λ§μ°¬κ°μ§λ‘ vk_typesλ₯Ό ν¬ν¨ν©λλ€. Vulkan νμ
μΈ VkExtent2Dκ° νμνκΈ° λλ¬Έμ
λλ€. VulkanEngineμ μ°λ¦¬κ° μμ
ν  ν΅μ¬μ
λλ€. μμ§μ΄ μννλ λλΆλΆμ μμ
μ μ΄ ν΄λμ€λ₯Ό μ€μ¬μΌλ‘ μμ
ν  κ²μ
λλ€. μ΄λ κ² νλ©΄ νλ‘μ νΈμ μν€ν
μ²λ₯Ό λ¨μνκ² λ§λ€ μ μμ΅λλ€.
μμ§μ΄ μ΄κΈ°ν λμλμ§λ₯Ό νμΈνλ νλκ·Έ, νλ μμ λνλ΄λ μ μ, κ·Έλ¦¬κ³ μ°½μ ν¬κΈ°λ₯Ό ν½μ  λ¨μλ‘ μ μ₯νλ λ³μλ₯Ό κ°μ΅λλ€.
μ μΈ struct SDL_Window* _window;λ νΉν μ€μν©λλ€. structκ° λ§¨ μμ μ¨ μ μ μ μν©μλ€. μ΄λ μ λ°© μ μΈμ΄λΌ λΆλ¦¬λ κ²μΌλ‘, VulkanEngine ν€λμ SDLμ ν¬ν¨μν€μ§ μκ³ λ ν΄λμ€ λ΄λΆμμ SDL_Window ν¬μΈν°λ₯Ό μ¬μ© κ°λ₯νκ² ν΄μ€λλ€. μ΄ λ³μλ μ ν리μΌμ΄μ
μμ λ§λ€ μ°½μ λ΄κ³  μμ΅λλ€.
λν Get() ν¨μλ₯Ό μ μ μ±ν΄ν€ ν¨ν΄μΌλ‘ μΆκ°νμ΅λλ€.
ν€λμ λν΄ λ€λ€λ³΄μμΌλ―λ‘, cpp νμΌμ μ΄ν΄ λ΄ μλ€.
vk_engine.cpp line 1
#include "vk_engine.h"
#include <SDL.h>
#include <SDL_vulkan.h>
#include <vk_initializers.h>
#include <vk_types.h>
#include <chrono>
#include <thread>
λ€λ₯Έ νμΌλ€κ³Όλ λ¬λ¦¬, μ¬κΈ°μλ λ λ§μ κ²λ€μ ν¬ν¨νκ³  μμ΅λλ€. <SDL.h>μ <SDL_vulkan.h> λͺ¨λλ₯Ό ν¬ν¨νκ³  μμ΅λλ€. SDL.hλ SDL λΌμ΄λΈλ¬λ¦¬μ μ°½μ μ΄κ³  μ
λ ₯μ λ°λ ν΅μ¬ λ°μ΄ν°λ₯Ό λ΄κ³  μμ΅λλ€. λ°λ©΄ SDL_vulkan.hλ Vulkan νΉμ νλκ·Έμ Vulkanκ³Ό νΈνλλ μ°½μ μ΄κΈ° μν κΈ°λ₯, κ·Έ μΈμλ λ€μν Vulkan νΉμ κΈ°λ₯μ λ΄κ³  μμ΅λλ€. λν μ¬μ©ν  λͺλͺ STL 컨ν
μ΄λλ μΆκ°ν©λλ€.
vk_engine.cpp, line 10
constexpr bool bUseValidationLayers = false;
VulkanEngine* loadedEngine = nullptr;
VulkanEngine& VulkanEngine::Get() { return *loadedEngine; }
void VulkanEngine::init()
{
    // only one engine initialization is allowed with the application.
    assert(loadedEngine == nullptr);
    loadedEngine = this;
    // We initialize SDL and create a window with it.
    SDL_Init(SDL_INIT_VIDEO);
    SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN);
    _window = SDL_CreateWindow(
        "Vulkan Engine",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        _windowExtent.width,
        _windowExtent.height,
        window_flags);
    // everything went fine
    _isInitialized = true;
}
SDL μ°½μ μμ±νλ 첫 λ²μ¬ μ½λλ₯Ό μ΄ν΄λ΄
μλ€. 첫 λ²μ§Έλ‘ νλ κ²μ SDL λΌμ΄λΈλ¬λ¦¬ μ΄κΈ°νμ
λλ€. SDL λΌμ΄λΈλ¬λ¦¬λ λ€μν κΈ°λ₯μ ν¬ν¨νκ³  μκΈ° λλ¬Έμ μ°λ¦¬κ° μ΄λ€ κΈ°λ₯μ μ¬μ©ν  κ²μΈμ§μ λν νλκ·Έλ₯Ό μ λ¬ν΄μΌ ν©λλ€. SDL_INIT_VIDEOλ SDLμκ² κΈ°λ³Έ μ°½ κΈ°λ₯μ μ¬μ©νκ³  μΆλ€λ κ²μ μλ €μ€λλ€. μ΄λ λν ν€λ³΄λμ λ§μ°μ€ μ
λ ₯μ λ°λ κΈ°λ³Έ μ
λ ₯ μ΄λ²€νΈλ ν¬ν¨ν©λλ€.
λν Vulkan μμ§μ μ±κΈν€μΌλ‘ μ°Έμ‘°νκΈ° μν μ μ ν¬μΈν°λ₯Ό μ€μ ν©λλ€. μ νμ μΈ μ±κΈν€ λμ μ΄μ²λΌ μμ νλ μ΄μ λ ν΄λμ€κ° μ΄κΈ°νλκ³ νκ΄΄λλ μμ μ λͺ μμ μΌλ‘ μ μ΄νκΈ° μν΄μμ λλ€. μΌλ°μ μΈ C++ μ±κΈν€ ν¨ν΄μ μ΄λ¬ν μ μ΄λ₯Ό ν μ μμ΅λλ€.
SDLμ΄ μ΄κΈ°νλλ©΄, μ΄λ₯Ό μ¬μ©ν΄ μ°½μ μμ±ν  κ²μ
λλ€. μ°½μ μ΄νμ μ¬μ©ν  μ μλλ‘ _window λ©€λ²μ λ΄κΉλλ€.
SDLμ΄ C λΌμ΄λΈλ¬λ¦¬μ΄κΈ° λλ¬Έμ, μμ±μμ νκ΄΄μλ₯Ό μ§μνμ§ μμ΅λλ€. λ°λΌμ μλμ μΌλ‘ νκ΄΄ν΄μ£Όμ΄μΌ ν©λλ€.
μ°½μ΄ λ§λ€μ΄μ§λ©΄ νκ΄΄λ μνν΄μΌ ν©λλ€.
void VulkanEngine::cleanup()
{
    if (_isInitialized) {
        SDL_DestroyWindow(_window);
    }
    // clear engine pointer
    loadedEngine = nullptr;
}
void VulkanEngine::draw()
{
    // nothing yet
}
SDL_CreateWindowμμ νλ κ²κ³Ό μ μ¬νκ², SDL_DestroyWindowλ μνν  νμκ° μμ΅λλ€. μ΄λ νλ‘κ·Έλ¨μ μ°½μ μ κ±°ν©λλ€. μ΄μ  μμ§μ΄ μμ ν μ λ¦¬λμμΌλ―λ‘, μ΄ μ§μ μμ μμ§μ κ°λ¦¬ν€λ μ±κΈν€ ν¬μΈν°λ μ΄κΈ°νν©λλ€.
μ΄νμ λ λ§μ ν¨μλ₯Ό cleanup ν¨μμ μΆκ°ν  κ²μ
λλ€.
draw ν¨μκ° μ§κΈμ λΉμ΄μμ§λ§, μ΄νμ λ λλ§ μ½λλ₯Ό μΆκ°ν  κ²μ
λλ€.
void VulkanEngine::run()
{
    SDL_Event e;
    bool bQuit = false;
    // main loop
    while (!bQuit) {
        // Handle events on queue
        while (SDL_PollEvent(&e) != 0) {
            // close the window when user alt-f4s or clicks the X button
            if (e.type == SDL_QUIT)
                bQuit = true;
            if (e.type == SDL_WINDOWEVENT) {
                if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
                    stop_rendering = true;
                }
                if (e.window.event == SDL_WINDOWEVENT_RESTORED) {
                    stop_rendering = false;
                }
            }
        }
        // do not draw if we are minimized
        if (stop_rendering) {
            // throttle the speed to avoid the endless spinning
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            continue;
        }
        draw();
    }
}
μ ν리μΌμ΄μ
μ λ©μΈ 루νμ
λλ€. while()μ 무ν λ°λ³΅λ¬Έμ΄ μμ΅λλ€. μ΄λ SDLμ΄ SDL_QUITλ₯Ό λ°μμ λμλ§ λ©μΆ₯λλ€.
λ§€ λ°λ³΅λ§λ€ SDL_PollEventλ₯Ό μνν©λλ€. μ΄λ SDLμκ² μ§λ νλ μλμ OSκ° μ ν리μΌμ΄μ
μΌλ‘ λ³΄λΈ λͺ¨λ  μ΄λ²€νΈλ₯Ό μμ²ν©λλ€. μ¬κΈ°μ ν€λ³΄λ μ
λ ₯, λ§μ°μ€ μ΄λ, μ°½ μμ§μ΄κΈ°, μ΅μν λ±μ νμΈν  μ μμ΅λλ€. μ§κΈμ SDL_QUITκ³Ό μ°½ μ΅μν/볡ꡬμλ§ μ κ²½μ°λ©΄ λ©λλ€. μ°½μ μ΅μννλ μ΄λ²€νΈλ₯Ό λ°μΌλ©΄, stop_rendering λΆ κ°μ trueλ‘ μ€μ ν΄ μ΅μν λμμ λ 그리λ κ²μ λ°©μ§ν©λλ€. μ°½μ 볡ꡬνλ©΄ μ΄λ₯Ό λ€μ falseλ‘ μ€μ ν΄ 그리기λ₯Ό κ³μνλλ‘ ν©λλ€.
λ§μ§λ§μΌλ‘, λ©μΈ 루νμ κ° λ°λ³΅λ§λ€ draw()λ₯Ό νΈμΆνκ±°λ κ·Έλ¦¬κΈ°κ° λΉνμ±νλ κ²½μ°μλ std::this_thread::sleep_forλ₯Ό νΈμΆν©λλ€. μ΄λ κ² νλ©΄ μ°½μ΄ μ΅μν λμμ λ μ€νλλ κ²μ λ°©μ§ν¨μΌλ‘μ μ±λ₯μ μ μ½ν  μ μμ΅λλ€.
μ΄μ  SDLμ΄ μ΄λ»κ² μ°½μ λ§λλμ§λ₯Ό μ΄ν΄λ΄€μ΅λλ€. μ¬μ€ κ·Έ μΈμλ ν° λ³νκ° μμ΅λλ€.
μ΄μ  SDL μ΄λ²€νΈλ₯Ό μ€νν΄λ³΄λ κ²λ§μ΄ λ¨μμ΅λλ€.
μ°μ΅μΌμ SDL2μ λ¬Έμλ₯Ό μ½μ΄ fmt::printλ₯Ό μ¬μ©ν΄ ν€λ³΄λ μ΄λ²€νΈλ₯Ό λ‘κ·Έλ‘ μΆλ ₯ν΄λ³΄λ κ²λ μ’μ΅λλ€.
μ΄μ  첫 μ±ν°λ₯Ό μ§νν μ μμ΅λλ€. λ λλ§ λ£¨νλ₯Ό ꡬνν΄λ³΄κ² μ΅λλ€.
Next: Initializing Vulkan