Category Archives: Lava

Hi there !
A Few weeks ago, February 16th to be precise, Vulkan, the new graphic API from Khronos was released. It is a new API which gives much more control about the GPUs than OpenGL (API I loved before Vulkan ^_^).

OpenGL’s problems

Driver Overhead

Fast rendering problems could be from the driver, video games don’t use perfectly the GPU (maybe 80% instead of 95-100% of use). Driver overheads have big costs and more recent OpenGL version tend to solve this problem with Bindless Textures, multi draws, direct state access, etc.
Keep in mind that each GPU calls could have a big cost.
Cass Everitt, Tim Foley, John McDonald, Graham Sellers presented Approaching Zero Driver Overhead with OpenGL in 2014.

Multi threading

With OpenGL, it is not possible to have an efficient multi threading, because an OpenGL context is for one and only one thread that is why it is not so easy to make a draw call from another thread ^_^.

Vulkan

Vulkan is not really a low level API, but it provides a far better abstraction for moderns hardwares. Vulkan is more than AZDO, it is, as Graham Sellers said, PDCTZO (Pretty Darn Close To Zero Overhead).

Series of articles about Lava

What is Lava ?

Lava is the name I gave to my new graphic (physics?) engine. It will let me learn how Vulkan work, play with it, implement some global illumination algorithms, and probably share with you my learnings and feelings about Vulkan. It is possible that I’ll make some mistakes, so, If I do, please let me know !

Why Lava ?

Vulkan makes me think about Volcano that make me think about Lava, so… I chose it 😀 .

Initialization

Now begins what I wanted to discuss, initialization of Vulkan.
First of all, you have to really know and understand what you will attend to do. For the beginning, we are going to see how to have a simple pink window.

Hello world with Vulkan

When you are developing with Vulkan, I advise you to have specifications from Khronos on another window (or screen if you are using multiple screens).
To have an easier way to manage windows, I am using GLFW 3.2, and yes, you are mandatory to compile it yourself ^_^, but it is not difficult at all, so it is not a big deal.

Instance

Contrary to OpenGL, in Vulkan, there is no global state, an instance could be similar to an OpenGL Context. An instance doesn’t know anything about other instances, is utterly isolate. The creation of an instance is really easy.

Physical devices, devices and queues

From this Instance, you could retrieve all GPUs on your computer.
You could create a connection between your application and the GPU you want using a VkDevice.
Creating this connection, you have to create as well queues.
Queues are used to perform tasks, you submit the task to a queue and it will be performed.
The queues are separated between several families.
A good way could be use several queues, for example, one for the physics and one for the graphics (or even 2 or three for this last).
You could as well give a priority (between 0 and 1) to a queue. Thanks to that, if you consider a task not so important, you just have to give to the used queue a low priority :).

The way to render something

A window is assigned to a Surface (VkSurfaceKHR). To draw something, you have to render into this surface via swapchains.

From notions of Swapchains

In Vulkan, you have to manage the double buffering by yourself via Swapchain. When you create a swapchain, you link it to a Surface and tell it how many images you need. For a double buffering, you need 2 images.

Once the swapchain was created, you should retrieve images and create frame buffers using them.

To notions of Render Pass

Right now, Vulkan should be initialized. To render something, we have to use render pass, and command buffer.

Command Buffers

Command buffer is quite similar to vertex array object (VAO) or display list (old old old OpenGL 😀 ).
You begin the recorded state, you record some “information” and you end the recorded state.
Command buffers are allocated from the CommandPool.

Vulkan provides two types of Command Buffer.

Primary level : They should be submitted within a queue.

Secondary level : They should be executed by a primary level command buffer.

CommandBuffer allocation

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

std::size_t CommandPool::allocateCommandBuffer(){

VkCommandBuffer cmd;

VkCommandBufferAllocateInfo info;

info.sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;

info.pNext=nullptr;

info.commandPool=mCommandPool;

info.commandBufferCount=1;

info.level=VK_COMMAND_BUFFER_LEVEL_PRIMARY;

vulkanCheckError(vkAllocateCommandBuffers(mDevice,&info,&cmd));

mCommandBuffers.emplace_back(cmd);

returnmCommandBuffers.size()-1;

}

Renderpass

One render pass is executed on one framebuffer. The creation is not easy at all. One render pass is componed with one or several subpasses.
I remind that framebuffers could have several attachments.
Each attachment are not mandatory to be used for all subpasses.

This piece of code to create one renderpass is not definitive at all and will be changed as soon as possible ^^. But for our example, it is correct.