Link

์—”์ง„์ด ์ ์  ์ปค์ง€๋ฉด์„œ ์Šฌ๋ผ์ด๋”, ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€๋Šฅํ•œ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ•„์š”ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ์— dear ImGui ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ui ์„ค์ •์ด๋‚˜ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์„ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ ๋„ ์Šฌ๋ผ์ด๋”๋‚˜ ๋ฒ„ํŠผ, ๊ธฐํƒ€ ํŽธ์ง‘๊ฐ€๋Šฅํ•œ ํ…์ŠคํŠธ์™€ ๊ฐ™์€ ์ƒํ˜ธ์ž‘์šฉ ์š”์†Œ์™€ ์ฐฝ์„ ๋งค์šฐ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

Immediate GPU ๋ช…๋ น

๋ณด๋ฅ˜ : ํ•ด๋‹น ์„น์…˜์€ ๋‚˜์ค‘์— ๋‹ค๋ฅธ ์œ„์น˜๋กœ ์ด๋™ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ตœ์‹  ๋ฒ„์ „์˜ ImGui์—์„œ๋Š” ์—…๋กœ๋“œ๋ฅผ ์œ„ํ•ด immediate ๋ช…๋ น์ด ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ, ์•„์ง์€ ์ถ”ํ›„ ํŠœํ† ๋ฆฌ์–ผ์—์„œ immediate ๋ช…๋ น์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

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

VulkanEngine ํด๋ž˜์Šค์— ์ด๋Ÿฌํ•œ ๊ตฌ์กฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•ฉ์‹œ๋‹ค.

class VulkanEngine{
public:
    // immediate submit structures
    VkFence _immFence;
    VkCommandBuffer _immCommandBuffer;
    VkCommandPool _immCommandPool;

	
	void immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function);

private:
	void init_imgui();
}

์šฐ๋ฆฌ๋Š” ํŽœ์Šค์™€ ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. immediate_submit ํ•จ์ˆ˜๋Š” ๋žŒ๋‹ค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก std::function์„ ์ฝœ๋ฐฑ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. init_imgui() ํ•จ์ˆ˜๋„ ์ถ”๊ฐ€ํ•˜๊ณ , init() ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๋น„์›Œ๋‘ก๋‹ˆ๋‹ค.

immediate submit์„ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ๋™๊ธฐํ™” ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋ฏ€๋กœ, init_commands() ํ•จ์ˆ˜์— ๋ช…๋ น ๋ถ€๋ถ„์„ ์—ฐ๊ฒฐํ•ฉ์‹œ๋‹ค.

void VulkanEngine::init_commands()
{
	VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &_immCommandPool));

	// allocate the command buffer for immediate submits
	VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_immCommandPool, 1);

	VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_immCommandBuffer));

	_mainDeletionQueue.push_function([=]() { 
	vkDestroyCommandPool(_device, _immCommandPool, nullptr);
	});

}

์ด๋Š” ํ”„๋ ˆ์ž„๋ณ„ ๋ช…๋ น์„ ์ž‘์„ฑํ•  ๋•Œ์™€ ๊ฐ™์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ญ์ œํ์— ์ง์ ‘ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ํŽœ์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” init_sync_structures()์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

void VulkanEngine::init_sync_structures()
{
	VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_immFence));
	_mainDeletionQueue.push_function([=]() { vkDestroyFence(_device, _immFence, nullptr); });
}

ํ”„๋ ˆ์ž„๋ณ„ ํŽœ์Šค์— ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์€ fenceCreateInfo๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ปค๋งจ๋“œ ๋ฒ„ํผ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด๋ฅผ ์‚ญ์ œ ํ์— ์ง์ ‘ ๋„ฃ์–ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด์ œ immediate_submit ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

