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 const MaxFramesInFlight = 2 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 swapchainFramebuffers []core1_0.Framebuffer renderPass core1_0.RenderPass pipelineLayout core1_0.PipelineLayout graphicsPipeline core1_0.Pipeline commandPool core1_0.CommandPool commandBuffers []core1_0.CommandBuffer imageAvailableSemaphore []core1_0.Semaphore renderFinishedSemaphore []core1_0.Semaphore inFlightFence []core1_0.Fence imagesInFlight []core1_0.Fence currentFrame int } 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|sdl.WINDOW_RESIZABLE) 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 } err = app.createImageViews() if err != nil { return err } err = app.createRenderPass() if err != nil { return err } err = app.createGraphicsPipeline() if err != nil { return err } err = app.createFramebuffers() if err != nil { return err } err = app.createCommandPool() if err != nil { return err } err = app.createCommandBuffers() if err != nil { return err } return app.createSyncObjects() } func (app *HelloTriangleApplication) mainLoop() error { rendering := true appLoop: for true { for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { switch e := event.(type) { case *sdl.QuitEvent: break appLoop case *sdl.WindowEvent: switch e.Event { case sdl.WINDOWEVENT_MINIMIZED: rendering = false case sdl.WINDOWEVENT_RESTORED: rendering = true case sdl.WINDOWEVENT_RESIZED: w, h := app.window.GetSize() if w > 0 && h > 0 { rendering = true app.recreateSwapChain() } else { rendering = false } } } } if rendering { err := app.drawFrame() if err != nil { return err } } } _, err := app.device.WaitIdle() return err } func (app *HelloTriangleApplication) cleanupSwapChain() { for _, framebuffer := range app.swapchainFramebuffers { framebuffer.Destroy(nil) } app.swapchainFramebuffers = []core1_0.Framebuffer{} if len(app.commandBuffers) > 0 { app.device.FreeCommandBuffers(app.commandBuffers) app.commandBuffers = []core1_0.CommandBuffer{} } if app.graphicsPipeline != nil { app.graphicsPipeline.Destroy(nil) app.graphicsPipeline = nil } if app.pipelineLayout != nil { app.pipelineLayout.Destroy(nil) app.pipelineLayout = nil } if app.renderPass != nil { app.renderPass.Destroy(nil) app.renderPass = nil } for _, imageView := range app.swapchainImageViews { imageView.Destroy(nil) } app.swapchainImageViews = []core1_0.ImageView{} if app.swapchain != nil { app.swapchain.Destroy(nil) app.swapchain = nil } } func (app *HelloTriangleApplication) cleanup() { app.cleanupSwapChain() for _, fence := range app.inFlightFence { fence.Destroy(nil) } for _, semaphore := range app.renderFinishedSemaphore { semaphore.Destroy(nil) } for _, semaphore := range app.imageAvailableSemaphore { semaphore.Destroy(nil) } if app.commandPool != nil { app.commandPool.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) recreateSwapChain() error { w, h := app.window.VulkanGetDrawableSize() if w == 0 || h == 0 { return nil } if (app.window.GetFlags() & sdl.WINDOW_MINIMIZED) != 0 { return nil } _, err := app.device.WaitIdle() if err != nil { return err } app.cleanupSwapChain() err = app.createSwapchain() if err != nil { return err } err = app.createImageViews() if err != nil { return err } err = app.createRenderPass() if err != nil { return err } err = app.createGraphicsPipeline() if err != nil { return err } err = app.createFramebuffers() if err != nil { return err } err = app.createCommandBuffers() if err != nil { return err } app.imagesInFlight = []core1_0.Fence{} for i := 0; i < len(app.swapchainImages); i++ { app.imagesInFlight = append(app.imagesInFlight, nil) } return nil } 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.New("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 app.swapchainImageFormat = surfaceFormat.Format return nil } func (app *HelloTriangleApplication) createImageViews() error { images, _, err := app.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: app.swapchainImageFormat, 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 return nil } func (app *HelloTriangleApplication) createRenderPass() error { renderPass, _, err := app.device.CreateRenderPass(nil, core1_0.RenderPassCreateInfo{ Attachments: []core1_0.AttachmentDescription{ { Format: app.swapchainImageFormat, Samples: core1_0.Samples1, LoadOp: core1_0.AttachmentLoadOpClear, StoreOp: core1_0.AttachmentStoreOpStore, StencilLoadOp: core1_0.AttachmentLoadOpDontCare, StencilStoreOp: core1_0.AttachmentStoreOpDontCare, InitialLayout: core1_0.ImageLayoutUndefined, FinalLayout: khr_swapchain.ImageLayoutPresentSrc, }, }, Subpasses: []core1_0.SubpassDescription{ { PipelineBindPoint: core1_0.PipelineBindPointGraphics, ColorAttachments: []core1_0.AttachmentReference{ { Attachment: 0, Layout: core1_0.ImageLayoutColorAttachmentOptimal, }, }, }, }, SubpassDependencies: []core1_0.SubpassDependency{ { SrcSubpass: core1_0.SubpassExternal, DstSubpass: 0, SrcStageMask: core1_0.PipelineStageColorAttachmentOutput, SrcAccessMask: 0, DstStageMask: core1_0.PipelineStageColorAttachmentOutput, DstAccessMask: core1_0.AccessColorAttachmentWrite, }, }, }) if err != nil { return err } app.renderPass = renderPass 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) vertexInput := &core1_0.PipelineVertexInputStateCreateInfo{} inputAssembly := &core1_0.PipelineInputAssemblyStateCreateInfo{ Topology: core1_0.PrimitiveTopologyTriangleList, PrimitiveRestartEnable: false, } vertStage := core1_0.PipelineShaderStageCreateInfo{ Stage: core1_0.StageVertex, Module: vertShader, Name: "main", } fragStage := core1_0.PipelineShaderStageCreateInfo{ Stage: core1_0.StageFragment, Module: fragShader, Name: "main", } viewport := &core1_0.PipelineViewportStateCreateInfo{ Viewports: []core1_0.Viewport{ { X: 0, Y: 0, Width: float32(app.swapchainExtent.Width), Height: float32(app.swapchainExtent.Height), MinDepth: 0, MaxDepth: 1, }, }, Scissors: []core1_0.Rect2D{ { Offset: core1_0.Offset2D{X: 0, Y: 0}, Extent: app.swapchainExtent, }, }, } rasterization := &core1_0.PipelineRasterizationStateCreateInfo{ DepthClampEnable: false, RasterizerDiscardEnable: false, PolygonMode: core1_0.PolygonModeFill, CullMode: core1_0.CullModeBack, FrontFace: core1_0.FrontFaceClockwise, DepthBiasEnable: false, LineWidth: 1.0, } multisample := &core1_0.PipelineMultisampleStateCreateInfo{ SampleShadingEnable: false, RasterizationSamples: core1_0.Samples1, MinSampleShading: 1.0, } colorBlend := &core1_0.PipelineColorBlendStateCreateInfo{ LogicOpEnabled: false, LogicOp: core1_0.LogicOpCopy, BlendConstants: [4]float32{0, 0, 0, 0}, Attachments: []core1_0.PipelineColorBlendAttachmentState{ { BlendEnabled: false, ColorWriteMask: core1_0.ColorComponentRed | core1_0.ColorComponentGreen | core1_0.ColorComponentBlue | core1_0.ColorComponentAlpha, }, }, } app.pipelineLayout, _, err = app.device.CreatePipelineLayout(nil, core1_0.PipelineLayoutCreateInfo{}) if err != nil { return err } pipelines, _, err := app.device.CreateGraphicsPipelines(nil, nil, []core1_0.GraphicsPipelineCreateInfo{ { Stages: []core1_0.PipelineShaderStageCreateInfo{ vertStage, fragStage, }, VertexInputState: vertexInput, InputAssemblyState: inputAssembly, ViewportState: viewport, RasterizationState: rasterization, MultisampleState: multisample, ColorBlendState: colorBlend, Layout: app.pipelineLayout, RenderPass: app.renderPass, Subpass: 0, BasePipelineIndex: -1, }, }) if err != nil { return err } app.graphicsPipeline = pipelines[0] return nil } func (app *HelloTriangleApplication) createFramebuffers() error { for _, imageView := range app.swapchainImageViews { framebuffer, _, err := app.device.CreateFramebuffer(nil, core1_0.FramebufferCreateInfo{ RenderPass: app.renderPass, Layers: 1, Attachments: []core1_0.ImageView{ imageView, }, Width: app.swapchainExtent.Width, Height: app.swapchainExtent.Height, }) if err != nil { return err } app.swapchainFramebuffers = append(app.swapchainFramebuffers, framebuffer) } return nil } func (app *HelloTriangleApplication) createCommandPool() error { indices, err := app.findQueueFamilies(app.physicalDevice) if err != nil { return err } pool, _, err := app.device.CreateCommandPool(nil, core1_0.CommandPoolCreateInfo{ QueueFamilyIndex: *indices.GraphicsFamily, }) if err != nil { return err } app.commandPool = pool return nil } func (app *HelloTriangleApplication) createCommandBuffers() error { buffers, _, err := app.device.AllocateCommandBuffers(core1_0.CommandBufferAllocateInfo{ CommandPool: app.commandPool, Level: core1_0.CommandBufferLevelPrimary, CommandBufferCount: len(app.swapchainImages), }) if err != nil { return err } app.commandBuffers = buffers for bufferIdx, buffer := range buffers { _, err = buffer.Begin(core1_0.CommandBufferBeginInfo{}) if err != nil { return err } err = buffer.CmdBeginRenderPass(core1_0.SubpassContentsInline, core1_0.RenderPassBeginInfo{ RenderPass: app.renderPass, Framebuffer: app.swapchainFramebuffers[bufferIdx], RenderArea: core1_0.Rect2D{ Offset: core1_0.Offset2D{X: 0, Y: 0}, Extent: app.swapchainExtent, }, ClearValues: []core1_0.ClearValue{ core1_0.ClearValueFloat{0, 0, 0, 1}, }, }) if err != nil { return err } buffer.CmdBindPipeline(core1_0.PipelineBindPointGraphics, app.graphicsPipeline) buffer.CmdDraw(3, 1, 0, 0) buffer.CmdEndRenderPass() _, err = buffer.End() if err != nil { return err } } return nil } func (app *HelloTriangleApplication) createSyncObjects() error { for i := 0; i < MaxFramesInFlight; i++ { semaphore, _, err := app.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{}) if err != nil { return err } app.imageAvailableSemaphore = append(app.imageAvailableSemaphore, semaphore) semaphore, _, err = app.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{}) if err != nil { return err } app.renderFinishedSemaphore = append(app.renderFinishedSemaphore, semaphore) fence, _, err := app.device.CreateFence(nil, core1_0.FenceCreateInfo{ Flags: core1_0.FenceCreateSignaled, }) if err != nil { return err } app.inFlightFence = append(app.inFlightFence, fence) } for i := 0; i < len(app.swapchainImages); i++ { app.imagesInFlight = append(app.imagesInFlight, nil) } return nil } func (app *HelloTriangleApplication) drawFrame() error { fences := []core1_0.Fence{app.inFlightFence[app.currentFrame]} _, err := app.device.WaitForFences(true, common.NoTimeout, fences) if err != nil { return err } imageIndex, res, err := app.swapchain.AcquireNextImage(common.NoTimeout, app.imageAvailableSemaphore[app.currentFrame], nil) if res == khr_swapchain.VKErrorOutOfDate { return app.recreateSwapChain() } else if err != nil { return err } if app.imagesInFlight[imageIndex] != nil { _, err := app.imagesInFlight[imageIndex].Wait(common.NoTimeout) if err != nil { return err } } app.imagesInFlight[imageIndex] = app.inFlightFence[app.currentFrame] _, err = app.device.ResetFences(fences) if err != nil { return err } _, err = app.graphicsQueue.Submit(app.inFlightFence[app.currentFrame], []core1_0.SubmitInfo{ { WaitSemaphores: []core1_0.Semaphore{app.imageAvailableSemaphore[app.currentFrame]}, WaitDstStageMask: []core1_0.PipelineStageFlags{core1_0.PipelineStageColorAttachmentOutput}, CommandBuffers: []core1_0.CommandBuffer{app.commandBuffers[imageIndex]}, SignalSemaphores: []core1_0.Semaphore{app.renderFinishedSemaphore[app.currentFrame]}, }, }) if err != nil { return err } res, err = app.swapchainExtension.QueuePresent(app.presentQueue, khr_swapchain.PresentInfo{ WaitSemaphores: []core1_0.Semaphore{app.renderFinishedSemaphore[app.currentFrame]}, Swapchains: []khr_swapchain.Swapchain{app.swapchain}, ImageIndices: []int{imageIndex}, }) if res == khr_swapchain.VKErrorOutOfDate || res == khr_swapchain.VKSuboptimal { return app.recreateSwapChain() } else if err != nil { return err } app.currentFrame = (app.currentFrame + 1) % MaxFramesInFlight 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) } }