DEV Community

lim73011968
lim73011968

Posted on • Edited on

Part1 Vulkan: Drawing Triangle 1

This is not a guide, just me writing down my notes
This fallows the Vulkan Tutorial link

Some details I learned along the way

1.Base Code section

  • This is more related with Cpp than Vulkan but I'm just gonna write it as a reminder to myself. Members in cpp or oop means the components in the class which are (filed/variable) and (method/function)

  • When objects are created it uses vkCreateXXX or allocate using vkAllocateXXX. However when these objects are no longer needed, these are destroyed using vkDestroyXXX and vkFreeXXX

  • pAllocator. This is an optional parameter that allows you to specify callbacks for a custom memory allocator. We will ignore this parameter in the tutorial and always pass nullptr as argument.(cause we are beginners)

2.Instance section

  • Instance is the connection between "Your Application" and "The Vulkan Library". Also it initializes the Vulkan Library

  • VkApplicationInfo --> this just specify application information. This code is not really required but it provides some useful info to dirver.

//not really required but still provide the info needed for the dirver.
void createInstance() {
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;
}
Enter fullscreen mode Exit fullscreen mode
  • VkInstanceCreateInfo --> this is not optional and tells the Vulkan driver which global extensions and validation layers we want to use. You don't have to know what global extension and validation layers are because they will be mentioned in next chapter.
//this creates the info full detail is in the end of the code.
//for now don't think to much about this.

VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
Enter fullscreen mode Exit fullscreen mode

-vkCreateInstance --> now the important part! we are going to store the "createInfo" inside the "vkInstance instance variable". Not complicated!!

//What is "nullptr" inside the second parameter???
//that is the callback which we will not use cause... I mentioned this before
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
Enter fullscreen mode Exit fullscreen mode
  • vkEnumerateInstanceExtensionProperties --> this just retrieves the supported extension before actually creating an instance(not really required but cool to have/checks which support is missing)
    uint32_t extensionCount = 0;
    //you can just think of this like an array
    //where inside the extensions vector the information about supported extensions will be stored annnnd 
    //it could be accesed by using index named extension count
    std::vector<VkExtensionProperties> extensions(extensionCount);
    //this like mentioned above the actual supported extensions details will bbe stored in extensions vector
    //------explaination of the code-------
    //vkEnumerateInstanceExtensionProperties(the callback which we will not be using, the address of "number of extensions", the actual vector data);
    vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());

    //now actually printing
    std::cout << "available extensions:\n";
    //this is not realted with vulkan but still gonna write it down
    //(const auto& extension : extensions)
    // ":" --> bring the first value in extensions vecotr and put it inside extension variable
    // auto --> let computer choose what data type will the extension be
    // & point to the actual value of extension variable do not make a copy of extension varaible
    for (const auto& extension : extensions) {
        std::cout << '\t' << extension.extensionName << '\n';
    }
Enter fullscreen mode Exit fullscreen mode

The Full Code

#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
//the vulkan header has been replaced becuase the GLFW will call it automatically
#include <iostream>
#include <cstdlib>

//this is a constant definition
//uint32_t is 32bit integer
const uint32_t WIDTH = 800;
const uint32_t HEIGHT = 600;

class HelloTriangleApplication {
public:
    void run() {
    //add this method initWindow() to run
    initWindow();
        initVulkan();
        mainLoop();
        cleanup();
    }

private:
    //write a private member that creates the actual window
    GLFWwindow* window;
    //a private member that hold the instance
    VkInstance instance;

    void initWindow(){
    //the very first call is glfwInit();
    //why?? you may ask because we have to tell the glfw it is not an OpenGL and should tell glfw to NOT create OpenGL Context
    glfwInit();
    //not sure what these 2 does
    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    //this as you know creates an actual window
    //detail of this line
    //window = glfwCreateWindow(the width, the height, the title, which monitor to open the window, this is only relevent to OpenGL);
    window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
    }

    void initVulkan() {
    //a self made function that creates an instance
    createInstance();
    }