void VulkanEngine::immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function)
{
	VK_CHECK(vkResetFences(_device, 1, &_immFence));
	VK_CHECK(vkResetCommandBuffer(_immCommandBuffer, 0));

	VkCommandBuffer cmd = _immCommandBuffer;

	VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);

	VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));

	function(cmd);

	VK_CHECK(vkEndCommandBuffer(cmd));

	VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd);
	VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr);

	// submit command buffer to the queue and execute it.
	//  _renderFence will now block until the graphic commands finish execution
	VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence));

	VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true, 9999999999));
}

์ด ํ•จ์ˆ˜๊ฐ€ GPU์—์„œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•˜์„ธ์š”.

๊ฑฐ์˜ ๋™์ผํ•˜์ง€๋งŒ ์ œ์ถœํ•  ๋•Œ ์Šค์™‘์ฒด์ธ๊ณผ ๋™๊ธฐํ™”๋Š” ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ์—…๋กœ๋“œ์™€ ๋ Œ๋”๋ง ๋ฃจํ”„ ์™ธ๋ถ€์—์„œ ๊ธฐํƒ€ โ€œ์ฆ‰๊ฐ์ ์ธโ€ ์ž‘์—…์— ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” ๊ทธ๋ž˜ํ”ฝ์Šค ํ์™€๋Š” ๋‹ค๋ฅธ ํ์—์„œ ์‹คํ–‰ํ•˜์—ฌ ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ๋ฉ”์ธ ๋ Œ๋”๋ง ๋ฃจํ”„์™€ ๊ฒน์ณ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ImGui ์„ค์ •

์ด์ œ ImGui๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ๋ช‡๊ฐ€์ง€ ํ—ค๋”๋ฅผ vk_engine.cpp์— ํฌํ•จํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_vulkan.h"

์ด๋Š” ์ฃผ์š” imgui ํ—ค๋”์™€, SDL 2 ๋ฐ Vulkan ๋ฐฑ์—”๋“œ ๊ตฌํ˜„ ํ—ค๋”์ž…๋‹ˆ๋‹ค.

์ด์ œ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

