632 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			632 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package main
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"embed"
							 | 
						||
| 
								 | 
							
									"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"
							 | 
						||
| 
								 | 
							
									"github.com/vkngwrapper/extensions/v2/khr_swapchain"
							 | 
						||
| 
								 | 
							
									vkng_sdl2 "github.com/vkngwrapper/integrations/sdl2/v2"
							 | 
						||
| 
								 | 
							
									"log"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//go:embed shaders
							 | 
						||
| 
								 | 
							
								var shaders embed.FS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var validationLayers = []string{"VK_LAYER_KHRONOS_validation"}
							 | 
						||
| 
								 | 
							
								var deviceExtensions = []string{khr_swapchain.ExtensionName}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const enableValidationLayers = true
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type QueueFamilyIndices struct {
							 | 
						||
| 
								 | 
							
									GraphicsFamily *int
							 | 
						||
| 
								 | 
							
									PresentFamily  *int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (i *QueueFamilyIndices) IsComplete() bool {
							 | 
						||
| 
								 | 
							
									return i.GraphicsFamily != nil && i.PresentFamily != nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type SwapChainSupportDetails struct {
							 | 
						||
| 
								 | 
							
									Capabilities *khr_surface.SurfaceCapabilities
							 | 
						||
| 
								 | 
							
									Formats      []khr_surface.SurfaceFormat
							 | 
						||
| 
								 | 
							
									PresentModes []khr_surface.PresentMode
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									swapchainExtension   khr_swapchain.Extension
							 | 
						||
| 
								 | 
							
									swapchain            khr_swapchain.Swapchain
							 | 
						||
| 
								 | 
							
									swapchainImages      []core1_0.Image
							 | 
						||
| 
								 | 
							
									swapchainImageFormat core1_0.Format
							 | 
						||
| 
								 | 
							
									swapchainExtent      core1_0.Extent2D
							 | 
						||
| 
								 | 
							
									swapchainImageViews  []core1_0.ImageView
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = app.createLogicalDevice()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = app.createSwapchain()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return app.createGraphicsPipeline()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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() {
							 | 
						||
| 
								 | 
							
									for _, imageView := range app.swapchainImageViews {
							 | 
						||
| 
								 | 
							
										imageView.Destroy(nil)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if app.swapchain != nil {
							 | 
						||
| 
								 | 
							
										app.swapchain.Destroy(nil)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									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
							 | 
						||
| 
								 | 
							
									extensionNames = append(extensionNames, deviceExtensions...)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// 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) createSwapchain() error {
							 | 
						||
| 
								 | 
							
									app.swapchainExtension = khr_swapchain.CreateExtensionFromDevice(app.device)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									swapchainSupport, err := app.querySwapChainSupport(app.physicalDevice)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									surfaceFormat := app.chooseSwapSurfaceFormat(swapchainSupport.Formats)
							 | 
						||
| 
								 | 
							
									presentMode := app.chooseSwapPresentMode(swapchainSupport.PresentModes)
							 | 
						||
| 
								 | 
							
									extent := app.chooseSwapExtent(swapchainSupport.Capabilities)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									imageCount := swapchainSupport.Capabilities.MinImageCount + 1
							 | 
						||
| 
								 | 
							
									if swapchainSupport.Capabilities.MaxImageCount > 0 && swapchainSupport.Capabilities.MaxImageCount < imageCount {
							 | 
						||
| 
								 | 
							
										imageCount = swapchainSupport.Capabilities.MaxImageCount
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sharingMode := core1_0.SharingModeExclusive
							 | 
						||
| 
								 | 
							
									var queueFamilyIndices []int
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									indices, err := app.findQueueFamilies(app.physicalDevice)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if *indices.GraphicsFamily != *indices.PresentFamily {
							 | 
						||
| 
								 | 
							
										sharingMode = core1_0.SharingModeConcurrent
							 | 
						||
| 
								 | 
							
										queueFamilyIndices = append(queueFamilyIndices, *indices.GraphicsFamily, *indices.PresentFamily)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									swapchain, _, err := app.swapchainExtension.CreateSwapchain(app.device, nil, khr_swapchain.SwapchainCreateInfo{
							 | 
						||
| 
								 | 
							
										Surface: app.surface,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										MinImageCount:    imageCount,
							 | 
						||
| 
								 | 
							
										ImageFormat:      surfaceFormat.Format,
							 | 
						||
| 
								 | 
							
										ImageColorSpace:  surfaceFormat.ColorSpace,
							 | 
						||
| 
								 | 
							
										ImageExtent:      extent,
							 | 
						||
| 
								 | 
							
										ImageArrayLayers: 1,
							 | 
						||
| 
								 | 
							
										ImageUsage:       core1_0.ImageUsageColorAttachment,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										ImageSharingMode:   sharingMode,
							 | 
						||
| 
								 | 
							
										QueueFamilyIndices: queueFamilyIndices,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										PreTransform:   swapchainSupport.Capabilities.CurrentTransform,
							 | 
						||
| 
								 | 
							
										CompositeAlpha: khr_surface.CompositeAlphaOpaque,
							 | 
						||
| 
								 | 
							
										PresentMode:    presentMode,
							 | 
						||
| 
								 | 
							
										Clipped:        true,
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									app.swapchainExtent = extent
							 | 
						||
| 
								 | 
							
									app.swapchain = swapchain
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									images, _, err := swapchain.SwapchainImages()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									app.swapchainImages = images
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var imageViews []core1_0.ImageView
							 | 
						||
| 
								 | 
							
									for _, image := range images {
							 | 
						||
| 
								 | 
							
										view, _, err := app.device.CreateImageView(nil, core1_0.ImageViewCreateInfo{
							 | 
						||
| 
								 | 
							
											ViewType: core1_0.ImageViewType2D,
							 | 
						||
| 
								 | 
							
											Image:    image,
							 | 
						||
| 
								 | 
							
											Format:   surfaceFormat.Format,
							 | 
						||
| 
								 | 
							
											Components: core1_0.ComponentMapping{
							 | 
						||
| 
								 | 
							
												R: core1_0.ComponentSwizzleIdentity,
							 | 
						||
| 
								 | 
							
												G: core1_0.ComponentSwizzleIdentity,
							 | 
						||
| 
								 | 
							
												B: core1_0.ComponentSwizzleIdentity,
							 | 
						||
| 
								 | 
							
												A: core1_0.ComponentSwizzleIdentity,
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
											SubresourceRange: core1_0.ImageSubresourceRange{
							 | 
						||
| 
								 | 
							
												AspectMask:     core1_0.ImageAspectColor,
							 | 
						||
| 
								 | 
							
												BaseMipLevel:   0,
							 | 
						||
| 
								 | 
							
												LevelCount:     1,
							 | 
						||
| 
								 | 
							
												BaseArrayLayer: 0,
							 | 
						||
| 
								 | 
							
												LayerCount:     1,
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
										})
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										imageViews = append(imageViews, view)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									app.swapchainImageViews = imageViews
							 | 
						||
| 
								 | 
							
									app.swapchainImageFormat = surfaceFormat.Format
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func bytesToBytecode(b []byte) []uint32 {
							 | 
						||
| 
								 | 
							
									byteCode := make([]uint32, len(b)/4)
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(byteCode); i++ {
							 | 
						||
| 
								 | 
							
										byteIndex := i * 4
							 | 
						||
| 
								 | 
							
										byteCode[i] = 0
							 | 
						||
| 
								 | 
							
										byteCode[i] |= uint32(b[byteIndex])
							 | 
						||
| 
								 | 
							
										byteCode[i] |= uint32(b[byteIndex+1]) << 8
							 | 
						||
| 
								 | 
							
										byteCode[i] |= uint32(b[byteIndex+2]) << 16
							 | 
						||
| 
								 | 
							
										byteCode[i] |= uint32(b[byteIndex+3]) << 24
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return byteCode
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) createGraphicsPipeline() error {
							 | 
						||
| 
								 | 
							
									// Load vertex shader
							 | 
						||
| 
								 | 
							
									vertShaderBytes, err := shaders.ReadFile("shaders/vert.spv")
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									vertShader, _, err := app.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
							 | 
						||
| 
								 | 
							
										Code: bytesToBytecode(vertShaderBytes),
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									defer vertShader.Destroy(nil)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Load fragment shader
							 | 
						||
| 
								 | 
							
									fragShaderBytes, err := shaders.ReadFile("shaders/frag.spv")
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									fragShader, _, err := app.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
							 | 
						||
| 
								 | 
							
										Code: bytesToBytecode(fragShaderBytes),
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									defer fragShader.Destroy(nil)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_ = &core1_0.PipelineShaderStageCreateInfo{
							 | 
						||
| 
								 | 
							
										Stage:  core1_0.StageVertex,
							 | 
						||
| 
								 | 
							
										Module: vertShader,
							 | 
						||
| 
								 | 
							
										Name:   "main",
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_ = &core1_0.PipelineShaderStageCreateInfo{
							 | 
						||
| 
								 | 
							
										Stage:  core1_0.StageFragment,
							 | 
						||
| 
								 | 
							
										Module: fragShader,
							 | 
						||
| 
								 | 
							
										Name:   "main",
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) chooseSwapSurfaceFormat(availableFormats []khr_surface.SurfaceFormat) khr_surface.SurfaceFormat {
							 | 
						||
| 
								 | 
							
									for _, format := range availableFormats {
							 | 
						||
| 
								 | 
							
										if format.Format == core1_0.FormatB8G8R8A8SRGB && format.ColorSpace == khr_surface.ColorSpaceSRGBNonlinear {
							 | 
						||
| 
								 | 
							
											return format
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return availableFormats[0]
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) chooseSwapPresentMode(availablePresentModes []khr_surface.PresentMode) khr_surface.PresentMode {
							 | 
						||
| 
								 | 
							
									for _, presentMode := range availablePresentModes {
							 | 
						||
| 
								 | 
							
										if presentMode == khr_surface.PresentModeMailbox {
							 | 
						||
| 
								 | 
							
											return presentMode
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return khr_surface.PresentModeFIFO
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) chooseSwapExtent(capabilities *khr_surface.SurfaceCapabilities) core1_0.Extent2D {
							 | 
						||
| 
								 | 
							
									if capabilities.CurrentExtent.Width != -1 {
							 | 
						||
| 
								 | 
							
										return capabilities.CurrentExtent
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									widthInt, heightInt := app.window.VulkanGetDrawableSize()
							 | 
						||
| 
								 | 
							
									width := int(widthInt)
							 | 
						||
| 
								 | 
							
									height := int(heightInt)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if width < capabilities.MinImageExtent.Width {
							 | 
						||
| 
								 | 
							
										width = capabilities.MinImageExtent.Width
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if width > capabilities.MaxImageExtent.Width {
							 | 
						||
| 
								 | 
							
										width = capabilities.MaxImageExtent.Width
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if height < capabilities.MinImageExtent.Height {
							 | 
						||
| 
								 | 
							
										height = capabilities.MinImageExtent.Height
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if height > capabilities.MaxImageExtent.Height {
							 | 
						||
| 
								 | 
							
										height = capabilities.MaxImageExtent.Height
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return core1_0.Extent2D{Width: width, Height: height}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) querySwapChainSupport(device core1_0.PhysicalDevice) (SwapChainSupportDetails, error) {
							 | 
						||
| 
								 | 
							
									var details SwapChainSupportDetails
							 | 
						||
| 
								 | 
							
									var err error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									details.Capabilities, _, err = app.surface.PhysicalDeviceSurfaceCapabilities(device)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return details, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									details.Formats, _, err = app.surface.PhysicalDeviceSurfaceFormats(device)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return details, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									details.PresentModes, _, err = app.surface.PhysicalDeviceSurfacePresentModes(device)
							 | 
						||
| 
								 | 
							
									return details, err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) isDeviceSuitable(device core1_0.PhysicalDevice) bool {
							 | 
						||
| 
								 | 
							
									indices, err := app.findQueueFamilies(device)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									extensionsSupported := app.checkDeviceExtensionSupport(device)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var swapChainAdequate bool
							 | 
						||
| 
								 | 
							
									if extensionsSupported {
							 | 
						||
| 
								 | 
							
										swapChainSupport, err := app.querySwapChainSupport(device)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										swapChainAdequate = len(swapChainSupport.Formats) > 0 && len(swapChainSupport.PresentModes) > 0
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return indices.IsComplete() && extensionsSupported && swapChainAdequate
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (app *HelloTriangleApplication) checkDeviceExtensionSupport(device core1_0.PhysicalDevice) bool {
							 | 
						||
| 
								 | 
							
									extensions, _, err := device.EnumerateDeviceExtensionProperties()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for _, extension := range deviceExtensions {
							 | 
						||
| 
								 | 
							
										_, hasExtension := extensions[extension]
							 | 
						||
| 
								 | 
							
										if !hasExtension {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |