Vulkan Shaders for rasterization on GPUs
In this article we will go over the basics of Vulkan API, an SDK from the LunarG for Khronos group. This can be run on any GPU ( Nvidia/AMD/Mac OS).
It is basically a cross platform abstraction over GPUs. In this article we will look at the Rasterization part of drawing an Image. In yet another article to be followed soon we will look at the RayTracing part of the Vulkan API.
The primitives are Vertices(points) , lines and Triangles.
Requirements:
Vulkan will run on any GPU hardware. In this example we will use a Nvidia chipset.
Software:
I have tested this on ‘g2.xlarge’ GPU on ec2 instance in AWS. The Operating system is Ubuntu 22.04 LTS Jammy.
- Linux: GCC 11+ or Clang 14+.
- Vulkan SDK from LunarG.
- ‘make’ utility .
You do not need any of the other Nvidia’s CUDA eco-system. Only the Physical Hardware with NVIDIA chipt set or any other GPU that supports Vulkan(AMD/Metal..).
The vulkan SDK can be installed from my Github Repo . The link to the example is (https://github.com/rangapv/ML/blob/main/tile/Vulkan/install_vulkanAPI.sh).
Now run this script….
#!/usr/bin/env bash
#author:rangapv@yahoo.com
#14-09-2026
#vlkver="1.3.296.0"
vlkver="1.4.357.0"
vulk_depend() {
echo "installing dependencies"
vkins01=`sudo apt-get update`
vkins1=`sudo apt-get -y install libtbb-dev`
vkins11=`sudo apt install -y x11-apps libx11-dev xserver-xorg-dev xorg-dev`
vkins12=`sudo apt install -y xz-utils`
vkins13=`sudo apt install -y libxinerama-dev libxi-dev`
vkins14=`sudo apt install -y libxcursor-dev`
vkins2=`sudo apt install -y libxcb-xinput0 libxcb-xinerama0 libxcb-cursor-dev`
vkins3=`sudo apt-get -y install libglm-dev libglfw3-dev cmake libxcb-dri3-0 libxcb-present0 libpciaccess0 \
libpng-dev libxcb-keysyms1-dev libxcb-dri3-dev libx11-dev g++ gcc \
libwayland-dev libxrandr-dev libxcb-randr0-dev libxcb-ewmh-dev \
git python-is-python3 bison libx11-xcb-dev liblz4-dev libzstd-dev \
ocaml-core ninja-build pkg-config libxml2-dev wayland-protocols python3-jsonschema \
clang-format qtbase5-dev qt6-base-dev qt6-wayland-dev`
}
install_vkAPI(){
vkins4=`wget https://sdk.lunarg.com/sdk/download/${vlkver}/linux/vulkansdk-linux-x86_64-${vlkver}.tar.xz`
vkins5=`wget https://sdk.lunarg.com/sdk/download/${vlkver}/linux/config.json`
vkins41=`tar -xvf ./vulkansdk-linux-x86_64-${vlkver}.tar.xz -C ~/`
vkins42=`cd ~/${vlkver}/;source ~/${vlkver}/setup-env.sh;vulkaninfo --summary`
}
vulkinfo () {
vlkinfo=`vulkaninfo --summary`
echo "$vlkinfo"
}
startover() {
vrm1=`rm -r ~/1.4.357.0`
vrm3=`rm -r ~/ml1/tile/Vulkan/vulkansdk-linux-x86_64-1.4.357.0.tar*`
}
vulk_depend
install_vkAPI
vulkinfo
#startoverOnce the script installs the Vulkan SDK from the LunarG website. Lets check the details…
$ vulkaninfo --summary
WARNING: [Loader Message] Code 0 : Layer VK_LAYER_MESA_device_select uses API version 1.3 which is older than the application specified API version of 1.4. May cause issues.
WARNING: [Loader Message] Code 0 : terminator_CreateInstance: Received return code -3 from call to vkCreateInstance in ICD /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so. Skipping this driver.
WARNING: [Loader Message] Code 0 : ICD for selected physical device does not export vkGetPhysicalDeviceDisplayPropertiesKHR!
WARNING: [Loader Message] Code 0 : ICD for selected physical device does not export vkGetPhysicalDeviceDisplayPlanePropertiesKHR!
WARNING: [Loader Message] Code 0 : ICD for selected physical device does not export vkGetPhysicalDeviceDisplayPropertiesKHR!
==========
VULKANINFO
==========
Vulkan Instance Version: 1.4.357
Instance Extensions: count = 23
-------------------------------
VK_EXT_acquire_drm_display : extension revision 1
VK_EXT_acquire_xlib_display : extension revision 1
VK_EXT_debug_report : extension revision 10
VK_EXT_debug_utils : extension revision 2
VK_EXT_direct_mode_display : extension revision 1
VK_EXT_display_surface_counter : extension revision 1
VK_EXT_surface_maintenance1 : extension revision 1
VK_EXT_swapchain_colorspace : extension revision 4
VK_KHR_device_group_creation : extension revision 1
VK_KHR_display : extension revision 23
VK_KHR_external_fence_capabilities : extension revision 1
VK_KHR_external_memory_capabilities : extension revision 1
VK_KHR_external_semaphore_capabilities : extension revision 1
VK_KHR_get_display_properties2 : extension revision 1
VK_KHR_get_physical_device_properties2 : extension revision 2
VK_KHR_get_surface_capabilities2 : extension revision 1
VK_KHR_portability_enumeration : extension revision 1
VK_KHR_surface : extension revision 25
VK_KHR_surface_protected_capabilities : extension revision 1
VK_KHR_wayland_surface : extension revision 6
VK_KHR_xcb_surface : extension revision 6
VK_KHR_xlib_surface : extension revision 6
VK_LUNARG_direct_driver_loading : extension revision 1
Instance Layers: count = 12
---------------------------
VK_LAYER_INTEL_nullhw INTEL NULL HW 1.1.73 version 1
VK_LAYER_KHRONOS_profiles Khronos Profiles layer 1.4.357 version 1
VK_LAYER_KHRONOS_shader_object Khronos Shader object layer 1.4.357 version 1
VK_LAYER_KHRONOS_synchronization2 Khronos Synchronization2 layer 1.4.357 version 1
VK_LAYER_KHRONOS_validation Khronos Validation Layer 1.4.357 version 1
VK_LAYER_LUNARG_api_dump LunarG API dump layer 1.4.357 version 2
VK_LAYER_LUNARG_crash_diagnostic Crash Diagnostic Layer is a crash/hang debugging tool that helps determines GPU progress in a Vulkan application. 1.4.357 version 1
VK_LAYER_LUNARG_gfxreconstruct GFXReconstruct Capture Layer Version 1.0.5 1.4.357 version 4194309
VK_LAYER_LUNARG_monitor Execution Monitoring Layer 1.4.357 version 1
VK_LAYER_LUNARG_screenshot LunarG image capture layer 1.4.357 version 1
VK_LAYER_MESA_device_select Linux device selection layer 1.3.211 version 1
VK_LAYER_MESA_overlay Mesa Overlay layer 1.3.211 version 1
Devices:
========
GPU0:
apiVersion = 1.3.255
driverVersion = 0.0.1
vendorID = 0x10005
deviceID = 0x0000
deviceType = PHYSICAL_DEVICE_TYPE_CPU
deviceName = llvmpipe (LLVM 15.0.7, 256 bits)
driverID = DRIVER_ID_MESA_LLVMPIPE
driverName = llvmpipe
driverInfo = Mesa 23.2.1-1ubuntu3.1~22.04.4 (LLVM 15.0.7)
conformanceVersion = 1.3.1.1
deviceUUID = 6d657361-3233-2e32-2e31-2d3175627500
driverUUID = 6c6c766d-7069-7065-5555-494400000000
ubuntu@ip-172-31-2-110:~/ml1/tile/Vulkan$This is a simple understanding on the use of the Vulkan API calls for Graphics drawing. We will draw different geometrics like triangle/rectangle/Circle onto the screen.
The Basics of Vulkan:
We will highlight the important built-in functions that is needed to draw graphics
void initVulkan() {
createInstance();
setupDebugMessenger();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createCommandBuffers();
createSyncObjects();
}- create a glfw window: Since Vulkan does not have a built in window to display we use the “GLFW” library. Also Vulkan does not have a built in linear algebra operation hence we need “GLM” library. This is part of the pre-requisites, we need before we install the Vulkan SDK from Lunar. The above script does this for us.
- create an instance: An Vulkan instance is created by intializing the Vulkan Library. And then fill the struct with Vulkan Application information.
- create an surface: To interact with the Window system to present the images.
- pickPhysicalDevice(): Basically to query the Vulkan provided hardware.
- createLogicalDevice(); Create an instance of the device and assign the desired features to it. And create Ques to be processed further.
- createSwapChain(); This is required for the collection of render targets. A queue similar to Framebuffers.
- createImageViews(); This is required to refer to specific part of the image. This and Framebuffer take the SwapChain output of images to be worked on.
- createRenderPass(); The type of image that are going to be used.
- createGraphicsPipeline(): This needs to be setup to mention the configurable state of the Garphics card. (Rasterization is part of this function as fixed commands)
- createFramebuffers(); This refers to the color and depth of the image
- createCommandPool(); A pool of commands that are to be submitted to the queue
- createCommandBuffers(); The list is first recored into the buffer.
- Rasterizer(): Takes the geometry shaped by the Vertex shader and turns it into fragments that is to be colored by fragment shader. It also does depth test, face culling and scissor test.
typical rasterize call...
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSEVaidation layers are optional components that hookup to Vulkan functions for additional checks.
We need a make file to compile the C++ code for drawing. We also need the shader details which gets compiled by the glsc.
Example 1: Lets draw a Circle.
The make file details is as shown below…
CFLAGS = -std=c++17 -O2
LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
VulkanTest: tria.cpp
g++ $(CFLAGS) -o VulkanTest tria.cpp $(LDFLAGS)
.PHONY: test clean
test: VulkanTest
./VulkanTest
clean:
rm -f VulkanTestThe directory file structure is as shown below….
$ ls -l
-rw-rw-r-- 1 ubuntu ubuntu 244 Oct 21 05:54 maketest
drwxrwxr-x 2 ubuntu ubuntu 4096 Oct 28 08:58 shaders
-rw-rw-r-- 1 ubuntu ubuntu 37330 Oct 28 09:00 tria.cpp
-rw-rw-r-- 1 ubuntu ubuntu 570 Oct 21 05:33 shader.vert
-rw-rw-r-- 1 ubuntu ubuntu 19138 Oct 23 10:30 shader.frag
-rw-rw-r-- 1 ubuntu ubuntu 19216 Oct 23 10:35 shadercompile.sh
:~/ml1/tile/Vulkan/shaders$ ls
frag.spv frag1.spv frag2.spv vert.spv vert1.spv vert2.spvThe compiled vertex and fragment files goes into the shader directory.
The vertex and fragment files needs to be compiled into the intermediate SPIR-V byte code file for the Vulkan to draw the images. The vertex files follow the primitives mentioned in the beginning of this article and the Fragment files are for identifying which part of the Images needs to be colored. These files are written in the SLang format. Originally developed by Nvidia.
This script will compile the shaders to the equivalent SPIR-V file (https://github.com/rangapv/ML/blob/main/tile/Vulkan/Pipeline/shadercompile.sh).
#!/usr/bin/env bash
#author:rangapv@yahoo.com
#23-10-2026
vert1=`~/1.4.357.0/setup-env.sh;~/1.4.357.0/x86_64/bin/glslc shader.vert -o vert.spv`
vert1s="$?"
frag1=`~/1.4.357.0/setup-env.sh;~/1.4.357.0/x86_64/bin/glslc shader.frag -o frag.spv`
frag1s="$?"
if [ $vert1s == "0" ]
then
echo "Vertex shaders has been compiled"
else
echo "error in Vertex shader compilation"
fi
if [ $frag1s == "0" ]
then
echo "Fragment shaders has been compiled"
else
echo "Error in Fragment shader compilation"
fiAfter the compilation move the .spv files to the shaders directory in the path of your application C++ file.
:~/ml1/tile/Vulkan/shaders$ ls
frag.spv frag1.spv frag2.spv vert.spv vert1.spv vert2.spvNow the main C++ file…. The link to the file is( https://github.com/rangapv/ML/blob/main/tile/Vulkan/tria.cpp)
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <limits>
#include <optional>
#include <set>
const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;
const int MAX_FRAMES_IN_FLIGHT = 2;
static constexpr uint32_t CIRCLE_SEGMENTS = 64;
const std::vector<const char*> validationLayers = {
"VK_LAYER_KHRONOS_validation"
};
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
#ifdef NDEBUG
const bool enableValidationLayers = false;
#else
const bool enableValidationLayers = true;
#endif
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) {
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
} else {
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
struct QueueFamilyIndices {
std::optional<uint32_t> graphicsFamily;
std::optional<uint32_t> presentFamily;
bool isComplete() {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
...
....
.....
bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
uint32_t extensionCount;
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
for (const auto& extension : availableExtensions) {
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
QueueFamilyIndices indices;
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
int i = 0;
for (const auto& queueFamily : queueFamilies) {
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.graphicsFamily = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
if (presentSupport) {
indices.presentFamily = i;
}
if (indices.isComplete()) {
break;
}
i++;
}
return indices;
}
std::vector<const char*> getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enableValidationLayers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
bool checkValidationLayerSupport() {
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
for (const char* layerName : validationLayers) {
bool layerFound = false;
for (const auto& layerProperties : availableLayers) {
if (strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
}
if (!layerFound) {
return false;
}
}
return true;
}
static std::vector<char> readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("failed to open file!");
}
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
return VK_FALSE;
}
};
int main() {
HelloTriangleApplication app;
try {
app.run();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}The Vertex and Fragment files for drawing a circle…
Get Rangaswamy P V’s stories in your inbox
Join Medium for free to get updates from this writer.
‘shader.vert’ file is as shown below
#version 450
layout(location = 0) out vec3 fragColor;
const int CIRCLE_SEGMENTS = 64; // must match CIRCLE_SEGMENTS in tria.cpp
const float RADIUS = 0.5;
const float TWO_PI = 6.28318530718;
// Rim colors vary with angle so you can see the shape
vec3 rimColor(float a) {
return vec3(0.5 + 0.5 * cos(a),
0.5 + 0.5 * cos(a + 2.094), // +120 degrees
0.5 + 0.5 * cos(a + 4.189)); // +240 degrees
}
void main() {
int segment = gl_VertexIndex / 3; // which triangle of the fan
int corner = gl_VertexIndex % 3; // 0 = center, 1 = rim a0, 2 = rim a1
float a0 = TWO_PI * float(segment) / float(CIRCLE_SEGMENTS);
float a1 = TWO_PI * float(segment + 1) / float(CIRCLE_SEGMENTS);
vec3 pos;
vec3 col;
if (corner == 0) { // center, white
pos = vec3(0.0, 0.0, 0.5);
col = vec3(1.0, 1.0, 1.0);
} else if (corner == 1) { // first rim point
pos = vec3(RADIUS * cos(a0), RADIUS * sin(a0), 0.5);
col = rimColor(a0);
} else { // second rim point
pos = vec3(RADIUS * cos(a1), RADIUS * sin(a1), 0.5);
col = rimColor(a1);
}
gl_Position = vec4(pos, 1.0);
fragColor = col;
}the ‘shader.frag’ file is as shown below…
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}Lets run the program…
ubuntu@ip-172-31-2-165:~/ml1/tile/Vulkan$ source ~/1.4.357.0/setup-env.sh
ubuntu@ip-172-31-2-165:~/ml1/tile/Vulkan$ make -f maketest
g++ -std=c++17 -O2 -o VulkanTest tria.cpp -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
$ ls -l
-rwxrwxr-x 1 ubuntu ubuntu 69448 Oct 29 08:40 VulkanTestThe output: We have successfully compiled the Drawing file and we have the Output Object “VulkanTest”. Just execute it (like ./VulkanTest) . If you are testing this like me on an Ubuntu or any Linux server from the Cloud make sure your X11 setup is working fine. Our dependencies install took care of that..just making sure… And BINGO!
Example2: Lets Draw a rectangle
The C++ file is in this link….(https://github.com/rangapv/ML/blob/main/tile/Vulkan/triaworksrect.cpp)
Lets look at the Vertex and Fragmnet shader files in the Slang format…
‘shader.vertex’
#version 450
layout(location = 0) out vec3 fragColor;
vec3 positions[6] = vec3[](
vec3( 0.5, -0.5, 0.5), // triangle 1
vec3( 0.5, 0.5, 0.5),
vec3(-0.5, 0.5, 0.5),
vec3( 0.5, -0.5, 0.5), // triangle 2
vec3(-0.5, 0.5, 0.5),
vec3(-0.5, -0.5, 0.5)
);
vec3 colors[6] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0),
vec3(1.0, 0.0, 0.0),
vec3(0.0, 0.0, 1.0),
vec3(1.0, 1.0, 0.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 1.0);
fragColor = colors[gl_VertexIndex];
}‘shader.frag’
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}Use the “shadercompile.sh” script mentioned earlier in this article and move the .spv SPIR-V files into the shaders directory as before for the drawing Circle just shown in the previous chapter.
Compile the file using the make just like before.
$ make -f ./maketest
g++ -std=c++17 -O2 -o VulkanTest tria.cpp -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
ubuntu@ip-172-31-2-165:~/ml1/tile/Vulkan$And the output..
$ ./VulkanTest
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_intel.so
validation layer: Searching for ICD drivers named libGLX_nvidia.so.0
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_lvp.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_radeon.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_intel_hasvk.so
validation layer: Loading layer library /home/ubuntu/1.4.357.0/x86_64/share/vulkan/explicit_layer.d/../../../lib/libVkLayer_khronos_validation.so
validation layer: Loading layer library libVkLayer_MESA_device_select.so
validation layer: terminator_CreateInstance: Received return code -3 from call to vkCreateInstance in ICD /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so. Skipping this driver.
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: vkCreateDevice(): pCreateInfo->enabledLayerCount is 1 (not zero).
Device Layers have never worked since Vulkan 1.0 and only Instance Layers should be used instead: https://docs.vulkan.org/spec/latest/appendices/legacy.html#legacy-devicelayers.
The Vulkan spec states: enabledLayerCount must be 0 (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/devsandqueues.html#VUID-VkDeviceCreateInfo-enabledLayerCount-12384)
validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x59b7e180a200, but it may still be in use by VkSwapchainKHR 0x30000000003.
Most recently acquired image indices: [0], 1, 2.
(Brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation.)
Swapchain image 0 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 2.
Hint: See https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html for details on swapchain semaphore reuse. Examples of possible approaches:
a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image.
b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation.
The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067)
validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x59b7e180a200, but it may still be in use by VkSwapchainKHR 0x30000000003.
Most recently acquired image indices: 0, [1], 2, 0.
(Brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation.)
Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 0.
Hint: See https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html for details on swapchain semaphore reuse. Examples of possible approaches:
a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image.
b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation.
The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067)The output..
Example 3: Lets draw a triangle
The main C++ file is this… (https://github.com/rangapv/ML/blob/main/tile/Vulkan/triaworks.cpp)
The ‘shader.vert’ file is this…
#version 450
layout(location = 0) out vec3 fragColor;
vec2 positions[3] = vec2[](
vec2(0.0, -0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5)
);
vec3 colors[3] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0)
);
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = colors[gl_VertexIndex];
}The ‘shader.frag’ file is this…
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}Run the ‘shadercompile.sh’ file to generate the SPIR-V files and move it to the shader sub directory.
Now lets run ‘make’ and see the output…
$ make -f ./maketest
g++ -std=c++17 -O2 -o VulkanTest tria.cpp -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
ubuntu@ip-172-31-2-165:~/ml1/tile/Vulkan$ ./VulkanTest
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_intel.so
validation layer: Searching for ICD drivers named libGLX_nvidia.so.0
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_lvp.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_radeon.so
validation layer: Searching for ICD drivers named /usr/lib/x86_64-linux-gnu/libvulkan_intel_hasvk.so
validation layer: Loading layer library /home/ubuntu/1.4.357.0/x86_64/share/vulkan/explicit_layer.d/../../../lib/libVkLayer_khronos_validation.so
validation layer: Loading layer library libVkLayer_MESA_device_select.so
validation layer: terminator_CreateInstance: Received return code -3 from call to vkCreateInstance in ICD /usr/lib/x86_64-linux-gnu/libvulkan_virtio.so. Skipping this driver.
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: Copying old device 0 into new device 0
validation layer: Copying old device 1 into new device 1
validation layer: vkCreateDevice(): pCreateInfo->enabledLayerCount is 1 (not zero).
Device Layers have never worked since Vulkan 1.0 and only Instance Layers should be used instead: https://docs.vulkan.org/spec/latest/appendices/legacy.html#legacy-devicelayers.
The Vulkan spec states: enabledLayerCount must be 0 (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/devsandqueues.html#VUID-VkDeviceCreateInfo-enabledLayerCount-12384)
validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x5e7011cd82c0, but it may still be in use by VkSwapchainKHR 0x30000000003.
Most recently acquired image indices: [0], 1, 2.
(Brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation.)
Swapchain image 0 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 2.
Hint: See https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html for details on swapchain semaphore reuse. Examples of possible approaches:
a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image.
b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation.
The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067)
validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x5e7011cd82c0, but it may still be in use by VkSwapchainKHR 0x30000000003.
Most recently acquired image indices: 0, [1], 2, 0.
(Brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation.)
Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 0.
Hint: See https://docs.vulkan.org/guide/latest/swapchain_semaphore_reuse.html for details on swapchain semaphore reuse. Examples of possible approaches:
a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image.
b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation.
The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.357.0/linux/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067)The output..
If you have any issues, you can open an issue in the github repo page or alternatively contact me at rangapv@yahoo.com and you could also find me on X.com(twitter) @rangapv . My linkedin is https://linkedin.com/in/rangapv
Reference:
Vulkan API Guide: https://docs.vulkan.org/refpages/latest/refpages/index.html
Vulkan tutorial: https://vulkan-tutorial.com/Introduction
Vulkan SDK from LunarG:
https://vulkan.lunarg.com/sdk/home
https://sdk.lunarg.com/sdk/download/1.3.296.0/linux/vulkansdk-linux-x86_64-1.3.296.0.tar.xz
Rasterization call : https://docs.vulkan.org/refpages/latest/refpages/source/VkPipelineRasterizationStateCreateInfo.html
Some of my other related works on Medium…









