๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์์ํ๊ธฐ ์ ์, ๋ช ๊ฐ์ง ์ถ๊ฐ๋ก ๊ตฌํํด์ผ ํ ์์ ์ด ์์ต๋๋ค. ๋จผ์ , ์ ์ ๋์ด๋๋ ๊ฐ์ฒด๋ค์ ์์ ํ๊ฒ ์ ๋ฆฌํ ์ ์๋๋ก ์ญ์ ํ๋ฅผ ๋ง๋ค๊ณ , ๋ ๋๋ง ๋ฃจํ๋ฅผ ์์ ํ์ฌ ์ค์์ฒด์ธ ์ด๋ฏธ์ง๊ฐ ์๋ ๊ณณ์ ๋จผ์ ๊ทธ๋ฆฐ ํ ํด๋น ์ด๋ฏธ์ง๋ฅผ ์ค์์ฒด์ธ๋ก ๋ณต์ฌํ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ญ์ ํ
์ ์ ๋ ๋ง์ 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