    void mainLoop() {
    //run the application untill the window is closed or an error occurs
        while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
    }
    }
    //we need to clean up resources
    void cleanup() {
    //destroyes the instance
    vkDestroyInstance(instance, nullptr );
    //this destroyes the window
    glfwDestroyWindow(window);

    glfwTerminate();
    }

    void createInstance() {
    //VkApplicationInfo-Structure specifiying application information
    //this whole code is kind of optional but it provieds some useful information to the driver
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;

    //the code below is not optional and tells the Vulkan driver which global extensions and validation layers we want to use. 
    //You don't have to know what global extension and validation layers are 
    //because they will be mentioned in next chapter

    VkInstanceCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;

    //---------EXTENSION PART---------

    //how many extension do we need??
    uint32_t glfwExtensionCount = 0;
    //a storage for extension names
    const char** glfwExtensions;

    //this basically fills in two variable we mentioned
    //1. glfwExtensions
    //2. glfwExtensionCount
    //Asks GLFW: "How many extensions do we need??? and which one??"
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    //we put the "count" and the "name tag" inside the VkInstanceCreateInfo createInfo we mentioned earlier
    createInfo.enabledExtensionCount = glfwExtensionCount;
    createInfo.ppEnabledExtensionNames = glfwExtensions;

    //we are not touching this for now
    createInfo.enabledLayerCount = 0;

    //this is like giving information "Which stuff I am going to use to make Vulkan Project!!!"  
    //1.the first parameter is the pointer to the createInfo. See the code we have been putting information about "Extensions" in the createInfo.
    //2.the second paramter is the callback which we don't have to know cause it will always stay nullptr.
    //3.remember the "Vkinstance instance;" we mentioned earlier outside private and public??? we are going to put the created instance inside that variable.
    VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);

    //the error checker
    if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
        throw std::runtime_error("failed to create instance!");
    }

    uint32_t extensionCount = 0;
    //you can just think of this like an array
    //where inside the extensions vector the information about supported extensions will be stored annnnd 
    //it could be accesed by using index named extension count
    std::vector<VkExtensionProperties> extensions(extensionCount);
    //this like mentioned above the actual supported extensions details will bbe stored in extensions vector
    //------explaination of the code-------
    //vkEnumerateInstanceExtensionProperties(the callback which we will not be using, the address of "number of extensions", the actual vector data);
    vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());

    //now actually printing
    std::cout << "available extensions:\n";
    //this is not realted with vulkan but still gonna write it down
    //(const auto& extension : extensions)
    // ":" --> bring the first value in extensions vecotr and put it inside extension variable
    // auto --> let computer choose what data type will the extension be
    // & point to the actual value of extension variable do not make a copy of extension varaible
    for (const auto& extension : extensions) {
        std::cout << '\t' << extension.extensionName << '\n';
    }
    }
};

int main() {
    HelloTriangleApplication app;

    try {
        app.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Enter fullscreen mode Exit fullscreen mode

3.Validation layers

  • Validation layers --> Because Vulkan is such a verbose API, error checking is difficult to handle. So we use something called validation layers so we can check errors efficiently. There are two layers we will see: instance and device specific

  • instance layers would only check calls related to global Vulkan objects like instances

  • device specific layers would only check calls related to a specific GPU.

  • VK_LAYER_KHRONOS_validation --> all of the standard validation layer is inside this.

// All of the useful standard validation is bundled into a layer included in the SDK that is known as VK_LAYER_KHRONOS_validation.
const std::vector<const char*> validationLayers = {
    "VK_LAYER_KHRONOS_validation"
};
Enter fullscreen mode Exit fullscreen mode
  • Now checking if all the requested validation layers actually exsist
    bool checkValidationLayerSupport() {

    //this just checks if all of the requested layers are available ---------------
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
    //and we are going to store all the available layers inside the availableLayers vector
    //just like before!! we did this when making instance
    std::vector<VkLayerProperties> availableLayers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
    //------------------------------------------------------------------------------

    //this just checks if availableLayers are actual thing in validationLayers
    //we created validationLayers look at the top of the code

    for (const char* layerName : validationLayers) {
        bool layerFound = false;

        for (const auto& layerProperties : availableLayers) {
        //not related with vulkan but related with cpp
        //strcmp is just coparing the strings
        //does layerName == layerProperties
        if (strcmp(layerName, layerProperties.layerName) == 0) {
            //if availableLayer is among the whole validationLayers that we return true
            layerFound = true;
            break;
        }
        }

        if (!layerFound) {
        return false;
        }
    }

    return true;
    }

Enter fullscreen mode Exit fullscreen mode
  • the code below just tells the instance(we just created) how many validationLayers are available and what we will e using.
//this just tells the createInfo how many validationLayers available
    //you know it contains
    if (enableValidationLayers) {
        createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
        createInfo.ppEnabledLayerNames = validationLayers.data();
    } else {
        createInfo.enabledLayerCount = 0;
    }

Enter fullscreen mode Exit fullscreen mode
  • VK_EXT_debug_utils --> this just tells which message we want to produce because not all messages are valuable. The whole process here is getting all the glfw extensions becuase anything that is related with glfw is a must.
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;
}
Enter fullscreen mode Exit fullscreen mode
  • What is Callback --> it's a function that is called when something happens to code automatically by vulkan.

  • Debug Call Back --> this actually prints out the error message.

  • first parameter --> is the error message and it fallows this errores.
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: Diagnostic message
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: Informational message like the creation of a resource
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: Message about behavior that is not necessarily an error, but very likely a bug in your application
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: Message about behavior that is invalid and may cause crashes

  • second parameter --> The messageType parameter can have the following values:
    VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: Some event has happened that is unrelated to the specification or performance
    VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: Something has happened that violates the specification or indicates a possible mistake
    VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: Potential non-optimal use of Vulkan

  • the rest --> The pCallbackData parameter refers to a VkDebugUtilsMessengerCallbackDataEXT struct containing the details of the message itself, with the most important members being:

pMessage: The debug message as a null-terminated string
pObjects: Array of Vulkan object handles related to the message
objectCount: Number of objects in array

the actual code

    static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
    //the ranking of severity of a message
    VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
    //is it making the computer slower??or is it an error
    VkDebugUtilsMessageTypeFlagsEXT messageType,
    //a varaible that will contain your error message
    const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
    //don't really know what is the purpose of this void*
    void* pUserData) {
        //printing out actual error messgae
        std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
        //just let the program move on...
        //if it was true than it would immediatly stop the program
        return VK_FALSE;
    }
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)