[vk] Drawing triangle - graphics pipeline basics - Render passes

2021. 8. 9. 05:12그래픽스/vk

Render passes

 

1. Setup

- 파이프라인을 생성하기전에 Vulkan에게 렌더링 동안 사용될 framebuffer를 알려줘야함

(need to tell Vulkan about the framebuffer attachments)

- 얼마나 많은 색상 과 깊이 버퍼가 있는지,

- 각각에 대해 사용할 샘플 수 및 렌더링 작업 전체에서 내용을 처리하는 방법을 지정해야함.

- 이러한 모든정보들은 render pass object에 래핑됨 

- createRenderPass 함수를 새로 만들어 initVulkan에  createGraphicsPipeline전에 호출할것임

void initVulkan() {
    createInstance();
    setupDebugMessenger();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
}

...

void createRenderPass() {

}

 

 

 

 

 

2. Attachment description

VkAttachmentDescription

-  지금은 swap chain의 images 중 하나가 나타내는 단일 color buffer 만 설정함.

   (따라서 color에 대한 구조체만 만듬)

 

void createRenderPass() {
    VkAttachmentDescription colorAttachment{};
    colorAttachment.format = swapChainImageFormat;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
}

- format 은 swapchain image의 형식과 일치해야함 (format of image view)

- sample: 멀티 샘플링을 설정하지 않았으므로 1을 할당해준것 (the number of samples of the image.)

- 나머지 op가 붙은 멤버들은 수행할 작업을 결정함.

 

 

 

2.1 loadOp, storeOp

colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;

- loadOp, storeOp: 렌더링 전과 렌더링 후에 첨부된 데이터에 수행할 작업을 결정함.

- loadOp는 다음과 같은 선택 항목이 있음.

  • VK_ATTACHMENT_LOAD_OP_LOAD: 첨부파일 기존 내용 유지
  • VK_ATTACHMENT_LOAD_OP_CLEAR: 시작할때 값을 상수로 초기화
  • VK_ATTACHMENT_LOAD_OP_DONT_CARE: Existing contents are undefined; 무엇이든 상관없음

- 튜토리얼에선 새로운 프레임을 그리기 전에 clear 연산을 사용하여 framebuffer를 지울것임

 

  • VK_ATTACHMENT_STORE_OP_STORE: 렌더링된 내용이 메모리에 보관되고 나중에 읽을 수 있음.
  • VK_ATTACHMENT_STORE_OP_DONT_CARE: 렌더링 작업 후의 framebuffer의 내용이 정의되지않음

- 튜토리얼에선 삼각형을 screen에 띄우는게 목표이므로 store 연산을 STORE로 설정한것

- loadOp 와 storeOp는 색상과 depth data에 적용하는것.

 

 

 

 

2.2 stencilLoadOp/ stencilStoreOp

colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

- stencilLoadOp / stencilStoreOp 은 stencil data에 적용하는것

- 현재 stencil buffer를 사용하지 않으므로 무엇이든 상관이 없음.

 

 

 

 

2.3 layout

colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

- Vulkan의 Textures 그리고 framebuffers은 특정 pixel format과함께 VkImage 객체로 표현함.

- 하지만 이미지에서 수행하려고 하는 작업에 따라 메모리의 픽셀 레이아웃이 변경될 수 있음.

- 가장 일반적인 레이아웃은 아래와 같음

  • VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : Images used as color attachment
  • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : Images to be presented in the swap chain
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL : Images to be used as destination for a memory copy operation

- 이 토픽은 texturing 챕터에서 깊게 들어갈것임

- 중요한것은, 이미지를 다음 작업에 적합한 특정 레이아웃으로 전환해야한다는것임.

 

- initialLayout 은 renderpass 가 시작되기전에 이미지가 가질 레이아웃을 지정함.

- finalLayout은 렌더 패스가 완료 될 때 자동으로 전환 할 레이아웃을 지정함.

- initialLayout에 VK_IMAGE_LAYOUT_UNDEFINED 을 사용한다는것은 이전 이미지가 무엇이든 상관없다는것이다.

- undefined 주의사항(caveat) : 이미지 내용물을 보존한다는것을 보장하지못함. (이전의 내용)

- - - - 어쨋든 clear하기 때문에 중요하진 않음

- 렌더링 후 swapchain을 사용함으로써 이미지가 준비되기를 원하기 위해 

- - - - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 를 finalLayout으로 설정한것임.

 

 

 

 

 

3. Subpasses and attachment references

- single render pass는 multiple subpasses로 구성될 수 있음.

- subpasses 은 이전 passes의 framebuffers 내용에 의존하는 후속 렌더링 연산임.

   (ex. 차례대로 처리되는 후처리효과 시퀀스)

