๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์์ํ๊ธฐ ์ ์, ๋ช ๊ฐ์ง ์ถ๊ฐ๋ก ๊ตฌํํด์ผ ํ ์์ ์ด ์์ต๋๋ค. ๋จผ์ , ์ ์  ๋์ด๋๋ ๊ฐ์ฒด๋ค์ ์์ ํ๊ฒ ์ ๋ฆฌํ ์ ์๋๋ก ์ญ์  ํ๋ฅผ ๋ง๋ค๊ณ , ๋ ๋๋ง ๋ฃจํ๋ฅผ ์์ ํ์ฌ ์ค์์ฒด์ธ ์ด๋ฏธ์ง๊ฐ ์๋ ๊ณณ์ ๋จผ์  ๊ทธ๋ฆฐ ํ ํด๋น ์ด๋ฏธ์ง๋ฅผ ์ค์์ฒด์ธ๋ก ๋ณต์ฌํ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ญ์  ํ
์ ์  ๋ ๋ง์ Vulkan ๊ตฌ์กฐ์ฒด๋ฅผ ์ถ๊ฐํ๊ฒ ๋๋ฉด์, ํ๊ดด๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ  ์๋จ์ด ํ์ํฉ๋๋ค. cleanup()ํจ์์ ํญ๋ชฉ์ ์ถ๊ฐํ  ์๋ ์์ง๋ง, ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก ์ ์ง๋ณด์๊ฐ ์ด๋ ต๊ณ , ๋๊ธฐํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์์ง๋๋ค. ๊ทธ๋์ VulkanEngine์ ์ญ์  ํ๋ผ๋ ์๋ก์ด ๊ตฌ์กฐ์ฒด๋ฅผ ์ถ๊ฐํ๊ฒ ์ต๋๋ค. ์ด๋ ๋ง์ ์์ง์์ ์ฌ์ฉํ๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ์์ผ๋ก, ์ญ์ ํ  ๊ฐ์ฒด๋ค์ ํ์ ๋ด์ ๋์๋ค๊ฐ, ํ๋ฅผ ์คํํจ์ผ๋ก์จ ๊ฐ์ฒด๋ค์ ์ฌ๋ฐ๋ฅธ ์์๋ก ์ญ์ ํ๋ ๊ตฌ์กฐ์
๋๋ค. ์ด ๊ตฌํ์์๋ ๋จ์ํจ์ ์ฐ์ ์ํ์ฌ std::function ์ฝ๋ฐฑ์ ๋ฑ(deque)์ ์ ์ฅํ  ๊ฒ์
๋๋ค. ๋ฑ์ FILO(First In Last Out)ํ ์ฒ๋ผ ์ฌ์ฉํ  ๊ฒ์ด๋ฏ๋ก, ๋ง์ง๋ง์ ์ถ๊ฐ๋ ๊ฐ์ฒด๋ถํฐ ๋จผ์  ํ๊ดด๋ฉ๋๋ค.
ํด๋น ๊ตฌํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
struct DeletionQueue
{
	std::deque<std::function<void()>> deletors;
	void push_function(std::function<void()>&& function) {
		deletors.push_back(function);
	}
	void flush() {
		// reverse iterate the deletion queue to execute all the functions
		for (auto it = deletors.rbegin(); it != deletors.rend(); it++) {
			(*it)(); //call functors
		}
		deletors.clear();
	}
};
std::function์ ๋๋ค๋ฅผ ๋ด์ ์ฝ๋ฐฑ๊ณผ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๋ด์ ์ ์์ผ๋ฉฐ, ์ด๋ฌํ ์ฉ๋์ ์ ํฉํฉ๋๋ค.
ํ์ง๋ง ๊ฐ ๊ฐ์ฒด๋ง๋ค std::function์ ์ ์ฅํ๋ ๋ฐฉ์์ ๊ท๋ชจ๊ฐ ์ปค์ง ๊ฒฝ์ฐ ๋นํจ์จ์ ์
๋๋ค. ์ด ํํ ๋ฆฌ์ผ์์ ์ฌ์ฉํ  ๊ฐ์ฒด ์์๋ ํฐ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง, ์์ฒ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ ๋น ๋ฅด๊ฒ ํ๊ดดํ๊ณ  ์ถ๋ค๋ฉด VkImage, VkBuffer๊ณผ ๊ฐ์ ๋ค์ํ ํ์
์ Vulkan ํธ๋ค์ ์ ์ฅํด ๋ฐ๋ณต๋ฌธ์์ ํ๊ดดํ๋ ํธ์ด ๋ซ์ต๋๋ค.
๋ค์ํ ์๋ช
 ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ์ญ์  ํ๋ฅผ ์ฌ๋ฌ ์์น์ ๋ ๊ฒ์
