μ΄μ μ»΄ν¨νΈ μ °μ΄λμ νμν μ½λλ₯Ό μμ±νκ² μ΅λλ€. λ¨Όμ μ΄λ―Έμ§λ₯Ό μ λ ₯λ°μ μ°λ λ ID κΈ°λ°μΌλ‘ μμμ κ³μ°νμ¬ κ·ΈλΌλμΈνΈλ₯Ό νμ±νλ λ§€μ° λ¨μν μ °μ΄λλΆν° μμνκ² μ΅λλ€. μ΄ μ °μ΄λλ μ΄λ―Έ shaders ν΄λμ μμ±λμ΄ μμ΅λλ€. μμΌλ‘ μΆκ°λ λͺ¨λ μ °μ΄λλ ν΄λΉ ν΄λμ μΆκ°νμ¬ CMake μ€ν¬λ¦½νΈλ₯Ό ν΅ν΄ μλμΌλ‘ λΉλλλλ‘ κ΅¬μ±ν κ²μ λλ€.
gradient.comp
//GLSL version to use
#version 460
//size of a workgroup for compute
layout (local_size_x = 16, local_size_y = 16) in;
//descriptor bindings for the pipeline
layout(rgba16f,set = 0, binding = 0) uniform image2D image;
void main()
{
ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(image);
if(texelCoord.x < size.x && texelCoord.y < size.y)
{
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
if(gl_LocalInvocationID.x != 0 && gl_LocalInvocationID.y != 0)
{
color.x = float(texelCoord.x)/(size.x);
color.y = float(texelCoord.y)/(size.y);
}
imageStore(image, texelCoord, color);
}
}
GLSL λ²μ μ μ§μ νλ κ²μΌλ‘ μμν©λλ€. 460μ GLSL 4.6λ²μ μ λμλ©λλ€.
λ€μμ μμ κ·Έλ£Ήμ ν¬κΈ°λ₯Ό μ μνλ layout ꡬ문μ μμ±ν©λλ€. μμ μ€λͺ νλ―μ΄, μ»΄ν¨νΈ μ °μ΄λλ μ¬λ¬ μ°λ λ(νΉμ λ μΈ)λ‘ μ΄λ£¨μ΄μ§ κ·Έλ£Ή λ¨μλ‘ μ€νλ©λλ€. μ¬κΈ°μλ x=16, y=16, z=1(κΈ°λ³Έκ°)μΌλ‘ μ§μ νκ³ μλλ°, μ΄λ κ° μμ κ·Έλ£Ήμ΄ 16 * 16 μ°λ λ λ¨μλ‘ μμ νλ€λ κ²μ μλ―Έν©λλ€.
λ€μμ μ
°μ΄λ μ
λ ₯μ μν λμ€ν¬λ¦½ν° μ
μ μ€μ ν©λλ€. μ¬κΈ°μλ set 0, binding 0μμΉμ λ¨μΌ image2D
λ₯Ό λ°μΈλ©νκ³ μμ΅λλ€. Vulkanμμλ νλμ λμ€ν¬λ¦½ν° μ
μ΄ μ¬λ¬ λ°μΈλ©μ κ°μ§ μ μμΌλ©°, μ΄λ ν΄λΉ μ
μ λ°μΈλ©ν λ ν¨κ» λ°μΈλ©λλ μμλ€μ
λλ€. λ°λΌμ λμ€ν¬λ¦½ν° μ
νλμ 0λ² μΈλ±μ€μ λ¨μΌ μ΄λ―Έμ§λ₯Ό 0λ² λ°μΈλ©μ ν¬ν¨μν€λ ꡬ쑰μ
λλ€.
μ΄ μ °μ΄λλ global invocation IDμ μ’νλ₯Ό κΈ°λ°μΌλ‘ κ·ΈλΌλμΈνΈλ₯Ό νμ±νλ λ§€μ° λ¨μν λλ―Έ μ °μ΄λμ λλ€. λ§μ½ local Invocation IDκ° X Yμμ 0μ΄λΌλ©΄, κ²μ μμ΄ λμ¬ κ²μ λλ€. κ²°κ³Όμ μΌλ‘ μ °μ΄λ μμ κ·Έλ£Ή νΈμΆμ μκ°μ μΌλ‘ 보μ¬μ£Όλ 그리λλ₯Ό μμ±ν κ²μ λλ€.
μ °μ΄λ μ½λλ₯Ό μμ ν λλ λ°λμ ν΄λΉ μ °μ΄λλ₯Ό νκ²μΌλ‘ μΆκ°ν΄ μ»΄νμΌν΄μΌ ν©λλ€. λν μ νμΌμ μΆκ°νλ€λ©΄ CMakeλ₯Ό λ€μ ꡬμ±ν΄μΌλ§ ν©λλ€. μ΄ κ³Όμ μμ μ€λ₯ μμ΄ μ±κ³΅ν΄μΌ GPUμμ ν΄λΉ μ °μ΄λλ₯Ό μ€ννλ λ° νμν spirv νμΌλ€μ΄ μ μμ μΌλ‘ μμ±λ©λλ€. μ€ν¨ν κ²½μ°, νλ‘μ νΈμμ μ °μ΄λλ₯Ό μ€ννκΈ° μν΄ νμν spirvνμΌμ΄ λλ½λ μ μμ΅λλ€.
λμ€ν¬λ¦½ν° μ λ μ΄μμ μ€μ νκΈ°
μ»΄ν¨νΈ νμ΄νλΌμΈμ ꡬμ±νκΈ° μν΄ νμ΄νλΌμΈ λ μ΄μμμ ꡬμ±ν΄μΌ ν©λλ€. μ΄ κ²½μ°, λ°μΈλ© 0λ²μ μ΄λ―Έμ§λ₯Ό ν¬ν¨νλ λ¨μΌ λμ€ν¬λ¦½ν° μ λ§μ λ΄λ λ μ΄μμμ΄ μ¬μ©λ©λλ€.
λμ€ν¬λ¦½ν° μ
λ μ΄μμμ μμ±νκΈ° μν΄, λ°μΈλ©μ μ 보λ₯Ό λ°°μ΄ ννλ‘ μ μ₯ν΄μΌ ν©λλ€. μ΄λ₯Ό κ°νΈνκ² λ€λ£¨κΈ° μν΄ μ΄ κ³Όμ μ μΆμννλ ꡬ쑰체λ₯Ό μ μν΄λ³΄κ² μ΅λλ€. λμ€ν¬λ¦½ν° μΆμνλ vk_descriptors.h/cpp
μ μμ±ν κ²μ
λλ€.
struct DescriptorLayoutBuilder {
std::vector<VkDescriptorSetLayoutBinding> bindings;
void add_binding(uint32_t binding, VkDescriptorType type);
void clear();
VkDescriptorSetLayout build(VkDevice device, VkShaderStageFlags shaderStages, void* pNext = nullptr, VkDescriptorSetLayoutCreateFlags flags = 0);
};
config/info κ΅¬μ‘°μ²΄μΈ VkDescriptorSetLayoutBinding
λ₯Ό λ°°μ΄λ‘ λ΄μ κ²μ
λλ€. κ·Έλ¦¬κ³ μ΄λ₯Ό κΈ°λ°μΌλ‘ μ€μ Vulkan κ°μ²΄ VkDescriptorSetLayout
μ λ§λλ build()ν¨μλ₯Ό μ μνκ² μ΅λλ€.
μ΄μ Builderλ₯Ό μν ν¨μλ₯Ό μμ±νκ² μ΅λλ€.
void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type)
{
VkDescriptorSetLayoutBinding newbind {};
newbind.binding = binding;
newbind.descriptorCount = 1;
newbind.descriptorType = type;
bindings.push_back(newbind);
}
void DescriptorLayoutBuilder::clear()
{
bindings.clear();
}
λ¨Όμ , add_binding
ν¨μλ₯Ό μμ±νκ² μ΅λλ€. μ΄ ν¨μλ VkDescriptorSetLayoutBinding
ꡬ쑰체λ₯Ό ꡬμ±ν λ€ λ°°μ΄μ μΆκ°ν©λλ€. λ μ΄μμ λ°μΈλ©μ μμ±ν λ μ§κΈμ λ°μΈλ© μ«μμ λμ€ν¬λ¦½ν° νμ
λ§ μκ³ μμΌλ©΄ μΆ©λΆν©λλ€. μμμ λ€λ£¬ μ»΄ν¨νΈ μ
°μ΄λ μμ μμλ λ°μΈλ© 0λ²μ νμ
μ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
μ
λλ€. μ΄ νμ
μ μ°κΈ° κ°λ₯ν μ΄λ―Έμ§λ₯Ό μλ―Έν©λλ€.
μ΄μ μ€μ λ μ΄μμμ μμ±ν΄λ΄ μλ€.
VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device, VkShaderStageFlags shaderStages, void* pNext, VkDescriptorSetLayoutCreateFlags flags)
{
for (auto& b : bindings) {
b.stageFlags |= shaderStages;
}
VkDescriptorSetLayoutCreateInfo info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
info.pNext = pNext;
info.pBindings = bindings.data();
info.bindingCount = (uint32_t)bindings.size();
info.flags = flags;
VkDescriptorSetLayout set;
VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &set));
return set;
}
λ¨Όμ λ°μΈλ©μ μννλ©° μ °μ΄λ μ€νλ¨κ³ νλκ·Έ(stage flag)λ₯Ό μΆκ°ν©λλ€. νλμ λμ€ν¬λ¦½ν° μ λ΄μμ κ° λμ€ν¬λ¦½ν° λ°μΈλ©μ νλκ·Έλ¨ΌνΈ μ °μ΄λλ μ μ μ °μ΄λ λ± μλ‘ λ€λ₯Έ μ °μ΄λ λ¨κ³μμ μ¬μ©λ μ μμ΅λλ€. κ·Έλ¬λ μ΄λ² ꡬνμμλ λ°μΈλ©λ³ μ °μ΄λ λ¨κ³ νλκ·Έλ μ§μνμ§ μκ³ λμ€ν¬λ¦½ν° μ μ 체μ λμΌν μ °μ΄λ λ¨κ³κ° μ μ©λλλ‘ κ°μ ν κ²μ λλ€.
λ€μμ VkDescriptorSetLayoutCreateInfo
λ₯Ό ꡬμ±νκ² μ΅λλ€. μ€μ ν κ²μ΄ λ§μ§λ μμλ°, λμ€ν¬λ¦½ν° λ°μΈλ©μ λ°°μ΄μ μ°κ²°νκΈ°λ§ νλ©΄ λ©λλ€. μ΄ν vkCreateDescriptorSetLayout
μ νΈμΆν΄ λμ€ν¬λ¦½ν° μ
λ μ΄μμμ μμ±ν©λλ€.
λμ€ν¬λ¦½ν° ν λΉκΈ°
λμ€ν¬λ¦½ν° μ λ μ΄μμμ΄ κ΅¬νλμμΌλ λμ€ν¬λ¦½ν° μ μ ν λΉν μ μμ΅λλ€. λ§μ°¬κ°μ§λ‘ μ½λλ₯Ό κ°κ²°νκ² μμ±ν μ μλλ‘ μ΄λ₯Ό μΆμνν ν λΉμ ꡬ쑰체λ₯Ό μμ±ν΄λ³΄κ² μ΅λλ€.
struct DescriptorAllocator {
struct PoolSizeRatio{
VkDescriptorType type;
float ratio;
};
VkDescriptorPool pool;
void init_pool(VkDevice device, uint32_t maxSets, std::span<PoolSizeRatio> poolRatios);
void clear_descriptors(VkDevice device);
void destroy_pool(VkDevice device);
VkDescriptorSet allocate(VkDevice device, VkDescriptorSetLayout layout);
};
λμ€ν¬λ¦½ν° ν λΉμ VkDescriptorPool
μ ν΅ν΄ μ΄λ£¨μ΄μ§λλ€. μ΄ κ°μ²΄λ λμ€ν¬λ¦½ν° μ
μ ν¬κΈ°μ νμ
μ 미리 μ§μ νμ¬ μ΄κΈ°νν΄μΌ ν©λλ€. νΉμ λμ€ν¬λ¦½ν°λ₯Ό μν λ©λͺ¨λ¦¬ ν λΉμλ‘ μκ°ν μλ μμ΅λλ€. λ§€μ° ν° 1κ°μ λμ€ν¬λ¦½ν° μ
μ΄ μ 체 μμ§μ κ΄λ¦¬ν μλ μμ§λ§, μ΄ κ²½μ° μ¬μ©ν λͺ¨λ λμ€ν¬λ¦½ν°λ₯Ό μ¬μ μ μμμΌ νλ€λ μ μ½μ΄ μμ΅λλ€. μ΄λ κ·λͺ¨κ° 컀μ§μλ‘ λ§€μ° μ΄λ ΅κ³ 볡μ‘ν μ μμ΅λλ€. λμ μ‘°κΈ λ λ¨μν λ°©μμ ꡬμ±νκ² μ΅λλ€. νλ‘μ νΈμ λ€μν λΆλΆμ μ¬λ¬ λμ€ν¬λ¦½ν° νμ ν λΉνκ³ μν©μ λ§κ² λ μ νν μ€μ νλ κ²μ
λλ€.
λμ€ν¬λ¦½ν° ν κ΄λ ¨ν΄μ ν κ°μ§ λ§€μ° μ€μν μ μ νμ 리μ νλ©΄ ν΄λΉ νμμ ν λΉλ λͺ¨λ λμ€ν¬λ¦½ν° μ μ΄ νκ΄΄λλ€λ κ²μ λλ€. μ΄λ νλ μ λ¨μλ‘ μμ±λλ λμ€ν¬λ¦½ν° μ μ λ€λ£° λ λ§€μ° μ μ©ν©λλ€. ν νλ μ λμλ§ μ¬μ©λλ λμ€ν¬λ¦½ν° μ μ λμ μΌλ‘ ν λΉνκ³ , νλ μ μμ μ νλ²μ λͺ¨λ μ κ±°ν μ μμ΅λλ€. μ΄ λ°©μμ GPU μ μ‘°μ¬μ μν΄ κ°μ₯ λΉ λ₯Έ κ²½λ‘λ‘ μ΅μ ν λμμμ΄ νμΈλμμΌλ©°, νλ μλ³ λμ€ν¬λ¦½ν° μ μ ꡬμ±ν λ μ¬μ©ν κ²μ κΆμ₯ν©λλ€.
DescriptorAllocator
μλ λμ€ν¬λ¦½ν° ν μ΄κΈ°ν, ν΄μ , κ·Έλ¦¬κ³ λμ€ν¬λ¦½ν° μ
μ ν λΉνλ ν¨μλ§μ μ μΈνκ² μ΅λλ€.
μ΄μ μ½λλ₯Ό μμ±ν΄λ΄ μλ€.
void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets, std::span<PoolSizeRatio> poolRatios)
{
std::vector<VkDescriptorPoolSize> poolSizes;
for (PoolSizeRatio ratio : poolRatios) {
poolSizes.push_back(VkDescriptorPoolSize{
.type = ratio.type,
.descriptorCount = uint32_t(ratio.ratio * maxSets)
});
}
VkDescriptorPoolCreateInfo pool_info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
pool_info.flags = 0;
pool_info.maxSets = maxSets;
pool_info.poolSizeCount = (uint32_t)poolSizes.size();
pool_info.pPoolSizes = poolSizes.data();
vkCreateDescriptorPool(device, &pool_info, nullptr, &pool);
}
void DescriptorAllocator::clear_descriptors(VkDevice device)
{
vkResetDescriptorPool(device, pool, 0);
}
void DescriptorAllocator::destroy_pool(VkDevice device)
{
vkDestroyDescriptorPool(device,pool,nullptr);
}
μμ±κ³Ό νκ΄΄ ν¨μλ₯Ό μΆκ°νμ΅λλ€. clear
ν¨μλ νκ΄΄κ° μλλΌ λ¦¬μ
νλ ν¨μμ
λλ€. μ΄ ν¨μλ νλ‘λΆν° μμ±λ λͺ¨λ λμ€ν¬λ¦½ν° μ
μ νκ΄΄νκ³ νμ μ΄κΈ° μνλ‘ λλ리μ§λ§, VkDescriptorPool
κ·Έ μ체λ₯Ό νκ΄΄νμ§λ μμ΅λλ€.
λμ€ν¬λ¦½ν° νμ μ΄κΈ°ννλ €λ©΄ vkCreateDescriptorPool
μ μ¬μ©νκ³ , μ¬κΈ°μ PoolSizeRatio
μ λ°°μ΄μ μ λ¬ν΄μΌ ν©λλ€. μ΄ κ΅¬μ‘°μ²΄λ λμ€ν¬λ¦½ν° νμ
(VkDescriptorType
, μμ λ°μΈλ©μμ μ¬μ©ν κ²κ³Ό λμΌ)κ³Ό maxSets
νλΌλ―Έν°μ κ³±ν΄μ§ λΉμ¨μ ν¨κ» ν¬ν¨νκ³ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ λμ€ν¬λ¦½ν° νμ΄ μΌλ§λ ν΄μ§λ₯Ό μ§μ μ μ΄ν μ μμ΅λλ€. maxSets
μ ν΄λΉ νμμ μμ±ν μ μλ VkDescriptorSets
μ μ΅λ κ°μλ₯Ό μλ―Ένκ³ , poolSizes
λ νΉμ νμ
μ λμ€ν¬λ¦½ν° λ°μΈλ©μ΄ λͺ κ° μ‘΄μ¬ν μ§λ₯Ό λνλ
λλ€.
λ§μ§λ§μΌλ‘ DescriptorAllocator::allocate
ν¨μλ₯Ό μμ±ν΄μΌ ν©λλ€. μλ μ½λλ₯Ό νμΈν΄λ³΄μΈμ.
VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout)
{
VkDescriptorSetAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
allocInfo.pNext = nullptr;
allocInfo.descriptorPool = pool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
VkDescriptorSet ds;
VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds));
return ds;
}
VkDescriptorSetAllocateInfo
λ₯Ό μ±μμΌ ν©λλ€. μ΄ κ΅¬μ‘°μ²΄μλ λμ€ν¬λ¦½ν° μ
μ ν λΉλ°μ λμ€ν¬λ¦½ν° ν, ν λΉν λμ€ν¬λ¦½ν° μ
μ κ°μ, κ·Έλ¦¬κ³ λμ€ν¬λ¦½ν° μ
λ μ΄μμμ΄ νμν©λλ€.
λμ€ν¬λ¦½ν° μ κ³Ό λ μ΄μμ μ΄κΈ°ννκΈ°
VulkanEngine
μ μλ‘μ΄ ν¨μμ μ¬μ©ν λ©€λ²λ₯Ό μΆκ°ν©μλ€.
#include <vk_descriptors.h>
struct VulkanEngine{
public:
DescriptorAllocator globalDescriptorAllocator;
VkDescriptorSet _drawImageDescriptors;
VkDescriptorSetLayout _drawImageDescriptorLayout;
private:
void init_descriptors();
}
μ΄λ¬ν DescriptorAllocator
μ€ νλλ₯Ό μ μ ν λΉκΈ°λ‘μ μμ§μ μ μ₯ν κ²μ
λλ€. κ·Έ ν λ λλ§ν μ΄λ―Έμ§λ₯Ό λ°μΈλ©ν λμ€ν¬λ¦½ν° μ
κ³Ό, ν΄λΉ νμ
μ λμ€ν¬λ¦½ν°λ₯Ό μν λμ€ν¬λ¦½ν° μ
λ μ΄μμμ μ μ₯ν΄μΌ ν©λλ€. μ΄ λ μ΄μμμ μ΄ν νμ΄νλΌμΈ μμ± μ νμνκ² λ©λλ€.
init_descriptors()
ν¨μλ₯Ό VulkanEngine
μ init()
ν¨μ λ΄μμ sync_structures
μ΄ν μΆκ°νλ κ²μ μμ§ λ§μΈμ.
void VulkanEngine::init()
{
//other code
init_commands();
init_sync_structures();
init_descriptors();
//everything went fine
_isInitialized = true;
}
μ΄μ ν¨μλ₯Ό μμ±ν μ μμ΅λλ€.
void VulkanEngine::init_descriptors()
{
//create a descriptor pool that will hold 10 sets with 1 image each
std::vector<DescriptorAllocator::PoolSizeRatio> sizes =
{
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 }
};
globalDescriptorAllocator.init_pool(_device, 10, sizes);
//make the descriptor set layout for our compute draw
{
DescriptorLayoutBuilder builder;
builder.add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
_drawImageDescriptorLayout = builder.build(_device, VK_SHADER_STAGE_COMPUTE_BIT);
}
}
λ¨Όμ DescriptorAllocator
λ₯Ό μ΄κΈ°ν ν λ, μ΄10κ°μ λμ€ν¬λ¦½ν° μ
μ ν λΉ κ°λ₯νλλ‘ νκ³ , κ° λμ€ν¬λ¦½ν° μ
μλ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
νμ
μ λμ€ν¬λ¦½ν°λ₯Ό 1κ°μ© κ°μ§λλ‘ μ€μ ν©λλ€. μ΄λ μ»΄ν¨νΈ μ
°μ΄λμμ μμ±ν μ μλ μ΄λ―Έμ§μ μ¬μ©λλ νμ
μ
λλ€.
κ·Έ ν, λ μ΄μμ λΉλλ₯Ό μ¬μ©ν΄ νμν λμ€ν¬λ¦½ν° μ
λ μ΄μμμ ꡬμ±ν μ μμ΅λλ€. μ΄λ λ°μΈλ© 0λ²μ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
λ₯Ό λνλ΄λ λ μ΄μμμ
λλ€.(μμ μ μν λμ€ν¬λ¦½ν° νκ³Ό μΌμΉν©λλ€.)
μ΄ κ³Όμ μ ν΅ν΄, μ»΄ν¨νΈ μ
°μ΄λ 그리기μ μ¬μ©ν μ μλ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
νμ
μ λμ€ν¬λ¦½ν° μ
μ 10κ°κΉμ§ ν λΉν μ μμ΅λλ€.
μ΄μ΄μ κ·Έ μ€ νλλ₯Ό μ€μ λ‘ ν λΉν λ€, λ λλ§ν μ΄λ―Έμ§λ₯Ό μ°Έμ‘°νλλ‘ λμ€ν¬λ¦½ν°λ₯Ό μμ±ν©λλ€.
void VulkanEngine::init_descriptors()
{
// other code
//allocate a descriptor set for our draw image
_drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout);
VkDescriptorImageInfo imgInfo{};
imgInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
imgInfo.imageView = _drawImage.imageView;
VkWriteDescriptorSet drawImageWrite = {};
drawImageWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
drawImageWrite.pNext = nullptr;
drawImageWrite.dstBinding = 0;
drawImageWrite.dstSet = _drawImageDescriptors;
drawImageWrite.descriptorCount = 1;
drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
drawImageWrite.pImageInfo = &imgInfo;
vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr);
//make sure both the descriptor allocator and the new layout get cleaned up properly
_mainDeletionQueue.push_function([&]() {
globalDescriptorAllocator.destroy_pool(_device);
vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr);
});
}
λ¨Όμ μμ μμ±ν DescriptorAllocator
, _drawImageDescriptorLayout
μ ν΅ν΄ λμ€ν¬λ¦½ν° μ
κ°μ²΄λ₯Ό ν λΉν©λλ€. κ·Έ ν λμ€ν¬λ¦½ν° μ
μ μ°λ¦¬κ° μ¬μ©ν μ΄λ―Έμ§μ μ°κ²°ν΄μ£Όμ΄μΌ ν©λλ€. μ΄λ₯Ό μν΄ vkUpdateDescriptorSets
ν¨μλ₯Ό μ¬μ©ν©λλ€. μ΄ ν¨μλ κ°λ³ μ
λ°μ΄νΈ μ 보λ₯Ό λ΄λ VkWriteDescriptorSets
λ°°μ΄μ μΈμλ‘ λ°μ΅λλ€. ν λΉν λμ€ν¬λ¦½ν° μ
μ λ°μΈλ© 0λ²μ κ°λ¦¬ν€λ λ¨μΌ writeλ₯Ό μμ±νμ¬ μ¬λ°λ₯Έ λμ€ν¬λ¦½ν° νμ
μ λͺ
μν©λλ€. μ΄λ λν λ°μΈλ©ν μ€μ μ΄λ―Έμ§ λ°μ΄ν°λ₯Ό λ΄λ VkDescriptorImageInfo
λ₯Ό κ°λ¦¬ν΅λλ€. VkDescriptorImageInfo
λ 그릴 μ΄λ―Έμ§μ μ΄λ―Έμ§ λ·°λ₯Ό ν¬ν¨ν©λλ€.
μ΄ κ³Όμ μ λ§μΉλ©΄, 그릴 μ΄λ―Έμ§μ λ°μΈλ©ν λμ€ν¬λ¦½ν° μ κ³Ό νμν λ μ΄μμμ΄ λ§λ€μ΄μ‘μ΅λλ€. λ§μΉ¨λ΄ μ»΄ν¨νΈ νμ΄νλΌμΈμ μμ±ν μ μμ΅λλ€.
μ»΄ν¨νΈ νμ΄νλΌμΈ
λμ€ν¬λ¦½ν° μ
λ μ΄μμμ΄ μ€λΉλμμΌλ―λ‘ νμ΄νλΌμΈ λ μ΄μμμ μμ±ν κΈ°λ°μ΄ λ§λ ¨λμμ΅λλ€. νμ΄νλΌμΈμ μμ±νκΈ° μ μ λ§μ§λ§μΌλ‘ ν΄μΌ ν μμ
μ΄ λ¨μμμ΅λλ€. μ
°μ΄λ μ½λλ₯Ό λΆλ¬μ λλΌμ΄λ²μ μ°κ²°νλ κ²μ
λλ€. Vulkan νμ΄νλΌμΈμμλ μ
°μ΄λλ₯Ό μ€μ νκΈ° μν΄ VkShaderModule
μ μμ±ν΄μΌ ν©λλ€. μ΄λ₯Ό λΆλ¬μ€λ ν¨μλ₯Ό vk_pipelines.h/cpp
μ μΆκ°ν©μλ€.
λ€μμ vk_pipelines.cpp
μ μΆκ°ν©λλ€.
#include <vk_pipelines.h>
#include <fstream>
#include <vk_initializers.h>
μ΄ ν¨μλ₯Ό μΆκ°νκ³ ν€λμλ μ μΈλΆλ₯Ό μΆκ°ν΄μ€λλ€.
bool vkutil::load_shader_module(const char* filePath,
VkDevice device,
VkShaderModule* outShaderModule)
{
// open the file. With cursor at the end
std::ifstream file(filePath, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
return false;
}
// find what the size of the file is by looking up the location of the cursor
// because the cursor is at the end, it gives the size directly in bytes
size_t fileSize = (size_t)file.tellg();
// spirv expects the buffer to be on uint32, so make sure to reserve a int
// vector big enough for the entire file
std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t));
// put file cursor at beginning
file.seekg(0);
// load the entire file into the buffer
file.read((char*)buffer.data(), fileSize);
// now that the file is loaded into the buffer, we can close it
file.close();
// create a new shader module, using the buffer we loaded
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.pNext = nullptr;
// codeSize has to be in bytes, so multply the ints in the buffer by size of
// int to know the real size of the buffer
createInfo.codeSize = buffer.size() * sizeof(uint32_t);
createInfo.pCode = buffer.data();
// check that the creation goes well.
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
return false;
}
*outShaderModule = shaderModule;
return true;
}
μ΄ ν¨μλ λ¨Όμ νμΌμ std::vector<uint32_t>
νμμΌλ‘ λΆλ¬μ΅λλ€. μ΄λ μ»΄νμΌλ μ
°μ΄λ λ°μ΄ν°λ₯Ό λ΄μ κ²μ΄λ©°, vkCreateShaderModule
μ νΈμΆν λ μ¬μ©λ©λλ€. μ
°μ΄λ λͺ¨λμ createInfo
λ μ
°μ΄λ λ°μ΄ν°λ₯Ό λ΄λ λ°°μ΄ μΈμλ νΉλ³ν νμν μ 보λ μμ΅λλ€. μ
°μ΄λ λͺ¨λμ νμ΄νλΌμΈμ μμ±ν λμλ§ νμνλ©°, νμ΄νλΌμΈμ΄ μμ±λ μ΄νμλ μμ νκ² νκ΄΄ν΄λ λλ―λ‘ μ΄λ₯Ό VulkanEngine
μ λ΄μ νμλ μμ΅λλ€.
μ΄μ VulkanEngine
μΌλ‘ λμμ νμν μλ‘μ΄ λ©€λ²λ₯Ό μΆκ°νκ³ init_pipelines()
ν¨μμ init_background_pipelines()
ν¨μλ₯Ό μΆκ°ν©μλ€. init_pipelines()
λ μΆν νν 리μΌμ΄ μ§νλλ©° μΆκ°ν λ€λ₯Έ νμ΄νλΌμΈ μ΄κΈ°ν ν¨μλ νΈμΆν κ²μ
λλ€.
class VulkanEngine{
public:
VkPipeline _gradientPipeline;
VkPipelineLayout _gradientPipelineLayout;
private:
void init_pipelines();
void init_background_pipelines();
}
μ΄λ₯Ό init
ν¨μμ μΆκ°νκ³ vk_pipelines.h
λ₯Ό νμΌ μλ¨μ ν¬ν¨μν΅λλ€. init_pipelines()
ν¨μλ init_background_pipelines()
λ₯Ό νΈμΆν κ²μ
λλ€.
#include <vk_pipelines.h>
void VulkanEngine::init()
{
//other code
init_commands();
init_sync_structures();
init_descriptors();
init_pipelines();
//everything went fine
_isInitialized = true;
}
void VulkanEngine::init_pipelines()
{
init_background_pipelines();
}
μ΄μ νμ΄νλΌμΈμ μμ±νκ² μ΅λλ€. λ¨Όμ νμ΄νλΌμΈ λ μ΄μμμ λ§λ€μ΄μΌ ν©λλ€.
void VulkanEngine::init_background_pipelines()
{
VkPipelineLayoutCreateInfo computeLayout{};
computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
computeLayout.pNext = nullptr;
computeLayout.pSetLayouts = &_drawImageDescriptorLayout;
computeLayout.setLayoutCount = 1;
VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout));
}
νμ΄νλΌμΈμ μμ±νλ €λ©΄ μ¬μ©ν λμ€ν¬λ¦½ν° μ λ μ΄μμ λ°°μ΄κ³Ό νΈμμμμ κ°μ μΆκ° ꡬμ±μ΄ νμν©λλ€. μ΄ μ °μ΄λμμλ μ΄λ¬ν ꡬμ±μ΄ νμ μκΈ° λλ¬Έμ, λμ€ν¬λ¦½ν° μ λ μ΄μμλ§ μ¬μ©νλ©΄ λ©λλ€.
μ΄μ μ
°μ΄λ λͺ¨λμ λΆλ¬μ λ€λ₯Έ μ΅μ
λ€μ VkComputePipelineCreateInfo
μ μΆκ°ν¨μΌλ‘μ νμ΄νλΌμΈ κ°μ²΄ μ체λ₯Ό μμ±ν©λλ€.
void VulkanEngine::init_background_pipelines()
{
//layout code
VkShaderModule computeDrawShader;
if (!vkutil::load_shader_module("../../shaders/gradient.comp.spv", _device, &computeDrawShader))
{
fmt::print("Error when building the compute shader \n");
}
VkPipelineShaderStageCreateInfo stageinfo{};
stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stageinfo.pNext = nullptr;
stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stageinfo.module = computeDrawShader;
stageinfo.pName = "main";
VkComputePipelineCreateInfo computePipelineCreateInfo{};
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
computePipelineCreateInfo.pNext = nullptr;
computePipelineCreateInfo.layout = _gradientPipelineLayout;
computePipelineCreateInfo.stage = stageinfo;
VK_CHECK(vkCreateComputePipelines(_device,VK_NULL_HANDLE,1,&computePipelineCreateInfo, nullptr, &_gradientPipeline));
}
λ¨Όμ μμ λ§λ ν¨μλ₯Ό μ¬μ©ν΄ VkShaderModule
μ λΆλ¬μ νμΌμ μ€λ₯κ° μλμ§ νμΈν©λλ€. μ¬κΈ°μ κ²½λ‘λ κΈ°λ³Έ Windows + msvc λΉλ ν΄λλ₯Ό κΈ°μ€μΌλ‘ μ€μ λμ΄ μλ€λ μ μ μ μνμΈμ. λ§μ½ λ€λ₯Έ μ΅μ
μ μ¬μ©νλ€λ©΄ νμΌ κ²½λ‘κ° μ λλ‘ λμλμ§ νμΈνμΈμ. κ΅¬μ± νμΌμ μ€μ λ ν΄λλ₯Ό κΈ°μ€μΌλ‘ κ²½λ‘λ₯Ό μΆμννλ κ²λ κ³ λ €ν΄λ³΄μΈμ
κ·Έ λ€μμΌλ‘ μ
°μ΄λλ₯Ό VkPipelineShaderStageCreateInfo
λ‘ μ°κ²°ν©λλ€. μ¬κΈ°μ μ μν μ μ μ¬μ©ν μ
°μ΄λμ ν¨μμ μ΄λ¦μ μ λ¬νλ€λ μ μ
λλ€. μ¬κΈ°μλ main()
μ
λλ€. μ΄λ λμΌν μ
°μ΄λ νμΌ μμ λ€λ₯Έ μ§μ
μ μ μ€μ ν¨μΌλ‘μ¨ μ¬λ¬ μ»΄ν¨νΈ μ
°μ΄λλ₯Ό λ΄μ μ μλ€λ μλ―Έμ
λλ€.
λ§μ§λ§μΌλ‘ VkComputePipelineCreateInfo
λ₯Ό μ€μ ν©λλ€. μ΄λ μ»΄ν¨νΈ μ
°μ΄λμ λ¨κ³ μ 보μ λ μ΄μμμ΄ νμν©λλ€. κ·Έ ν vkCreateComputePipelines
λ₯Ό νΈμΆν μ μμ΅λλ€.
ν¨μμ λμμ μμ νμ λ±λ‘νμ¬ νλ‘κ·Έλ¨ μ’ λ£ μ ꡬ쑰체λ€μ΄ μ 리λλλ‘ ν©λλ€.
vkDestroyShaderModule(_device, computeDrawShader, nullptr);
_mainDeletionQueue.push_function([&]() {
vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr);
vkDestroyPipeline(_device, _gradientPipeline, nullptr);
});
μ °μ΄λ λͺ¨λμ ν΄λΉ ν¨μλ΄μμ λ°λ‘ νκ΄΄ν μ μμ΅λλ€. νμ΄νλΌμΈμ΄ μ΄λ―Έ μμ±λμκΈ° λλ¬Έμ λ μ΄μ νμνμ§ μμ΅λλ€. λ°λ©΄, νμ΄νλΌμΈκ³Ό νμ΄νλΌμΈ λ μ΄μμμ κ³μ νμνλ―λ‘ νλ‘κ·Έλ¨μ΄ μ’ λ£ μ νκ΄΄ν©λλ€.
μ΄μ 그릴 μ€λΉκ° λλ¬μ΅λλ€.
μ»΄ν¨νΈ μ °μ΄λ 그리기
draw_background()
ν¨μλ‘ λμμ vkCmdClear
λ₯Ό μ»΄ν¨νΈ μ
°μ΄λ νΈμΆλ‘ λ체ν©λλ€.
// bind the gradient drawing compute pipeline
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline);
// bind the descriptor set containing the draw image for the compute pipeline
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr);
// execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it
vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1);
λ¨Όμ vkCmdBindPipeline
μ μ¬μ©νμ¬ νμ΄νλΌμΈμ λ°μΈλ©ν©λλ€. μ»΄ν¨νΈ μ
°μ΄λλ₯Ό μ¬μ©νκΈ° λλ¬Έμ VK_PIPELINE_BIND_POINT_COMPUTE
λ₯Ό μ¬μ©ν©λλ€. κ·Έ ν, 그릴 μ΄λ―Έμ§λ₯Ό λ΄κ³ μλ λμ€ν¬λ¦½ν° μ
μ λ°μΈλ©νμ¬ μ
°μ΄λκ° μ κ·Όν μ μλλ‘ ν©λλ€. λ§μ§λ§μΌλ‘ vkCmdDispatch
λ₯Ό μ¬μ©νμ¬ μ»΄ν¨νΈ μ
°μ΄λλ₯Ό μ€νν©λλ€. μΌλ§λ μ
°μ΄λκ° λͺ λ² μ€ν λ μ§λ, μμ
κ·Έλ£ΉλΉ 16 * 16 μ°λ λκ° μ€νλλ€λ μ μ κ³ λ €νμ¬, 그릴 μ΄λ―Έμ§μ ν΄μλλ₯Ό 16μΌλ‘ λλκ³ λ°μ¬λ¦Όν¨μΌλ‘μ¨ κ²°μ ν©λλ€.
μ΄ μμ μμ νλ‘κ·Έλ¨μ μ€ννλ©΄ μ΄λ―Έμ§κ° νμλ κ²μ λλ€. λ§μ½ μ °μ΄λλ₯Ό λΆλ¬μ€λ κ³Όμ μμ μ€λ₯κ° λ°μνλ€λ©΄ CMakeλ₯Ό μ¬μ€ννκ³ μ °μ΄λ νκ²μ μ¬μ€μ νμ¬ μ °μ΄λλ₯Ό λΆλ¬μ€μΈμ. μ΄λ μμ§μ ꡬμ±ν λ μλμΌλ‘ ꡬμ±λμ§ μκΈ° λλ¬Έμ μ °μ΄λκ° λ°λ λ λ§λ€ μλμΌλ‘ μ¬λΉλν΄μΌν©λλ€.
Next: Setting up IMGUI