void VulkanEngine::init_imgui()
{
	// 1: create descriptor pool for IMGUI
	//  the size of the pool is very oversize, but it's copied from imgui demo
	//  itself.
	VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
		{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
		{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
		{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
		{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
		{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
		{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
		{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
		{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
		{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
		{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } };

	VkDescriptorPoolCreateInfo pool_info = {};
	pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
	pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
	pool_info.maxSets = 1000;
	pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes);
	pool_info.pPoolSizes = pool_sizes;

	VkDescriptorPool imguiPool;
	VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool));

	// 2: initialize imgui library

	// this initializes the core structures of imgui
	ImGui::CreateContext();

	// this initializes imgui for SDL
	ImGui_ImplSDL2_InitForVulkan(_window);

	// this initializes imgui for Vulkan
	ImGui_ImplVulkan_InitInfo init_info = {};
	init_info.Instance = _instance;
	init_info.PhysicalDevice = _chosenGPU;
	init_info.Device = _device;
	init_info.Queue = _graphicsQueue;
	init_info.DescriptorPool = imguiPool;
	init_info.MinImageCount = 3;
	init_info.ImageCount = 3;
	init_info.UseDynamicRendering = true;

	//dynamic rendering parameters for imgui to use
	init_info.PipelineRenderingCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO};
	init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1;
	init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &_swapchainImageFormat;
	

	init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;

	ImGui_ImplVulkan_Init(&init_info);

	ImGui_ImplVulkan_CreateFontsTexture();

	// add the destroy the imgui created structures
	_mainDeletionQueue.push_function([=]() {
		ImGui_ImplVulkan_Shutdown();
		vkDestroyDescriptorPool(_device, imguiPool, nullptr);
	});
}

์ด ํ•จ์ˆ˜๋ฅผ init_pipelines(); ์ดํ›„์— VulkanEngine::init()์˜ ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ImGui ๋ฐ๋ชจ์—์„œ ๊ฐ€์ ธ์˜จ ์˜ˆ์ œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ImGui์—์„œ ์š”๊ตฌํ•˜๋Š” ๊ตฌ์กฐ์ฒด, ์˜ˆ๋ฅผ ๋“ค์–ด ImGui ์ „์šฉ ๋””์Šคํฌ๋ฆฝํ„ฐ ํ’€ ๋“ฑ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ์˜ ๋””์Šคํฌ๋ฆฝํ„ฐ ํ’€์€ 1000๊ฐœ์˜ ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ๋‹ด๋Š”๋ฐ, ์ด๋Š” ์•ฝ๊ฐ„ ๊ณผํ•˜์ง€๋งŒ, ๊ณต๊ฐ„ ํ™œ์šฉ ๋ฉด์—์„œ ์•ฝ๊ฐ„ ๋น„ํšจ์œจ์ ์ผ ๋ฟ ๋ฌธ์ œ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  CreateContext(), Imgui_ImplSDL2_InitForVulkan๊ณผ ImGUI_ImplVulkan_Init์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋“ค์€ ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ImGui์˜ ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Vulkan์—์„œ๋Š” ๋””๋ฐ”์ด์Šค, ์ธ์Šคํ„ด์Šค, ํ์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์„ ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ ์€ UseDynamicRendering์„ true๋กœ ์„ค์ •ํ•˜๊ณ , ColorAttachmentFormat์„ ์Šค์™‘์ฒด์ธ ํฌ๋งท์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด Vulkan ๋ Œ๋” ํŒจ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋™์ ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ปดํ“จํŠธ ์…ฐ์ด๋”์™€๋Š” ๋‹ฌ๋ฆฌ dear imgui๋Š” ์Šค์™‘์ฒด์ธ์— ์ง์ ‘ ๋ Œ๋”๋ง๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ImGui_ImplVulkan_Init์„ ํ˜ธ์ถœํ•œ ํ›„, ํฐํŠธ ํ…์Šค์ณ๋ฅผ ์—…๋กœ๋“œ ํ•˜๊ธฐ ์œ„ํ•ด immediate submit์„ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์ด ์‹คํ–‰ ๋˜๋ฉด DestroyFontUploadObjects๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ImGui๊ฐ€ ์ž„์‹œ ๊ตฌ์กฐ์ฒด๋ฅผ ์ •๋ฆฌํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ •๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‚ญ์ œ ํ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Imgui ๋ Œ๋”๋ง ๋ฃจํ”„

Imgui๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์—ˆ์ง€๋งŒ ์ด๋ฅผ ๋ Œ๋”๋ง ๋ฃจํ”„์— ์—ฐ๊ฒฐํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ImGui ์ฝ”๋“œ๋ฅผ run()ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

//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;
        }
    }

    //send SDL event to imgui for handling
    ImGui_ImplSDL2_ProcessEvent(&e);
}

//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;
}		

// imgui new frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();

//some imgui UI to test
ImGui::ShowDemoWindow();

//make imgui calculate internal draw structures
ImGui::Render();

//our draw function
draw();

pollEvent ๋ฃจํ”„์—์„œ ๋ฐœ์ƒํ•œ SDL ์ด๋ฒคํŠธ๋ฅผ ImGui๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ, ImGui์˜ ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„์„ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์„ธ ๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์ด ์ˆ˜ํ–‰๋˜๋ฉด UI ๋ช…๋ น์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๋ฐ๋ชจ ์ฐฝ๋งŒ์„ ํ‘œ์‹œํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ImGui::Render()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ImGui๊ฐ€ ํ”„๋ ˆ์ž„์„ ๊ทธ๋ฆฌ๊ธฐ์— ํ•„์š”ํ•œ ์ •์ /๋“œ๋กœ์šฐ์ฝœ ๋“ฑ์„ ๊ณ„์‚ฐํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ ํ™”๋ฉด์— ๊ทธ๋ฆฌ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๋ Œ๋”๋ง์€ draw()ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ๊ณ„์†ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋™์  ๋ Œ๋”๋ง

