본문 바로가기
Vulkan

6. RenderPass

by SimonLee 2024. 7. 1.

렌더 패스는 하나 이상의 서브패스와 첨부로 구성 되어있습니다.

렌더 패스의 목적은 프레임 버퍼의 메모리 접근을 최적화 합니다.

 

서브패스는 여러 종류의 첨부에 컬러, 뎁스 렌더링을 합니다.

그리고 첨부를 읽어서 스왑체인 이미지에 최종 결과물을 렌더링 합니다.

서브 패스는 프레임 버퍼와 연결 되어있으며,  연결된 첨부를 읽고 씁니다.

 

 

                                                            < 그림 1 >

 

< 그림 1>은  2개의 서브패스와 2개의 첨부로 이루어져 있습니다.

첫 번째 서브 패스는

인덱스 1> 컬러 첨부에 렌더링을 하고,

인덱스 2> 뎁스 첨부에 렌더링을 합니다.

두번째 서브패스는

첫 번째의 결과물을 읽고, 스왑체인 이미지 인덱스 0번 이미지에 렌더링을 합니다.

 

그럼 우리는 첨부는 어떻게 생성이되고, 서브패스는 어떻게 설정하는지 알아보죠

 

첨부 (AttachMent) 란 

첨부는 드로잉 명령을 렌더링 할때 사용하는 화면 영역(색상, 깊이/스텐실)을 가리킨다.

 

5가지 유형이 있다.

1. 색상(color) 첨부 : 렌더링 프리미티브가 그려지는 대상 이미지를 나타낸다.

2. 깊이(depth) 첨부 : 깊이 정보를 저장하고 깊이/스텐실 테스트 연산에 사용한다.

3. 리졸브(resolve) 첨부 : 리졸브 첨부는 서브패스의 마지막 단계일 때 자동으로 멀티 샘플링 첨부에서 싱글 샘플링 

첨부로 다운 샘플링 된다.

4. 입력 첨부 : 셰이더와 공유할 첨부의 목록으로 구성된다. 입력 첨부는 기능이 제한된 텍스처와 유사

5. 보존(Preserve) 첨부 : 이 하위 패스에서는 사용되지 않지만 데이터를 보존해야 하는 첨부 파일

 

첨부의 유형은 뭐 이렇게 되어있는데,

어떻게 생성이 되고 활용을 하는지에 대해 이해해보자.

 

 

위 서브패스 들이 렌더링 하고 그 결과를

최종적으로 스왑체인 이미지에 렌더링 하는 과정을 정리해보자.

뎁스 어태치먼트는 제외하고, 컬러 어태치먼트만 적용해보자.

 

First Subpass  렌더링

index 1번의 컬러 첨부의 생성과정

1) 이미지를 생성한다.

2) 이미지 메모리 할당 및 데이터 초기화 진행

3) 이미지 뷰 생성

4) 프레임 버퍼를 생성하여 프레임 버퍼와 이미지뷰와 연결.

5) 렌더 패스를 시작할때, 프레임 버퍼 (G buffer ) 를 바인딩 한다. 

6) 렌더링을 한다.

7) 렌더링의 결과는 프레임 버퍼를 통하여 이미지에 저장이 되어있음.

 

 

Second Subpass 렌더링

1) 위에서 생성한 G buffer 에 해당하는 1개의 VkAttachmentDescription 2개를 생성한다.

  1-1)컬러 첨부에 해당하는 VkAttachmentDescription 다음과 같이 설정한다.

  - loadOp : VK_ATTACHMENT_LOAD_OP_LOAD

  - storeOp : VK_ATTACHMENT_STORE_OP_DONT_CARE

  - initialLayout : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL

  - finalLayout : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR

2) G buffer 에 해당하는 서브패스도 생성해준다.

  - colorAttachmentCount = 1

  - pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS

3) 렌더 패스 생성해준다.

   - renderPassCreateInfo.subpassCount = 2;
   - renderPassCreateInfo.pSubpasses = &subpass;

 