- 이러한 렌더링 작업들을 하나의 render pass로 그룹화하면,

- Vulkan은 작업을 재정렬할 수 있고

- 더 나은 성능을 위해 메모리 대역폭을 절약할 수 있음.

- 그러나 첫번째 삼각형의 경우 단일 하위패스만 사용할것임

 

 

 

 

3.1 Attachment reference

모든 subpass는 이전 섹션에서 다룬 attachment 구조체를 하나 또는 그이상 참조함.

- 이러한 attachment에 대한 참조는 VkAttachmentReference  구조체에서 다룸

 

VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

- attachment :  참조할 VkRenderPassCreateInfo::pAttachments 의 인덱스 

- - - - 또는 VK_ATTACHMENT_UNUSED로 사용하지 않음을 명시.

         (renderpass의 attachment의 인덱스)

         (const VkAttachmentDescription* pAttachments)

- 1개의 VkAttachmentDescription로 구성된 배열이므로 index는 0으로 설정

- layout 은 subpass가 attachment를 참조하는동안 사용할 레이아웃을 지정.

- Vulkan은 subpass가 시작되면, 자동적으로 attachment의 레이아웃을 이 값으로 전환한다.

 

- 지금은 attachment를 color buffer로 사용할것임.

- VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 레이아웃은 이름에서 알 수 있듯이 최상의 성능을 제공함.

 

 

 

 

3.2 SubpassDescription

- subpass는 VkSubpassDescription 구조체를 사용하여 기술함.

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

- Vulkan은 미래에 compute subpasses를 지원할 수 있으므로 그래픽 서브패스라는 점에 대해 명시해야함.

- 다음으로, color attachment에 대해 명시해야함.

subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;

- pColorAttachments 의 배열은 layout(location = 0) out vec4 outColor 지시문을 사용한 fragment shader의 반환값에 직접 참조됨.

Each element of the pColorAttachments array corresponds to an output location in the shader, i.e. if the shader declares an output variable decorated with a Location value of X, then it uses the attachment provided in pColorAttachments[X].

If the 
attachment member of any element of pColorAttachments is VK_ATTACHMENT_UNUSED, or if Color Write Enable has been disabled for the corresponding attachment index, then writes to the corresponding location by a fragment shader are discarded.

  • pColorAttachments is a pointer to an array of colorAttachmentCount VkAttachmentReference structures defining the color attachments for this subpass and their layouts.

- 다른 타입의 Attachments 또한 subpass에 의해 참조될 수 있음

  • pInputAttachments: Attachments that are read from a shader
  • pResolveAttachments: Attachments used for multisampling color attachments
  • pDepthStencilAttachment: Attachment for depth and stencil data
  • pPreserveAttachments: Attachments that are not used by this subpass, but for which the data must be preserved

 

4. Render pass

- attachment와 이를 참조하는 basic subpass에 대해 기술했으므로

- render pass 그 자체를 만들 수 있다.

- 새로운 class member 변수인 VkRenderPass object를 만들자.

VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;

- render pass object 를 생성하기 위해서는

- VkRenderPassCreateInfo 구조체에 attachment의 배열 그리고 subpass의 배열을 기술해야함.

  (VkAttachmentReference 객체는 이 배열들의 인덱스를 사용하여 attachment를 참조하는것.)

VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;

if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
    throw std::runtime_error("failed to create render pass!");
}

- pipeline layout과 같이 render pass 는 program 전체에 걸쳐 참조될것임.

- 따라서 아래와 같이 destroy해줘야함을 잊지말자

void cleanup() {
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
    vkDestroyRenderPass(device, renderPass, nullptr);
    ...
}

- 다음장에서 이제 grphics pipeline object를 만들것임!

    void createRenderPass() {
        VkAttachmentDescription colorAttachment{};
        colorAttachment.format = swapChainImageFormat;
        colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
        colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
        colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
        colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

        VkAttachmentReference colorAttachmentRef{};
        colorAttachmentRef.attachment = 0;
        colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkSubpassDescription subpass{};
        subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
        subpass.colorAttachmentCount = 1;
        subpass.pColorAttachments = &colorAttachmentRef;

        VkRenderPassCreateInfo renderPassInfo{};
        renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
        renderPassInfo.attachmentCount = 1;
        renderPassInfo.pAttachments = &colorAttachment;
        renderPassInfo.subpassCount = 1;
        renderPassInfo.pSubpasses = &subpass;

        if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
            throw std::runtime_error("failed to create render pass!");
        }
    }

 

 

 

 

 

 

전체 코드

C++ code / Vertex shader / Fragment shader

 

 

https://vulkan-tutorial.com/en/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes