zvulkan/zvulkan-go/steps/05_window_surface/main.go
2023-09-17 21:01:13 +08:00

353 lines
8.7 KiB
Go

package main
import (
"github.com/pkg/errors"
"github.com/veandco/go-sdl2/sdl"
"github.com/vkngwrapper/core/v2"
"github.com/vkngwrapper/core/v2/common"
"github.com/vkngwrapper/core/v2/core1_0"
"github.com/vkngwrapper/extensions/v2/ext_debug_utils"
"github.com/vkngwrapper/extensions/v2/khr_portability_enumeration"
"github.com/vkngwrapper/extensions/v2/khr_portability_subset"
"github.com/vkngwrapper/extensions/v2/khr_surface"
vkng_sdl2 "github.com/vkngwrapper/integrations/sdl2/v2"
"log"
)
var validationLayers = []string{"VK_LAYER_KHRONOS_validation"}
const enableValidationLayers = true
type QueueFamilyIndices struct {
GraphicsFamily *int
PresentFamily *int
}
func (i *QueueFamilyIndices) IsComplete() bool {
return i.GraphicsFamily != nil && i.PresentFamily != nil
}
type HelloTriangleApplication struct {
window *sdl.Window
loader core.Loader
instance core1_0.Instance
debugMessenger ext_debug_utils.DebugUtilsMessenger
surface khr_surface.Surface
physicalDevice core1_0.PhysicalDevice
device core1_0.Device
graphicsQueue core1_0.Queue
presentQueue core1_0.Queue
}
func (app *HelloTriangleApplication) Run() error {
err := app.initWindow()
if err != nil {
return err
}
err = app.initVulkan()
if err != nil {
return err
}
defer app.cleanup()
return app.mainLoop()
}
func (app *HelloTriangleApplication) initWindow() error {
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
return err
}
window, err := sdl.CreateWindow("Vulkan", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, 800, 600, sdl.WINDOW_SHOWN|sdl.WINDOW_VULKAN)
if err != nil {
return err
}
app.window = window
app.loader, err = core.CreateLoaderFromProcAddr(sdl.VulkanGetVkGetInstanceProcAddr())
if err != nil {
return err
}
return nil
}
func (app *HelloTriangleApplication) initVulkan() error {
err := app.createInstance()
if err != nil {
return err
}
err = app.setupDebugMessenger()
if err != nil {
return err
}
err = app.createSurface()
if err != nil {
return err
}
err = app.pickPhysicalDevice()
if err != nil {
return err
}
return app.createLogicalDevice()
}
func (app *HelloTriangleApplication) mainLoop() error {
appLoop:
for true {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) {
case *sdl.QuitEvent:
break appLoop
}
}
}
return nil
}
func (app *HelloTriangleApplication) cleanup() {
if app.device != nil {
app.device.Destroy(nil)
}
if app.debugMessenger != nil {
app.debugMessenger.Destroy(nil)
}
if app.surface != nil {
app.surface.Destroy(nil)
}
if app.instance != nil {
app.instance.Destroy(nil)
}
if app.window != nil {
app.window.Destroy()
}
sdl.Quit()
}
func (app *HelloTriangleApplication) createInstance() error {
instanceOptions := core1_0.InstanceCreateInfo{
ApplicationName: "Hello Triangle",
ApplicationVersion: common.CreateVersion(1, 0, 0),
EngineName: "No Engine",
EngineVersion: common.CreateVersion(1, 0, 0),
APIVersion: common.Vulkan1_2,
}
// Add extensions
sdlExtensions := app.window.VulkanGetInstanceExtensions()
extensions, _, err := app.loader.AvailableExtensions()
if err != nil {
return err
}
for _, ext := range sdlExtensions {
_, hasExt := extensions[ext]
if !hasExt {
return errors.Errorf("createinstance: cannot initialize sdl: missing extension %s", ext)
}
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, ext)
}
if enableValidationLayers {
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, ext_debug_utils.ExtensionName)
}
_, enumerationSupported := extensions[khr_portability_enumeration.ExtensionName]
if enumerationSupported {
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, khr_portability_enumeration.ExtensionName)
instanceOptions.Flags |= khr_portability_enumeration.InstanceCreateEnumeratePortability
}
// Add layers
layers, _, err := app.loader.AvailableLayers()
if err != nil {
return err
}
if enableValidationLayers {
for _, layer := range validationLayers {
_, hasValidation := layers[layer]
if !hasValidation {
return errors.Errorf("createInstance: cannot add validation- layer %s not available- install LunarG Vulkan SDK", layer)
}
instanceOptions.EnabledLayerNames = append(instanceOptions.EnabledLayerNames, layer)
}
// Add debug messenger
instanceOptions.Next = app.debugMessengerOptions()
}
app.instance, _, err = app.loader.CreateInstance(nil, instanceOptions)
if err != nil {
return err
}
return nil
}
func (app *HelloTriangleApplication) debugMessengerOptions() ext_debug_utils.DebugUtilsMessengerCreateInfo {
return ext_debug_utils.DebugUtilsMessengerCreateInfo{
MessageSeverity: ext_debug_utils.SeverityError | ext_debug_utils.SeverityWarning,
MessageType: ext_debug_utils.TypeGeneral | ext_debug_utils.TypeValidation | ext_debug_utils.TypePerformance,
UserCallback: app.logDebug,
}
}
func (app *HelloTriangleApplication) setupDebugMessenger() error {
if !enableValidationLayers {
return nil
}
var err error
debugLoader := ext_debug_utils.CreateExtensionFromInstance(app.instance)
app.debugMessenger, _, err = debugLoader.CreateDebugUtilsMessenger(app.instance, nil, app.debugMessengerOptions())
if err != nil {
return err
}
return nil
}
func (app *HelloTriangleApplication) createSurface() error {
surfaceLoader := khr_surface.CreateExtensionFromInstance(app.instance)
surface, err := vkng_sdl2.CreateSurface(app.instance, surfaceLoader, app.window)
if err != nil {
return err
}
app.surface = surface
return nil
}
func (app *HelloTriangleApplication) pickPhysicalDevice() error {
physicalDevices, _, err := app.instance.EnumeratePhysicalDevices()
if err != nil {
return err
}
for _, device := range physicalDevices {
if app.isDeviceSuitable(device) {
app.physicalDevice = device
break
}
}
if app.physicalDevice == nil {
return errors.Errorf("failed to find a suitable GPU!")
}
return nil
}
func (app *HelloTriangleApplication) createLogicalDevice() error {
indices, err := app.findQueueFamilies(app.physicalDevice)
if err != nil {
return err
}
uniqueQueueFamilies := []int{*indices.GraphicsFamily}
if uniqueQueueFamilies[0] != *indices.PresentFamily {
uniqueQueueFamilies = append(uniqueQueueFamilies, *indices.PresentFamily)
}
var queueFamilyOptions []core1_0.DeviceQueueCreateInfo
queuePriority := float32(1.0)
for _, queueFamily := range uniqueQueueFamilies {
queueFamilyOptions = append(queueFamilyOptions, core1_0.DeviceQueueCreateInfo{
QueueFamilyIndex: queueFamily,
QueuePriorities: []float32{queuePriority},
})
}
var extensionNames []string
// Makes this example compatible with vulkan portability, necessary to run on mobile & mac
extensions, _, err := app.physicalDevice.EnumerateDeviceExtensionProperties()
if err != nil {
return err
}
_, supported := extensions[khr_portability_subset.ExtensionName]
if supported {
extensionNames = append(extensionNames, khr_portability_subset.ExtensionName)
}
app.device, _, err = app.physicalDevice.CreateDevice(nil, core1_0.DeviceCreateInfo{
QueueCreateInfos: queueFamilyOptions,
EnabledFeatures: &core1_0.PhysicalDeviceFeatures{},
EnabledExtensionNames: extensionNames,
})
if err != nil {
return err
}
app.graphicsQueue = app.device.GetQueue(*indices.GraphicsFamily, 0)
app.presentQueue = app.device.GetQueue(*indices.PresentFamily, 0)
return nil
}
func (app *HelloTriangleApplication) isDeviceSuitable(device core1_0.PhysicalDevice) bool {
indices, err := app.findQueueFamilies(device)
if err != nil {
return false
}
return indices.IsComplete()
}
func (app *HelloTriangleApplication) findQueueFamilies(device core1_0.PhysicalDevice) (QueueFamilyIndices, error) {
indices := QueueFamilyIndices{}
queueFamilies := device.QueueFamilyProperties()
for queueFamilyIdx, queueFamily := range queueFamilies {
if (queueFamily.QueueFlags & core1_0.QueueGraphics) != 0 {
indices.GraphicsFamily = new(int)
*indices.GraphicsFamily = queueFamilyIdx
}
supported, _, err := app.surface.PhysicalDeviceSurfaceSupport(device, queueFamilyIdx)
if err != nil {
return indices, err
}
if supported {
indices.PresentFamily = new(int)
*indices.PresentFamily = queueFamilyIdx
}
if indices.IsComplete() {
break
}
}
return indices, nil
}
func (app *HelloTriangleApplication) logDebug(msgType ext_debug_utils.DebugUtilsMessageTypeFlags, severity ext_debug_utils.DebugUtilsMessageSeverityFlags, data *ext_debug_utils.DebugUtilsMessengerCallbackData) bool {
log.Printf("[%s %s] - %s", severity, msgType, data.Message)
return false
}
func main() {
app := &HelloTriangleApplication{}
err := app.Run()
if err != nil {
log.Fatalf("%+v\n", err)
}
}