Link

์šฐ์„  ํ•„์š”ํ•œ ๋™๊ธฐํ™” ๊ตฌ์กฐ์ฒด๋ฅผ FrameData ๊ตฌ์กฐ์ฒด์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด์— ์ƒˆ๋กœ์šด ๋ฉค๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

struct FrameData {
	VkSemaphore _swapchainSemaphore, _renderSemaphore;
	VkFence _renderFence;
};

2๊ฐœ์˜ ์„ธ๋งˆํฌ์–ด์™€ ๋ฉ”์ธ ๋ Œ๋”๋ง ํŽœ์Šค๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์ƒ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

_swapchainSemaphore๋Š” ๋ Œ๋”๋ง ๋ช…๋ น์ด ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€ ์š”์ฒญ์„ ๋Œ€๊ธฐํ•˜๋„๋ก ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. _renderSemaphore๋Š” ๊ทธ๋ฆฌ๊ธฐ๊ฐ€ ๋๋‚ฌ์„ ๋•Œ ์ด๋ฏธ์ง€๋ฅผ OS์— ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์„ ์ œ์–ดํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค._renderFence๋Š” ์ฃผ์–ด์ง„ ํ”„๋ ˆ์ž„์˜ ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์ด ๋๋‚  ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด๋ด…์‹œ๋‹ค. vk_initializers.cpp ์ฝ”๋“œ์— ์žˆ๋Š” VkFenceCreateInfo์™€ VkSemaphoreCreateInfo๋ฅผ ์ž‘์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

VkFenceCreateInfo vkinit::fence_create_info(VkFenceCreateFlags flags /*= 0*/)
{
    VkFenceCreateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    info.pNext = nullptr;

    info.flags = flags;

    return info;
}

VkSemaphoreCreateInfo vkinit::semaphore_create_info(VkSemaphoreCreateFlags flags /*= 0*/)
{
    VkSemaphoreCreateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    info.pNext = nullptr;
    info.flags = flags;
    return info;
}

๋‘ ๊ตฌ์กฐ์ฒด ๋ชจ๋‘ ๊ฝค ๋‹จ์ˆœํ•˜๋ฉฐ, ํŠน๋ณ„ํ•œ ์˜ต์…˜ ์—†์ด ๋ช‡ ๊ฐ€์ง€ ํ”Œ๋ž˜๊ทธ๋งŒ ์„ค์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ๋ณด๋ ค๋ฉด ๋‹ค์Œ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”. VkFenceCreateInfo, VkSemaphoreCreateInfo

์ด์ œ ์‹ค์ œ๋กœ ์ƒ์„ฑํ•ด๋ด…์‹œ๋‹ค.

void VulkanEngine::init_sync_structures()
{
	//create syncronization structures
	//one fence to control when the gpu has finished rendering the frame,
	//and 2 semaphores to syncronize rendering with swapchain
	//we want the fence to start signalled so we can wait on it on the first frame
	VkFenceCreateInfo fenceCreateInfo = vkinit::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT);
	VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphore_create_info();

	for (int i = 0; i < FRAME_OVERLAP; i++) {
		VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_frames[i]._renderFence));

		VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._swapchainSemaphore));
		VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._renderSemaphore));
	}
}

ํŽœ์Šค์—์„œ VK_FENCE_CREATE_SIGNALED_BIT๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ํ•ฉ๋‹ˆ๋‹ค. ํŽœ์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ž ๋งˆ์ž ๋Œ€๊ธฐํ•˜๊ฒŒ ํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ด๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ฒซ ํ”„๋ ˆ์ž„์—์„œ WaitFences๋ฅผ ํ˜ธ์ถœ ํ•  ๋•Œ GPU๊ฐ€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋„ ์ „์— ํ˜ธ์ถœ ๋˜๋ฏ€๋กœ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ž ๊น๋‹ˆ๋‹ค.