๋๋ค. ๊ทธ์ค ํ๋๋ VulkanEngine ํด๋์ค์์ ์์ง์ด ์ข
๋ฃ ์ ์ ์ญ ๊ฐ์ฒด๋ค์ ์ ๋ฆฌํ๋ ์ญํ ์ ํฉ๋๋ค. ๋ํ ๊ฐ ํ๋ ์๋ง๋ค ๋ณ๋์ ์ญ์  ํ๋ฅผ ๋์ด, ์ฌ์ฉ์ด ๋๋ ๊ฐ์ฒด๋ฅผ ๋ค์ ํ๋ ์์ ์์ ํ๊ฒ ์ญ์ ํ  ์ ์๋๋ก ํ๊ฒ ์ต๋๋ค. ์ด ์ญ์  ํ๋ฅผ VulkanEngine ํด๋์ค์ ๊ทธ ๋ด๋ถ์ ์๋ FrameData ๊ตฌ์กฐ์ฒด์ ์ถ๊ฐํฉ๋๋ค.
์ด๋ฅผ VulkanEngine ํด๋์ค์ FrameData ๊ตฌ์กฐ์ฒด ๋ด๋ถ์ ์ถ๊ฐํฉ๋๋ค.
struct FrameData {
	 //other data
      DeletionQueue _deletionQueue;
};
class VulkanEngine{
    //other data
    DeletionQueue _mainDeletionQueue;
}
์ญ์  ํ๋ฅผ ๋ ์์น์์ ํธ์ถํฉ๋๋ค. ํ๋๋ ํ๋ ์๋ง๋ค ํ์ค๋ฅผ ๋๊ธฐํ ์งํ, ๋ค๋ฅธ ํ๋๋ WaitIdle ํธ์ถ ์ดํ cleanup()ํจ์์์์
๋๋ค. ํ์ค ๋ฐ๋ก ๋ค์์ ์ญ์  ํ๋ฅผ ๋น์์ผ๋ก์จ, 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));
	get_current_frame()._deletionQueue.flush();
    //other code
}
void VulkanEngine::cleanup()
{	
	if (_isInitialized) {
		
		//make sure the gpu has stopped doing its things				
		vkDeviceWaitIdle(_device);
		
		//free per-frame structures and deletion queue
		for (int i = 0; i < FRAME_OVERLAP; i++) {
			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);
			_frames[i]._deletionQueue.flush();
		}
		//flush the global deletion queue
		_mainDeletionQueue.flush();
		//rest of cleanup function
	}
}
์ญ์  ํ๋ฅผ ์ค์ ํ์ผ๋ฏ๋ก ์ด์  Vulkan ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ๋ง๋ค ์ธ์ ๋ ์ง ํ์ ๋ฃ์ ์ ์์ต๋๋ค.
๋ฉ๋ชจ๋ฆฌ ํ ๋น
๋ ๋๋ง ๋ฃจํ๋ฅผ ๊ฐ์ ํ๋ ค๋ฉด ์ด๋ฏธ์ง๋ฅผ ํ ๋นํด์ผ ํ๋ฉฐ, ์ด๋ Vulkan์์ ๊ฐ์ฒด๊ฐ ์ด๋ป๊ฒ ํ ๋น๋๋์ง ์์์ผ ํ๋ค๋ ๋ป์ ๋๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๋ Vulkan Memory Allocator ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก ์ ๋ฐ์ ์ธ ๊ณผ์ ์ ์๋ตํ ๊ฒ์ ๋๋ค. ์ด๋ฏธ์ง ์ ๋ ฌ๊ณผ ๊ฐ์ ์๊ฒฉํ ์ ์ฝ ์กฐ๊ฑด์ ๊ฐ๋ ๊ฐ์ฒด์ ๋ค์ํ ๋ฉ๋ชจ๋ฆฌ ํ์ ๋ค๋ฃจ๋ ๊ฒ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ๊ณ , ํนํ ์ฑ๋ฅ๊น์ง ๊ณ ๋ คํ๋ค๋ฉด ๊ตฌํํ๊ธฐ ๋งค์ฐ ์ด๋ ต์ต๋๋ค. VMA๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฌํ ์์ ๋ค์ ์๋ตํ ์ ์์ผ๋ฉฐ, ๊ฒ์ฆ๋ ๋ฐฉ์์ผ๋ก ์์ ์ ์ธ ๋์์ ๋ณด์ฅ๋ฐ์ ์ ์์ต๋๋ค. ์ค์ ๋ก pcsx3 ์๋ฎฌ๋ ์ดํฐ ํ๋ก์ ํธ์์๋ ํ ๋น ๋ฐฉ์์ VMA๋ก ๊ต์ฒดํ ๊ฒฐ๊ณผ ํ๋ ์ ๋ ์ดํธ๊ฐ 20% ํฅ์๋์์ต๋๋ค.
vk_types.h์๋ ์ด๋ฏธ VMA ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํฌํจํ๋ ์ฝ๋๊ฐ ์์ง๋ง, ๋ช ๊ฐ์ง ์ถ๊ฐํ  ์์
์ด ์์ต๋๋ค.
vk_engine.cpp์์๋ VMA_IMPLEMENTATION๋ฅผ ์ ์ํ ์ํ๋ก VMA๋ฅผ ํฌํจํด์ผ ํฉ๋๋ค
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
VMA๋ ์ผ๋ฐ์ ์ธ ํค๋์ ๊ตฌํ์ด ๊ฐ์ ํค๋ ํ์ผ์ ๋ค์ด ์๋ ๋ฐฉ์์
๋๋ค. ํ๋ก์ ํธ์ .cpp ํ์ผ ์ค ํ ๊ณณ์ VMA_IMPLEMENTATION์ ์ ์ํด์ผ ํ๋ฉฐ, ๊ทธ ํ์ผ์ VMA ํจ์๋ค์ ์ ์๊ฐ ์ปดํ์ผ ๋  ๊ฒ์
๋๋ค.
VMA๋ฅผ VulkanEngine ํด๋์ค์ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
class VulkanEngine{
    VmaAllocator _allocator;
}
์ด์  init_vulkan()ํจ์์ ๋ ๋ถ๋ถ์์ ํ ๋น๊ธฐ ์ด๊ธฐํ๋ฅผ ์ํํ๊ฒ ์ต๋๋ค.
    // initialize the memory allocator
    VmaAllocatorCreateInfo allocatorInfo = {};
    allocatorInfo.physicalDevice = _chosenGPU;
    allocatorInfo.device = _device;
    allocatorInfo.instance = _instance;
    allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
    vmaCreateAllocator(&allocatorInfo, &_allocator);
    _mainDeletionQueue.push_function([&]() {
        vmaDestroyAllocator(_allocator);
    });
