Vulkan & OpenGL Differences
When setting up a shaded triangle, Vulkan offers several features and concepts that are either absent or significantly different from OpenGL. Here are some key differences:
1. Explicit Control
2. Command Buffers
3. Multiple Queues
4. Pipeline Creation
5. Synchronization
6. Resource Binding
7. Shader Modules
8. Render Passes
Summary
OpenGL Steps
+--------------------------------------+
| OpenGL Application |
| (Setup and Initialization) |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Shader Program |
| GLuint shaderProgram = glCreateProgram(); |
| glAttachShader(shaderProgram, vertexShader); |
| glAttachShader(shaderProgram, fragmentShader); |
| glLinkProgram(shaderProgram); |
+--------------------------------------+
|
v
+--------------------------------------+
| Setup Vertex Array Object |
| GLuint VAO; |
| glGenVertexArrays(1, &VAO); |
| glBindVertexArray(VAO); |
| glGenBuffers(1, &VBO); |
| glBindBuffer(GL_ARRAY_BUFFER, VBO); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
| glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); |
| glEnableVertexAttribArray(0); |
+--------------------------------------+
|
v
+--------------------------------------+
| Draw Call |
| glUseProgram(shaderProgram); |
| glBindVertexArray(VAO); |
| glDrawArrays(GL_TRIANGLES, 0, 3);|
+--------------------------------------+
|
v
+--------------------------------------+
| Framebuffer |
| SwapBuffers(window); |
+--------------------------------------+
Vulkan Steps
+--------------------------------------+
| Vulkan Application |
| (Setup and Initialization) |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Instance |
| vkCreateInstance(&instanceInfo, &instance); |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Device |
| vkCreateDevice(instance, &deviceCreateInfo, &device); |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Shader Modules |
| vkCreateShaderModule(device, &vertShaderInfo, &vertShaderModule); |
| vkCreateShaderModule(device, &fragShaderInfo, &fragShaderModule); |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Graphics Pipeline |
| vkCreateGraphicsPipelines(device, &pipelineInfo, &pipeline); |
+--------------------------------------+
|
v
+--------------------------------------+
| Create Vertex Buffer |
| vkCreateBuffer(device, &bufferCreateInfo, &vertexBuffer); |
| vkBindBufferMemory(device, vertexBuffer, vertexBufferMemory); |
+--------------------------------------+
|
v
+--------------------------------------+
| Allocate Command Buffer |
| vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); |
+--------------------------------------+
|
v
+--------------------------------------+
| Record Command Buffer |
| vkBeginCommandBuffer(commandBuffer, &beginInfo); |
| vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); |
| vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets); |
| vkCmdDraw(commandBuffer, 3, 1, 0, 0); |
| vkEndCommandBuffer(commandBuffer); |
+--------------------------------------+
|
v
+--------------------------------------+
| Submit Command Buffer |
| vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); |
+--------------------------------------+
|
v
+--------------------------------------+
| Render Pass |
| vkBeginRenderPass(commandBuffer, &renderPassInfo); |
+--------------------------------------+
|
v
+--------------------------------------+
| Framebuffer |
| vkEndRenderPass(commandBuffer); |
| vkQueuePresentKHR(presentQueue, &presentInfo); |
+--------------------------------------+
1. Initialize Vulkan
- Create a Vulkan Instance:
- Use
vkCreateInstance
to create a Vulkan instance. This is the starting point for any Vulkan application and contains information about the application and the Vulkan API version.
- Use
2. Select a Physical Device
- Enumerate Physical Devices:
- Use
vkEnumeratePhysicalDevices
to find available physical devices (GPUs) on the system.
- Use
- Select a Suitable Device:
- Choose a physical device that supports the features and queues you need (like graphics, compute, etc.).
3. Create a Logical Device
- Create Logical Device:
- Use
vkCreateDevice
to create a logical device that allows your application to interact with the physical device. Specify the queue types needed, such as a graphics queue.
- Use
4. Create a Swap Chain
- Choose Swap Chain Parameters:
- Determine the swap chain's surface format, presentation mode, and extent.
- Create Swap Chain:
- Use
vkCreateSwapchainKHR
to create the swap chain, which handles presenting images to the screen.
- Use
5. Create Image Views
- Create Image Views:
- For each image in the swap chain, create an image view using
vkCreateImageView
. This allows Vulkan to access the images in the swap chain.
- For each image in the swap chain, create an image view using
6. Create Render Pass
- Define Render Pass:
- Use
vkCreateRenderPass
to create a render pass that defines how framebuffer attachments (color, depth, etc.) are used during rendering.
- Use
7. Create Framebuffers
- Create Framebuffers:
- For each image view in the swap chain, create a framebuffer using
vkCreateFramebuffer
. This links the image views with the render pass.
- For each image view in the swap chain, create a framebuffer using
8. Create Shaders
- Load Shader Code:
- Load the vertex and fragment shader code, typically written in GLSL or HLSL.
- Create Shader Modules:
- Use
vkCreateShaderModule
to create shader modules for both the vertex and fragment shaders.
- Use
9. Create Graphics Pipeline
- Define Graphics Pipeline:
- Use
vkCreateGraphicsPipelines
to create the graphics pipeline. This involves specifying the shader stages, fixed-function state (like viewport, rasterization, blending), and the render pass.
- Use
10. Create Vertex Buffer
- Create Buffer:
- Use
vkCreateBuffer
to create a vertex buffer that holds the triangle's vertex data (positions, colors, etc.).
- Use
- Allocate Memory:
- Allocate memory for the vertex buffer using
vkAllocateMemory
and bind it usingvkBindBufferMemory
.
- Allocate memory for the vertex buffer using
- Copy Data:
- Map the memory, copy the vertex data into it, and unmap the memory.
11. Create Command Buffers
- Allocate Command Buffers:
- Use
vkAllocateCommandBuffers
to allocate command buffers for recording commands.
- Use
12. Record Commands
- Begin Command Buffer:
- Use
vkBeginCommandBuffer
to start recording commands in the command buffer.
- Use
- Begin Render Pass:
- Use
vkCmdBeginRenderPass
to start the render pass.
- Use
- Bind Graphics Pipeline:
- Use
vkCmdBindPipeline
to bind the graphics pipeline.
- Use
- Bind Vertex Buffer:
- Use
vkCmdBindVertexBuffers
to bind the vertex buffer.
- Use
- Draw Command:
- Use
vkCmdDraw
to issue the draw command for the triangle.
- Use
- End Render Pass:
- Use
vkCmdEndRenderPass
to finish the render pass.
- Use
- End Command Buffer:
- Use
vkEndCommandBuffer
to finalize the command buffer.
- Use
13. Submit Command Buffer
- Submit to Queue:
- Use
vkQueueSubmit
to submit the recorded command buffer to the graphics queue for execution.
- Use
14. Presenting the Image
- Present the Frame:
- Use
vkQueuePresentKHR
to present the rendered image from the swap chain to the screen.
- Use
15. Cleanup
- Cleanup Resources:
- Destroy resources in the reverse order of their creation, such as pipelines, framebuffers, swap chains, and the Vulkan instance.