๊ฐ ํ”„๋ ˆ์ž„๋งˆ๋‹ค 3๊ฐœ์˜ ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•ด ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

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

ํŽœ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GPU์˜ ์ž‘์—… ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์œผ๋กœ ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

void VulkanEngine::draw()
{
	// wait until the gpu has finished rendering the last frame. Timeout of 1
	// second
	VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000));
	VK_CHECK(vkResetFences(_device, 1, &get_current_frame()._renderFence));
}

vkWaitForFences()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GPU๊ฐ€ ์ž‘์—…์„ ๋๋‚ผ ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐํ•œ ํ›„, ํŽœ์Šค๋ฅผ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค. ํŽœ์Šค๋Š” ์‚ฌ์šฉ ํ›„ ๋ฐ˜๋“œ์‹œ ๋ฆฌ์…‹ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ํŽœ์Šค๋ฅผ ๋ฆฌ์…‹ํ•˜์ง€ ์•Š๊ณ  ์—ฌ๋Ÿฌ GPU ๋ช…๋ น์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

WaitFences ํ˜ธ์ถœ์˜ ์‹œ๊ฐ„ ์ œํ•œ์€ 1์ดˆ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋‚˜๋…ธ์ดˆ ๋‹จ์œ„๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ œํ•œ์„ 0์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด GPU๊ฐ€ ๋ช…๋ น์„ ์‹คํ–‰์ค‘์ธ์ง€ ํ™•์ธํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ๋Š” ์Šค์™‘์ฒด์ธ์—๊ฒŒ ์ด๋ฏธ์ง€ ์ธ๋ฑ์Šค๋ฅผ ์š”์ฒญํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

	//request image from the swapchain
	uint32_t swapchainImageIndex;
	VK_CHECK(vkAcquireNextImageKHR(_device, _swapchain, 1000000000, get_current_frame()._swapchainSemaphore, nullptr, &swapchainImageIndex));

vkAcquireNextImageKHR์„ ์‚ฌ์šฉํ•ด ์Šค์™‘์ฒด์ธ์—์„œ ์ด๋ฏธ์ง€ ์ธ๋ฑ์Šค๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€๊ฐ€ ์—†๋‹ค๋ฉด ์„ค์ •ํ•œ ์‹œ๊ฐ„ ์ œํ•œ๊นŒ์ง€ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ œํ•œ ์‹œ๊ฐ„์€ 1์ดˆ์ž…๋‹ˆ๋‹ค.

_swapchainSemaphore๋ฅผ ์–ด๋–ป๊ฒŒ ์ „๋‹ฌํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์„ธ์š”. ์ด๋Š” ์Šค์™‘์ฒด์ธ์—์„œ ๋ Œ๋”๋งํ•  ์ด๋ฏธ์ง€๋ฅผ ์ค€๋น„ํ•˜๋Š” ์ž‘์—…๊ณผ ๋‹ค๋ฅธ ์—ฐ์‚ฐ๋“ค์„ ๋™๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜๋œ ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•ด ๊ทธ๋ฆด ์Šค์™‘์ฒด์ธ์˜ ์ด๋ฏธ์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋ Œ๋”๋ง ๋ช…๋ น์„ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋ ˆ์ž„์˜ ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ๋ฆฌ์…‹ํ•˜๊ณ  ์‹œ์ž‘(begin)ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์‚ฌ์šฉํ•  ๋˜ ๋‹ค๋ฅธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

VkCommandBufferBeginInfo vkinit::command_buffer_begin_info(VkCommandBufferUsageFlags flags /*= 0*/)
{
    VkCommandBufferBeginInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    info.pNext = nullptr;

    info.pInheritanceInfo = nullptr;
    info.flags = flags;
    return info;
}

์ปค๋งจ๋“œ ๋ฒ„ํผ๊ฐ€ ์‹œ์ž‘๋˜๋ฉด, ๋ช‡ ๊ฐ€์ง€ ์†์„ฑ์„ ๋‹ด์€ info ๊ตฌ์กฐ์ฒด๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์ƒ์† ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ nullptr๋กœ ์„ค์ •ํ•˜์ง€๋งŒ, flags ์†์„ฑ์€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