์ค๋ช
ํ  ๊ฒ์ด ๋ง์ง๋ ์์ต๋๋ค. _allocator ๋ฉค๋ฒ๋ฅผ ์ด๊ธฐํํ ๋ค, ํด๋น ํ๊ดด ํจ์๋ฅผ ์ญ์  ํ์ ์ถ๊ฐํด ์์ง์ด ์ข
๋ฃ๋  ๋ ์๋์ผ๋ก ์ ๋ฆฌ๋๋๋ก ํ์ต๋๋ค. ๋ฌผ๋ฆฌ ๋๋ฐ์ด์ค, ์ธ์คํด์ค, ๋๋ฐ์ด์ค๋ ์์ฑ ํจ์์ ์ฐ๊ฒฐํ์์ผ๋ฉฐ, VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT๋ฅผ ์ ๋ฌํด ์ดํ GPU ํฌ์ธํฐ๊ฐ ํ์ํ  ๋ ์ฌ์ฉํ  ์ ์๋๋ก ์ค์ ํ์ต๋๋ค. VMA ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Vulkan API์ ์ ์ฌํ ํธ์ถ ๋ฐฉ์์ ๋ฐ๋ฅด๋ฏ๋ก, ์ ์ฌํ info ๊ตฌ์กฐ์ฒด๋ฅผ ์ฌ์ฉํด ๋์ํฉ๋๋ค.
์๋ก์ด ๋ ๋๋ง ๋ฃจํ
์ค์์ฒด์ธ์ ์ง์  ๋ ๋๋งํ๋ ๋ฐฉ์์ ๋ง์ ํ๋ก์ ํธ์์ ๋ฌด๋ํ๊ฒ ์ฌ์ฉ๋๋ฉฐ, ์ค๋งํธํฐ๊ณผ ๊ฐ์ ํ๊ฒฝ์์๋ ์คํ๋ ค ์ต์ ์ ๋ฐฉ์์ผ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ช ๊ฐ์ง ์ ์ฝ์ด ๋ฐ๋ฆ ๋๋ค. ๊ฐ์ฅ ์ค์ํ ์ ์ฝ์ ์ค์์ฒด์ธ์ ์ฌ์ฉ๋๋ ์ด๋ฏธ์ง์ ํฌ๋งท์ด ๋ณด์ฅ๋์ง ์๋๋ค๋ ์ ์ ๋๋ค. OS, ๋๋ผ์ด๋ฒ, ์ฐฝ ๋ชจ๋์ ๋ฐ๋ผ ์ต์ ์ ์ค์์ฒด์ธ ํฌ๋งท์ด ๋ฌ๋ผ์ง ์ ์์ต๋๋ค. HDR์ ์ง์ํ๋ ค๋ฉด ํน์ ํ ํฌ๋งท์ด ์๊ตฌ๋๊ธฐ๋ ํฉ๋๋ค. ๋ ๋ค๋ฅธ ์ ์ฝ์ ์ฐ๋ฆฌ๋ ์ฐฝ ํ์ ์์คํ ์ผ๋ก๋ถํฐ ์ค์์ฒด์ธ ์ด๋ฏธ์ง ์ธ๋ฑ์ค๋ง์ ์ป์ด์จ๋ค๋ ์ ์ ๋๋ค. ์ผ๋ถ ์ ์ง์ฐ ๋ ๋๋ง ๊ธฐ์ ์์๋ ๋ค๋ฅธ ์ด๋ฏธ์ง์ ๋ ๋๋ง ํ ๋ค, ๊ทธ ์ด๋ฏธ์ง๋ฅผ ์ค์์ฒด์ธ์ ๋งค์ฐ ์งง์ ์ง์ฐ์ผ๋ก ์ ์กํ๋ ๋ฐฉ์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ ๊ฐ์ง ์ค์ํ ์ ํ์ ํด์๋๊ฐ ์ฐฝ ํฌ๊ธฐ์ ๊ณ ์ ๋์ด ์๋ค๋ ์ ์ ๋๋ค. ๋ ๋์, ํน์ ๋ ๋ฎ์ ํด์๋๋ก ๋ ๋๋งํ๊ณ ์ถ๋ค๋ฉด, ํ๋/์ถ์ ๋ก์ง์ ๊ตฌํํด์ผ ํ๋ฉฐ, ๊ทธ๋ฆฌ๊ธฐ ์์ ์ ๋ณ๋์ ์ํํด์ผ ํฉ๋๋ค.
๋ํ, ๋๋ถ๋ถ์ ์ค์์ฒด์ธ ํฌ๋งท์ ๋ฎ์ ์ ๋ฐ๋๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. HDR ๋ ๋๋ง์ ์ง์ํ๋ ์ผ๋ถ ํ๋ซํผ์ ๋ ๋์ ์ ๋ฐ๋์ ํฌ๋งท์ ์ ๊ณตํ์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก๋ ์์๋น 8๋นํธ๊ฐ ๊ธฐ๋ณธ์ ๋๋ค. ๋ฐ๋ผ์ ๋์ ์ ๋ฐ๋์ ์กฐ๋ช ๊ณ์ฐ์ด๋ ๋ฐด๋ฉ ํ์์ ๋ฐฉ์งํ๊ณ , ์ ๊ทํ๋ ์์ ๋ฒ์(1.0)์ ์ด๊ณผํ๋ ํํ์ ํ๊ณ ์ถ๋ค๋ฉด, ๋ณ๋์ ์ด๋ฏธ์ง์ ๋ ๋๋ง์ ํด์ผ ํฉ๋๋ค.
์ด๋ฌํ ์ด์ ๋ก, ์ด ํํ ๋ฆฌ์ผ์์๋ ์ค์์ฒด์ธ ์ด๋ฏธ์ง๊ฐ ์๋ ๋ณ๋์ ์ด๋ฏธ์ง์ ๋ ๋๋ง ํ ํ ๊ทธ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง๋ฅผ ์ค์์ฒด์ธ์ ๋ณต์ฌํ์ฌ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ์ด๋ฏธ์ง๋ RGBA 16๋นํธ float ํฌ๋งท์ ์ฌ์ฉํ ๊ฒ์ ๋๋ค. ์ด๋ ๋ค์ ๊ณผํ ์ ์์ง๋ง ์กฐ๋ช ๊ณ์ฐ๊ณผ ๊ณ ํ์ง ๋ ๋๋ง ์ ์ ์ฉํ ๋์ ์ ๋ฐ๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
Vulkan ์ด๋ฏธ์ง
์ค์์ฒด์ธ์ ์ค์ ํ ๋ ์ด๋ฏธ์ง์ ๋ํด ๊ฐ๋จํ ๋ค๋ฃฌ ์ ์ด ์์ง๋ง, ๊ทธ ๊ณผ์ ์ VkBootstrap์ด ์ฒ๋ฆฌํด์ฃผ์์ต๋๋ค. ์ด๋ฒ์๋ ์ด๋ฏธ์ง๋ฅผ ์ง์  ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ , VulkanEngine ํด๋์ค์ ํ์ํ ์๋ก์ด ๋ฉค๋ฒ๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
vk_types.h์ ์ด๋ฏธ์ง ์์ฑ์ ์ํด ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๊ตฌ์กฐ์ฒด๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด ๊ตฌ์กฐ์ฒด์๋ VkImage๋ฅผ ๊ธฐ๋ณธ VkImageView, ์ด๋ฏธ์ง ๋ฉ๋ชจ๋ฆฌ ํ ๋น ๊ฐ์ฒด, ๊ทธ๋ฆฌ๊ณ  ์ด๋ฏธ์ง ํฌ๊ธฐ์ ํฌ๋งท๊ณผ ํจ๊ป ์ ์ฅํฉ๋๋ค. ์ด๋ฌํ ์ ๋ณด๋ค์ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃฐ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ  ์ ์์ต๋๋ค. ๋ํ ๋ ๋๋งํ  ํด์๋๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉํ  _drawExtent๋ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
struct AllocatedImage {
    VkImage image;
    VkImageView imageView;
    VmaAllocation allocation;
    VkExtent3D imageExtent;
    VkFormat imageFormat;
};
class VulkanEngine{
	//draw resources
	AllocatedImage _drawImage;
	VkExtent2D _drawExtent;
}
vk_intializers ํจ์์ ์ด๋ฏธ์ง์ ์ด๋ฏธ์ง๋ทฐ์ createInfo ํจ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
VkImageCreateInfo vkinit::image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent)
{
    VkImageCreateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    info.pNext = nullptr;
    info.imageType = VK_IMAGE_TYPE_2D;
    info.format = format;
    info.extent = extent;
    info.mipLevels = 1;
    info.arrayLayers = 1;
    //for MSAA. we will not be using it by default, so default it to 1 sample per pixel.
    info.samples = VK_SAMPLE_COUNT_1_BIT;
    //optimal tiling, which means the image is stored on the best gpu format
    info.tiling = VK_IMAGE_TILING_OPTIMAL;
    info.usage = usageFlags;
    return info;
}
VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags)
{
    // build a image-view for the depth image to use for rendering
    VkImageViewCreateInfo info = {};
    info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    info.pNext = nullptr;
    info.viewType = VK_IMAGE_VIEW_TYPE_2D;
    info.image = image;
    info.format = format;
    info.subresourceRange.baseMipLevel = 0;
    info.subresourceRange.levelCount = 1;
    info.subresourceRange.baseArrayLayer = 0;
    info.subresourceRange.layerCount = 1;
    info.subresourceRange.aspectMask = aspectFlags;
    return info;
}
์ด๋ฏธ์ง ํ์ผ๋ง์ VK_IMAGE_TILING_OPTIMAL๋ก ํ๋์ฝ๋ฉํ  ๊ฒ์
๋๋ค. ์ด๋ GPU๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ถ์ ์ผ๋ก ์ต์ ํํ์ฌ ์์ ๋กญ๊ฒ ์ฌ๋ฐฐ์นํ  ์ ์๋๋ก ํ์ฉํ๋ค๋ ์๋ฏธ์
๋๋ค. ๋ง์ฝ CPU์์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ ์ง์  ์ฝ๊ณ  ์ถ์ ๋, VK_IMAGE_TILING_LINEAR์ ์ฌ์ฉํด์ผ ํ๋ฉฐ, ์ด๋ GPU ๋ฐ์ดํฐ๋ฅผ ๋จ์ํ 2์ฐจ์ ๋ฐฐ์ด๋ก ๋ง๋ค์ด์ค๋๋ค. ํ์ง๋ง ์ด ๋ฐฉ์์ GPU์ ํ์ฉ์ ํฐ ์ ์ฝ์ ์ฃผ๊ธฐ ๋๋ฌธ์, LINEAR ํ์ผ๋ง์ ์ค์  ์ฉ๋ก๋ CPU์์ ์ฝ๋ ๊ฒฝ์ฐ๋ฐ์ ์์ต๋๋ค.
์ด๋ฏธ์ง ๋ทฐ๋ฅผ ์์ฑํ  ๋์๋ subresource๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค. ์ด๋ ํ์ดํ๋ผ์ธ ๋ฐฐ๋ฆฌ์ด์์ ์์ฑํ๋ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค.
์ด์  init_swapchain์ ๋ง์ง๋ง ๋ถ๋ถ์ ์ด๋ฏธ์ง ์์ฑ ์ฝ๋๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
	//draw image size will match the window
	VkExtent3D drawImageExtent = {
		_windowExtent.width,
		_windowExtent.height,
		1
	};
	//hardcoding the draw format to 32 bit float
	_drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
	_drawImage.imageExtent = drawImageExtent;
	VkImageUsageFlags drawImageUsages{};
	drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
	drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
	drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT;
	drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
	VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent);
	//for the draw image, we want to allocate it from gpu local memory
	VmaAllocationCreateInfo rimg_allocinfo = {};
	rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
	rimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
	//allocate and create the image
	vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr);
	//build a image-view for the draw image to use for rendering
	VkImageViewCreateInfo rview_info = vkinit::imageview_create_info(_drawImage.imageFormat, _drawImage.image, VK_IMAGE_ASPECT_COLOR_BIT);
	VK_CHECK(vkCreateImageView(_device, &rview_info, nullptr, &_drawImage.imageView));
	//add to deletion queues
	_mainDeletionQueue.push_function([=]() {
		vkDestroyImageView(_device, _drawImage.imageView, nullptr);
		vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation);
	});
