Search code examples
goglfwvulkan

How does CreateWindowSurface from GLFW work in vulkan-go?


I am trying to create a vk.Surface using go and the github.com/vulkan-go bindings.

Usually the glfw.CreateWindowSurface is declared with four parameters like this CreateWindowSurface(instance interface{}, window *glfw.Window, allocCallbacks unsafe.Pointer, *vk.Surface) so we can declare a variable of type vk.Surface and pass the address of it to the function.

But the glfw.CreateWindowSurface function in vulkan-go is declared like this CreateWindowSurface(instance interface{}, allocCallbacks unsafe.Pointer) and it does not return a vk.Surface but an uintptr of an unsafe.Pointer to a C.VkSurfaceKHR.

I tried drilling thru the pointers and type cast to vk.Surface like this (vk.Surface)(unsafe.Pointer(surface)), but this does not seam to work because when i later pass this to a vk.GetPhysicalDeviceSurfaceSupport function the validation layer outputs the error Validation Error: [ VUID-vkGetPhysicalDeviceSurfaceSupportKHR-surface-parameter ] Object 0: handle = 0x22ce94b8e60, type = VK_OBJECT_TYPE_INSTANCE; | MessageID = 0x801f247e | Invalid VkSurfaceKHR Object 0xc000014148. The Vulkan spec states: surface must be a valid VkSurfaceKHR handle (https://vulkan.lunarg.com/doc/view/1.3.211.0/windows/1.3-extensions/vkspec.html#VUID-vkGetPhysicalDeviceSurfaceSupportKHR-surface-parameter).

This is my code, it's a bit long but maybe it will help you better understand what i am doing:

package main

import (
    "bytes"
    "errors"
    "fmt"
    "log"
    "unsafe"

    "github.com/fijosilo/osies/internal/utils"
    "github.com/vulkan-go/glfw/v3.3/glfw"
    vk "github.com/vulkan-go/vulkan"
)

const debug bool = true

var width int = 1280
var height int = 720

var vulkanSelectedValidationLayers = []string{}
var vulkanSelectedExtensions = []string{}
var vulkanApplicationInfo *vk.ApplicationInfo = nil
var vulkanCreateInstanceInfo *vk.InstanceCreateInfo = nil

var vulkanDebugReportCallbackCreateInfo *vk.DebugReportCallbackCreateInfo = nil
var vulkanDebugReportCallback *vk.DebugReportCallback = new(vk.DebugReportCallback)

func main() {
    logFile := utils.SetupLogOutputFile()
    defer logFile.Close()

    // init glfw
    initializeGLFW()
    // create the window
    window := createGLFWWindow(width, height, "OSIES")

    // init vulkan
    initializeVulkan()
    // create vulkan instance
    instance := createVulkanInstance(window)
    // setup validations
    if debug {
        vk.CreateDebugReportCallback(instance, getVulkanDebugReportCallbackCreateInfo(), nil, vulkanDebugReportCallback)
    }
    // window surface
    surface := createVulkanSurface(instance, window)
    // select physical device
    physicalDevice, queueFamiliesIndices := selectVulkanPhysicalDeviceAndQueueFamily(instance, &surface)
    // create logical device
    logicalDevice := createVulkanLogicalDevice(physicalDevice, queueFamiliesIndices)
    var graphycsQueue vk.Queue
    vk.GetDeviceQueue(logicalDevice, queueFamiliesIndices["graphics"], 0, &graphycsQueue)
    var surfaceQueue vk.Queue
    vk.GetDeviceQueue(logicalDevice, queueFamiliesIndices["surface"], 0, &surfaceQueue)

    // loop
    for ; !window.ShouldClose() ; {
        glfw.PollEvents()
    }

    // cleanup
    vk.DestroyDevice(logicalDevice, nil)
    vk.DestroySurface(instance, surface, nil)
    if debug {
        vk.DestroyDebugReportCallback(instance, *vulkanDebugReportCallback, nil)
    }
    vk.DestroyInstance(instance, nil)
    window.Destroy()
    glfw.Terminate()
}

/* Initializes the GLFW api.
 */
func initializeGLFW() {
    err := glfw.Init()
    if err != nil{
        log.Println(err)
        panic(err)
    }
}

/* Creates and returns a GLFW window.
 */
func createGLFWWindow(width int, height int, title string) *glfw.Window {
    glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
    glfw.WindowHint(glfw.Resizable, glfw.False)
    window, err := glfw.CreateWindow(width, height, title, nil, nil)
    if err != nil{
        log.Println(err)
        panic(err)
    }

    return window
}

/* Initializes the Vulkan api. It requires the GLFW api to be initialized.
 */
func initializeVulkan() {
    vk.SetGetInstanceProcAddr(glfw.GetVulkanGetInstanceProcAddress())
    err := vk.Init()
    if err != nil{
        log.Println(err)
        panic(err)
    }
}

/* Adds the layer to the slice of selected layers if it is not on the slice yet.
 It requires the Vulkan api to be initialized.
 */
func selectVulkanValidationLayer(selectedLayer string) {
    vulkanSelectedValidationLayers = utils.AppendStringIfNotFound(vulkanSelectedValidationLayers, vk.ToString([]byte(selectedLayer)))
}

/* Returns a slice of strings where each string is the name of a selected and supported vulkan validation layer.
 It requires the Vulkan api to be initialized.
 */
func getVulkanValidationLayers(selectedLayers []string) []string {
    // get supported layers' count
    var supportedLayerCount uint32
    result := vk.EnumerateInstanceLayerProperties(&supportedLayerCount, nil)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan validation layers count with error code %d\n", result)
        return []string{}
    }
    // get supported layers' properties
    supportedLayerProperties := make([]vk.LayerProperties, supportedLayerCount)
    result = vk.EnumerateInstanceLayerProperties(&supportedLayerCount, supportedLayerProperties)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan validation layers with error code %d\n", result)
        return []string{}
    }
    // get supported layers' property name to slice of strings
    supportedLayers := make([]string, supportedLayerCount)
    for n, layer := range supportedLayerProperties {
        layer.Deref()
        supportedLayers[n] = vk.ToString(layer.LayerName[:])
    }
    // filter the selected layers from the supported layers
    layers := []string{}
    for _, supportedLayer := range supportedLayers {
        // find layer in selected layers
        found := false
        for _, selectedLayer := range selectedLayers {
            if selectedLayer == supportedLayer {
                found = true
                break
            }
        }
        // add selected supported layer to the slice of layers
        if found {
            layers = append(layers, supportedLayer)
        }
    }
    // debug
    if debug {
        // log selected validation layers
        log.Print("Vulkan selected validation layers:\n")
        for _, layer := range selectedLayers {
            log.Printf("    %s\n", layer)
        }
        // log supported validation layers
        log.Print("Vulkan supported validation layers:\n")
        for _, layer := range supportedLayers {
            log.Printf("    %s\n", layer)
        }
        // log selected and supported validation layers
        log.Print("Vulkan selected and supported validation layers:\n")
        for _, layer := range layers {
            log.Printf("    %s\n", layer)
        }
    }

    return layers
}