VkCommandBufferBeginInfo์—์„œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

VulkanEngine::draw()๋กœ ๋Œ์•„๊ฐ€, ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ๋ฆฌ์…‹ํ•˜๊ณ  ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

	//naming it cmd for shorter writing
	VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer;

	// now that we are sure that the commands finished executing, we can safely
	// reset the command buffer to begin recording again.
	VK_CHECK(vkResetCommandBuffer(cmd, 0));

	//begin the command buffer recording. We will use this command buffer exactly once, so we want to let vulkan know that
	VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);

	//start the command buffer recording
	VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));

FrameData ๊ตฌ์กฐ์ฒด์—์„œ ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ cmd๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ๋ณต์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ดํ›„ ์ฝ”๋“œ์—์„œ ์ฐธ์กฐ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. Vulkan ํ•ธ๋“ค์€ 64๋น„ํŠธ ํ•ธ๋“ค ํ˜น์€ ํฌ์ธํ„ฐ์ผ ๋ฟ์ด๋ฏ€๋กœ ๋ณต์‚ฌํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Š” Vulkan์ด ๊ด€๋ฆฌํ•จ์„ ๊ธฐ์–ตํ•˜์„ธ์š”.

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

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

์ฆ‰, ๋ Œ๋”ํŒจ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ปค๋งจ๋“œ ๋ฒ„ํผ ๋‚ด๋ถ€์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•  ์ˆ˜๋‹จ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ vk_images.h์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#pragma once 

#include <vulkan/vulkan.h>

namespace vkutil {

void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout);
}

์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐ์—๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ ˆ์ด์•„์›ƒ๊ณผ ์ƒˆ๋กœ์šด ๋ ˆ์ด์•„์›ƒ๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Vulkan 1.3์— ํฌํ•จ๋œ Synchronization 2 ๊ธฐ๋Šฅ ํ™•์žฅ์„ ์‚ฌ์šฉํ•ด ํŒŒ์ดํ”„๋ผ์ธ ๋ฐฐ๋ฆฌ์–ด๋ฅผ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํŒŒ์ดํ”„๋ผ์ธ ๋ฐฐ๋ฆฌ์–ด๋Š” ๋ช…๋ น ๊ฐ„ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์ž‘์—…์„ ๋™๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜, ํ•œ ๋ช…๋ น์ด ์ด๋ฏธ์ง€์— ๊ทธ๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ๋ช…๋ น์ด ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ์ฝ๋„๋ก ์ œ์–ดํ•˜๋Š” ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•จ์ˆ˜๋ฅผ vk_images.cpp์— ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#include <vk_initializers.h>