다시 작성하자

 
 
 

FrameBuffer와 Render pass 관계

 

 

 

 

 

 

 

첨부 생성 메타데이터

// Provided by VK_VERSION_1_0
typedef struct VkAttachmentDescription {
    VkAttachmentDescriptionFlags    flags;
    VkFormat                        format;
    VkSampleCountFlagBits           samples;
    VkAttachmentLoadOp              loadOp;
    VkAttachmentStoreOp             storeOp;
    VkAttachmentLoadOp              stencilLoadOp;
    VkAttachmentStoreOp             stencilStoreOp;
    VkImageLayout                   initialLayout;
    VkImageLayout                   finalLayout;
} VkAttachmentDescription;

- flags : VkAttachmentDescriptionFlags

- format : 첨부에서 사용하는 이미지의 포맷

- sample  : 랜더 패스 첨부에 사용하는 샘플링 수이다.

- loadOp : 이 필드는 색상과 깊이 첨부로 동작을 정의한다. 서브패스의 시작 부분에서 처리 한다.

- storeOp : 이 필드는 색상과 깊이 첨부로 동작을 정의한다. 서브패스의 끝에서 어떤 처리가 되는지 처리 한다.

- stencilLoadOp : 이 필드는 VkAttachmentLoadOp를 사용할 때 깊이/스텐실 첨부의 스텐실 비율 동작을 정의 한다.

서브패스를 시작할때 어떻게 처리할 것 인지를 정의한다.

-  stencilStoreOp : 이 필드는 VkAttachmentStoreOp를 사용할 때, 깊이/스텐실 첨부의 스텐실 비율 동작을 정의한다.

즉 서브패스를 시작할 때 어떻게 처리할 것인지를 정의한다.

-  initialLayout : 이 필드는 첨부 이미지의 레이아웃을 정의한다.

렌더 패스 인스턴스가 시작할때 첨부는 설정된 레이아웃으로 변환된다.

-  finalLayout : 이 필드는 첨부 이미지의 레이아웃을 정의한다.

렌더 패스 인스턴스가 끝나면 첨부는 설정된 이미지 레이아웃으로 변환된다.

주어진 렌더 패스 인스턴스에서 첨부는 각 서브패스의 필요에 따라 다른 레이아웃을 가질 수 있다.

 

// Provided by VK_VERSION_1_0
typedef enum VkSampleCountFlagBits {
    VK_SAMPLE_COUNT_1_BIT = 0x00000001,
    VK_SAMPLE_COUNT_2_BIT = 0x00000002,
    VK_SAMPLE_COUNT_4_BIT = 0x00000004,
    VK_SAMPLE_COUNT_8_BIT = 0x00000008,
    VK_SAMPLE_COUNT_16_BIT = 0x00000010,
    VK_SAMPLE_COUNT_32_BIT = 0x00000020,
    VK_SAMPLE_COUNT_64_BIT = 0x00000040,
} VkSampleCountFlagBits;

 

samples 값을 VK_SAMPLE_COUNT_1_BIT로 하게되면

우리는 스왑체인의 이미지 중 하나로 표현되는 단일 색상 버퍼 첨부 파일만 갖게된다.

 

LoadOp : Device Memory(FrameBuffer) -> Tile Memory

// Provided by VK_VERSION_1_0
typedef enum VkAttachmentLoadOp {
    VK_ATTACHMENT_LOAD_OP_LOAD = 0,
    VK_ATTACHMENT_LOAD_OP_CLEAR = 1,
    VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2,
} VkAttachmentLoadOp;

VK_ATTACHMENT_LOAD_OP_LOAD : 프레임 버퍼 내용(Device memory)를 타일 메모리로 로드.

렌더 패스의 실행 과정에서 렌더링되는 영역의 내용이 그대로 보존됨. 메모리 복사 발생