/* Adds the extension to the slice of selected extensions if it is not on the slice yet.
 It requires the Vulkan api to be initialized.
 */
func selectVulkanExtension(selectedExtension string) {
    vulkanSelectedExtensions = utils.AppendStringIfNotFound(vulkanSelectedExtensions, vk.ToString([]byte(selectedExtension)))
}

/* Gets the GLFW required vulkan extensions and adds them to the slice off selected vulkan extensions.
 It requires the Vulkan api to be initialized.
 */
func selectGLFWRequiredExtensions(window *glfw.Window) {
    // get GLFW required vulkan extensions
    glfwExtensions := window.GetRequiredInstanceExtensions()
    if glfwExtensions == nil {
        log.Println("Vulkan is not supported in this system")
        panic("Vulkan is not supported in this system")
    }
    // add glfw required extensions to the slice of selected extensions
    for _, extension := range glfwExtensions {
        selectVulkanExtension(extension)
    }
}

/* Returns a slice of strings where each string is the name of a selected and supported vulkan extension.
 It requires the Vulkan api to be initialized.
 */
func getVulkanExtensions(selectedExtensions []string) []string {
    // get supported extensions' count
    var supportedExtensionCount uint32
    result := vk.EnumerateInstanceExtensionProperties("", &supportedExtensionCount, nil)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan extensions count with error code %d\n", result)
        return []string{}
    }
    // get supported extensions' properties
    supportedExtensionProperties := make([]vk.ExtensionProperties, supportedExtensionCount)
    result = vk.EnumerateInstanceExtensionProperties("", &supportedExtensionCount, supportedExtensionProperties)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan extensions with error code %d\n", result)
        return []string{}
    }
    // get supported supportedExtensions' property name to slice of strings
    supportedExtensions := make([]string, supportedExtensionCount)
    for n, ext := range supportedExtensionProperties {
        ext.Deref()
        supportedExtensions[n] = vk.ToString(ext.ExtensionName[:])
    }
    // filter the selected extensions from the supported extensions
    extensions := []string{}
    for _, supportedExtension := range supportedExtensions {
        // find extension in selected extensions
        found := false
        for _, selectedExtension := range selectedExtensions {
            if selectedExtension == supportedExtension {
                found = true
                break
            }
        }
        // add selected supported extension to the slice of extensions
        if found {
            extensions = append(extensions, supportedExtension)
        }
    }
    // debug
    if debug {
        // log selected extensions
        log.Print("Vulkan selected extensions:\n")
        for _, extension := range selectedExtensions {
            log.Printf("    %s\n", extension)
        }
        // log supported extensions
        log.Print("Vulkan supported extensions:\n")
        for _, extension := range supportedExtensions {
            log.Printf("    %s\n", extension)
        }
        // log selected and supported extensions
        log.Print("Vulkan selected and supported extensions:\n")
        for _, extension := range extensions {
            log.Printf("    %s\n", extension)
        }
    }

    return extensions
}