void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout)
{
    VkImageMemoryBarrier2 imageBarrier {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2};
    imageBarrier.pNext = nullptr;

    imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
    imageBarrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT;
    imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
    imageBarrier.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT;

    imageBarrier.oldLayout = currentLayout;
    imageBarrier.newLayout = newLayout;

    VkImageAspectFlags aspectMask = (newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
    imageBarrier.subresourceRange = vkinit::image_subresource_range(aspectMask);
    imageBarrier.image = image;

    VkDependencyInfo depInfo {};
    depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
    depInfo.pNext = nullptr;

    depInfo.imageMemoryBarrierCount = 1;
    depInfo.pImageMemoryBarriers = &imageBarrier;

    vkCmdPipelineBarrier2(cmd, &depInfo);
}

VkImageMemoryBarrier2๋Š” ์ฃผ์–ด์ง„ ์ด๋ฏธ์ง€ ๋ฐฐ๋ฆฌ์–ด์˜ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ณ€๊ฒฝ ์ „ ๋ ˆ์ด์•„์›ƒ๊ณผ ๋ณ€๊ฒฝ ํ›„ ๋ ˆ์ด์•„์›ƒ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. StageMask๋Š” VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” GPU ํŒŒ์ดํ”„๋ผ์ธ์ด ์•ฝ๊ฐ„ ๋ฉˆ์ถ”๊ฒŒ ํ•˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋ช‡ ๋ฒˆ๋งŒ ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ›„์ฒ˜๋ฆฌ ๊ณผ์ •์—์„œ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ ํ”ผํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ์ˆ˜ํ–‰ํ•˜๋ ค๋Š” ์ž‘์—…์— ๋” ์ ์ ˆํ•œ StageMask๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐ๋ฆฌ์–ด์˜ VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT์„ค์ •์€ GPU์˜ ๋ชจ๋“  ๋ช…๋ น์ด ๋ฐฐ๋ฆฌ์–ด์—์„œ ์™„์ „ํžˆ ๋ฉˆ์ถ˜๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์กฐ๊ธˆ ๋” ์„ธ๋ฐ€ํ•œ StageMask๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, GPU ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ถ€๋ถ„์ ์œผ๋กœ ๊ฒน์ณ์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. AccessMask๋„ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” GPU์˜ ํŠน์ • ์ž์› ์ ‘๊ทผ์„ ์ œํ•œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” VK_ACCESS_2_MEMORY_WRITE_BIT๋ฅผ ์†Œ์Šค๋กœ ์„ค์ •ํ•˜๊ณ , VK_ACCESS_2_MEMORY_READ_BIT๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ ์ ˆํ•œ ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ํŒŒ์ดํ”„๋ผ์ธ ๋ฐฐ๋ฆฌ์–ด๊ฐ€ ์“ฐ์ด๋Š” ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋” ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด Khronos Vulkan Documentation: Syncronization examples์—์„œ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ์ด ๋ฐฉ์‹์ด ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ, ํ•„์š”ํ•˜๋‹ค๋ฉด ๋” ์ •๋ฐ€ํ•˜๊ณ  ๊ฐ€๋ฒผ์šด ๋ณ€ํ™˜ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐฐ๋ฆฌ์–ด ์„ค์ •์˜ ์ผ๋ถ€๋กœ VkImageSubresourceRange๋„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • ์ด๋ฏธ์ง€์˜ ์ผ๋ถ€๋งŒ ๋ฐฐ๋ฆฌ์–ด๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ฐฐ์—ด ์ด๋ฏธ์ง€๋‚˜ ๋ฐ‰๋งต ์ด๋ฏธ์ง€์—์„œ ํŠน์ • ๋ ˆ์ด์–ด๋‚˜ ๋ฐ‰๋งต ๋‹จ๊ณ„์—๋งŒ ๋ฐฐ๋ฆฌ์–ด๋ฅผ ์ ์šฉํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ์˜ˆ์ œ์—์„œ๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ๋ฐ‰๋งต ๋‹จ๊ณ„์™€ ๋ชจ๋“  ๋ ˆ์ด์–ด๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

VkImageSubresourceRange vkinit::image_subresource_range(VkImageAspectFlags aspectMask)
{
    VkImageSubresourceRange subImage {};
    subImage.aspectMask = aspectMask;
    subImage.baseMipLevel = 0;
    subImage.levelCount = VK_REMAINING_MIP_LEVELS;
    subImage.baseArrayLayer = 0;
    subImage.layerCount = VK_REMAINING_ARRAY_LAYERS;

    return subImage;
}

์ด ๊ตฌ์กฐ์ฒด์—์„œ ์ค‘์š”ํ•œ ์š”์†Œ๋Š” AspectMask์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ƒ‰์ƒ ์ด๋ฏธ์ง€์˜ ๊ฒฝ์šฐ VK_IMAGE_ASPECT_COLOR_BIT, ๊นŠ์ด ์ด๋ฏธ์ง€์˜ ๊ฒฝ์šฐ VK_IMAGE_ASPECT_DEPTH_BIT๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ๊นŠ์ด ๋ฒ„ํผ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•  VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL์„ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋“  ๊ฒฝ์šฐ์— VK_IMAGE_ASPECT_COLOR_BIT๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฒ”์œ„(VkImageSubresourceRange)์™€ ๋ฐฐ๋ฆฌ์–ด(VkImageMemoryBarrier2)๋ฅผ ์„ค์ •ํ•œ ํ›„, ์ด๋ฅผ VkDependencyInfo ๊ตฌ์กฐ์ฒด์— ๋‹ด์•„ VkCmdPipelineBarrier2๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. VkImageMemoryBarrier2 ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ VkDependencyInfo์— ์ถ”๊ฐ€ํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์˜ ๋ ˆ์ด์•„์›ƒ์„ ๋™์‹œ์— ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ๋™์‹œ์— ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ๋ฆฌ์–ด๋ฅผ ์ ์šฉํ•  ๊ฒฝ์šฐ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ€ํ™˜ ํ•จ์ˆ˜๊ฐ€ ๊ตฌํ˜„๋˜์—ˆ์œผ๋ฏ€๋กœ, ์ด์ œ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž‘์„ฑํ•œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด vk_images.h๋ฅผ vk_engine.cpp ์ƒ๋‹จ์— ํฌํ•จํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


	//make the swapchain image into writeable mode before rendering
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);

	//make a clear-color from frame number. This will flash with a 120 frame period.
	VkClearColorValue clearValue;
	float flash = std::abs(std::sin(_frameNumber / 120.f));
	clearValue = { { 0.0f, 0.0f, flash, 1.0f } };

	VkImageSubresourceRange clearRange = vkinit::image_subresource_range(VK_IMAGE_ASPECT_COLOR_BIT);

	//clear image
	vkCmdClearColorImage(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);

	//make the swapchain image into presentable mode
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex],VK_IMAGE_LAYOUT_GENERAL, 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));

๋จผ์ € ์Šค์™‘์ฒด์ธ์˜ ์ด๋ฏธ์ง€๋ฅผ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. VK_IMAGE_LAYOUT_UNDEFINED๋Š” ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ ๋ ˆ์ด์•„์›ƒ์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•ด๋‹น ๋ ˆ์ด์•„์›ƒ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€์˜ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๊ฐ€ ์ค‘์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, GPU๊ฐ€ ์ด๋ฅผ ๋ฎ์–ด ์จ๋„ ์ƒ๊ด€์—†์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋ชฉํ‘œ ๋ ˆ์ด์•„์›ƒ์€ VK_IMAGE_LAYOUT_GENERAL์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋ฒ”์šฉ ๋ชฉ์ ์˜ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ, ์ด๋ฏธ์ง€๋กœ๋ถ€ํ„ฐ ์ฝ๊ธฐ์“ฐ๊ธฐ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ Œ๋”๋ง์— ์ตœ์ ํ™”๋œ ๋ ˆ์ด์•„์›ƒ์€ ์•„๋‹ˆ์ง€๋งŒ, vkCmdClearColorImage๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ปดํ“จํŠธ ์…ฐ์ด๋”์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์“ฐ๊ธฐํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฏธ์ง€ ๋ ˆ์ด์•„์›ƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ฝ๊ธฐ ์ „์šฉ ์ด๋ฏธ์ง€ ํ˜น์€ ๋ž˜์Šคํ„ฐํ™” ๋ช…๋ น์— ์‚ฌ์šฉ๋˜๋Š” ์ด๋ฏธ์ง€๋ฅผ ์›ํ•œ๋‹ค๋ฉด ๋” ๋‚˜์€ ์„ ํƒ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€ ๋ ˆ์ด์•„์›ƒ์— ๊ด€ํ•ด ์„ธ๋ถ€์‚ฌํ•ญ์„ ํ™•์ธํ•ด๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด Vulkan Spec: image layouts์„ ํ™•์ธํ•ด๋ณด์„ธ์š”.