VK_ATTACHMENT_LOAD_OP_CLEAR : 타일 메모리를 특정 색상으로 초기화

각 렌더 패스의 실행과 함께 배경은 특정 색상으로 초기화 되고, 그 위에 프리미티브가 그려진다.
VK_ATTACHMENT_LOAD_OP_DONT_CARE : 렌더링 영역 내부의 내용은 정의되지 않고,

보존되지도 않는다. 사용시 메모리 대역폭 줄어듬.

이는 렌더 패스 인스턴스가 완료될 떄까지 응용 프로그램이 버퍼의 내용을 요구하지 않음.

 

Ex)

1) AR 화면 :  VK_ATTACHMENT_LOAD_OP_LOAD 

- 카메라 이미지를 가져와서 타일 메모리로 Load 해야 함.

- 메모리 복사 발생

2) 배경이 있는 프피리미티브 화면 : VK_ATTACHMENT_LOAD_OP_CLEAR 

- 타일 메모리를 특정 색으로 clear bit.

3) 전체화면 렌더링 : VK_ATTACHMENT_LOAD_OP_DONT_CARE 

- 타일 메모리가 쓰레기 값을 채워진다.

 

StoreOP ( Tile Memory -> Device Memory(FrameBuffer) )

// Provided by VK_VERSION_1_0
typedef enum VkAttachmentStoreOp {
    VK_ATTACHMENT_STORE_OP_STORE = 0,
    VK_ATTACHMENT_STORE_OP_DONT_CARE = 1,
} VkAttachmentStoreOp;

VK_ATTACHMENT_STORE_OP_STORE : 렌더링 영역 내의 내용이 Device Memory(FrameBuffer) 에 쓰이고 렌더 패스 인스턴스가 완료된 후에 읽기를 할 수 있다는 것을 뜻한다.

메모리 복사 발생

VK_ATTACHMENT_STORE_OP_DONT_CARE : 렌더링 영역 내의 내용이 렌더링 후에는 더는 필요하지 않으므로 버려져도 된다는 뜻.

사용시 메모리 대역폭 줄어듬.

 

Ex)

렌더링된 버퍼 내용은 저장해야 하므로 VK_ATTACHMENT_STORE_OP_STORE  사용.

뎁스 버퍼는 저장할 필요 없음 VK_ATTACHMENT_STORE_OP_DONT_CARE 

 

레이아웃 설정의 관련해서는 

initialLayout을 VK_IMAGE_LAYOUT_UNDEFINED를 설정하면, 이전의 어떤 레이아웃인지 신경 쓰지 않음.

finalLayout을 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR를 설정하면, 스왑 체인을 사용하여 이미지를 프레젠테이션할

준비가 됨.

 

--------------------------------------------------------------------------

Load OP, Store OP를 좀더 잘 이해하기 위해 Tile Memory의 대해서 알아보자.

 

모바일 GPU는 일반적으로 Tile Memory를 가지고 있다.
Tile Memory는 간단히 설명하면 GPU의 on-chip 메모리로 상대적으로 높은 대역폭, 상대적으로 낮은 Latency, 그리고 일반 비디오 메모리(시스템 메모리)에 비해 전력을 덜 소모하는 특징을 가지고 있다. 

모바일 기기에서는 일반적으로 CPU와 GPU가 이 (Physical한) 시스템 메모리를 공유한다.

 

Vulkan은 RenderPass를 명시적으로 선언할 수 있어서

Tile 메모리를 디바이스(비디오) 메모리로 옮길지 여부를 결정할 수 있다.

Tile 메모리에 있는 렌더 패스의 결과물이 다음 렌더 패스에서 필요하지 않은 경우

명시적으로 Tile 메모리의 데이터를 버릴수 있습니다.

(Memory Less, Tile Memory의 데이터를 비디오 메모리로 옮기지 않는다)