/* Returns a pointer to the application info struct.
 It requires the Vulkan api to be initialized.
 */
func getVulkanApplicationInfo() *vk.ApplicationInfo {
    if vulkanApplicationInfo == nil {
        // create the application info struct
        vulkanApplicationInfo = new(vk.ApplicationInfo)
        vulkanApplicationInfo.SType = vk.StructureTypeApplicationInfo
        vulkanApplicationInfo.PNext = nil
        vulkanApplicationInfo.PApplicationName = "OSIES"
        vulkanApplicationInfo.ApplicationVersion = vk.MakeVersion(1, 0, 0)
        vulkanApplicationInfo.PEngineName = "No Engine"
        vulkanApplicationInfo.EngineVersion = vk.MakeVersion(1, 0, 0)
        vulkanApplicationInfo.ApiVersion = vk.ApiVersion10
    }

    return vulkanApplicationInfo
}

/* Returns a pointer to the instance create info struct.
 It requires the Vulkan api to be initialized.
 */
func getVulkanInstanceCreateInfo(window *glfw.Window) *vk.InstanceCreateInfo {
    if vulkanCreateInstanceInfo == nil {
        // get choosen and supported validation layers
        vkLayers := []string{}
        if debug {
            selectVulkanValidationLayer("VK_LAYER_KHRONOS_validation")
            vkLayers = getVulkanValidationLayers(vulkanSelectedValidationLayers)
            selectVulkanExtension(vk.ExtDebugUtilsExtensionName)
            selectVulkanExtension(vk.ExtDebugReportExtensionName)
        }
        // add GLFW required vulkan extensions to the slice of selected vulkan extensions
        selectGLFWRequiredExtensions(window)
        // get choosen and supported extensions
        vkExtensions := getVulkanExtensions(vulkanSelectedExtensions)
        // create the instance create info struct
        vulkanCreateInstanceInfo = new(vk.InstanceCreateInfo)
        vulkanCreateInstanceInfo.SType = vk.StructureTypeInstanceCreateInfo
        vulkanCreateInstanceInfo.PNext = nil
        vulkanCreateInstanceInfo.Flags = 0
        vulkanCreateInstanceInfo.PApplicationInfo = getVulkanApplicationInfo()
        vulkanCreateInstanceInfo.EnabledLayerCount = uint32(len(vkLayers))
        vulkanCreateInstanceInfo.PpEnabledLayerNames = vkLayers
        vulkanCreateInstanceInfo.EnabledExtensionCount = uint32(len(vkExtensions))
        vulkanCreateInstanceInfo.PpEnabledExtensionNames = vkExtensions
    }

    return vulkanCreateInstanceInfo
}