์ด์ œ _frameNumber๋ฅผ ์‚ฌ์šฉํ•œ ๊ธฐ๋ณธ์ ์ธ ๊ณต์‹์„ ํ†ตํ•ด ์ดˆ๊ธฐํ™” ์ƒ‰์ƒ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. sinํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ํŒŒ๋ž€์ƒ‰๊ณผ ๊ฒ€์€์ƒ‰ ๊ฐ„ ๋ณด๊ฐ„ํ•˜๋ฉฐ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.

vkCmdClearColorImage๋Š” ์„ธ๊ฐ€์ง€ ์ฃผ์š” ์ธ์ž๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์ด๋ฏธ์ง€์ด๋ฉฐ, ์Šค์™‘์ฒด์ธ์—์„œ ๊ฐ€์ ธ์˜จ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ์ดˆ๊ธฐํ™” ์ƒ‰์ƒ์ž…๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์€ ์ด๋ฏธ์ง€์˜ ์–ด๋А ๋ถ€๋ถ„์„ ์ดˆ๊ธฐํ™”ํ•  ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” SubresourceRange๋ฅผ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ธฐ๋ณธ ImageSubresourceRange๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ดˆ๊ธฐํ™” ๋ช…๋ น์ด ์‹คํ–‰ํ•œ ํ›„, ์ด๋ฏธ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ฐ”๊พธ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ๋Š” vkEndCommandBuffer๋ฅผ ํ˜ธ์ถœํ•ด ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ GPU์— ์ œ์ถœํ•  ์ค€๋น„๊ฐ€ ๋œ ์ปค๋งจ๋“œ ๋ฒ„ํผ๊ฐ€ ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ VkQueueSubmit์„ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ๋™๊ธฐํ™” ๊ตฌ์กฐ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ์Šค์™‘์ฒด์ธ๊ณผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ช…๋ น์„ ์ œ์ถœํ•  ๋•Œ๋Š” vkQueueSubmit2๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” synchronization-2์˜ ์ผ๋ถ€๋กœ, Vulkan 1.0 ๋ฒ„์ „์—์„œ์˜ VkQueueSubmit์ด ๊ฐœ์„ ๋œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ œ์ถœ์— ํ•„์š”ํ•œ ์„ธ๋งˆํฌ์–ด์˜ ์ •๋ณด๋ฅผ ๋‹ด๋Š” VkSubmitInfo2๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ์‹คํ–‰ ์™„๋ฃŒ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํŽœ์Šค๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. VkSubmitInfo2๋Š” ์‚ฌ์šฉ๋˜๋Š” ๊ฐ๊ฐ์˜ ์„ธ๋งˆํฌ์–ด์— ๋Œ€ํ•ด VkSemaphoreSubmitInfo๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ์ œ์ถœ๋  ์ปค๋งจ๋“œ ๋ฒ„ํผ๋“ค์— ๋Œ€ํ•ด์„œ๋Š” VkCommandBufferSubmitInfo๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ตฌ์กฐ์ฒด๋“ค์„ ์ƒ์„ฑํ•˜๋Š” vkinitํ•จ์ˆ˜๋“ค์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

VkSemaphoreSubmitInfo vkinit::semaphore_submit_info(VkPipelineStageFlags2 stageMask, VkSemaphore semaphore)
{
	VkSemaphoreSubmitInfo submitInfo{};
	submitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
	submitInfo.pNext = nullptr;
	submitInfo.semaphore = semaphore;
	submitInfo.stageMask = stageMask;
	submitInfo.deviceIndex = 0;
	submitInfo.value = 1;

	return submitInfo;
}

VkCommandBufferSubmitInfo vkinit::command_buffer_submit_info(VkCommandBuffer cmd)
{
	VkCommandBufferSubmitInfo info{};
	info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
	info.pNext = nullptr;
	info.commandBuffer = cmd;
	info.deviceMask = 0;

	return info;
}