๋จผ์  ์ํ๋ ์ด๋ฏธ์ง ํฌ๊ธฐ์ VkExtent3d ๊ตฌ์กฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค. ์ด๋ ์ฐฝ์ ํฌ๊ธฐ์ ์ผ์นํ๋๋ก ํ  ๊ฒ์ด๋ฉฐ, ํด๋น ๊ฐ์ AllocatedImage ๊ตฌ์กฐ์ฒด์ ๋ณต์ฌํฉ๋๋ค.
๊ทธ ๋ค์์ผ๋ก๋ usage ํ๋๊ทธ๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค. Vulkan์์๋ ๋ชจ๋  ๋ฒํผ์ ์ด๋ฏธ์ง์ ๋ํด ์ฌ์ฉ ๋ชฉ์ ์ ๋ํ๋ด๋ usage ํ๋๊ทธ๋ฅผ ์ง์ ํด์ผ ํ๋ฉฐ, ์ด๋ ๋๋ผ์ด๋ฒ๊ฐ ํด๋น ๋ฆฌ์์ค์ ์ฌ์ฉ ๋ฐฉ์์ ๊ธฐ๋ฐ์ผ๋ก ๋ด๋ถ์ ์ผ๋ก ์ต์ ํ๋ฅผ ์ํํ  ์ ์๋๋ก ๋์์ค๋๋ค. ์ฐ๋ฆฌ์ ๊ฒฝ์ฐ ์ด๋ฏธ์ง ๊ฐ ๋ณต์ฌ๋ฅผ ์ํด TransferSRC์ TransferDST๋ฅผ ์ค์ ํ๊ณ , ์ปดํจํธ ์
ฐ์ด๋๊ฐ ์ฐ๊ธฐ๋ฅผ ํ  ์ ์๋๋ก STORAGE๋ฅผ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ํฝ์ค ํ์ดํ๋ผ์ธ์ผ๋ก ๋ํ์ ๊ทธ๋ฆด ์ ์๋๋ก COLOR_ATTACHMENT๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์ด๋ฏธ์ง ํฌ๋งท์ VK_FORMAT_R16G16B16A16_SFLOAT๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค. ์ด๋ 4์ฑ๋์ 16๋นํธ float์ด๋ฉฐ, ํฝ์
๋น 64๋นํธ๋ฅผ ์ฐจ์งํฉ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ธ 8๋นํธ ์์ ํฌ๋งท์ ๋ ๋ฐฐ ์ฉ๋์ด์ง๋ง, ์กฐ๋ช
 ๊ณ์ฐ ๋ฑ์์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋  ๊ฒ์
๋๋ค.
์ด๋ฏธ์ง๋ฅผ ์ค์ ๋ก ์์ฑํ  ๋๋ imageInfo ๊ตฌ์กฐ์ฒด์ allocInfo ๊ตฌ์กฐ์ฒด๋ฅผ VMA์ ์ ๋ฌํด์ผ ํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด VMA๋ ๋ด๋ถ์ ์ผ๋ก Vulkan ์์ฑ ํจ์๋ฅผ ํธ์ถํ์ฌ ์ด๋ฏธ์ง๋ฅผ ์์ฑํด์ค๋๋ค. ์ฌ๊ธฐ์ ์ค์ํ ์ ์ usage์ required ํ๋๊ทธ์
๋๋ค. VMA_MEMORY_USAGE_GPU_ONLY๋ฅผ ์ฌ์ฉํด VMA์๊ฒ ํด๋น ์ด๋ฏธ์ง๊ฐ CPU์์ ์ ๊ทผ๋์ง ์๋ GPU ์ ์ฉ ํ
์ค์ณ ์์ ์๋ ค์ฃผ์ด GPU VRAM์ ๋ฐฐ์นํ๊ฒ ํฉ๋๋ค. ์ถ๊ฐ๋ก VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT๋ ์ค์ ํ์ฌ GPU ์ ์ฉ ๋ฉ๋ชจ๋ฆฌ์ ํ ๋น๋๋๋ก ๋ช
์ํฉ๋๋ค. ์ด ํ๋๊ทธ๋ GPU VRAM๋ง์ ์ํ ํ๋๊ทธ๋ก, ๊ฐ์ฅ ๋น ๋ฅธ ์ ๊ทผ ์๋๋ฅผ ๋ณด์ฅํฉ๋๋ค.
Vulkan์์๋ ์ด๋ฏธ์ง์ ๋ฒํผ๋ฅผ ํ ๋นํ ์ ์๋ ์ฌ๋ฌ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ด ์์ต๋๋ค. ์ ์ฉ GPU๋ฅผ ๊ฐ๋ PC์์๋ ์ผ๋ฐ์ ์ผ๋ก CPU RAM ์์ญ, GPU VRAM ์์ญ, ๊ทธ๋ฆฌ๊ณ CPU๊ฐ ์ ๊ทผํ ์ ์๋ ํน์ํ GPU VRAM ์์ญ์ธ โ์ ๋ก๋ ํโ ์ด ์์ต๋๋ค. ๋ง์ฝ resizable BAR๊ฐ ํ์ฑํ๋์ด ์๋ค๋ฉด, ์ ๋ก๋ ํ์ด ์ ์ฒด VRAM์ด ๋ ์ ์์ต๋๋ค. ๊ทธ๋ ์ง ์๋ค๋ฉด ์ผ๋ฐ์ ์ผ๋ก 256MB์ ๋๋ค. VMA์๊ฒ GPU_ONLY๋ก ํ ๋นํ๋ผ๊ณ ์ง์ํ์ฌ, ์ ๋ก๋ ํ ์ธ๋ถ์ ์์ VRAM์ ์์์ ๋ฐฐ์นํ๋๋ก ํฉ๋๋ค.
์ด๋ฏธ์ง๋ฅผ ํ ๋นํ ํ์๋ ๋์๋๋ ์ด๋ฏธ์ง ๋ทฐ๋ ์์ฑํด์ผ ํฉ๋๋ค. Vulkan์์๋ ์ด๋ฏธ์ง์ ์ ๊ทผํ๊ธฐ ์ํด ๋ฐ๋์ ์ด๋ฏธ์ง ๋ทฐ๊ฐ ํ์ํฉ๋๋ค. ์ด๋ ์ด๋ฏธ์ง ์์ ์กด์ฌํ๋ ์์ ๋ํผ์ ๋๋ค. ์๋ฅผ ๋ค์ด ํ๋์ ๋ฐ๋งต ๋ ๋ฒจ์๋ง ์ ๊ทผํ๋๋ก ์ ํํ ์๋ ์์ต๋๋ค. ์ด ํํ ๋ฆฌ์ผ์์๋ ํญ์ VkImage์ ๊ทธ์ ํด๋นํ๋ ๊ธฐ๋ณธ ์ด๋ฏธ์ง ๋ทฐ๋ฅผ ์์ผ๋ก ๋ฌถ์ด ์ฌ์ฉํฉ๋๋ค.
์๋ก์ด ๋ ๋๋ง ๋ฃจํ
์ด์  ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋ฆด ์ ์๋ ๊ณต๊ฐ์ด ๋ง๋ จ๋์์ต๋๋ค. ๋ ๋๋ง ๋ฃจํ์ ํด๋น ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
๊ทธ ์ ์ ์ด๋ฏธ์ง๊ฐ ๋ณต์ฌ๊ฐ ํ์ํ๋ฏ๋ก, vk_images.cpp์ ์ด๋ฏธ์ง ๋ณต์ฌ ํจ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize)
{
	VkImageBlit2 blitRegion{ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, .pNext = nullptr };
	blitRegion.srcOffsets[1].x = srcSize.width;
	blitRegion.srcOffsets[1].y = srcSize.height;
	blitRegion.srcOffsets[1].z = 1;
	blitRegion.dstOffsets[1].x = dstSize.width;
	blitRegion.dstOffsets[1].y = dstSize.height;
	blitRegion.dstOffsets[1].z = 1;
	blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	blitRegion.srcSubresource.baseArrayLayer = 0;
	blitRegion.srcSubresource.layerCount = 1;
	blitRegion.srcSubresource.mipLevel = 0;
	blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
	blitRegion.dstSubresource.baseArrayLayer = 0;
	blitRegion.dstSubresource.layerCount = 1;
	blitRegion.dstSubresource.mipLevel = 0;
	VkBlitImageInfo2 blitInfo{ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr };
	blitInfo.dstImage = destination;
	blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
	blitInfo.srcImage = source;
	blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
	blitInfo.filter = VK_FILTER_LINEAR;
	blitInfo.regionCount = 1;
	blitInfo.pRegions = &blitRegion;
	vkCmdBlitImage2(cmd, &blitInfo);
}
๋์๋๋ ์ ์ธ์ vk_images.h์ ์ถ๊ฐํฉ๋๋ค.
	namespace vkutil {
	    // Existing:
	    void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout);
		void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize);	
	}