Tile Memory의 데이터를 비디오(Device) 메모리로 옮기지 않고 이후 렌더 패스에서 그대로 사용하는 것을 선언할 수 있다.

이를 통해 메모리 대역폭을 절약할 수도 있다.

 

OpenGL ES의 경우 RenderPass라는 개념이 없기 때문에 

Tile 메모리의 데이터를 비디오 메모리로 옮기지 않고 버리려면

“glClear” 함수와 "glInvalidateFrameBuffer" 함수를 사용해서 GPU 드라이버에 이를 알릴 수 있다.

 

 

 

Tile Memory는 화면에 출력된  GPU가 직접 접근할수 있는 메모리이며, 매우 빠른 처리 속도를 제공

Device Memory (Vram)는 FrameBuffer 동일하다.

 

loadOp api는 Device memory --> Tile memory

storeOp api는 Tile memory --> Device memory

타일 ​​셰이더를 사용하는 앱은 중간 결과를 장치(device) 메모리에 저장하지 않고

더 빠른 타일 메모리에 데이터를 저장하여 시간을 절약할 수 있습니다.

 

2. 서브 패스 

단일 렌더패스는 여러 개의 서브패스로 구성될 수 있습니다.

서브 패스는 이전 패스의 프레임 버퍼 내용에 따라 달라지는 후속 렌더링 작업 즉 사후처리 효과 입니다.

렌더링 작업을 하나의 렌더패스로 그룹화 하면 Vulkan은 작업을 재정렬하고 메모리 대역폭을 보존하여 더 나은 성능을 낼수 있음.

 

모든 서브패스는 하나 이상의 Attachment를 referencing (참조) 합니다.

// Provided by VK_VERSION_1_0
typedef struct VkAttachmentReference {
    uint32_t         attachment;
    VkImageLayout    layout;
} VkAttachmentReference;

- attachment : 첨부 배열(VkAttachmentDescription)의 인덱스

- layout : 서브패스에서의 첨부의 layout (VkImageLayout)

해당 서브패스가 시작 되면 layout이 해당 값으로 변경.

 

렌더 패스에서 서브패스는 연결된 첨부(Attachment)를 읽고 쓴다.

// Provided by VK_VERSION_1_0
typedef struct VkSubpassDescription {
    VkSubpassDescriptionFlags       flags;
    VkPipelineBindPoint             pipelineBindPoint;
    uint32_t                        inputAttachmentCount;
    const VkAttachmentReference*    pInputAttachments;
    uint32_t                        colorAttachmentCount;
    const VkAttachmentReference*    pColorAttachments;
    const VkAttachmentReference*    pResolveAttachments;
    const VkAttachmentReference*    pDepthStencilAttachment;
    uint32_t                        preserveAttachmentCount;
    const uint32_t*                 pPreserveAttachments;
} VkSubpassDescription;

- flags : 예약된 필드

- pipelineBindPoint : 서브패스가 그래픽스 큐 또는 컴퓨팅 큐에 속하는지를 지정한다.

- inputAttachmentCount  : 입력첨부 수를 지정

- pInputAttachments : VkAttachmentReference 구조체 배열, 크기는 inputAttachmentCount와 같다.

어떤 첨부가 공유 스테이지에서 읽을 수 있는지, 서브패스 중에 첨부 이미지의 레이아웃이 무엇인지를 지정한다.

- colorAttachmentCount : 색상 첨부의 개수를 지정

- pColorAttachments : VkAttachmentReference 구조체 배열로 colorAttachmentCount 와 같은 크기를 갖는다.

렌더 패스의 첨부를 포함하고 있어, 서브 패스에서 색상 첨부로 사용된다.

사용할 첨부 이미지의 레이아웃을 지정한다.

- pResolveAttachments : VkAttachmentReference 구조체 형식에 배열의 포인터로 색상 첨부와 연결

- pDepthStencilAttachment : Dpeth Stencil VkAttachmentReference 형식으로 된 포인터