/* Returns a slice of strings where each string is the name of a vulkan extension available on the current system.
 It requires the Vulkan api to be initialized.
 */
func createVulkanInstance(window *glfw.Window) vk.Instance {
    // create vulkan instance
    var instance vk.Instance
    result := vk.CreateInstance(getVulkanInstanceCreateInfo(window), nil, &instance)
    if result != vk.Success {
        log.Printf("Failed to create vulkan instance with error code %d\n", result)
        panic("Failed to create vulkan instance")
    }

    return instance
}

/* Callback function to log vulkan debug messages.
 It requires the Vulkan api to be initialized.
 */
var vulkanDebugCallback vk.DebugReportCallbackFunc = func(flags vk.DebugReportFlags, objectType vk.DebugReportObjectType, object uint64, location uint, 
                        messageCode int32, layerPrefix string, message string, userData unsafe.Pointer) vk.Bool32 {
    log.Printf("Vulkan: %s\n", message)

    return vk.False
}

/* Returns a pointer to the debug report callback create info struct.
 It requires the Vulkan api to be initialized.
 */
func getVulkanDebugReportCallbackCreateInfo() *vk.DebugReportCallbackCreateInfo {
    if vulkanDebugReportCallbackCreateInfo == nil {
        // create the application info struct
        vulkanDebugReportCallbackCreateInfo = new(vk.DebugReportCallbackCreateInfo)
        vulkanDebugReportCallbackCreateInfo.SType = vk.StructureTypeDebugReportCallbackCreateInfo
        vulkanDebugReportCallbackCreateInfo.PNext = nil
        vulkanDebugReportCallbackCreateInfo.Flags = vk.DebugReportFlags(vk.DebugReportPerformanceWarningBit | vk.DebugReportWarningBit | vk.DebugReportErrorBit)
        vulkanDebugReportCallbackCreateInfo.PfnCallback = vulkanDebugCallback
        vulkanDebugReportCallbackCreateInfo.PUserData = nil
    }

    return vulkanDebugReportCallbackCreateInfo
}

/* Crete and return a window surface.*/
func createVulkanSurface(instance vk.Instance, window *glfw.Window) vk.Surface {
    surface, err := window.CreateWindowSurface(instance, nil)
    if err != nil {
        err := "Vulkan: failed to create a surface"
        log.Println(err)
        panic(err)
    }

    // vk.Surface(unsafe.Pointer(surface))
    return (vk.Surface)(unsafe.Pointer(surface))
}

/* Finds and returns a slice of physical devices available on the system.*/
func getVulkanPhysicalDevices(instance vk.Instance) []vk.PhysicalDevice {
    // get physical devices count
    var deviceCount uint32
    result := vk.EnumeratePhysicalDevices(instance, &deviceCount, nil)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan physical devices count with error code %d\n", result)
    }
    // get physical devices
    devices := make([]vk.PhysicalDevice, deviceCount)
    result = vk.EnumeratePhysicalDevices(instance, &deviceCount, devices)
    if result != vk.Success {
        log.Printf("VULKAN: Failed to retrieve vulkan physical devices with error code %d\n", result)
    }

    return devices
}

/* Filters physical devices that have the properties and features that our app needs.*/
func filterVulkanPropertiesAndFeaturesCompatiblePhysicalDevices(devices []vk.PhysicalDevice) []vk.PhysicalDevice {
    supportedDevices := []vk.PhysicalDevice{}
    // iterate devices
    log.Print("Vulkan physical devices available:\n")
    for _, device := range devices {
        // get device properties
        var deviceProperties vk.PhysicalDeviceProperties
        vk.GetPhysicalDeviceProperties(device, &deviceProperties)
        deviceProperties.Deref()
        // log device
        deviceName := string(bytes.Split(deviceProperties.DeviceName[:], []byte{0})[0])
        log.Printf("    {name: %s, type: %d, apiVersion: %d, driverVersion: %d}\n", deviceName, deviceProperties.DeviceType, deviceProperties.ApiVersion, deviceProperties.DriverVersion)
        // get device features
        var deviceFeatures vk.PhysicalDeviceFeatures
        vk.GetPhysicalDeviceFeatures(device, &deviceFeatures)
        deviceFeatures.Deref()
        // check if device is compatible
        if deviceProperties.DeviceType == vk.PhysicalDeviceTypeDiscreteGpu && 
        deviceFeatures.GeometryShader == vk.True {
            supportedDevices = append(supportedDevices, device)
        }
    }

    return supportedDevices
}