Vulkan์ ์ด๋ฏธ์ง๋ฅผ ๋ณต์ฌํ๋ 2๊ฐ์ง ์ฃผ์ ๋ฐฉ๋ฒ์ ์ ์ํฉ๋๋ค. VkCmdCopyImage์ VkCmdBlitImage์
๋๋ค. CopyImage๋ ์๋๋ ๋น ๋ฅด์ง๋ง ์ ์ฝ์ด ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋ ์ด๋ฏธ์ง์ ํด์๋๊ฐ ๋ฐ๋์ ๊ฐ์์ผ ํฉ๋๋ค. ๋ฐ๋ฉด blitImage๋ ์๋ก ๋ค๋ฅธ ํฌ๋งท๊ณผ ํฌ๊ธฐ์ ์ด๋ฏธ์ง๋ ๋ณต์ฌํ  ์ ์์ต๋๋ค. ๋ณต์ฌ์ ์ฌ์ฉํ  ์์ค ์ฌ๊ฐํ๊ณผ ํ๊ฒ ์ฌ๊ฐํ์ ์ง์ ํ๋ฉด, ์์คํ
์ด ํด๋น ์์น๋ก ์ด๋ฏธ์ง๋ฅผ ๋ณต์ฌํฉ๋๋ค. ์ด ๋ ํจ์๋ ์์ง ์ด๊ธฐ ์ค์  ๋จ๊ณ์์๋ ์ ์ฉํ์ง๋ง, ์ดํ์๋ ์ ์ฒด ํ๋ฉด ํ๋๊ทธ๋จผํธ ์
ฐ์ด๋์ ์ปค์คํ
 ๋ก์ง์ ์ถ๊ฐํ๋ ๋ฐฉ์์ผ๋ก ๋์ฒดํ๋ ํธ์ด ์ข์ต๋๋ค.