- preserveAttachmentCount : 보존 첨부의 개수 지정

- pPreserveAttachments : 각 배열 요소인 렌더 패스 첨부인 인덱스들은 서브패스에 의해 사용되지 않는 첨부를 지정한다.

즉 인덱스가 지정한 첨부의 내용은 서브패스 동안 보존돼야 한다.

 

 

렌더 패스 생성

// Provided by VK_VERSION_1_0
VkResult vkCreateRenderPass(
    VkDevice                                    device,
    const VkRenderPassCreateInfo*               pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkRenderPass*                               pRenderPass);

- device : 논리장치

- pCreateInfo : VkRenderPassCreateInfo 구조체 포인터

- pAllocator : 호스트 메모리 할당 해제 제어과정

- pRenderPass : VkRenderPass 개체 포인터 생성

 

렌드 패스 생성 메타데이터

// Provided by VK_VERSION_1_0
typedef struct VkRenderPassCreateInfo {
    VkStructureType                   sType;
    const void*                       pNext;
    VkRenderPassCreateFlags           flags;
    uint32_t                          attachmentCount;
    const VkAttachmentDescription*    pAttachments;
    uint32_t                          subpassCount;
    const VkSubpassDescription*       pSubpasses;
    uint32_t                          dependencyCount;
    const VkSubpassDependency*        pDependencies;
} VkRenderPassCreateInfo;

- type : VK_STRUCTURE_TYPE_REDNER_PASS_CREATE_INFO

- next : 확장판 지정 구조체 포인터

- flags : 예약된 필드

- attachmentCount : 이 필드는 렌더 패스 인스턴스에 의해 사용되는 첨부수를 지정, 0이면 랜더 패스내 attachment 없다는 것을 의미

- attachments : 랜더 패스 첨부 포인터

- subpassCount : 렌더 패스 내 서브패스의 개수 지정

- subpasses : VkSubpassDescription 구조체의 배열로 서브패스들의 속성들 지정

- dependencyCount : 서브패스 쌍의 대한 종속성 개수 지정

- dependencies : VkSubpassDependency 구조체의 배열로, 배열의 크기는 dependencyCount와 같다.

 

전체 코드>

VkAttachmentDescription attachmentDescription{
        .format = mSwapChainImageFormat,
        .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));

 

------------------------------------------------------------------------------------------

FrameBuffer 란

 

프레임 버퍼는 그래픽스 파이프라인의 출력이 저장될 위치를 정의한 리소스

프레임 버퍼는 이미지 뷰의 집합으로 구성됩니다.

스와프 체인 이미지 마다 프레임 버퍼가 필요합니다.

 

예를 들어 이중 버퍼 스와프 체인이미지가 있는 경우, 두 개의 프레임 버퍼가 필요한데,

하나는 프론트 버퍼, 나머지는 백 버퍼이다.

 

FrameBuffer와 Render pass 관계는 다음과 같다.

FrameBuffer와 Render pass 관계

VkFrameBuffer의 pAttachments 변수는 VkImgeViews 배열을 나타낸다.

VkRenderPass의 pAttachments 변수는 VkAttachmentDescriptions를 나타낸다.

위 두개의 Attachment는 구조체는 다르지만 의미상으로 동일한것으로 본다.

 

 

Framebuffer는 ImageView의 집합이다.

그림에서는 

FrameBuffer0은 ImageView 타입과 동일한 ColorBufferImage0을,

FrameBuffer1은 ColorBufferImage1을 포함하고 있다.

공통으로는 DepthBufferImage를 추가로 포함하고 있다.

각각은 attachment라 칭한다.

 

렌더 패스에서는 두 가지가 있는데 pAttachments, pSubPasses 포인터이다.

pAttachments는 FrameBuffer의 attachment와 의미상 동일하다.

pSubPasses 는  pAttachments 배열의 특정 인덱스를 레퍼런싱 하고있다. 

 

정리하면

