Link

이제 μ»΄ν“¨νŠΈ 셰이더에 ν•„μš”ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ² μŠ΅λ‹ˆλ‹€. λ¨Όμ € 이미지λ₯Ό μž…λ ₯λ°›μ•„ μ“°λ ˆλ“œ 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λ₯Ό μž¬μ‹€ν–‰ν•˜κ³  셰이더 νƒ€κ²Ÿμ„ μž¬μ„€μ •ν•˜μ—¬ 셰이더λ₯Ό λΆˆλŸ¬μ˜€μ„Έμš”. μ΄λŠ” 엔진을 ꡬ성할 λ•Œ μžλ™μœΌλ‘œ κ΅¬μ„±λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 셰이더가 λ°”λ€” λ•Œ λ§ˆλ‹€ μˆ˜λ™μœΌλ‘œ μž¬λΉŒλ“œν•΄μ•Όν•©λ‹ˆλ‹€.

chapter2

Next: Setting up IMGUI