Link

ํ•ต์‹ฌ Vulkan ๊ตฌ์กฐ์ฒด ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ

0์žฅ์˜ ์ฝ”๋“œ์—์„œ ์‹œ์ž‘ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด 0์žฅ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ค์ •ํ•œ ํ›„ ์ฝ๊ธฐ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์šฐ์„  ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํžˆ ํ•˜๊ธฐ ์œ„ํ•œ vkBootstrap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ #include๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด vk_engine.cpp ์ƒ๋‹จ์— "VkBootstrap.h"ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

// --- other includes ---
#include <vk_types.h>
#include <vk_initializers.h>

//bootstrap library
#include "VkBootstrap.h"

VkBootstrap์€ ์—”์ง„์˜ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ ๋Œ€ํญ ์ค„์—ฌ ์ˆ˜๋ฐฑ ์ค„์˜ ์ฝ”๋“œ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. VkBootstrap์—†์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด vulkan-tutorial์„ ์ฝ์–ด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ์ดˆ๊ธฐํ™”ํ•ด์•ผ๋˜๋Š” ๊ฒƒ์€ Vulkan Instance์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ƒˆ๋กœ์šด ํ•จ์ˆ˜์™€ ํ•ธ๋“ค์„ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ VulkanEngineํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋‹ค๋ฅธ ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•œ ๋ช‡๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

vk_engine.h

class VulkanEngine {
public:

// --- omitted ---

	VkInstance _instance;// Vulkan library handle
	VkDebugUtilsMessengerEXT _debug_messenger;// Vulkan debug output handle
	VkPhysicalDevice _chosenGPU;// GPU chosen as the default device
	VkDevice _device; // Vulkan device for commands
	VkSurfaceKHR _surface;// Vulkan window surface

private:

	void init_vulkan();
	void init_swapchain();
	void init_commands();
	void init_sync_structures();

์ด์ œ ์ด๋Ÿฌํ•œ init ํ•จ์ˆ˜๋“ค์„ ์—”์ง„์˜ initํ•จ์ˆ˜์—์„œ ํ˜ธ์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

vk_engine.cpp

constexpr bool bUseValidationLayers = false;

void VulkanEngine::init()
{
	// 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
	);

	init_vulkan();

	init_swapchain();

	init_commands();

	init_sync_structures();

	//everything went fine
	_isInitialized = true;
}

void VulkanEngine::init_vulkan()
{
    //nothing yet
}
void VulkanEngine::init_swapchain()
{
    //nothing yet
}
void VulkanEngine::init_commands()
{
    //nothing yet
}
void VulkanEngine::init_sync_structures()
{
    //nothing yet
}

๋ฉ”์ธ ํด๋ž˜์Šค์— VkDevice, VkPhysicalDevice, VkInstance, VkDebugUtilsMessengerEXT 4๊ฐœ์˜ ํ•ธ๋“ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Instance

์ด์ œ ์ƒˆ๋กœ์šด init_Vulkan ํ•จ์ˆ˜๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์œผ๋ฏ€๋กœ VkInstance๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

void VulkanEngine::init_vulkan()
{
	vkb::InstanceBuilder builder;

	//make the vulkan instance, with basic debug features
	auto inst_ret = builder.set_app_name("Example Vulkan Application")
		.request_validation_layers(bUseValidationLayers)
		.use_default_debug_messenger()
		.require_api_version(1, 3, 0)
		.build();

	vkb::Instance vkb_inst = inst_ret.value();

	//grab the instance 
	_instance = vkb_inst.instance;
	_debug_messenger = vkb_inst.debug_messenger;

}

