글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자입니다

 

목차

     

     


     

     


    인프런 삼각형님의 '삼각형의 실전! Vulkan 중급' 강의를 참고하였습니다. 

    😎 [삼각형의 실전! Vulkan 중급] 강의 들으러 가기!

     

     

     

    Vulkan Graphics pipeline


     

     

    Vulkan Graphics pipeline이란?

     

    Vulkan Graphics pipeline은 OpenGL과 동일하다.

    Vulkan은 OpenGL와 달리 Graphics pipeline의 모든 단계를 개발자가 직접 명시적으로 설정해야 한다.

    이러한 특징은 개발자에게 Graphics pipeline에 더 높은 수준제어가능하게 해준다.

     


     

     

    VkGraphicsPipelineCreateInfo 구조체

     

    typedef struct VkGraphicsPipelineCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineCreateFlags flags;
         uint32_t stageCount;
         const VkPipelineShaderStageCreateInfo* pStages;
         const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
         const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
         const VkPipelineTessellationStateCreateInfo* pTessellationState;
         const VkPipelineViewportStateCreateInfo* pViewportState;
         const VkPipelineRasterizationStateCreateInfo* pRasterizationState;
         const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
         const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
         const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
         const VkPipelineDynamicStateCreateInfo* pDynamicState;
         VkPipelineLayout layout;
         VkRenderPass renderPass;
         uint32_t subpass;
         VkPipeline basePipelineHandle;
         uint32_t basePipelineIndex;
    } VkGraphicsPipelineCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     stageCoun   VkPipelineShaderStageCreateInfo의 개수
     pStages   VkPipelineShaderStageCreateInfo 배열의 포인터
     pVertexInputState   VkPipelineVertexInputStateCreateInfo 변수의 포인터
     pInputAssemblyState   VkPipelineInputAssemblyStateCreateInfo 변수의 포인터
     pTessellationState   일단 NULL을 사용
     pViewportState   VkPipelineViewportStateCreateInfo 변수의 포인터
     pRasterizationState   VkPipelineRasterizationStateCreateInfo 변수의 포인터
     pMultisampleState  일단 NULL을 사용
     pDepthStencilState   VkPipelineDepthStencilStateCreateInfo 변수의 포인터
     pColorBlendState   VkPipelineColorBlendStateCreateInfo 변수의 포인터
     pDynamicState   VkPipelineDynamicStateCreateInfo 변수의 포인터
     layout   VkPipelineLayout
     renderPass   VkRenderPass
     subpass    일단 0을 사용
     basePipelineHandle   VK_NULL_HANDLE을 사용
     basePipelineIndex   일단 0을 사용

     

     

    주목할 Vulkan Graphics pipeline 단계

     

     

    삼각형에 그리기에 꼭 필요한 단계들

     

    Input Assembly 단계에서 정점을 삼각형으로 구성한다. 사실 이러한 처리는 Vertex Shader 다음 단계인 Primitive Assembly 단계에서 진행되는데 Vulkan에서는 이 단계를 따로 두지 않고 Input Assembly의 정보를 활용한다.

     

    Vertex Shader 단계에서는 삼각형의 각 정점의 위치를 정의한다.

     

    Viewport 단계에서는 렌더링 결과가 Framebuffer의 어느 부분에 그려질지를 결정한다.

     

    Rasterization 단계에서는 3D를 2D로 변환한다.

     

    Fragment Shader는 삼각형의 최종 색상을 결정한다. 즉, 삼각형의 내부가 어떤 색깔로 채워질지를 결정한다.

     

    Color Blend 단계에서 RGBA가 Framebuffer에 어떻게 쓰일지 결정한다. 이렇게 설정된 Graphics Pipeline을 가지고 렌더링을 하면 화면에 삼각형을 볼 수 있다. 


     

     

    VkPipelineShaderStageCreateInfo 구조체

     

    typedef struct VkPipelineShaderStageCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineShaderStageCreateFlags flags;
         VkShaderStageFlagBits stage;
         VkShaderModule module;
         const char* pName;
         const VkSpecializationInfo* pSpecializationInfo;
    } VkPipelineShaderStageCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     stage   VkShaderStageFlagBits 
     module   VkShaderModule
     pName   Entry point 이름
     pSpecializationInfo   일단 NULL 을 사용

     


     

     

    VkShaderStageFlagBits 열거형

     

    typedef enum VkShaderStageFlagBits {
         VK_SHADER_STAGE_VERTEX_BIT = 0x00000001,
         VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002,
         VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004,
         VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008,
         VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010,
         VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020,
         VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F,
         VK_SHADER_STAGE_ALL = 0x7FFFFFFF,
    } VkShaderStageFlagBits;

     


     

     

    VkPipelineInputAssemblyStateCreateInfo 구조체

     

    typedef struct VkPipelineInputAssemblyStateCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineInputAssemblyStateCreateFlags flags;
         VkPrimitiveTopology topology;
         VkBool32 primitiveRestartEnable;
    } VkPipelineInputAssemblyStateCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     topology   VkPrimitiveTopology
     primitiveRestartEnable   기본 도형 조합을 다시 시작할지 결정한다.
      인덱스가 0xFFFFFFFF인 경우에 다시 시작된다.

     

     

    VkPrimitiveTopology 열거형

     

    VkPrimitiveTopology 열거형은 정점들로부터 구성할 수 있는 다양한 기본 도형을 정의한다.

     

    typedef enum VkPrimitiveTopology {
         VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0,
         VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1,
         VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2,
         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3,
         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4,
         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5,
         VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6,
         VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7,
         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8,
         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9,
    } VkPrimitiveTopology;

     

     

    PrimitiveTopology 종류

     

     

     

    아래 예제에서는 삼각형을 그리기 위해 Triangle List 사용. 


     

     

    Primitive Restart란?

     

    Primitive Restart는 인덱스가 특정 값을 가질 때 Primitive의 구성을 새로 시작하는 기능이다.

     

    (a) Primitive Restart를 사용하지 않는 경우, (b) Primitive Restart를 사용하는 경우

     

     

    8번 인덱스의 값이 0xFFFFFFFF입니다.

     

     

    8번 인덱스의 값이 특수한 값으로 설정되었다. 8번에서 기본 도형의 구성이 끝나고 9번부터 다시 시작하는 것을 볼 수 있다.


     

     

    VkPipelineInputAssemblyStateCreateInfo 구조체

     

    VkPipelineInputAssemblyStateCreateInfo 구조체를 통해서 렌더링의 결과가 Framebuffer의 어느 부분에 그려질지를 정의하고 출력이 표시될 특정 영역을 지정한다.

     

    typedef struct VkPipelineViewportStageCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineViewportStageCreateFlags flags;
         uint32_t viewportCount;
         const VkViewport* pViewports;
         uint32_t scissorCount;
         const VkRect2D* pScissors;
    } VkPipelineViewportStageCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     viewportCount   VkViewport의 개수
     pViewports   VkViewport 배열의 포인터
     scissorCount   VkRect2D의 개수
     pScissors   VkRect2D 배열의 포인터

     


     

     

    VkPipelineViewportStateCreateInfo 구조체

     

    Viewport는 렌더링의 결과가 Framebuffer의 어느 부분에 그려질지를 정리하며,

    Scissor 테스트는 축력이 표시될 특정 영역을 지정한다.

     

    ViewportScissor를 통해 렌더링의 범위를 효과적으로 제어할 수 있다.


    위의 그림은 Viewport Scissor 테스트에 대해 보여준다.

     

    첫번째 예시에서는 Viewport가 Framebuffer의 절반으로 설정되어 있어 Framebuffer의 상단 부분에만 렌더링이 이루어진다. Scissor 영역은 이 Viewport보다 크기 때문에 렌더링 결과가 완전히 출력된다. 

     

    두번째 예시는 Viewport의 크기는 Framebuffer와 동일하고, 화면 전체의 렌더링의 결과가 모두 표시된다. 하지만 Scissor 영역이 Framebuffer의 절반이기 때문에 결과적으로 상단 부분만 출력이 되고 하단은 Scissor 테스트에 실패해서 출력되지 않는다. 이러한 설정은 렌더링 결과의 특정 부분만을 화면에 보여주고자 할 때 유용하다.


     

     

    VkViewport 구조체

     

    typedef struct VkViewport {
         float x;
         float y;
         float width;
         float height;
         float minDepth;
         float maxDepth;
    } VkViewport;

     

    멤버 변수  설명 
     x   좌측 상단의 x 좌표
     y   좌측 상단의 y 좌표
     width   너비
     height   높이
     minDepth   최소 깊이 값. 0에서 1사이의 값을 가진다.
     maxDepth   최대 깊이 값. 0에서 1사이의 값을 가진다.

     

     

    위의 코드에서는 Viewport가 Swapchain 이미지의 크기와 동일하게 설정함. 이 설정으로 인해 렌더링의 결과가 Framebuffer 전체에 그려지게 된다.


     

     

    VkPipelineViewportStateCreateInfo 구조체

     

     


     

     

    VkPipelineRasterizationStateCreateInfo 구조체

     

    VkPipelineRasterizationStateCreateInfo 구조체를 통해 rasterization이 어떻게 수행될지 정의할 수 있다. 

     

    typedef struct VkPipelineRasterizationStateCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineRasterizationStateCreateFlags flags;
         VkBool32 depthClampEnable;
         VkBool32 rasterizerDiscardEnable;
         VkPolygonMode polygonMode;
         VkCullModeFlags cullMode;
         VkFrontFace frontFace;
         VkBool32 depthBiasEnable;
         float depthBiasConstantFactor;
         float depthBiasClamp;
         float depthBiasSlopeFactor;
         float lineWidth;
    } VkPipelineRasterizationStateCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     depthClampEnable   일단 VK_FALSE을 사용
     rasterizerDiscardEnable   일단 VK_FALSE을 사용
     polygoneMode   VkPolygonMode
     cullMode   VkCullMode
     frontFace   VkFrontFace
     depthBiasEnable   일단 VK_FALSE을 사용
     depthBiasConstantFactor   일단 0을 사용
     depthBiasClamp   일단 0을 사용
     depthBiasSlopeFactor   일단 0을 사용
     lineWidth   선의 너비. 일단 1을 사용

     

     

     

    Rasterization은 3D 객체를 2D 모니터에 표시하기 위해 필요한 과정이다. 이 단계가 필요한 이유는 Vertex Shader 상에서 계산된 정점들은 3D 공간에 존재하기 때문이다. 3D 도형을 모니터와 같은 2D 디스플레이 장치로 표현하는 것은 불가능하므로 이를 2D로 변환해야 한다.

     

    모니터는 오른쪽 그림처럼 수많은 픽셀들로 구성되어 있다. Rasterization 과정에서 이러한 픽셀에 맞게 3D 정보를 2D로 변환하는 작업이 수행된다.


     

     

    VkPolygonMode 열거형

     

    typedef enum VkPolygonMode {
         VK_POLYGON_MODE_FILL = 0,
         VK_POLYGON_MODE_LINE = 1,
         VK_POLYGON_MODE_POINT = 2,
         VK_POLYGON_MODE_FILL_RECTANGLE_NV = 1000153000,
         VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF
    } VkPolygonMode;

     

     

    Fill은 도형 내부를 완전히 채운다. 가장 많이 사용.

    Line은 윤곽선만 그린다. 디버깅 시 자주 사용.

    Point는 도형의 정점만 그린다. 디버깅 시 자주 사용. Particle 구현 시 사용.


     

     

    VkCullModeFlagBits 열거형

     

    Cull Mode는 렌더링 과정에서 도형의 앞면 또는 뒷면이 화면에 보여질지 말지를 결정한다.

    typedef enum VkCullModeFlagBits {
         VK_CULL_MODE_NONE = 0,							// 컬링 적용X. 모든 면이 렌더링 됨.
         VK_CULL_MODE_FRONT_BIT = 0x00000001,			// 도형의 전면을 컬링. 눈 앞에 보이는 삼각형이 렌더링X.
         VK_CULL_MODE_BACK_BIT = 0x00000002,			// 도형의 후면을 컬링. 정육면체의 뒷면에 표시되는 삼각형이 렌더링X.
         VK_CULL_MODE_FRONT_AND_BACK = 0x00000003,
         VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
    } VkCullModeFlagBits;

     


     

     

    VkFrontFace 열거형

     

    typedef enum VkFrontFace {
         VK_FRONT_FACE_COUNTER_CLOCK_WISE = 0,
         VK_FRONT_FACE_CLOCK_WISE = 1,
    } VkFrontFace;

     

    Counter Clock Wise는 시계 반대 방향을 의미. 반시계 방향으로 삼각형 그려졌을 때 앞면으로 인식이 된다. 

     


     

     

    VkPipelineRasterizationStateCreateInfo 구조체

     

     

     


     

     

    VkPipelineColorBlendStateCreateInfo 구조체

     

    typedef struct VkPipelineColorBlendStateCreateInfo {
         VkStructureType sType;
         const void* pNext;
         VkPipelineColorBlendStateCreateFlags flags;
         VkBool32 logicOpEnable;
         VkLogicOp logicOp;
         uint32_t attachmentCount;
         const VkPipelineColorBlendAttachmentState* pAttachments;
         float blendConstants[4];
    } VkPipelineColorBlendStateCreateInfo;

     

    멤버 변수  설명 
     sType   구조체 타입
     pNext   NULL 또는 확장 기능 구조체의 포인터
     flags   일단 0을 사용
     logicOpEnable   일단 VK_FALSE 을 사용
     logicOp   일단 스킵
     attachmentCount   VkPipelineColorBlendAttachmentState의 개수
     pAttachments   VkPipelineColorBlendAttachmentState 배열의 포인터
     blendConstants   컬러 블렌딩에 사용되는 상수 값

     


     

     

    VkPipelineColorBlendAttachmentState 구조체

     

    각 Color Attachment가 어떻게 블렌딩 될지는 VkPipelineColorBlendAttachmentState 구조체를 통해 정의할 수 있다.

    이 구조체를 올바르게 설정하지 않으면 렌더링 결과가 Framebuffer에 기록되지 않는다.

     

    typedef struct VkPipelineColorBlendAttachmentState {
         VkBool32 blendEnable;
         VkBlendFactor srcColorBlendFactor;
         VkBlendFactor dstColorBlendFactor;
         VkBlendOp colorBlendOp;
         VkBlendFactor srcAlphaBlendFactor;
         VkBlendFactor dstAlphaBlendFactor;
         VkBlendOp alphaBlendOp;
         VkColorComponentFlags colorWriteMask;
    } VkPipelineColorBlendAttachmentState;

     

    멤버 변수  설명 
     blendEnable   컬러 블렌딩을 활성화 또는 비활성화한다.
     srcColorBlendFactor  
     dstColorBlendFactor  
     colorBlendOp  
     srcAlphaBlendFactor  
     dstAlphaBlendFactor  
     alphaBlendOp  
     colorWriteMask   VkColorComponentFlagBits의 조합. 각 채널의 쓰기를 활성화한다.

     

     

    VkColorComponentFlagBits 열거형

     

    ColorWriteMask는 VkColorComponentFlagBits의 조합으로 정의할 수 있다. 

    렌더링 결과를 Framebuffer에 온전히 기록하기 위해서는 R, G, B, A를 모두 활성화해야 한다.

     

    typedef enum VkColorComponentFlagBits {
         VK_COLOR_COMPONENT_R_BIT = 0x00000001,
         VK_COLOR_COMPONENT_G_BIT = 0x00000002,
         VK_COLOR_COMPONENT_B_BIT = 0x00000004,
         VK_COLOR_COMPONENT_A_BIT = 0x00000007,
    } VkColorComponentFlagBits;

     

     

     

    VkPipelineColorBlendAttachmentState 구조체

     

    모든 채널을 활성화시키기 위해서 R, G, B, A를 OR 연산을 통해서 정의한다.


     

     

    VkPipelineColorBlendStateCreateInfo 구조체

     


     

     

    VkGraphicsPipelineCreateInfo 구조체

     

    이제 Graphics Pipeline을 생성하기 위한 모든 준비를 마쳤다. Graphics Pipeline을 생성하기 위해 VkGraphicsPipelineCreateInfo 구조체를 정의한다. 대부분 정의한 구조체 변수에 포인터를 설정한다.

     

     


     

     

    Vulkan Graphics pipeline 생성

     

    VkResult vkCreateGraphicsPipelines(
         VkDevice device,
         VkPipelineCache pipelineCache,
         uint32_t createInfoCount,
         const VkGraphicsPipelineCreateInfo* pCreateInfos,
         const VkAllocationCallbacks* pAllocator,
         VkPipeline* pPipelines);

     

    멤버 변수  설명 
     device   VkDevice
     pipelineCache   VK_NULL_HANDLE을 사용
     createInfoCount   VkGraphicsPipelineCreateInfo의 개수
     pCreateInfos   VkGraphicsPipelineCreateInfo 배열의 포인터
     pAllocator   일단 NULL을 사용
     pPipelines   VkPipeline 변수의 포인터

     

     

    Vulkan Graphics pipeline 생성

     


     

     

    Vulkan Graphics pipeline 파괴

     

    void vkDestroyPipeline(
         VkDevice device,
         VkPipeline pipeline,
         const VkAllocationCallbacks* pAllocator);

     

    멤버 변수  설명 
     device   VkDevice
     pipeline   VkPipeline
     pAllocator   일단 NULL을 사용

     


     

     

    Vulkan Graphics pipeline 바인드하기

     

    생성한 Graphics pipeline을 사용하기 위해 바인드한다. 이 함수는 Command Buffer의 Command를 기록하는 방식으로 작동한다.

     

    void vkCmdBindPipeline(
         VkCommandBuffer commandBuffer,
         VkPipelineBindPoint pipelineBindPoint,
         VkPipeline pipeline);

     

    멤버 변수  설명 
    commandBuffer   VkCommandBuffer
    pipelineBindPoint   일단 VK_PIPELINE_BIND_POINT_GRAPHICS을 사용
    pipeline   VkPipeline

     

     

    mPipeline을 바인딩한다.


     

     

    삼각형 그리기

     

    void vkCmdDraw(
         VkCommandBuffer commandBuffer,
         uint32_t vertexCount,
         uint32_t instanceCount,
         uint32_t firstVertex,
         uint32_t firstInstance);

     

    파라미터 설명 
    commandBuffer   VkCommandBuffer
    vertexCount   그릴 정점의 개수
    instanceCount   일단 1을 사용
    firstVertex   일단 0을 사용
    firstInstance   일단 0을 사용

     


     

     

    코드

     

    #include ...
    using namespace std;
    
    VkRenderer::VkRenderer(ANativeWindow *window) {
        // 1. VkInstance 생성
        // 2. VkPhysicalDevice 선택
        // 3. VkDevice 생성
        // 4. VkSurface 생성
        // 5. VkSwapchain 생성
        
        mSwapchainImageViews.resize(swapchainImageCount); // ImageView를 Swapchain의 개수만큼 생성
        for (auto i = 0; i != swapchainImageCount; ++i) {
            // 6. VkImageView 생성 
        }
        // 7. VkCommandPool 생성
        // 8. VkCommandBuffer 할당
        // 9. VkFence 생성
        // 10. VkSemaphore 생성
        // 11. VkRenderPass 생성
        
        mFramebuffers.resize(swapchainImageCount);
        for (auto i = 0; i != swapchainImageCount; ++i) {
            // 12. VkFramebuffer 생성
        }
       
        // 13. Vertex VkShaderModule 생성
        // 14. Fragment VkShaderModule 생성
        
        // ================================================================================
        // 15. VkPipelineLayout 생성
        // ================================================================================
        VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
        };
    
        VK_CHECK_ERROR(vkCreatePipelineLayout(mDevice,
                                              &pipelineLayoutCreateInfo,
                                              nullptr,
                                              &mPipelineLayout));
    
        // ================================================================================
        // 16. Graphics VkPipeline 생성
        // ================================================================================
        array<VkPipelineShaderStageCreateInfo, 2> pipelineShaderStageCreateInfos{
            VkPipelineShaderStageCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                .stage = VK_SHADER_STAGE_VERTEX_BIT,
                .module = mVertexShaderModule,
                .pName = "main"
            },
            VkPipelineShaderStageCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
                .module = mFragmentShaderModule,
                .pName = "main"
            }
        };
    
        VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
        };
    
        VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
            .topology =VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
        };
    
        VkViewport viewport{
            .width = static_cast<float>(mSwapchainImageExtent.width),
            .height = static_cast<float>(mSwapchainImageExtent.height),
            .maxDepth = 1.0f
        };
    
        VkRect2D scissor{
            .extent = mSwapchainImageExtent
        };
    
        VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
            .viewportCount = 1,
            .pViewports = &viewport,
            .scissorCount = 1,
            .pScissors = &scissor
        };
    
        VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
            .polygonMode = VK_POLYGON_MODE_FILL,
            .cullMode = VK_CULL_MODE_NONE,
            .lineWidth = 1.0f
        };
    
        VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
            .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
        };
    
        VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
        };
    
        VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState{
            .colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
                              VK_COLOR_COMPONENT_G_BIT |
                              VK_COLOR_COMPONENT_B_BIT |
                              VK_COLOR_COMPONENT_A_BIT
        };
    
        VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo{
            .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            .attachmentCount = 1,
            .pAttachments = &pipelineColorBlendAttachmentState
        };
    
        VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{
            .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
            .stageCount = pipelineShaderStageCreateInfos.size(),
            .pStages = pipelineShaderStageCreateInfos.data(),
            .pVertexInputState = &pipelineVertexInputStateCreateInfo,
            .pInputAssemblyState = &pipelineInputAssemblyStateCreateInfo,
            .pViewportState = &pipelineViewportStateCreateInfo,
            .pRasterizationState = &pipelineRasterizationStateCreateInfo,
            .pMultisampleState = &pipelineMultisampleStateCreateInfo,
            .pDepthStencilState = &pipelineDepthStencilStateCreateInfo,
            .pColorBlendState = &pipelineColorBlendStateCreateInfo,
            .layout = mPipelineLayout,
            .renderPass = mRenderPass
        };
    
        VK_CHECK_ERROR(vkCreateGraphicsPipelines(mDevice,
                                                 VK_NULL_HANDLE,
                                                 1,
                                                 &graphicsPipelineCreateInfo,
                                                 nullptr,
                                                 &mPipeline));
    }
    
    VkRenderer::~VkRenderer() { 
    	vkDestroyPipelineLayout(mDevice, mPipelineLayout, nullptr);
        vkDestroyPipeline(mDevice, mPipeline, nullptr);
        ...
    }
    
    void VkRenderer::render() {
        // 1. 화면에 출력할 수 있는 VkImage 얻기
        // 2. VkFence 기다린 후 초기화
        // 3. VkCommandBuffer 초기화
        // 4. VkCommandBuffer 기록 시작
        // 5. VkRenderPass 시작
        
       // ================================================================================
        // 6. Graphics VkPipeline 바인드
        // ================================================================================
        vkCmdBindPipeline(mCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline);
    
        // ================================================================================
        // 7. 삼각형 그리기
        // ================================================================================
        vkCmdDraw(mCommandBuffer, 3, 1, 0, 0);
    
        // ================================================================================
        // 8. VkRenderPass 종료
        // ================================================================================
        vkCmdEndRenderPass(mCommandBuffer);
        
        // 9. Clear 색상 갱신
        // 10. VkCommandBuffer 기록 종료
        // 11. VkCommandBuffer 제출
        // 12. VkImage 화면에 출력
    }

     

     

    전체 코드

    더보기
    // MIT License
    //
    // Copyright (c) 2024 Daemyung Jang
    //
    // Permission is hereby granted, free of charge, to any person obtaining a copy
    // of this software and associated documentation files (the "Software"), to deal
    // in the Software without restriction, including without limitation the rights
    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    // copies of the Software, and to permit persons to whom the Software is
    // furnished to do so, subject to the following conditions:
    //
    // The above copyright notice and this permission notice shall be included in all
    // copies or substantial portions of the Software.
    //
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    // SOFTWARE.
    
    #include <cassert>
    #include <array>
    #include <vector>
    #include <iomanip>
    
    #include "VkRenderer.h"
    #include "VkUtil.h"
    #include "AndroidOut.h"
    
    using namespace std;
    
    VkRenderer::VkRenderer(ANativeWindow *window) {
        // ================================================================================
        // 1. VkInstance 생성
        // ================================================================================
        // VkApplicationInfo 구조체 정의
        VkApplicationInfo applicationInfo{
            .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
            .pApplicationName = "Practice Vulkan",
            .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
            .apiVersion = VK_MAKE_API_VERSION(0, 1, 3, 0)
        };
    
        // 사용할 수 있는 레이어를 얻어온다.
        uint32_t instanceLayerCount;
        VK_CHECK_ERROR(vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
    
        vector<VkLayerProperties> instanceLayerProperties(instanceLayerCount);
        VK_CHECK_ERROR(vkEnumerateInstanceLayerProperties(&instanceLayerCount,
                                                          instanceLayerProperties.data()));
    
        // 활성화할 레이어의 이름을 배열로 만든다.
        vector<const char*> instanceLayerNames;
        for (const auto &layerProperty : instanceLayerProperties) {
            instanceLayerNames.push_back(layerProperty.layerName);
        }
    
        uint32_t instanceExtensionCount; // 사용 가능한 InstanceExtension 개수
        VK_CHECK_ERROR(vkEnumerateInstanceExtensionProperties(nullptr,
                                                              &instanceExtensionCount,
                                                              nullptr));
    
        vector<VkExtensionProperties> instanceExtensionProperties(instanceExtensionCount);
        VK_CHECK_ERROR(vkEnumerateInstanceExtensionProperties(nullptr,
                                                              &instanceExtensionCount,
                                                              instanceExtensionProperties.data()));
    
        vector<const char *> instanceExtensionNames; // instanceExtensionName을 담는 배열
        for (const auto &properties: instanceExtensionProperties) {
            if (properties.extensionName == string("VK_KHR_surface") ||
                properties.extensionName == string("VK_KHR_android_surface")) {
                instanceExtensionNames.push_back(properties.extensionName);
            }
        }
        assert(instanceExtensionNames.size() == 2); // 반드시 2개의 이름이 필요하기 때문에 확인
    
        // sType: 구조체의 타입, pApplicationInfo: 어플리케이션의 이름
        // enabledLayerCount, ppEnableLayerNames: 사용할 레이어의 정보를 정의
        VkInstanceCreateInfo instanceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            .pApplicationInfo = &applicationInfo,
            .enabledLayerCount = static_cast<uint32_t>(instanceLayerNames.size()),
            .ppEnabledLayerNames = instanceLayerNames.data(),
            .enabledExtensionCount = static_cast<uint32_t>(instanceExtensionNames.size()),
            .ppEnabledExtensionNames = instanceExtensionNames.data()
        };
    
        // vkCreateInstance로 인스턴스 생성. 생성된 인스턴스가 mInstance에 쓰여진다.
        VK_CHECK_ERROR(vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance));
    
    
        // ================================================================================
        // 2. VkPhysicalDevice 선택
        // ================================================================================
        uint32_t physicalDeviceCount;
        VK_CHECK_ERROR(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr));
    
        vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
        VK_CHECK_ERROR(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, physicalDevices.data()));
    
        // 간단한 예제를 위해 첫 번째 VkPhysicalDevice를 사용
        mPhysicalDevice = physicalDevices[0];
    
        VkPhysicalDeviceProperties physicalDeviceProperties; // 이 구조체 안에 GPU에 필요한 모든 정보가 있다.
        vkGetPhysicalDeviceProperties(mPhysicalDevice, &physicalDeviceProperties);
    
        aout << "Selected Physical Device Information ↓" << endl;
        aout << setw(16) << left << " - Device Name: "
             << string_view(physicalDeviceProperties.deviceName) << endl;
        aout << setw(16) << left << " - Device Type: "
             << vkToString(physicalDeviceProperties.deviceType) << endl;
        aout << std::hex;
        aout << setw(16) << left << " - Device ID: " << physicalDeviceProperties.deviceID << endl;
        aout << setw(16) << left << " - Vendor ID: " << physicalDeviceProperties.vendorID << endl;
        aout << std::dec;
        aout << setw(16) << left << " - API Version: "
             << VK_API_VERSION_MAJOR(physicalDeviceProperties.apiVersion) << "."
             << VK_API_VERSION_MINOR(physicalDeviceProperties.apiVersion);
        aout << setw(16) << left << " - Driver Version: "
             << VK_API_VERSION_MAJOR(physicalDeviceProperties.driverVersion) << "."
             << VK_API_VERSION_MINOR(physicalDeviceProperties.driverVersion);
    
    
        // ================================================================================
        // 3. VkDevice 생성
        // ================================================================================
        uint32_t queueFamilyPropertiesCount;
    
        //---------------------------------------------------------------------------------
        //** queueFamily 속성을 조회
        // 사용 가능한 queueFamily의 수(=queueFamilyPropertiesCount)를 얻어온다.
        vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyPropertiesCount, nullptr);
    
        vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyPropertiesCount);
        // 해당 queueFamily들의 속성을 배열에 얻어온다.
        vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyPropertiesCount, queueFamilyProperties.data());
        //---------------------------------------------------------------------------------
    
        // 특정 queueFamilyProperties가 VK_QUEUE_GRAPHICS_BIT를 지원하는지 확인.
        // 지원하는 queueFamilyProperties를 찾으면 break. queueFamily에 대한 정보는 mQueueFamilyIndex에 저장.
        for (mQueueFamilyIndex = 0;
             mQueueFamilyIndex != queueFamilyPropertiesCount; ++mQueueFamilyIndex) {
            if (queueFamilyProperties[mQueueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
                break;
            }
        }
    
        // 생성할 큐를 정의
        const vector<float> queuePriorities{1.0};
        VkDeviceQueueCreateInfo deviceQueueCreateInfo{
                .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                .queueFamilyIndex = mQueueFamilyIndex,      // queueFamilyIndex
                .queueCount = 1,                            // 생성할 큐의 개수
                .pQueuePriorities = queuePriorities.data()  // 큐의 우선순위
        };
    
        uint32_t deviceExtensionCount; // 사용 가능한 deviceExtension 개수
        VK_CHECK_ERROR(vkEnumerateDeviceExtensionProperties(mPhysicalDevice,
                                                            nullptr,
                                                            &deviceExtensionCount,
                                                            nullptr));
    
        vector<VkExtensionProperties> deviceExtensionProperties(deviceExtensionCount);
        VK_CHECK_ERROR(vkEnumerateDeviceExtensionProperties(mPhysicalDevice,
                                                            nullptr,
                                                            &deviceExtensionCount,
                                                            deviceExtensionProperties.data()));
    
        vector<const char *> deviceExtensionNames;
        for (const auto &properties: deviceExtensionProperties) {
            if (properties.extensionName == string("VK_KHR_swapchain")) {
                deviceExtensionNames.push_back(properties.extensionName);
            }
        }
        assert(deviceExtensionNames.size() == 1); // VK_KHR_swapchain이 반드시 필요하기 때문에 확인
    
        // 생성할 Device 정의
        VkDeviceCreateInfo deviceCreateInfo{
                .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                .queueCreateInfoCount = 1,                   // 큐의 개수
                .pQueueCreateInfos = &deviceQueueCreateInfo, // 생성할 큐의 정보
                .enabledExtensionCount = static_cast<uint32_t>(deviceExtensionNames.size()),
                .ppEnabledExtensionNames = deviceExtensionNames.data() // 활성화하려는 deviceExtension들을 넘겨줌
        };
    
        // vkCreateDevice를 호출하여 Device 생성(= mDevice 생성)
        VK_CHECK_ERROR(vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice));
        // 생성된 Device(= mDevice)로부터 큐를 vkGetDeviceQueue를 호출하여 얻어온다.
        vkGetDeviceQueue(mDevice, mQueueFamilyIndex, 0, &mQueue);
    
    
        // ================================================================================
        // 4. VkSurface 생성
        // ================================================================================
        VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo{
                .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
                .window = window
        };
    
        // surface 생성.
        VK_CHECK_ERROR(vkCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr, &mSurface));
    
        VkBool32 supported; // surface 지원 여부
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice,
                                                            mQueueFamilyIndex,
                                                            mSurface,
                                                            &supported)); // 지원 여부를 받아옴.
        assert(supported);
    
    
        // ================================================================================
        // 5. VkSwapchain 생성
        // ================================================================================
        VkSurfaceCapabilitiesKHR surfaceCapabilities;
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
                                                                 mSurface,
                                                                 &surfaceCapabilities));
    
        VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR;
        for (auto i = 0; i <= 4; ++i) {
            if (auto flag = 0x1u << i; surfaceCapabilities.supportedCompositeAlpha & flag) {
                compositeAlpha = static_cast<VkCompositeAlphaFlagBitsKHR>(flag);
                break;
            }
        }
        assert(compositeAlpha != VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR);
    
        VkImageUsageFlags swapchainImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
        assert(surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    
        uint32_t surfaceFormatCount = 0;
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice,
                                                            mSurface,
                                                            &surfaceFormatCount,
                                                            nullptr));
    
        vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatCount);
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice,
                                                            mSurface,
                                                            &surfaceFormatCount,
                                                            surfaceFormats.data()));
    
        uint32_t surfaceFormatIndex = VK_FORMAT_MAX_ENUM;
        for (auto i = 0; i != surfaceFormatCount; ++i) {
            if (surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
                surfaceFormatIndex = i;
                break;
            }
        }
        assert(surfaceFormatIndex != VK_FORMAT_MAX_ENUM);
    
        uint32_t presentModeCount;
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
                                                                 mSurface,
                                                                 &presentModeCount,
                                                                 nullptr));
    
        vector<VkPresentModeKHR> presentModes(presentModeCount);
        VK_CHECK_ERROR(vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
                                                                 mSurface,
                                                                 &presentModeCount,
                                                                 presentModes.data()));
    
        uint32_t presentModeIndex = VK_PRESENT_MODE_MAX_ENUM_KHR;
        for (auto i = 0; i != presentModeCount; ++i) {
            if (presentModes[i] == VK_PRESENT_MODE_FIFO_KHR) {
                presentModeIndex = i;
                break;
            }
        }
        assert(presentModeIndex != VK_PRESENT_MODE_MAX_ENUM_KHR);
    
        VkSwapchainCreateInfoKHR swapchainCreateInfo{
                .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
                .surface = mSurface,
                .minImageCount = surfaceCapabilities.minImageCount,
                .imageFormat = surfaceFormats[surfaceFormatIndex].format,
                .imageColorSpace = surfaceFormats[surfaceFormatIndex].colorSpace,
                .imageExtent = surfaceCapabilities.currentExtent,
                .imageArrayLayers = 1,
                .imageUsage = swapchainImageUsage,
                .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
                .preTransform = surfaceCapabilities.currentTransform,
                .compositeAlpha = compositeAlpha,
                .presentMode = presentModes[presentModeIndex]
        };
    
        VK_CHECK_ERROR(vkCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &mSwapchain));
    
        uint32_t swapchainImageCount;
        VK_CHECK_ERROR(vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount, nullptr));
    
        mSwapchainImages.resize(swapchainImageCount);
        VK_CHECK_ERROR(vkGetSwapchainImagesKHR(mDevice,
                                               mSwapchain,
                                               &swapchainImageCount,
                                               mSwapchainImages.data()));
    
    
        mSwapchainImageViews.resize(swapchainImageCount); // ImageView를 Swapchain의 개수만큼 생성
        for (auto i = 0; i != swapchainImageCount; ++i) {
            // ================================================================================
            // 6. VkImageView 생성
            // ================================================================================
            VkImageViewCreateInfo imageViewCreateInfo{ // 생성할 ImageView를 정의
                    .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
                    .image = mSwapchainImages[i],
                    .viewType = VK_IMAGE_VIEW_TYPE_2D,
                    .format = surfaceFormats[surfaceFormatIndex].format, // Swapchain 이미지 포맷과 동일한 포맷으로 설정
                    .components = {
                            .r = VK_COMPONENT_SWIZZLE_R,
                            .g = VK_COMPONENT_SWIZZLE_G,
                            .b = VK_COMPONENT_SWIZZLE_B,
                            .a = VK_COMPONENT_SWIZZLE_A,
                    },
                    .subresourceRange = { // 모든 이미지에 대해서 이 이미지 뷰가 접근할 수 있도록 설정
                            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                            .baseMipLevel = 0,
                            .levelCount = 1,
                            .baseArrayLayer = 0,
                            .layerCount = 1
                    }
            };
    
            VK_CHECK_ERROR(vkCreateImageView(mDevice,
                                             &imageViewCreateInfo,
                                             nullptr,
                                             &mSwapchainImageViews[i])); // mSwapchainImageViews[i] 생성
        }
    
        // ================================================================================
        // 7. VkCommandPool 생성
        // ================================================================================
        VkCommandPoolCreateInfo commandPoolCreateInfo{
                .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
                .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |           // command buffer가 자주 변경될 것임을 알려줌
                         VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, // command buffer를 개별적으로 초기화 가능하게 설정
                .queueFamilyIndex = mQueueFamilyIndex
        };
    
        VK_CHECK_ERROR(vkCreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &mCommandPool)); // mCommandPool 생성
    
        // ================================================================================
        // 8. VkCommandBuffer 할당
        // ================================================================================
        VkCommandBufferAllocateInfo commandBufferAllocateInfo{ // 할당하려는 command buffer 정의
                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
                .commandPool = mCommandPool,
                .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
                .commandBufferCount = 1
        };
    
        VK_CHECK_ERROR(vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, &mCommandBuffer));
    
    
        // ================================================================================
        // 9. VkFence 생성
        // ================================================================================
        VkFenceCreateInfo fenceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
        }; // 생성할 Fence의 정보를 해당 구조체에서 정의
    
        VK_CHECK_ERROR(vkCreateFence(mDevice, &fenceCreateInfo, nullptr, &mFence)); // mFence 생성. flag에 아무것도 넣어주지 않았기 때문에 생성된 Fence의 초기 상태는 Unsignal 상태다.
    
    
        // ================================================================================
        // 10. VkSemaphore 생성
        // ================================================================================
        VkSemaphoreCreateInfo semaphoreCreateInfo{
                .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
        };
    
        VK_CHECK_ERROR(vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphore));
    
    
        // ================================================================================
        // 11. VkRenderPass 생성
        // ================================================================================
        VkAttachmentDescription attachmentDescription{
                .format = surfaceFormats[surfaceFormatIndex].format,
                .samples = VK_SAMPLE_COUNT_1_BIT,
                .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
                .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
                .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
                .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
        };
    
        VkAttachmentReference attachmentReference{
                .attachment = 0,
                .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        };
    
        VkSubpassDescription subpassDescription{
                .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
                .colorAttachmentCount = 1,
                .pColorAttachments = &attachmentReference
        };
    
        VkRenderPassCreateInfo renderPassCreateInfo{
                .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
                .attachmentCount = 1,
                .pAttachments = &attachmentDescription,
                .subpassCount = 1,
                .pSubpasses = &subpassDescription
        };
    
        VK_CHECK_ERROR(vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mRenderPass)); // mRenderPass 생성.
    
        mFramebuffers.resize(swapchainImageCount);
        for (auto i = 0; i != swapchainImageCount; ++i) {
            // ================================================================================
            // 12. VkFramebuffer 생성
            // ================================================================================
            VkFramebufferCreateInfo framebufferCreateInfo{
                    .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                    .renderPass = mRenderPass,
                    .attachmentCount = 1,
                    .pAttachments = &mSwapchainImageViews[i], // ImageView
                    .width = mSwapchainImageExtent.width,
                    .height = mSwapchainImageExtent.height,
                    .layers = 1
            };
    
            VK_CHECK_ERROR(vkCreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mFramebuffers[i]));// mFramebuffers[i] 생성
        }
    
    
        // ================================================================================
        // 13. Vertex VkShaderModule 생성
        // ================================================================================
        string_view vertexShaderCode = {
                "#version 310 es                                        \n"
                "                                                       \n"
                "void main() {                                          \n"
                "    vec2 pos[3] = vec2[3](vec2(-0.5,  0.5),            \n"
                "                          vec2( 0.5,  0.5),            \n"
                "                          vec2( 0.0, -0.5));           \n"
                "                                                       \n"
                "    gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); \n"
                "}                                                      \n"
        };
    
        std::vector<uint32_t> vertexShaderBinary;
        // VKSL을 SPIR-V로 변환.
        VK_CHECK_ERROR(vkCompileShader(vertexShaderCode,
                                       VK_SHADER_TYPE_VERTEX,
                                       &vertexShaderBinary));
    
        VkShaderModuleCreateInfo vertexShaderModuleCreateInfo{
                .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
                .codeSize = vertexShaderBinary.size() * sizeof(uint32_t), // 바이트 단위.
                .pCode = vertexShaderBinary.data()
        };
    
        VK_CHECK_ERROR(vkCreateShaderModule(mDevice,
                                            &vertexShaderModuleCreateInfo,
                                            nullptr,
                                            &mVertexShaderModule)); // mVertexShaderModule 생성.
    
        // ================================================================================
        // 14. Fragment VkShaderModule 생성
        // ================================================================================
        string_view fragmentShaderCode = {
                "#version 310 es                                        \n"
                "precision mediump float;                               \n"
                "                                                       \n"
                "layout(location = 0) out vec4 fragmentColor;           \n"
                "                                                       \n"
                "void main() {                                          \n"
                "    fragmentColor = vec4(1.0, 0.0, 0.0, 1.0);          \n"
                "}                                                      \n"
        };
    
        std::vector<uint32_t> fragmentShaderBinary;
        VK_CHECK_ERROR(vkCompileShader(fragmentShaderCode,
                                       VK_SHADER_TYPE_FRAGMENT,
                                       &fragmentShaderBinary));
    
        VkShaderModuleCreateInfo fragmentShaderModuleCreateInfo{
                .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
                .codeSize = fragmentShaderBinary.size() * sizeof(uint32_t),
                .pCode = fragmentShaderBinary.data()
        };
    
        VK_CHECK_ERROR(vkCreateShaderModule(mDevice,
                                            &fragmentShaderModuleCreateInfo,
                                            nullptr,
                                            &mFragmentShaderModule));
    
        // ================================================================================
        // 15. VkPipelineLayout 생성
        // ================================================================================
        VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
        };
    
        VK_CHECK_ERROR(vkCreatePipelineLayout(mDevice,
                                              &pipelineLayoutCreateInfo,
                                              nullptr,
                                              &mPipelineLayout));
    
        // ================================================================================
        // 16. Graphics VkPipeline 생성
        // ================================================================================
        array<VkPipelineShaderStageCreateInfo, 2> pipelineShaderStageCreateInfos{
                VkPipelineShaderStageCreateInfo{
                        .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                        .stage = VK_SHADER_STAGE_VERTEX_BIT,
                        .module = mVertexShaderModule,
                        .pName = "main"
                },
                VkPipelineShaderStageCreateInfo{
                        .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
                        .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
                        .module = mFragmentShaderModule,
                        .pName = "main"
                }
        };
    
        VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
        };
    
        VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
                .topology =VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
        };
    
        VkViewport viewport{
                .width = static_cast<float>(mSwapchainImageExtent.width),
                .height = static_cast<float>(mSwapchainImageExtent.height),
                .maxDepth = 1.0f
        };
    
        VkRect2D scissor{
                .extent = mSwapchainImageExtent
        };
    
        VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
                .viewportCount = 1,
                .pViewports = &viewport,
                .scissorCount = 1,
                .pScissors = &scissor
        };
    
        VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
                .polygonMode = VK_POLYGON_MODE_FILL,
                .cullMode = VK_CULL_MODE_NONE,
                .lineWidth = 1.0f
        };
    
        VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
                .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
        };
    
        VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
        };
    
        VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState{
                .colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
                                  VK_COLOR_COMPONENT_G_BIT |
                                  VK_COLOR_COMPONENT_B_BIT |
                                  VK_COLOR_COMPONENT_A_BIT
        };
    
        VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo{
                .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
                .attachmentCount = 1,
                .pAttachments = &pipelineColorBlendAttachmentState
        };
    
        VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{
                .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
                .stageCount = pipelineShaderStageCreateInfos.size(),
                .pStages = pipelineShaderStageCreateInfos.data(),
                .pVertexInputState = &pipelineVertexInputStateCreateInfo,
                .pInputAssemblyState = &pipelineInputAssemblyStateCreateInfo,
                .pViewportState = &pipelineViewportStateCreateInfo,
                .pRasterizationState = &pipelineRasterizationStateCreateInfo,
                .pMultisampleState = &pipelineMultisampleStateCreateInfo,
                .pDepthStencilState = &pipelineDepthStencilStateCreateInfo,
                .pColorBlendState = &pipelineColorBlendStateCreateInfo,
                .layout = mPipelineLayout,
                .renderPass = mRenderPass
        };
    
        VK_CHECK_ERROR(vkCreateGraphicsPipelines(mDevice,
                                                 VK_NULL_HANDLE,
                                                 1,
                                                 &graphicsPipelineCreateInfo,
                                                 nullptr,
                                                 &mPipeline));
    
    }
    
    VkRenderer::~VkRenderer() {
        vkDestroyPipelineLayout(mDevice, mPipelineLayout, nullptr);
        vkDestroyPipeline(mDevice, mPipeline, nullptr);
        vkDestroyShaderModule(mDevice, mVertexShaderModule, nullptr);
        vkDestroyShaderModule(mDevice, mFragmentShaderModule, nullptr);
        for (auto framebuffer : mFramebuffers) {
            vkDestroyFramebuffer(mDevice, framebuffer, nullptr);
        }
        mFramebuffers.clear();
        vkDestroyRenderPass(mDevice, mRenderPass, nullptr);
        for (auto imageView : mSwapchainImageViews) {
            vkDestroyImageView(mDevice, imageView, nullptr);
        }
        mSwapchainImageViews.clear();
        vkDestroySemaphore(mDevice, mSemaphore, nullptr);
        vkDestroyFence(mDevice, mFence, nullptr);
        vkFreeCommandBuffers(mDevice, mCommandPool, 1, &mCommandBuffer);
        vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
        vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr);
        vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
        vkDestroyDevice(mDevice, nullptr); // Device 파괴. queue의 경우 Device를 생성하면서 생겼기 때문에 따로 파괴하는 API가 존재하지 않는다.
        vkDestroyInstance(mInstance, nullptr);
    }
    
    void VkRenderer::render() {
        // ================================================================================
        // 1. 화면에 출력할 수 있는 VkImage 얻기
        // ================================================================================
        uint32_t swapchainImageIndex;
        VK_CHECK_ERROR(vkAcquireNextImageKHR(mDevice,
                                             mSwapchain,
                                             UINT64_MAX,
                                             VK_NULL_HANDLE,
                                             mFence,                 // Fence 설정
                                             &swapchainImageIndex)); // 사용 가능한 이미지 변수에 담기
        //auto swapchainImage = mSwapchainImages[swapchainImageIndex]; // swapchainImage에 더 이상 직접 접근하지 않으므로 이제 사용X
        auto framebuffer = mFramebuffers[swapchainImageIndex];
    
        // ================================================================================
        // 2. VkFence 기다린 후 초기화
        // ================================================================================
        // mFence가 Signal 될 때까지 기다린다.
        VK_CHECK_ERROR(vkWaitForFences(mDevice, 1, &mFence, VK_TRUE, UINT64_MAX));
        // mFence가 Siganl이 되면 vkResetFences를 호출해서 Fence의 상태를 다시 초기화한다.
        // 초기화하는 이유: vkAcquireNextImageKHR을 호출할 때 이 Fence의 상태는 항상 Unsignal 상태여야 하기 때문이다.
        VK_CHECK_ERROR(vkResetFences(mDevice, 1, &mFence));
    
        // ================================================================================
        // 3. VkCommandBuffer 초기화
        // ================================================================================
        vkResetCommandBuffer(mCommandBuffer, 0);
    
        // ================================================================================
        // 4. VkCommandBuffer 기록 시작
        // ================================================================================
        VkCommandBufferBeginInfo commandBufferBeginInfo{
                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
                .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT // 한 번만 기록되고 다시 리셋 될 것이라는 의미
        };
    
        // mCommandBuffer를 기록중인 상태로 변경.
        VK_CHECK_ERROR(vkBeginCommandBuffer(mCommandBuffer, &commandBufferBeginInfo));
    
    
        // ================================================================================
        // 5. VkRenderPass 시작
        // ================================================================================
        VkRenderPassBeginInfo renderPassBeginInfo{
                .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
                .renderPass = mRenderPass,
                .framebuffer = framebuffer,
                .renderArea{
                        .extent = mSwapchainImageExtent
                },
                .clearValueCount = 1,
                .pClearValues = &mClearValue
        };
    
        vkCmdBeginRenderPass(mCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
    
        // ================================================================================
        // 6. Graphics VkPipeline 바인드
        // ================================================================================
        vkCmdBindPipeline(mCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline);
    
        // ================================================================================
        // 7. 삼각형 그리기
        // ================================================================================
        vkCmdDraw(mCommandBuffer, 3, 1, 0, 0);
    
        // ================================================================================
        // 8. VkRenderPass 종료
        // ================================================================================
        vkCmdEndRenderPass(mCommandBuffer);
    
        // ================================================================================
        // 9. Clear 색상 갱신
        // ================================================================================
        for (auto i = 0; i != 4; ++i) {
            mClearValue.color.float32[i] = fmodf(mClearValue.color.float32[i] + 0.01, 1.0);
        }
    
        // ================================================================================
        // 10. VkCommandBuffer 기록 종료
        // ================================================================================
        VK_CHECK_ERROR(vkEndCommandBuffer(mCommandBuffer)); // mCommandBuffer는 Executable 상태가 된다.
    
        // ================================================================================
        // 11. VkCommandBuffer 제출
        // ================================================================================
        VkSubmitInfo submitInfo{
                .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
                .commandBufferCount = 1,
                .pCommandBuffers = &mCommandBuffer,
                .signalSemaphoreCount = 1,
                .pSignalSemaphores = &mSemaphore
        };
    
        // submitInfo 구조체를 넘김으로써 commandBuffer 정보를 queue에 제출
        VK_CHECK_ERROR(vkQueueSubmit(mQueue, 1, &submitInfo, VK_NULL_HANDLE));
    
        // ================================================================================
        // 12. VkImage 화면에 출력
        // ================================================================================
        VkPresentInfoKHR presentInfo{
                .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
                .waitSemaphoreCount = 1,
                .pWaitSemaphores = &mSemaphore,
                .swapchainCount = 1,
                .pSwapchains = &mSwapchain,
                .pImageIndices = &swapchainImageIndex
        };
    
        VK_CHECK_ERROR(vkQueuePresentKHR(mQueue, &presentInfo)); // 화면에 출력.
        VK_CHECK_ERROR(vkQueueWaitIdle(mQueue));
    }

     

     

    실행화면

     




    '⭐ Vulkan & CMake > Vulkan' 카테고리의 다른 글

    [Vulkan] Vulkan Memory  (0) 2024.09.12
    [Vulkan] Vulkan Buffer  (0) 2024.09.12
    [Vulkan] Vulkan Shader module  (0) 2024.09.10
    [Vulkan] VKSL 주의사항  (0) 2024.09.10
    [Vulkan] SPIR-V  (0) 2024.09.10