스왑체인에서 생성한 이미지는 이미지뷰로 랩핑되고,

프레임버퍼와 이미지뷰는 1:1 이 이상적이라고 했으니

렌더패스에서 정의한 서브패스의 어태치먼트 디스크립션에 의해

스왑체인에서 생성된 이미지는 컬러첨부로 사용할수가 있는 걸로 이해했습니다.

1:1 맵핑 관계가 ( 이미지 == 이미지뷰 ==  컬러 첨부 or 뎁스 첨부 == 프레임 버퍼)

 

질문입니다.

예를 들어 3개의 스왑 이미지를 할당 받고,

하나는 출력용 프론트버퍼, 하나는 백버퍼,

하나는 G 버퍼를 사용하기 위해 컬러 첨부로 정의했다고 가정하면

추후 vkAcquireNextImageKHR 함수를 통해 렌더링할 프레임버퍼 인덱스를 가져오면..

예를들어 G 버퍼는 첨부이니 프레젠테이션 엔진에서 알아서 인덱스를 제외해주고

인덱스를 리턴해주는건가요 ?

--> 아니요 G 버퍼의 경우는 프레젠테이션 이미지가 아니기에 출력을 할 수 없습니다.

스왑체인 이미지의 경우 vkGetSwapchainImagesKHR 을 통해서 생성하고

G버퍼용 이미지의 경우 vkCreateImage 을 통해서 생성 합니다

 

 

또 하나의 특징은 특정 경우에 Vulkan Render Pass 호환이 된다.

 

프레임버퍼를 생성할때 렌더패스를 인자로 넣게 되어 있다.

VkCmdBeginRenderPass() 호출할때 프레임버퍼와 렌더패스를 둘다 넣게 되어있는데,

이때 프레임버퍼에서 생성할때 넣은 렌더패스가 아닌 다른 렌더패스를 넣어도 된다는 의미.

 

렌더 패스 호환되는 조건은 2개는

1) RenderPass의 AttachmentDescription VkFormat과 VkSampleCountFlagBits와 

    ImageView 생성시, VkImageViewCreateInfo 의 VkFormat과 VkSampleCountFlagBits 가 동일할때

2) RenderPass의 pAttachment의 개수와 FrameBuffer의 pAttachment 개수가 동일할때

 

 

프레임 버퍼 생성 메타 데이터

// Provided by VK_VERSION_1_0
typedef struct VkFramebufferCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkFramebufferCreateFlags    flags;
    VkRenderPass                renderPass;
    uint32_t                    attachmentCount;
    const VkImageView*          pAttachments;
    uint32_t                    width;
    uint32_t                    height;
    uint32_t                    layers;
} VkFramebufferCreateInfo;

- sType : VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO

- pNext : 확장판 지정 구조체 가리키는 포인터

- flags : 추후 사용

- renderPass : VkRenderPass 개체, 위에서 생성한 렌더패스 개체

- attachment-Count : 프레임 버퍼와 연결된 첨부의 개수를 지정

- pAttachment : 각 이미지의 대한 VKImageView 핸들의 배열로,

각 요소는 렌더 패스 인스턴스 내의 해당 첨부와 같이 사용된다.

- width : 프레임 버퍼의 폭을 픽셀 단위로 지정한다.

- height : 프레임 버퍼의 높이를 픽셀 단위로 지정

- layers : 프레임 버퍼 내의 레이어를 지정

 

프레임버퍼 생성

// Provided by VK_VERSION_1_0
VkResult vkCreateFramebuffer(
    VkDevice                                    device,
    const VkFramebufferCreateInfo*              pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkFramebuffer*                              pFramebuffer);

- device : 논리적 장치 핸들로 프레임 버퍼를 연결한다.

- pCreateInfo : VkFrameBufferCreateInfo 구조체 포인터

- pAllocator : 이 필드는 호스트 메머리 할당 해제 과정 제어