vkb::InstanceBuilder๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” VkBootstrap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฐ์ฒด๋กœ, VkInstance์ƒ์„ฑ์„ ๋ณด๋‹ค ๊ฐ„๋‹จํžˆ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถ”์ƒํ™”ํ•ฉ๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ โ€œExample Vulkan Applicationโ€์ด๋ผ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๊ณ , ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋ฅผ ํ™œ์„ฑํ™” ํ•˜๋ฉฐ, ๊ธฐ๋ณธ ๋””๋ฒ„๊ทธ ๋กœ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. โ€œExample Vulkan Applicationโ€์ด๋ผ๋Š” ์ด๋ฆ„์€ ์›ํ•˜๋Š” ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ๋Œ€์ฒดํ•ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค. VkInstance๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ, ์—”์ง„๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋“œ๋ผ์ด๋ฒ„ ์ œ์กฐ์‚ฌ๊ฐ€ ๊ฒŒ์ž„/์—”์ง„์˜ ์ด๋ฆ„์„ ์‰ฝ๊ฒŒ ์ฐพ์•„, ํ•ด๋‹น ์†Œํ”„ํŠธ์›จ์–ด์— ๋งž๊ฒŒ ๋‚ด๋ถ€ ๋“œ๋ผ์ด๋ฒ„ ๋กœ์ง์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์—๊ฒŒ๋Š” ๊ทธ๋ฆฌ ์ค‘์š”ํ•œ ์‚ฌํ•ญ์€ ์•„๋‹™๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋ฅผ ํ™œ์„ฑํ™”ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๊ฒ€์ฆ ๋ ˆ์ด์–ด๊ฐ€ ์˜ค๋ฅ˜๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์žก์•„์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋น„ํ™œ์„ฑํ™” ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋” ๊ณ ๊ธ‰ ์—”์ง„์—์„œ๋Š” ๋””๋ฒ„๊ทธ ๋ชจ๋“œ์—์„œ๋งŒ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์„ค์ •์„ ํ†ตํ•ด ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Vulkan ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋Š” Vulkan ํ˜ธ์ถœ ์„ฑ๋Šฅ์„ ์ƒ๋‹นํžˆ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋”ฐ๋ผ์„œ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ณต์žกํ•œ ์”ฌ์ด ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์‹ค์ œ ์ฝ”๋“œ์˜ ์„ฑ๋Šฅ์„ ํ•˜๋ ค๋ฉด ์ด๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Vulkan API 1.3๋ฒ„์ „์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋น„๊ต์  ์ตœ์‹  GPU์—์„œ ์ง€์›๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Vulkan 1.3์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ๋“ค์„ ํ™œ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์˜ค๋ž˜๋œ PC/GPU๋ฅผ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด Vulkan 1.1๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์ „ ๋ฒ„์ „์˜ vkguide๋ฅผ ๋ณด๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋””๋ฒ„๊ทธ ๋ฉ”์‹ ์ €๋ฅผ ์‚ฌ์šฉ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฒ€์ฆ ๋ ˆ์ด์–ด์˜ ์ถœ๋ ฅ์„ ๋กœ๊ทธ๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ •์˜ ๋””๋ฒ„๊ทธ ๋ฉ”์‹ ์ €๊ฐ€ ํ•„์š” ์—†์œผ๋ฏ€๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ธฐ๋ณธ๊ฐ’(์ฝ˜์†” ์ฐฝ ์ถœ๋ ฅ)์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ vkb::Instance ๊ฐ์ฒด์—์„œ VkInstance ํ•ธ๋“ค๊ณผ VkDebugUtilsMessengerEXT ํ•ธ๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ ์‹œ ์ด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋„๋ก VkDebugUtilsMessengerEXT๋ฅผ ๋”ฐ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Device

void VulkanEngine::init_vulkan()
{
	// other code ------

	SDL_Vulkan_CreateSurface(_window, _instance, &_surface);

	//vulkan 1.3 features
	VkPhysicalDeviceVulkan13Features features{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES };
	features.dynamicRendering = true;
	features.synchronization2 = true;

	//vulkan 1.2 features
	VkPhysicalDeviceVulkan12Features features12{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES };
	features12.bufferDeviceAddress = true;
	features12.descriptorIndexing = true;


	//use vkbootstrap to select a gpu. 
	//We want a gpu that can write to the SDL surface and supports vulkan 1.3 with the correct features
	vkb::PhysicalDeviceSelector selector{ vkb_inst };
	vkb::PhysicalDevice physicalDevice = selector
		.set_minimum_version(1, 3)
		.set_required_features_13(features)
		.set_required_features_12(features12)
		.set_surface(_surface)
		.select()
		.value();


	//create the final vulkan device
	vkb::DeviceBuilder deviceBuilder{ physicalDevice };

	vkb::Device vkbDevice = deviceBuilder.build().value();

	// Get the VkDevice handle used in the rest of a vulkan application
	_device = vkbDevice.device;
	_chosenGPU = physicalDevice.physical_device;
}

