GLFW 的 CreateWindowSurface 如何在 vulkan-go 中工作?

How does CreateWindowSurface from GLFW work in vulkan-go?

我正在尝试使用 go 和 github.com/vulkan-go 绑定创建 vk.Surface。

通常 glfw.CreateWindowSurface 声明有四个参数,像这样 CreateWindowSurface(instance interface{}, window *glfw.Window, allocCallbacks unsafe.Pointer, *vk.Surface) 所以我们可以声明一个 vk.Surface 类型的变量并将它的地址传递给函数。

但是 vulkan-go 中的 glfw.CreateWindowSurface 函数是这样声明的 CreateWindowSurface(instance interface{}, allocCallbacks unsafe.Pointer) 它不是 return 一个 vk.Surface 而是一个 [=33= 的 uintptr ] 到 C.VkSurfaceKHR.

我尝试钻取指针并像这样 vk.Surface 类型转换为 (vk.Surface)(unsafe.Pointer(surface)),但这并不能正常工作,因为当我稍后将其传递给 vk.GetPhysicalDeviceSurfaceSupport 函数时验证层输出错误 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).

这是我的代码,它有点长,但也许它会帮助你更好地理解我在做什么:

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
}

那么 GLFW 中的 CreateWindowSurface 如何在 vulkan-go 中工作?

在 vulkan-go 中,来自 GLFW 的 CreateWindowSurface returns 一个 unsafe.Pointer 到 vk.Surface 的 uintptr 所以要将它变成 vk.Surface 我们需要:

  1. 将 uintptr 转换为 unsafe.Pointer unsafe.Pointer(surface);
  2. 然后将结果 unsafe.Pointer 类型转换为 vk.Surface 指针 (*vk.Surface)(unsafe.Pointer(surface));
  3. 最后只获取 vk.Surface 的指针指向 *(*vk.Surface)(unsafe.Pointer(surface))

我们不能将指针类型转换为数据类型,我们要么将指针类型转换为我们想要的数据类型的指针,然后获取新指针指向的内容,要么首先获取该指针指向的内容at 并将其类型转换为我们想要的数据类型。