/* Finds and returns the first physical device that supports the queue families required by our app*/
func getVulkanQueueFamiliesCompatiblePhysicalDevice(devices []vk.PhysicalDevice, surface *vk.Surface) (vk.PhysicalDevice, map[string]uint32, error) {
    // iterate devices
    for _, device := range devices {
        // get queue families' count
        var queueFamiliesCount uint32
        vk.GetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, nil)
        // get queue families
        queueFamilies := make([]vk.QueueFamilyProperties, queueFamiliesCount)
        vk.GetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, queueFamilies)
        // check if device supports all the required queue families
        selectedQueueFamiliesIndices := map[string]uint32{}
        var queueFlags vk.QueueFlags = vk.QueueFlags(vk.QueueGraphicsBit)
        var queueCount uint32 = 1
        var surfaceSupport vk.Bool32 = vk.False
        for i, family := range queueFamilies {
            family.Deref()
            supportsGraphics := family.QueueFlags & queueFlags == queueFlags && family.QueueCount >= queueCount
            supportsSurface := vk.GetPhysicalDeviceSurfaceSupport(device, uint32(i), *surface, &surfaceSupport) == vk.Success
            // prefer a single queue family that supports all the requirements
            if supportsGraphics && supportsSurface {
                selectedQueueFamiliesIndices["graphics"] = uint32(i)
                selectedQueueFamiliesIndices["surface"] = uint32(i)
                break
            }
            // otherwise get multiple queue families
            _, ok := selectedQueueFamiliesIndices["graphics"]
            if !ok && supportsGraphics {
                selectedQueueFamiliesIndices["graphics"] = uint32(i)
            }
            _, ok = selectedQueueFamiliesIndices["surface"]
            if !ok && supportsSurface {
                selectedQueueFamiliesIndices["surface"] = uint32(i)
            }
        }
        // if the device supports all the required queue families exit early
        if len(selectedQueueFamiliesIndices) == 2 {
            return device, selectedQueueFamiliesIndices, nil
        }
    }

    return nil, nil, errors.New("Vulkan: failed to find a physical device that supports the queue families required by this app")
}

/* Selects a physical device and queue family compatible with vulkan and the requirements of our app*/
func selectVulkanPhysicalDeviceAndQueueFamily(instance vk.Instance, surface *vk.Surface) (vk.PhysicalDevice, map[string]uint32) {
    // get available devices
    physicalDevices := getVulkanPhysicalDevices(instance)
    if len(physicalDevices) < 1 {
        err := "Vulkan: failed to find a device compatible with vulkan"
        log.Println(err)
        panic(err)
    }
    // filter devices with the properties and features required by our app
    filteredPhysicalDevices := filterVulkanPropertiesAndFeaturesCompatiblePhysicalDevices(physicalDevices)
    if len(filteredPhysicalDevices) < 1 {
        err := "Vulkan: failed to find a device compatible with the properties and features required by our app"
        log.Println(err)
        panic(err)
    }
    // find the first device that supports the queue families required by our app
    selectedPhysicalDevice, selectedQueueFamiliesIndices, err := getVulkanQueueFamiliesCompatiblePhysicalDevice(filteredPhysicalDevices, surface)
    if err != nil {
        err := "Vulkan: failed to find a device compatible that supports the queue families required by our app"
        log.Println(err)
        panic(err)
    }

    return selectedPhysicalDevice, selectedQueueFamiliesIndices
}

/* Returns a pointer to the device queue create info struct.*/
func getVulkanDeviceQueueCreateInfo(queueFamilyIndex uint32) *vk.DeviceQueueCreateInfo {
    // create the device queue create info struct
    deviceQueueCreateInfo := new(vk.DeviceQueueCreateInfo)
    deviceQueueCreateInfo.SType = vk.StructureTypeDeviceQueueCreateInfo
    deviceQueueCreateInfo.PNext = nil
    deviceQueueCreateInfo.Flags = vk.DeviceQueueCreateFlags(0)
    deviceQueueCreateInfo.QueueFamilyIndex = queueFamilyIndex
    deviceQueueCreateInfo.QueueCount = 1
    deviceQueueCreateInfo.PQueuePriorities = []float32{1.0}

    return deviceQueueCreateInfo
}