VkSubmitInfo2 vkinit::submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo,
    VkSemaphoreSubmitInfo* waitSemaphoreInfo)
{
    VkSubmitInfo2 info = {};
    info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
    info.pNext = nullptr;

    info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1;
    info.pWaitSemaphoreInfos = waitSemaphoreInfo;

    info.signalSemaphoreInfoCount = signalSemaphoreInfo == nullptr ? 0 : 1;
    info.pSignalSemaphoreInfos = signalSemaphoreInfo;

    info.commandBufferInfoCount = 1;
    info.pCommandBufferInfos = cmd;

    return info;
}

command_buffer_submit_info๋Š” ์ปค๋งจ๋“œ ๋ฒ„ํผ๋งŒ์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ •๋ณด๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, deviceMask๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ 0์œผ๋กœ ๋‚จ๊ฒจ๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

semaphore_submit_info๋Š” StageMask๋ฅผ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” transition_image ํ•จ์ˆ˜์—์„œ ๋ณด์•˜๋˜ ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด์™ธ์—๋Š” ์„ธ๋งˆํฌ์–ด ํ•ธ๋“ค๋งŒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋””๋ฐ”์ด์Šค ์ธ๋ฑ์Šค ์ธ์ž๋Š” ์—ฌ๋Ÿฌ GPU๊ฐ„ ์„ธ๋งˆํฌ์–ด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•„์š”ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์„๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  value๋Š” ํƒ€์ž„๋ผ์ธ ์„ธ๋งˆํฌ์–ด์— ์‚ฌ์šฉ๋˜๋ฉฐ, ์ด๋Š” ์ด์ง„ ์ƒํƒœ ๋Œ€์‹  ์นด์šดํ„ฐ ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ ๋™์ž‘ํ•˜๋Š” ํŠน์ˆ˜ํ•œ ์„ธ๋งˆํฌ์–ด์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ฏ€๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ 1๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

submit_info๋Š” ๋ชจ๋“  ๊ฒƒ์„ ํ•˜๋‚˜๋กœ ๋ฌถ์Šต๋‹ˆ๋‹ค. ์ด๋Š” VkCommandSubmitInfo์™€ signalSemaphoreInfo,waitSemaphoreInfo๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๊ธฐ์™€ ์‹ ํ˜ธ์— ๊ฐ๊ฐ 1๊ฐœ์˜ ์„ธ๋งˆํฌ์–ด์”ฉ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด์ง€๋งŒ, ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์—ฌ๋Ÿฌ ์„ธ๋งˆํฌ์–ด๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ์กฐ์ฒด์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ •๋ณด๋Š” ๋‹ค์Œ ๋งํฌ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. VkCommandBufferSubmitInfo, VkSemaphoreSubmitInfo, VkSubmitInfo2

์ด์ œ ์ดˆ๊ธฐํ™”ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์œผ๋‹ˆ ์ œ์ถœ์„ ์ˆ˜ํ–‰ํ•ด๋ด…์‹œ๋‹ค.

	//prepare the submission to the queue. 
	//we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is ready
	//we will signal the _renderSemaphore, to signal that rendering has finished

	VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd);	
	
	VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore);
	VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore);	
	
	VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo,&signalInfo,&waitInfo);	

	//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, get_current_frame()._renderFence));

๋จผ์ € ํ•„์š”ํ•œ info ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด vkQueueSubmit2๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.cmdInfo์—๋Š” ์ž‘์„ฑํ•œ ์ปค๋งจ๋“œ ๋ฒ„ํผ๋ฅผ ์ „๋‹ฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. waitInfo์—๋Š” ํ˜„์žฌ ํ”„๋ ˆ์ž„์˜ ์Šค์™‘์ฒด์ธ ์„ธ๋งˆํฌ์–ด๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. vkAcquireNextImageKHR์„ ํ˜ธ์ถœํ•  ๋•Œ, ๊ฐ™์€ ์„ธ๋งˆํฌ์–ด๋ฅผ ์‹ ํ˜ธํ•˜๋„๋ก ์„ค์ •ํ–ˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€๊ฐ€ ์ค€๋น„๋  ๋•Œ ๊นŒ์ง€ ํ•ด๋‹น ๋ช…๋ น์ด ์‹คํ–‰๋˜์ง€ ์•Š๊ฒŒ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