์‚ฌ์šฉํ•  GPU๋ฅผ ์„ ํƒํ•˜๊ธฐ ์œ„ํ•ด vkb::PhysicalDeviceSelector๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋จผ์ €, SDL ์ฐฝ์œผ๋กœ๋ถ€ํ„ฐ VkSurfaceKHR ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ์‹ค์ œ๋กœ ๋ Œ๋”๋ง์ด ์ด๋ฃจ์–ด์งˆ ์ฐฝ์ด๋ฏ€๋กœ, PhysicalDeviceSelector์—๊ฒŒ ํ•ด๋‹น ์ฐฝ์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” GPU๋ฅผ ์„ ํƒํ•˜๋„๋ก ์ง€์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋Šฅ๋„ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ์„  Vulkan 1.3 ๊ธฐ๋Šฅ์œผ๋กœ๋Š” Dynamic Rendering๊ณผ Synchronization 2๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€ Vulkan 1.3์—์„œ ์ œ๊ณตํ•˜๋Š” ์„ ํƒ์ ์ธ ๊ธฐ๋Šฅ์œผ๋กœ, Dynamic rendering์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ Œ๋”ํŒจ์Šค์™€ ํ”„๋ ˆ์ž„๋ฒ„ํผ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ด์— ๋Œ€ํ•ด ๋” ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด ๊ตฌ๋ฒ„์ „์˜ VkGuide๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”). ๋˜ํ•œ ์ƒˆ๋กœ์šด ๋™๊ธฐํ™” ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด์™€ ํ•จ๊ป˜ Vulkan1.2 ๊ธฐ๋Šฅ์ธ BufferDeviceAddress์™€ descriptorIndexing๋„ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. BufferDeviceAddress๋Š” ๋ฒ„ํผ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์ง€ ์•Š๊ณ ๋„ GPU ํฌ์ธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋ฉฐ, descriptorIndexing์€ ๋ฐ”์ธ๋“œ๋ฆฌ์Šค ํ…์Šค์ณ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

vkb::PhysicalDeviceSelector์— VkPhysicalDeviceVulkan13Features๊ตฌ์กฐ์ฒด๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, vkbootstrap์—๊ฒŒ ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ์ง€์›ํ•˜๋Š” GPU๋ฅผ ์ฐพ๋„๋ก ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Vulkan ๋ฒ„์ „์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ ˆ๋ฒจ์˜ ๊ธฐ๋Šฅ ๊ตฌ์กฐ์ฒด๊ฐ€ ์žˆ์œผ๋ฉฐ, ์•„๋ž˜ ๋งํฌ์—์„œ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Vulkan Spec: 1.0 physical device features Vulkan Spec: 1.1 physical device features Vulkan Spec: 1.2 physical device features Vulkan Spec: 1.3 physical device features

VkPhysicalDevice๋ฅผ ์–ป์—ˆ์œผ๋ฉด ์ด๋กœ๋ถ€ํ„ฐ VkDevice๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์ƒ์„ฑ๋œ ํ•ธ๋“ค์„ ํด๋ž˜์Šค์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์œผ๋กœ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. Vulkan์ด ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ Vulkan ๋ช…๋ น์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์‹œ์ ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ Vulkan ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š์€ GPU๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ, ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์ตœ์‹ ์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”. ๋งŒ์•ฝ GPU ์ž์ฒด๊ฐ€ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ตฌ๋ฒ„์ „์˜ VkGuide๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

์Šค์™‘์ฒด์ธ ์„ค์ •ํ•˜๊ธฐ

ํ•ต์‹ฌ ์ดˆ๊ธฐํ™” ๊ณผ์ •์˜ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” ์Šค์™‘์ฒด์ธ์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์•ผ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์ด ์ƒ๊น๋‹ˆ๋‹ค.

๋จผ์ € VulkanEngine์— ์ƒˆ๋กœ์šด ๋ฉค๋ฒ„์™€ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

class VulkanEngine {
public:
	// --- other code ---

	VkSwapchainKHR _swapchain;
	VkFormat _swapchainImageFormat;

	std::vector<VkImage> _swapchainImages;
	std::vector<VkImageView> _swapchainImageViews;
	VkExtent2D _swapchainExtent;

private: 
	void create_swapchain(uint32_t width, uint32_t height);
	void destroy_swapchain();
}

VkSwapchainKHR ์ž์ฒด์™€ ํ•จ๊ป˜, ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€๋“ค์ด ๋ Œ๋”๋ง์‹œ ์‚ฌ์šฉํ•  ํฌ๋งท๋„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋‘ ๊ฐœ์˜ ๋ฐฐ์—ด์„ ์ €์žฅํ•˜๋Š”๋ฐ, ํ•˜๋‚˜๋Š” VkImage์˜ ๋ฐฐ์—ด์ด๊ณ  ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” VkImageView์˜ ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค.