/* Returns a pointer to the device queue create info struct.*/
func getPhysicalDeviceFeatures() *vk.PhysicalDeviceFeatures {
    physicalDeviceFeatures := new(vk.PhysicalDeviceFeatures)

    return physicalDeviceFeatures
}

/* Returns a pointer to the device queue create info struct.*/
func getVulkanDeviceCreateInfo(deviceQueueCreateInfos []vk.DeviceQueueCreateInfo, physicalDeviceFeatures vk.PhysicalDeviceFeatures) *vk.DeviceCreateInfo {
    deviceCreateInfo := new(vk.DeviceCreateInfo)
    deviceCreateInfo.SType = vk.StructureTypeDeviceCreateInfo
    deviceCreateInfo.PNext = nil
    deviceCreateInfo.Flags = vk.DeviceCreateFlags(0)
    deviceCreateInfo.QueueCreateInfoCount = uint32(len(deviceQueueCreateInfos))
    deviceCreateInfo.PQueueCreateInfos = deviceQueueCreateInfos
    // these validation layers are device specific and ignored on modern implementations of vulkan
    deviceCreateInfo.EnabledLayerCount = 0
    deviceCreateInfo.PpEnabledLayerNames = []string{}
    // These extensions are device specific
    deviceCreateInfo.EnabledExtensionCount = 0
    deviceCreateInfo.PpEnabledExtensionNames = []string{}
    deviceCreateInfo.PEnabledFeatures = []vk.PhysicalDeviceFeatures{physicalDeviceFeatures}

    return deviceCreateInfo
}

/* Creates and returns a vulkan logical device.*/
func createVulkanLogicalDevice(physicalDevice vk.PhysicalDevice, queueFamiliesIndices map[string]uint32) vk.Device {
    // get slice of unique queue families indices
    var uniqueQueueFamiliesIndices []uint32
    for _, value := range queueFamiliesIndices {
        isUnique := true
        for _, index := range uniqueQueueFamiliesIndices {
            if index == value {
                isUnique = false
                break
            }
        }
        if isUnique {
            uniqueQueueFamiliesIndices = append(uniqueQueueFamiliesIndices, value)
        }
    }
    // get a slice of queue create info struct, one for each unique queue family
    var deviceQueueCreateInfos []vk.DeviceQueueCreateInfo
    for _, index := range uniqueQueueFamiliesIndices {
        deviceQueueCreateInfos = append(deviceQueueCreateInfos, *getVulkanDeviceQueueCreateInfo(index))
    }
    // get physical device features struct
    physicalDeviceFeatures := getPhysicalDeviceFeatures()
    // get device create info struct
    deviceCreateInfo := getVulkanDeviceCreateInfo(deviceQueueCreateInfos, *physicalDeviceFeatures)
    // create the logical device
    var logicalDevice vk.Device
    result := vk.CreateDevice(physicalDevice, deviceCreateInfo, nil, &logicalDevice)
    if result != vk.Success {
        err := fmt.Sprintf("Vulkan: failed to create logical device with error code %d\n", result)
        log.Println(err)
        panic(err)
    }

    return logicalDevice
}

So how does CreateWindowSurface from GLFW work in vulkan-go?


Solution

  • In vulkan-go the CreateWindowSurface from GLFW returns an uintptr of an unsafe.Pointer to a vk.Surface so to get that to a vk.Surface we need to:

    1. convert the uintptr to an unsafe.Pointer unsafe.Pointer(surface);
    2. then type cast the resulting unsafe.Pointer to a vk.Surface pointer (*vk.Surface)(unsafe.Pointer(surface));
    3. and finally just grab what the pointer of vk.Surface is pointing to *(*vk.Surface)(unsafe.Pointer(surface)).

    We can't type cast a pointer to a data type, we either type cast a pointer to a pointer of the data type we want and then grab what this new pointer points at or we first get what this pointer points at and type cast that into the data type we want.