- pFrameBuffer : VkFrameBuffer 개체를 생성 하고 포인터를 반환

 

Usage >

void VkRenderer::CreateFrameBuffers() {
    auto swapchainImageCount = mSwapChainImages.size();
    mFramebuffers.resize(swapchainImageCount);

    SwapChainDetails swapChainDetails = GetSwapChainDetails();
    VkExtent2D extent = swapChainDetails.surfaceCapabilities.currentExtent;

    for (auto i = 0; i != swapchainImageCount; ++i) {
        // ================================================================================
        // 16. VkFramebuffer 생성
        // ================================================================================
        VkFramebufferCreateInfo framebufferCreateInfo {
                .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                .renderPass = mRenderPass,
                .attachmentCount = 1,
                .pAttachments = &mSwapChainImages[i].imageView,
                .width = extent.width,
                .height = extent.height,
                .layers = 1
        };

        VK_CHECK_ERROR(vkCreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mFramebuffers[i]));
    }
}

 

렌더 패스 시작 & 끝

FrameBuffer에 렌더링 하기 위해서는 Render Pass를 Begin 상태로 변경해야 한다.

렌더링이 완료 되면 Render Pass를 End 상태로 변경해야 한다.

// Provided by VK_VERSION_1_0
void vkCmdBeginRenderPass(
    VkCommandBuffer                             commandBuffer,
    const VkRenderPassBeginInfo*                pRenderPassBegin,
    VkSubpassContents                           contents);

- commandBuffer : VkCommandBuffer

- pRenderPassBegin : VkRenderPassBeginInfo 포인터

- contents : VK_SUBPASS_CONTENT_INLINE

// Provided by VK_VERSION_1_0
void vkCmdEndRenderPass(
    VkCommandBuffer                             commandBuffer);
);

- commandBuffer : VkCommandBuffer

 

Info

// Provided by VK_VERSION_1_0
typedef struct VkRenderPassBeginInfo {
    VkStructureType        sType;
    const void*            pNext;
    VkRenderPass           renderPass;
    VkFramebuffer          framebuffer;
    VkRect2D               renderArea;
    uint32_t               clearValueCount;
    const VkClearValue*    pClearValues;
} VkRenderPassBeginInfo;

typedef struct VkRect2D {
VkOffset2D offset;
VkExtent2D extent;
} VkRect2D

- sType : VK_STRUCTURE_TYPE_PRESENT_INFO_KHR

- pNext : 확장 기능 구조체 포인터

- renderPass : VkRenderPass

- framebuffer : VkFrameBuffer

- renderArea : 렌더패스에 의해 영향 받을 영역, 스왑체인에 지정한 이미지의 크기만큼 설정하자.

- clearValueCount : VkClearValue의 개수

- pClearValues : VkClearValue 배열의 포인터

 

VkRect2D

- offset : 사각형의 오프셋 (좌상단 point)

- extent : 사각형의 크기

 

typedef union VkClearValue {
    VkClearColorValue           color;
    VkClearDepthStencilValue    depthStencil;
} VkClearValue;

- color : color 클리어 값

- depthStencil : Depth와 Stencil 클리어 값

두개의 메모리 공간 공유 함, color or depthStencil 둘 중 하나만 설정 가능.

 

pClearValues 를 지정해주면 클리어값으로 정의 가능.

clear Color 후 아래 과정을 거쳐야 하지만 pClearValues를 지정해주면 불 필요한 메모리 복사 생략 가능.

- tile memory -> device Memory storeop

- device memory -> tile memory loadop

 

전체 코드>