์ด์  ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ๋๋ง ๋ฃจํ๋ฅผ ์
๋ฐ์ดํธํ  ์ ์์ต๋๋ค. draw()๊ฐ ์ ์  ๋ ์ปค์ง๊ณ  ์์ผ๋ฏ๋ก ๋๊ธฐํ, ์ปค๋งจ๋ ๋ฒํผ ๊ด๋ฆฌ, ์ด๋ฏธ์ง ๋ณํ ์ฒ๋ฆฌ๋ฅผ draw()ํจ์์ ๊ทธ๋๋ก ๋๊ณ  ์ค์  ๊ทธ๋ฆฌ๊ธฐ ๋ช
๋ น๋ง ๋ถ๋ฆฌํ์ฌ draw_background()ํจ์๋ก ์ฎ๊ธฐ๊ฒ ์ต๋๋ค.
void VulkanEngine::draw_background(VkCommandBuffer cmd)
{
	//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, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);
}
ํค๋์ ํจ์๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์ปค๋งจ๋ ๋ฒํผ์ ๊ธฐ๋กํ๋ ์ฝ๋๋ฅผ ์์ ํ ๊ฒ์ ๋๋ค. ๊ธฐ์กด ์ฝ๋๋ ์ญ์ ํ๊ณ ์๋ ์๋ก์ด ์ฝ๋๋ก ๋์ฒดํฉ๋๋ค.
	_drawExtent.width = _drawImage.imageExtent.width;
	_drawExtent.height = _drawImage.imageExtent.height;
	VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));	
	// transition our main draw image into general layout so we can write into it
	// we will overwrite it all so we dont care about what was the older layout
	vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
	draw_background(cmd);
	//transition the draw image and the swapchain image into their correct transfer layouts
	vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
	// 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 Present so we can show it on the screen
	vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_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));
