Link

๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ์ž‘์—…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ €, ์ ์  ๋Š˜์–ด๋‚˜๋Š” ๊ฐ์ฒด๋“ค์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์‚ญ์ œ ํ๋ฅผ ๋งŒ๋“ค๊ณ , ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ์Šค์™‘์ฒด์ธ ์ด๋ฏธ์ง€๊ฐ€ ์•„๋‹Œ ๊ณณ์— ๋จผ์ € ๊ทธ๋ฆฐ ํ›„ ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ์Šค์™‘์ฒด์ธ๋กœ ๋ณต์‚ฌํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ญ์ œ ํ

์ ์  ๋” ๋งŽ์€ 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