signalInfo์—๋Š” ํ˜„์žฌ ํ”„๋ ˆ์ž„์˜ _renderSemaphore๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ™”๋ฉด์— ์ด๋ฏธ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์ž‘์—…๊ณผ ๋™๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

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

๋งˆ์ง€๋ง‰์œผ๋กœ ํ•  ์ผ์€ ๊ทธ๋ฆฐ ์ด๋ฏธ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

	//prepare present
	// this will put the image we just rendered to into the visible window.
	// we want to wait on the _renderSemaphore for that, 
	// as its necessary that drawing commands have finished before the image is displayed to the user
	VkPresentInfoKHR presentInfo = {};
	presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
	presentInfo.pNext = nullptr;
	presentInfo.pSwapchains = &_swapchain;
	presentInfo.swapchainCount = 1;

	presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore;
	presentInfo.waitSemaphoreCount = 1;

	presentInfo.pImageIndices = &swapchainImageIndex;

	VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo));

	//increase the number of frames drawn
	_frameNumber++;

vkQueuePresent๋Š” info ๊ตฌ์กฐ์ฒด๋Š” ํ ์ œ์ถœ ๊ตฌ์กฐ์ฒด์™€ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ์ฒด์—๋Š” ์„ธ๋งˆํฌ์–ด์˜ ํฌ์ธํ„ฐ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋ฏธ์ง€ ์ธ๋ฑ์Šค์™€ ์Šค์™‘์ฒด์ธ์˜ ์ธ๋ฑ์Šค๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. _renderSemaphore๋ฅผ ๋Œ€๊ธฐํ•˜๊ณ  ์ด๋ฅผ ์Šค์™‘์ฒด์ธ๊ณผ ์—ฐ๊ฒฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด์ „์— ์ œ์ถœ๋œ ๋ Œ๋”๋ง ๋ช…๋ น์ด ์™„๋ฃŒ๋  ๋•Œ ๊นŒ์ง€ ์ด๋ฏธ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์˜ ๋์—์„œ ํ”„๋ ˆ์ž„ ์นด์šดํ„ฐ๋ฅผ ์ฆ๊ฐ€์‹œ์ผœ์ค๋‹ˆ๋‹ค.

์ด๋กœ์จ ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚จ์€ ๊ฒƒ์€ ๋™๊ธฐํ™” ์ž์›์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

	for (int i = 0; i < FRAME_OVERLAP; i++) {
	
		//already written from before
		vkDestroyCommandPool(_device, _frames[i]._commandPool, nullptr);

		//destroy sync objects
		vkDestroyFence(_device, _frames[i]._renderFence, nullptr);
		vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr);
		vkDestroySemaphore(_device ,_frames[i]._swapchainSemaphore, nullptr);
	}

์ด์ œ ์—”์ง„์„ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”. ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค๋ฉด ํŒŒ๋ž€ ํ™”๋ฉด์ด ๊นœ๋นก์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฒ€์ฆ ๋ ˆ์ด์–ด๋ฅผ ํ™•์ธํ•ด๋ณด์„ธ์š”.

์ด๊ฒƒ์œผ๋กœ 1์žฅ์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ปดํ“จํŠธ ์…ฐ์ด๋”๋ฅผ ์‚ฌ์šฉํ•ด ๋‹จ์ˆœํ•œ ๊นœ๋นก์ด๋Š” ํ™”๋ฉด๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ ๊ฒƒ์„ ๊ทธ๋ ค๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Chapter 2: Improving the render loop