๋ ๋๋ง ๋ฃจํ์ ์ฃผ์ ์ฐจ์ด์ ์ ๋ ์ด์ ์ค์์ฒด์ธ ์ด๋ฏธ์ง ๊ทธ ์์ฒด์ clear๋ฅผ ์ํํ์ง ์๋๋ค๋ ์ ์
๋๋ค. ๋์  _drawImage.image์ clear๋ฅผ ์ํํฉ๋๋ค. ์ด๋ฏธ์ง๊ฐ clear๋๋ฉด ์ค์์ฒด์ธ ์ด๋ฏธ์ง์ ๋ ๋๋ง๋ ์ด๋ฏธ์ง๋ฅผ ์ ์ก ๋ ์ด์์์ผ๋ก ๋ณํํ๊ณ  ๋ณต์ฌ ๋ช
๋ น์ ์คํํฉ๋๋ค. ๋ณต์ฌ๊ฐ ์ํ๋๋ฉด ์ค์์ฒด์ธ ์ด๋ฏธ์ง๋ฅผ ํ์ ๋ ์ด์์์ผ๋ก ์ ํํฉ๋๋ค. ํญ์ ๊ฐ์ ์ด๋ฏธ์ง์ ๋ ๋๋งํ๊ธฐ ๋๋ฌธ์ draw_image๊ฐ ๋์ด์ ์ค์์ฒด์ธ ์ธ๋ฑ์ค์ ์ ๊ทผํ  ํ์๊ฐ ์๊ณ , ๋จ์ํ draw_image๋ฅผ ์ด๊ธฐํ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๋ํ _drawExtent๊ฐ์ ์ค์ ํ์ฌ ์ดํ ๊ทธ๋ฆฌ๊ธฐ ์์ญ์ ์ฌ์ฉํ  ๊ฒ์
๋๋ค.
์ด์  ์ค์์ฒด์ธ ์ธ๋ถ์์ ์ด๋ฏธ์ง๋ฅผ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ต๋๋ค. ์๋นํ ํฝ์  ์ ๋ฐ๋๋ฅผ ์ป์์ผ๋ฉฐ ๋ค์ํ ์ถ๊ฐ ๊ธฐ๋ฒ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค
์ด์  ์ค์  ์ปดํจํธ ์ ฐ์ด๋ ์คํ ๋จ๊ณ๋ก ๋์ด๊ฐ ์ค๋น๊ฐ ๋๋ฌ์ต๋๋ค.
Next: Vulkan Shaders