void VkRenderer::Render() {
    // 1. 화면에 출력할 수 있는 Swap Image 얻기
    uint32_t swapchainImageIndex;
    VK_CHECK_ERROR(vkAcquireNextImageKHR(mDevice,
                                         mSwapchain,
                                         UINT64_MAX,
                                         VK_NULL_HANDLE,
                                         mFence,
                                         &swapchainImageIndex));
    auto swapchainImage = mSwapChainImages[swapchainImageIndex];

    // 2. 동기화 기법을 사용하여 사용가능한 Swap Image 나올때까지 기다림.
    auto status = vkGetFenceStatus(mDevice, mFence);
    if (status != VK_SUCCESS) {
        vkWaitForFences(mDevice, 1, &mFence, VK_TRUE, UINT64_MAX);
    }
    vkResetFences(mDevice, 1, &mFence);

    // 3. 커맨드 버퍼 초기화
    vkResetCommandBuffer(mCommandBuffer, 0);

    // 4. 커맨드 버퍼 기록 시작
    VkCommandBufferBeginInfo commandBufferBeginInfo {
            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
            .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT // 커맨드버퍼는 한번만 기록되고 다시 리셋될거라는 의미
    };
    vkBeginCommandBuffer(mCommandBuffer, &commandBufferBeginInfo);

    // VkRenderPass
    auto framebuffer = mFramebuffers[swapchainImageIndex];
    SwapChainDetails swapChainDetails = GetSwapChainDetails();
    VkExtent2D extent = swapChainDetails.surfaceCapabilities.currentExtent;

    VkRenderPassBeginInfo renderPassBeginInfo{
            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
            .renderPass = mRenderPass,
            .framebuffer = framebuffer,
            .renderArea{
                    .extent = extent
            },
            .clearValueCount = 1,
            .pClearValues = &mClearValue
    };

    vkCmdBeginRenderPass(mCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);

    vkCmdEndRenderPass(mCommandBuffer);

    // ================================================================================
    // 7. Clear 색상 갱신
    // ================================================================================
    for (auto i = 0; i != 4; ++i) {
        mClearValue.color.float32[i] = fmodf(mClearValue.color.float32[i] + 0.01, 1.0);
    }

    // 9. 커맨드 버퍼 기록 종료
    vkEndCommandBuffer(mCommandBuffer);
    VkSubmitInfo submitInfo {
            .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
            .commandBufferCount = 1,
            .pCommandBuffers = &mCommandBuffer,
            .signalSemaphoreCount = 1,
            .pSignalSemaphores = &mSemaphore,
    };

    // 10. 커맨드 버퍼 제출
    vkQueueSubmit(mGraphicQueue, 1, &submitInfo, VK_NULL_HANDLE);
    // vkQueueWaitIdle(mGraphicQueue); submitInfo Semaphore로 인하여 필요 없음.

    // 11. vkImage 화면에 출력
    VkPresentInfoKHR presentInfo{
            .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
            .waitSemaphoreCount = 1,
            .pWaitSemaphores = &mSemaphore,
            .swapchainCount = 1,
            .pSwapchains = &mSwapchain,
            .pImageIndices = &swapchainImageIndex
    };

    VK_CHECK_ERROR(vkQueuePresentKHR(mGraphicQueue, &presentInfo));
    VK_CHECK_ERROR(vkQueueWaitIdle(mGraphicQueue));
}

 

 

Reference

https://vulkan.lunarg.com/doc/view/latest/windows/tutorial/html/12-init_frame_buffers.html

 

https://graphicsimon.tistory.com/66

 

11. Image

벌칸은 기본적으로 버퍼와 이미지라는 두 가지 유형의 리소스를 갖고있다.이전 챕터에서 버퍼에 대해서 알아보았고,  이미지 리소스 개념에 대해서 알아본다. 이미지리소스는 1D, 2D 또는 3D 형

graphicsimon.tistory.com

 

 

 

'Vulkan' 카테고리의 다른 글

8. Vertex Buffer and Memory  (0) 2024.07.05
7. SPIR-V  (0) 2024.07.04
5. Presentation And Synchronize ( Fence, Semaphore, Event )  (0) 2024.06.27
4. Pipeline Barrier And Image Layout  (0) 2024.06.24
3. Command buffer  (0) 2024.06.21