Graphics API

OpenGL Graphics Pipeline

Graphics Pipeline is an abstract model that describes sequence of steps needed to render a 3D scene. 

asynchronous operation -  The CPU sends rendering commands to the GPU, which then perform rendering operations while the CPU continues with other tasks

VRAM - Memory core on the GPU which stores buffers

0. Core Concepts and Vocabulary 

rendering - Generate two-dimensional images of 3D scenes

shading - The darkness of an object not in direct light 

shadows - the silhouette of one object's shape on the surface of another object

frustrum - Region contained within the truncated pyramid shape outlined in white indicates the space visible to the camera.

pixel - specify colors using triples of floating-point numbers between 0 and 1 to represent the amount of red, green, and blue light present in a color; a value of 0 represents no amount of that color is present, while a value of 1 represents that color at full intensity

buffer (data buffer/buffer memory) is a part of a computer's memory that serves as temporary storage for data while it is being moved from one location to another.

1. Application Stage

Initializing the window where the rendered graphics will be displayed.

2. Geometry Processing

Determining the final position of each vertex of the geometric shapes to be rendered, implemented by a program called the vertex shader

mesh - a collection of points (vertices) that are grouped into lines or triangles to make a shape of a geometric object 

Vertex shader is applied to each of the vertices to determine the final position each point being rendered, which is typically calculated from a series of transformations: 

In addition to these transformation calculations, the vertex shader may perform additional calculations and send additional information to the fragment shader as needed.

The end of geometry processing 

3. Rasterization

geometric primitive -  How the vertices is connected to produce a shape. OpenGL supports ten of these types, including Points, lines, or triangles, which consist of sets of 1, 2, or 3 points.  

Rasterization - Process of filling in the horizontal spans of pixels belonging to a geometric primitive.

primitive assembly - process of grouping points to geometric primitives

Once the geometric primitives have been assembled, the next step is to determine which pixels correspond to the interior of each geometric primitive. Since pixels are discrete units, they will typically only approximate the continuous nature of a geometric shape, and a criterion must be given to clarify which pixels are in the interior.

Three simple criteria could be  

  1. The entire pixel area is contained within the shape  
  2. The center point of the pixel is contained within the shape  
  3. Any part of the pixel is contained within the shape 

fragment - For each pixel corresponding to the interior of a shape

4. Pixel Processing

This stage determinea the final color of each pixel, storing this data in the color buffer within the frame buffer.

During the first part, a program called the fragment shader is applied to each of the fragments to calculate their final color. This calculation involves various data stored in each fragment, in combination with global data available during rendering, such as 

The GPU handles the following:

Computer Graphics From Scratch

Computer Graphics From Scratch

Raytracing

Computer Graphics From Scratch

Rasterization

Color

 

Lines

1. draw line by using Interpolate to compute values of a linear function. 

Filled Triangles

draw lines to make wireframe of a triangle using 3 2D Vertices with connecting points 

  1.  Sort the points
  2. compute the x coordinate of the triangle edges
  3. concatenate the short sides
  4. determine which is ledt annd which is right
  5. draw the horizontal segments

Shaded Triangles 

 

 

Vulkan

vulkan low level modern graphics api 

Vulkan

Getting Started with Vulkan

Install Vulkan SDK:

https://vulkan.lunarg.com/

 

Vulkan

Vulkan & OpenGL Differences (Shaded Triangle)


1. Explicit Control

  • Vulkan: Provides explicit control over GPU resources and operations. You need to manage and allocate resources like memory, command buffers, and synchronization primitives directly.
  • OpenGL: Abstracts much of this complexity. It handles resource management for you, making it easier for developers but less flexible for advanced use cases.

2. Command Buffers

  • Vulkan: Uses command buffers to record rendering commands before submitting them to the GPU. You can record commands once and execute them multiple times, allowing for better performance optimization.
  • OpenGL: Commands are issued immediately and not recorded for later execution. This can be less efficient, especially in complex rendering scenarios.

3. Multiple Queues

  • Vulkan: Supports multiple queues for different operations (graphics, compute, transfer). You can use these queues in parallel to optimize performance.
  • OpenGL: Generally operates on a single command queue, meaning that all rendering commands are submitted sequentially.

4. Pipeline Creation

  • Vulkan: Requires explicit pipeline creation for each shader stage and configuration. This process can be cumbersome but allows for fine-tuned optimization and custom behavior.
  • OpenGL: Simplifies pipeline management. You can bind shaders and set states with fewer API calls, which makes setup quicker and more straightforward.

5. Synchronization

  • Vulkan: Provides detailed synchronization control using semaphores and fences. This allows you to manage resource access and rendering operations more precisely.
  • OpenGL: Uses simpler synchronization mechanisms. It abstracts the synchronization process, which can lead to issues like implicit synchronization overhead.

6. Resource Binding

  • Vulkan: Requires explicit binding of resources (like buffers and textures) to the pipeline, which can lead to better performance through optimization.
  • OpenGL: Uses a more implicit model for resource binding, where resources can be bound and unbound more flexibly but can introduce overhead.

7. Shader Modules

  • Vulkan: Utilizes shader modules that compile GLSL (or SPIR-V) into an intermediate representation. This approach provides more control over shader compilation and linking.
  • OpenGL: Shaders are compiled and linked at runtime, which is easier but provides less flexibility in optimizing shader performance.

8. Render Passes

  • Vulkan: Uses render passes to define the structure of rendering operations, allowing for more control over how framebuffer attachments are managed and used.
  • OpenGL: Does not have an explicit concept of render passes; instead, it relies on simpler framebuffer attachments and operations.

Summary

  • Vulkan provides more control, flexibility, and optimization opportunities compared to OpenGL, but at the cost of complexity. This makes Vulkan better suited for high-performance applications, while OpenGL is often preferred for simpler applications due to its ease of use and abstraction.
  • If you're setting up a shaded triangle in Vulkan, you'll need to manage more details, such as command buffer creation, resource binding, and synchronization, which are mostly handled automatically by OpenGL.

Shaded Triangle Steps

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

2. Select a Physical Device

3. Create a Logical Device

4. Create a Swap Chain

5. Create Image Views

6. Create Render Pass

7. Create Framebuffers

8. Create Shaders

9. Create Graphics Pipeline

10. Create Vertex Buffer

11. Create Command Buffers

12. Record Commands

13. Submit Command Buffer

14. Presenting the Image

15. Cleanup

RESOURCES

https://edw.is/learning-vulkan/#what-i-gained-from-switching-to-vulkan