VkImage๋Š” ํ…์Šค์ณ๋กœ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋ Œ๋”๋ง ๋Œ€์ƒ์ด ๋˜๋Š” ์‹ค์ œ ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ•ธ๋“ค์ž…๋‹ˆ๋‹ค. VkImageView๋Š” ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ๊ฐ์‹ธ๋Š” ๋ž˜ํผ๋กœ, ์ƒ‰์ƒ ํฌ๋งท์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ๋Š” ์ดํ›„์— ๋” ์ž์„ธํžˆ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ์Šค์™‘์ฒด์ธ์„ ์œ„ํ•œ ์ƒ์„ฑ ๋ฐ ์ œ๊ฑฐ ํ•จ์ˆ˜๋„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋“ค๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, vkb ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ์Šค์™‘์ฒด์ธ์„ ์ƒ์„ฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” VkInstance์™€ VkDevice๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

void VulkanEngine::create_swapchain(uint32_t width, uint32_t height)
{
	vkb::SwapchainBuilder swapchainBuilder{ _chosenGPU,_device,_surface };

	_swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM;

	vkb::Swapchain vkbSwapchain = swapchainBuilder
		//.use_default_format_selection()
		.set_desired_format(VkSurfaceFormatKHR{ .format = _swapchainImageFormat, .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR })
		//use vsync present mode
		.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
		.set_desired_extent(width, height)
		.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT)
		.build()
		.value();

	_swapchainExtent = vkbSwapchain.extent;
	//store swapchain and its related images
	_swapchain = vkbSwapchain.swapchain;
	_swapchainImages = vkbSwapchain.get_images().value();
	_swapchainImageViews = vkbSwapchain.get_image_views().value();
}

void VulkanEngine::init_swapchain()
{
	create_swapchain(_windowExtent.width, _windowExtent.height);
}

create_swapchain()์—์„œ ์Šค์™‘์ฒด์ธ ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๋’ค, init_swapchain()์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ํ‘œ์‹œ ๋ชจ๋“œ(Present Mode)๋กœ, VK_PRESENT_MODE_FIFO_KHR์„ ์„ค์ •ํ•œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ ์—„๊ฒฉํ•œ VSync๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์ „์ฒด ์—”์ง„์˜ FPS๋ฅผ ๋ชจ๋‹ˆํ„ฐ ์ฃผ์‚ฌ์œจ์— ๋งž์ถฐ ์ œํ•œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ์Šค์™‘์ฒด์ธ ์ƒ์„ฑ ์‹œ ์ฐฝ์˜ ํฌ๊ธฐ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์Šค์™‘์ฒด์ธ์„ ์ƒ์„ฑํ•˜๋ฉด ๋™์‹œ์— ํ•ด๋‹น ํฌ๊ธฐ์— ๋งž๋Š” ์ด๋ฏธ์ง€๋“ค๋„ ์ƒ์„ฑ๋˜๋ฏ€๋กœ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋Š” ๊ณ ์ •๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์ฐฝ ํฌ๊ธฐ ๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ์Šค์™‘์ฒด์ธ์„ ๋‹ค์‹œ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ดˆ๊ธฐํ™” ํ๋ฆ„๊ณผ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ดˆ๊ธฐํ™” ์‹œ์ ์—์„œ๋Š” ์ฐฝ์˜ ํฌ๊ธฐ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์Šค์™‘์ฒด์ธ์ด ์ƒ์„ฑ๋˜๋ฉด, ๊ทธ์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๋ฅผ VulkanEngine ํด๋ž˜์Šค์˜ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

destroy_swapchain()ํ•จ์ˆ˜๋„ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

void VulkanEngine::destroy_swapchain()
{
	vkDestroySwapchainKHR(_device, _swapchain, nullptr);

	// destroy swapchain resources
	for (int i = 0; i < _swapchainImageViews.size(); i++) {

		vkDestroyImageView(_device, _swapchainImageViews[i], nullptr);
	}
}

์šฐ์„  ์Šค์™‘์ฒด์ธ ๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•˜๋Š”๋ฐ, ์ด ์ž‘์—…์€ ๋‚ด๋ถ€์ ์œผ๋กœ ํฌํ•จ๋œ ์ด๋ฏธ์ง€๋“ค๋„ ํ•จ๊ป˜ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ, ์ด ์ด๋ฏธ์ง€๋“ค์— ํ•ด๋‹นํ•˜๋Š” ImageView๋“ค๋„ ๋”ฐ๋กœ ์ œ๊ฑฐํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ž์› ์ •๋ฆฌํ•˜๊ธฐ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ ์šฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑํ•œ ๋ชจ๋“  Vulkan ์ž์›๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ œ๋˜๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด VulkanEngine::cleanup()ํ•จ์ˆ˜๋กœ ์ด๋™ํ•ฉ์‹œ๋‹ค.

void VulkanEngine::cleanup()
{
	if (_isInitialized) {

		destroy_swapchain();

		vkDestroySurfaceKHR(_instance, _surface, nullptr);
		vkDestroyDevice(_device, nullptr);
		
		vkb::destroy_debug_utils_messenger(_instance, _debug_messenger);
		vkDestroyInstance(_instance, nullptr);
		SDL_DestroyWindow(_window);
	}
}

๊ฐ์ฒด๋“ค ๊ฐ„์—๋Š” ์˜์กด์„ฑ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋กœ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ์ƒ์„ฑํ•œ ์ˆœ์„œ์˜ ์—ญ์ˆœ์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒฝ์šฐ์—๋Š”, ๋งŒ์•ฝ ๊ตฌ์กฐ๋ฅผ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์ˆœ์„œ๋ฅผ ์•ฝ๊ฐ„ ๋ฐ”๊พธ์–ด๋„ ๋ฌธ์ œ๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

VkPhysicalDevice๋Š” Vulkan ์ž์› ๊ทธ ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ์Šคํ…œ ๋‚ด GPU๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ•ธ๋“ค์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” Window โ†’ Instance โ†’ Surface โ†’ Device โ†’ Swapchain ์ˆœ์„œ๋กœ ์ดˆ๊ธฐํ™”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์‚ญ์ œ๋Š” ์ •ํ™•ํžˆ ๊ทธ ๋ฐ˜๋Œ€ ์ˆœ์„œ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

์ง€๊ธˆ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ โ€œ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์Œโ€์—๋Š” ์—๋Ÿฌ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ฒฝ์šฐ์—๋Š” VkImage๋“ค์„ ๋ณ„๋„๋กœ ์‚ญ์ œํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ด๋ฏธ์ง€๋“ค์€ ์Šค์™‘์ฒด์ธ์— ์†Œ์œ ๋˜์–ด ์žˆ๊ณ , ์Šค์™‘์ฒด์ธ์„ ์‚ญ์ œํ•  ๋•Œ ํ•จ๊ป˜ ์‚ญ์ œ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ฒ€์ฆ ๋ ˆ์ด์–ด ์—๋Ÿฌ

๊ฒ€์ฆ ๋ ˆ์ด์–ด๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, ์ด๋ฒˆ์—๋Š” ์˜๋„์ ์œผ๋กœ ์ž˜๋ชป๋œ ์ˆœ์„œ๋กœ ํŒŒ๊ดด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

void VulkanEngine::cleanup()
{
	if (_isInitialized) {
		//ERROR - Instance destroyed before others
		vkDestroyInstance(_instance, nullptr);

		destroy_swapchain();

		vkDestroyDevice(_device, nullptr);
		vkDestroySurfaceKHR(_instance, _surface, nullptr);
		vkb::destroy_debug_utils_messenger(_instance, _debug_messenger);
		SDL_DestroyWindow(_window);
	}
}

์ง€๊ธˆ ์šฐ๋ฆฌ๋Š” VkInstance๋ฅผ VkDeivce์™€ VkInstance๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ VkSurfaceKHR๋ณด๋‹ค ๋จผ์ € ์‚ญ์ œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋กœ ๊ฒฝ๊ณ ๋ฅผ ์ถœ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

[ERROR: Validation]
Validation Error: [ VUID-vkDestroyInstance-instance-00629 ] Object 0: handle = 0x24ff02340c0, type = VK_OBJECT_TYPE_INSTANCE; Object 1: handle = 0xf8ce070000000002, type = VK_OBJECT_TYPE_SURFACE_KHR; | MessageID = 0x8b3d8e18 | OBJ ERROR : For VkInstance 0x24ff02340c0[], VkSurfaceKHR 0xf8ce070000000002[] has not been destroyed. The Vulkan spec states: All child objects created using instance must have been destroyed prior to destroying instance (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkDestroyInstance-instance-00629)

Vulkan ์ดˆ๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๊ณ  ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋„ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฏ€๋กœ, ์ด์ œ GPU๊ฐ€ ๋ฌด์–ธ๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ช…๋ น ๊ตฌ์กฐ์ฒด๋ฅผ ์ค€๋น„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Executing Vulkan Commands