2021. 8. 4. 14:06ㆍ그래픽스/vk
Summary
실제 프로그램은 더 많은 단계를 수행해야함
- 정점 버퍼 할당 (allocating vertex buffers)
- 유니폼 버퍼 생성 (creating uniform buffers)
- 텍스처 이미지 업로드 (uploading texture images)
하지만 여기선 "삼각형"을 생성하기위해, 정점버퍼대신 정점 셰이더에 정점좌표를 포함시킴.
(정점 버퍼를 관리하기 위해선 먼저 명령버퍼부터 익숙해져야함)
아래는 vulkan에서 삼각형 만들기 과정.
- Create a VkInstance
- Select a supported graphics card (VkPhysicalDevice)
- Create a VkDevice and VkQueue for drawing and presentation
- Create a window, window surface and swap chain
- Wrap the swap chain images into VkImageView
- Create a render pass that specifies the render targets and usage
- Create framebuffers for the render pass
- Set up the graphics pipeline
- Allocate and record a command buffer with the draw commands for every possible swap chain image
- Draw frames by acquiring images, submitting the right draw command buffer and returning the images back to the swap chain
Step 1 - Instance and physical device selection
- vulkan app 은 VkInstance로부터 vulkan api를 세팅하며 시작함.
- An instance is created by describing your application and any API extensions you will be using.
- After creating the instance, can query for Vulkan supported hw & one or more VkphysicalDevices
to use for operations
- query : properties (VRAM size, device capabilities)
Step 2 - Logical device and queue families
- 물리적 hw device를 선택한 뒤 VkDevice (logical device) 를 생성해야함
- VkPhysicalDeviceFeatures : 구체적인 사항을 작성해야함(멀티 뷰포트렌더링이나 64bit floats 등등)
- queue families : 사용하고싶으면 작성해야함.
- 대부분 연산은 Vulkan (draw commands, memory operation)
비동기적인 실행 : 명령들을 VkQueue 에 보냄
Queues
- queue families 로부터 할당받음
- 각각의 queue family 는 queues의 specific set의 연산을 서포트함.
(separate queue families for graphics, compute and memory transfer operations)
- queue families 는 physical device selection에서 distinguishing factor 로 사용할 수 있음.
- vulkan을 지원하는 장치가 이런 그래픽스 기능을 지원안할 수 있음,
- 하지만 모든 요즘 그래픽카드는 오늘날 대부분 queue 연산은 지원한다.
Step 3 - Window surface and swap chain
- offscreen rendering 뿐만이 아니라면 윈도우창을 만들고 이미지를 렌더해야함
- 기본 플랫폼 API 또는 GLFW, SDL 를 사용하여 띄울 수 있음
- 여기서는 GLFW를 사용하여 튜토리얼을 진행함.
window를 렌더링하기위해서 다음 두 요소가 필요
- window surface, swap chain : VkSurfaceKHR, VkSwapchainKHR
(khr 접미사 - vulkan extension의 일부분이라는의미)
- Vulkan API 는 그자체로 완전한 플랫폼 -> 표준인 WSI 를 사용해 window manager와 상호 작용
Surface
- 렌더링할 창에 대한 플랫폼 간 추상화, HWND windows 에서 기본창 핸들, 인스턴스화
- GLFW라이브러리가 세부정보를 처리하는 기능이 내장
Swap chain
- render targets 의 집합
- 기본적인 목적 : 현재 렌더링하고자 하는것과 스크린에 띄워진거의 차이를 확인.
- 완전한 이미지만 보여주기위해서 중요함.
- 매번 프레임에 그릴때, swapchain에 렌더링할 이미지를 제공해야함.
- 프레임에 그리는것을 마치면, 이미지는 swapchain에 반환 (다시 그리기위해)
- render targets의 개수 와 스크린에 이미지를 표시하기위한 조건은 present mode 에 의존함.
- Common present modes : double buffering (vsync) and triple buffering.
- 일부 플랫폼에서는 VK_KHR_display및 VK_KHR_display_swapchain확장을 통해 - 창 관리자와 상호 작용하지 않고 디스플레이에 직접 렌더링할 수 있음. - 이를 통해 전체 화면을 나타내는 표면을 만들 수 있으며 - 예를 들어 고유한 창 관리자를 구현하는 데 사용 |
Step 4 - Image views and framebuffers
Swap chain에서 받은 이미지를 그리기위해 랩핑해야함.
- VkImageView and VkFramebuffer.
- image view : 사용할 이미지의 특정 부분을 참조,
- framebuffer : color, depth, stencil targets같은 image views를 참조
- swap chain에는 다양한 이미지가 있을 수 있음
- 렌더 시간에 그릴 이미지를 찾기 위해
- 각각에대해 이미지 뷰와 프레임 버퍼를 미리 생성해야함.
(we'll preemptively create an image view and framebuffer)
Step 5 - Render passes
render passes : 렌더링 작업 중에 사용되는 이미지 유형, 이미지 사용방법, 콘테츠 처리 방법
(삼각형을 그릴때는 단일 이미지를 색상 대상으로 사용, draw직전에 단색으로 지운다고 vulkan에 알림)
- render type은 images type 만 기술하지만
- VkFramebuffer 는 실제로 특정 이미지를 이러한 slots에 bind
Step 6 - Graphics pipeline
Vulkan의 그래픽 파이프라인
- VkPipeline object 를 생성하여 설정
- 설정 가능한 state of the graphics card 를 작성해야함
(viewport size, depth buffer operation, programmable state -> VkShaderModule objects)
The VkShaderModule objects
- 쉐이더 바이트 코드에서 생성
- 드라이버는 또한 렌더 패스를 참조하여 지정하는 파이프라인에서 사용할 렌더 대상을 알아야함.
기존 API와 비교하여 가장 큰 특징
- 그래픽 파이프라인을 거의 모든 구성을 미리 설정해야함.
- 다른 셰이더로 전환하거나 정점 레이아웃을 약간 변경하려면
- 그래픽 파이프라인을 완전히 다시 만들어야함.
- 따라서 VkPipline object를 미리 많이 만들어야함.
- 뷰포트 크기, 및 선명한 색상같은 일부 기본 구성만 동적으로 변경할 수 있긴함
- 하지만 모든 상태도 명시적으로 작성해야함
(no default color blend state)
AOT(ahead-of-time) 컴파일, Just-in-time 컴파일에 해당하는 작업을 수행
=> 드라이버, 런타임 성능 최적화 기회가 더 많고
=> 더 예측 가능하다는 것
(because large state changes like switching to a different graphics pipeline are made very explicit.)
Step 7 - Command pools and command buffers
많은 operations (drawing 등) 은 queue에 submitt하는게 필요하다. 그 이전에
이러한 operations 은 첫번째로 VkCommandBuffer 에 record하는게 필요하다
-> 이러한 커맨드 버퍼들은 특정 queue family와 연관된 VkCommandPool 로부터 할당되어진다.
simple triangle 을 그리려면 다음과 같은 operation을 command buffer에 기록해야한다.
- 렌더 패스 시작 (Begin the render pass)
- 그래픽 파이프라인 바인딩 (Bind the graphics pipeline)
- 3개의 꼭짓점 그리기 (Draw 3 vertices)
- 랜더 패스 종료 (End the render pass)
the image in the framebuffer
- swap chain 이 제공하는 특정 이미지에 의존
- 가능한 각 이미지에 대한 command buffer를 기록하여
- 그릴 때 올바른것을 선택하게해야함.
-> 대안은 매 프레임 마다 명령 버퍼를 다시 기록하는것 (효율 하락)
Step 8 - Main loop
- step 7에서 drawing commands는 command buffer에 랩핑됨
- 메인 루프는 매우간단하게 작동한다.
1) swap chain에서 이미지를 받아오고 (vkAcquireNextImageKHR)
2) 해당 이미지에 적합한 명령 버퍼를 선택하고 실행한다. (vkQueueSubmit)
3) screen에 표시하기위해 이미지를 swap chain으로 반환 (vkQueuePresentKHR)
queues에 submit 된 operations은, 비동기적(asynchronously)으로 실행된다.
=> 그러므로 올바른 실행 순서를 보장하기위해 semaphores과 같은 object를 사용해야함.
( use synchronization objects )
=> draw command buffer 는 이미지 획득이 완료될 때 까지 기다리도록 설정해야함.
=> 그렇지 않으면, 화면에 표시하기 위해 여전히 읽고 있는 이미지로 렌더링하게됨
=> vkQueuePresentKHR 호출은 렌더링이 완료될 때까지 기다려야함.
(The vkQueuePresentKHR call in turn needs to wait for rendering to be finished,
for which we'll use a second semaphore that is signaled after rendering completes.)
API concepts
coding conventions
함수에는 소문자 vk 접두사
열거형, 구조체 VK 접두사
열거형 변수에는 VK_ 접두사
API는 함수에 매개변수를 제공하기 위해 구조체를 많이 사용함.
아래는 객체 생성 패턴
VkXXXCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_XXX_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.foo = ...;
createInfo.bar = ...;
VkXXX object;
if (vkCreateXXX(&createInfo, nullptr, &object) != VK_SUCCESS) {
std::cerr << "failed to create object" << std::endl;
return false;
}
1) sType멤버의 구조 유형을 명시적으로 지정해야한다.
2) pNext 멤버는 extension structure를 가리킬 수 있음 (튜토리얼에선 항상 nullptr)
3) 객체를 생성하거나 파괴하는 함수에는 VkAllocationCallbacks
드라이버 메모리에 대한 사용자 지정할당자를 사용할 수 있는 매개변수가 있슴.(x튜토리얼에선 nullptr)
4) 거의 모든 함수는 VkResult 를 반환, VK_SUCCESS 이 아니면 에러 코드임.
Validation layers
- vk는 고성능과 낮은 드라이버 오버헤드를 위해 설계됨
- 기본적으로 매우 제한된 오류검사, 디버깅 기능이 포함됨
- 드라이버가 잘못되면 오류 코드를 반환하는 대신 충돌하는 경우가 많음
=>>> 다른 하드웨어일 경우 문제 가능성이 높다.
=> Vulkan을 사용하면, 유효성 검사 계층이라는 기능을 통해 광범위한 검사를 활성화 할 수 있음
=> 유효성 검사 계층은 API와 그래픽 드라이버 사이에 삽입하여 함수 매개 변수에 대한 추가 검사를 실행
=> 메모리 관리 문제를 추적하는 것과 같은 작업을 수행할 수 있는 코드
=> 개발중에 활성화한 다음 오버헤드 없이 앱을 릴리즈할때 비활성화 가능
=> (레이어에서 디버그 메시지를 수신하려면 콜백함수 등록해야함)
'그래픽스 > vk' 카테고리의 다른 글
[vk] Drawing triangle - setup - Logical device and queue (0) | 2021.08.06 |
---|---|
[vk] Drawing triangle - setup - Physical devices and queue (0) | 2021.08.06 |
[vk] Drawing triangle - setup - Validation layers (0) | 2021.08.05 |
[vk] Drawing triangle - setup - Instance (0) | 2021.08.05 |
[vk] Drawing triangle - setup - base code (0) | 2021.08.04 |