ImGui๋Š” ๋ฉ”์‹œ์™€ ์…ฐ์ด๋”๋ฅผ ํฌํ•จํ•˜๋Š” ์‹ค์ œ GPU ๋“œ๋กœ์šฐ์ฝœ์„ ์‚ฌ์šฉํ•ด ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ํ˜„์žฌ ์šฐ๋ฆฌ๊ฐ€ ์ง„ํ–‰์ค‘์ธ ์ปดํ“จํŠธ ์…ฐ์ด๋” ๋ฐฉ์‹์˜ ๋“œ๋กœ์šฐ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๋„ํ˜•์„ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ๋ Œ๋” ํŒจ์Šค๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•˜์ง€๋งŒ, Vulkan 1.3๊ธฐ๋Šฅ์ธ ๋™์  ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ Œ๋” ํŒจ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. VkRenderPass ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ด VkCmdBeginRenderPass๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ๋Œ€์‹  ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋ฆด ๋•Œ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๋Š” VkRenderingInfo์™€ ํ•จ๊ป˜ VkBeginRendering์„ ํ˜ธ์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

VkRenderingInfo๋Š” ์šฐ๋ฆฌ์˜ ๊ทธ๋ฆด ์ด๋ฏธ์ง€์ธ ์—ฌ๋Ÿฌ VkRenderingAttachmentInfo๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ intializers์— ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

VkRenderingAttachmentInfo vkinit::attachment_info(
    VkImageView view, VkClearValue* clear ,VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/)
{
    VkRenderingAttachmentInfo colorAttachment {};
    colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
    colorAttachment.pNext = nullptr;

    colorAttachment.imageView = view;
    colorAttachment.imageLayout = layout;
    colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
    colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    if (clear) {
        colorAttachment.clearValue = *clear;
    }

    return colorAttachment;
}

attachment info๋ฅผ ์œ„ํ•ด ์„ ํƒ์ ์ธ ํฌ์ธํ„ฐ๋กœ clear value๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜, ํ˜น์€ ์ƒ๋žตํ•˜๊ณ  ๊ธฐ์กด ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€ ๋ทฐ์™€ ๋ ˆ์ด์•„์›ƒ์€ ํ‰์†Œ์ฒ˜๋Ÿผ ๋ชจ๋“  ๋ Œ๋”๋ง ๋ช…๋ น๊ณผ ์—ฐ๊ฒฐํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ loadOP์™€ storeOP์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํ•ด๋‹น ์–ดํƒœ์น˜๋จผํŠธ๊ฐ€ ๋ Œ๋”ํŒจ์Šค(๋™์  ๋ Œ๋”๋ง๊ณผ ์ผ๋ฐ˜ ๋ Œ๋”ํŒจ์Šค ๋ชจ๋‘)์—์„œ ์‚ฌ์šฉ๋  ๋•Œ ๋ Œ๋” ํƒ€๊ฒŸ์— ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. load ์„ ํƒ์ง€์—์„œ๋Š” LOAD๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ธฐ์กด ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. Clear๋Š” ์‹œ์ž‘ ์‹œ ์„ค์ •๋œ clear value๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋ฉฐ, ๋ชจ๋“  ํ”ฝ์…€์„ ๊ต์ฒดํ•  ๋•Œ์—๋Š” dont-care๋กœ ์„ค์ •ํ•ด GPU๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๊ฐ’์„ ์ฝ์ง€ ์•Š๊ฒŒ ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ์˜ store op๋Š” ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด store๋กœ ํ•˜๋“œ์ฝ”๋”ฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

attachment info๊ฐ€ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฏ€๋กœ VkRenderingInfo๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ImGui๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋ Œ๋”ํŒจ์Šค๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ draw_imgui()๋ฅผ VulkanEngine ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView)
{
	VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
	VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr);

	vkCmdBeginRendering(cmd, &renderInfo);

	ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd);

	vkCmdEndRendering(cmd);
}

๊ทธ๋ฆด ์‚ฌ๊ฐํ˜•์˜ ์˜์—ญ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด render extent๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๋ฉฐ, ์ƒ‰์ƒ ์–ดํƒœ์น˜๋จผํŠธ์™€ ๊นŠ์ด ์–ดํƒœ์น˜๋จผํŠธ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊นŠ์ด ์–ดํƒœ์น˜๋จผํŠธ๋Š” ๋‹น์žฅ์€ ํ•„์š” ์—†์ง€๋งŒ ์ดํ›„์— ๋‹ค๋ฃฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ดํ›„์—๋Š” draw() ํ•จ์ˆ˜์—์„œ ํ˜ธ์ถœํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

	// execute a copy from the draw image into the swapchain
	vkutil::copy_image_to_image(cmd, _drawImage.image, _swapchainImages[swapchainImageIndex], _drawExtent, _swapchainExtent);

	// set swapchain image layout to Attachment Optimal so we can draw it
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);

	//draw imgui into the swapchain image
	draw_imgui(cmd,  _swapchainImageViews[swapchainImageIndex]);

	// set swapchain image layout to Present so we can draw it
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);

	//finalize the command buffer (we can no longer add commands, but it can now be executed)
	VK_CHECK(vkEndCommandBuffer(cmd));

copy_image ๋ช…๋ น์€ ์ด์ „๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ดํ›„์˜ ๋ช…๋ น๋“ค์„ VkEndCommandBuffer ํ˜ธ์ถœ๊นŒ์ง€์˜ ๊ตฌ๊ฐ„์œผ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.

์ด์ „์—๋Š” ์Šค์™‘์ฒด์ธ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ „์†ก ๋ ˆ์ด์•„์›ƒ์—์„œ ํ‘œ์‹œ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ „ํ™˜ํ–ˆ์ง€๋งŒ, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL๋กœ ๋Œ€์‹  ๋ฐ”๊ฟ€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์—ฌ๊ธฐ์ฒ˜๋Ÿผ ๋ Œ๋”๋ง ๋ช…๋ น์„ ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ์ž…๋‹ˆ๋‹ค.

๊ทธ ๋‹ค์Œ, VkRenderingInfo๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๋‹จ์ผ ์ƒ‰์ƒ ์–ดํƒœ์น˜๋จผํŠธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒ‰์ƒ ์–ดํƒœ์น˜๋จผํŠธ๋Š” ์šฐ๋ฆฌ๊ฐ€ ํƒ€๊ฒŒํŒ…ํ•œ ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.

์ด์ œ VkRenderInfo๋ฅผ ์ค€๋น„ํ–ˆ์œผ๋ฏ€๋กœ vkCmdBeginRendering์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ Œ๋” ํŒจ์Šค๋ฅผ ์‹œ์ž‘ํ•˜๋ฉฐ ์ด์ œ ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ImGui Vulkan ๋ฐฑ์—”๋“œ์—์„œ ์šฐ๋ฆฌ์˜ ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด, ImGui๊ฐ€ ์ž์‹ ์˜ ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์„ ํ•ด๋‹น ๋ฒ„ํผ์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์™„๋ฃŒ๋˜๋ฉด vkCmdEndRendering์„ ํ˜ธ์ถœํ•ด ๋ Œ๋” ํŒจ์Šค๋ฅผ ๋๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ดํ›„, ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€๋ฅผ ์–ดํƒœ์น˜๋จผํŠธ ์ตœ์ ์—์„œ ํ‘œ์‹œ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜๊ณ  ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ๋๋ƒ…๋‹ˆ๋‹ค.

์ด ์‹œ์ ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•ด๋ณธ๋‹ค๋ฉด ImGui ๋ฐ๋ชจ์ฐฝ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋””๋ฒ„๊น… UI๋ฅผ ์…ฐ์ด๋”์™€ ์—ฐ๊ฒฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Push Constants and new shaders