package instance import ( "github.com/pkg/errors" "github.com/vkngwrapper/core/v2/core1_0" "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" "os" ) func (e *FInstance) createSurface() error { surfaceLoader := khr_surface.CreateExtensionFromInstance(e.ptr) surface, err := vkng_sdl2.CreateSurface(e.ptr, surfaceLoader, e.win.GetInstance()) if err != nil { return err } e.surface = surface return nil } func (e *FInstance) pickPhysicalDevice() error { physicalDevices, _, err := e.ptr.EnumeratePhysicalDevices() if err != nil { return err } for _, device := range physicalDevices { if help.isDeviceSuitable(device) { e.physicalDevice = device break } } if e.physicalDevice == nil { return errors.New("failed to find a suitable GPU!") } return nil } func (e *FInstance) createLogicalDevice() error { indices, err := help.findQueueFamilies(e.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 := e.physicalDevice.EnumerateDeviceExtensionProperties() if err != nil { return err } _, supported := extensions[khr_portability_subset.ExtensionName] if supported { extensionNames = append(extensionNames, khr_portability_subset.ExtensionName) } e.device, _, err = e.physicalDevice.CreateDevice(nil, core1_0.DeviceCreateInfo{ QueueCreateInfos: queueFamilyOptions, EnabledFeatures: &core1_0.PhysicalDeviceFeatures{}, EnabledExtensionNames: extensionNames, }) if err != nil { return err } e.graphicsQueue = e.device.GetQueue(*indices.GraphicsFamily, 0) e.presentQueue = e.device.GetQueue(*indices.PresentFamily, 0) return nil } func (e *FInstance) createSwapchain() error { e.swapchainExtension = khr_swapchain.CreateExtensionFromDevice(e.device) swapchainSupport, err := help.querySwapChainSupport(e.physicalDevice) if err != nil { return err } surfaceFormat := help.chooseSwapSurfaceFormat(swapchainSupport.Formats) presentMode := help.chooseSwapPresentMode(swapchainSupport.PresentModes) extent := help.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 := help.findQueueFamilies(e.physicalDevice) if err != nil { return err } if *indices.GraphicsFamily != *indices.PresentFamily { sharingMode = core1_0.SharingModeConcurrent queueFamilyIndices = append(queueFamilyIndices, *indices.GraphicsFamily, *indices.PresentFamily) } swapchain, _, err := e.swapchainExtension.CreateSwapchain(e.device, nil, khr_swapchain.SwapchainCreateInfo{ Surface: e.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 } e.swapchainExtent = extent e.swapchain = swapchain e.swapchainImageFormat = surfaceFormat.Format images, _, err := swapchain.SwapchainImages() if err != nil { return err } e.swapchainImages = images var imageViews []core1_0.ImageView for _, image := range images { view, _, err := e.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) } e.swapchainImageViews = imageViews return nil } func (e *FInstance) createRenderPass() error { renderPass, _, err := e.device.CreateRenderPass(nil, core1_0.RenderPassCreateInfo{ Attachments: []core1_0.AttachmentDescription{ { Format: e.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 } e.renderPass = renderPass return nil } func (e *FInstance) createGraphicsPipeline() error { // Load vertex shader vertShaderBytes, err := os.ReadFile("asset/shaders/vert.spv") if err != nil { return err } vertShader, _, err := e.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{ Code: bytesToBytecode(vertShaderBytes), }) if err != nil { return err } defer vertShader.Destroy(nil) // Load fragment shader fragShaderBytes, err := os.ReadFile("asset/shaders/frag.spv") if err != nil { return err } fragShader, _, err := e.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(e.swapchainExtent.Width), Height: float32(e.swapchainExtent.Height), MinDepth: 0, MaxDepth: 1, }, }, Scissors: []core1_0.Rect2D{ { Offset: core1_0.Offset2D{X: 0, Y: 0}, Extent: e.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, }, }, } e.pipelineLayout, _, err = e.device.CreatePipelineLayout(nil, core1_0.PipelineLayoutCreateInfo{}) if err != nil { return err } pipelines, _, err := e.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: e.pipelineLayout, RenderPass: e.renderPass, Subpass: 0, BasePipelineIndex: -1, }, }) if err != nil { return err } e.graphicsPipeline = pipelines[0] return nil } func (e *FInstance) createFramebuffers() error { for _, imageView := range e.swapchainImageViews { framebuffer, _, err := e.device.CreateFramebuffer(nil, core1_0.FramebufferCreateInfo{ RenderPass: e.renderPass, Layers: 1, Attachments: []core1_0.ImageView{ imageView, }, Width: e.swapchainExtent.Width, Height: e.swapchainExtent.Height, }) if err != nil { return err } e.swapchainFramebuffers = append(e.swapchainFramebuffers, framebuffer) } return nil } func (e *FInstance) createCommandPool() error { indices, err := help.findQueueFamilies(e.physicalDevice) if err != nil { return err } pool, _, err := e.device.CreateCommandPool(nil, core1_0.CommandPoolCreateInfo{ QueueFamilyIndex: *indices.GraphicsFamily, }) if err != nil { return err } e.commandPool = pool return nil } func (e *FInstance) createCommandBuffers() error { buffers, _, err := e.device.AllocateCommandBuffers(core1_0.CommandBufferAllocateInfo{ CommandPool: e.commandPool, Level: core1_0.CommandBufferLevelPrimary, CommandBufferCount: len(e.swapchainImages), }) if err != nil { return err } e.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: e.renderPass, Framebuffer: e.swapchainFramebuffers[bufferIdx], RenderArea: core1_0.Rect2D{ Offset: core1_0.Offset2D{X: 0, Y: 0}, Extent: e.swapchainExtent, }, ClearValues: []core1_0.ClearValue{ core1_0.ClearValueFloat{0, 0, 0, 1}, }, }) if err != nil { return err } buffer.CmdBindPipeline(core1_0.PipelineBindPointGraphics, e.graphicsPipeline) buffer.CmdDraw(3, 1, 0, 0) buffer.CmdEndRenderPass() _, err = buffer.End() if err != nil { return err } } return nil } func (e *FInstance) createSyncObjects() error { for i := 0; i < MaxFramesInFlight; i++ { semaphore, _, err := e.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{}) if err != nil { return err } e.imageAvailableSemaphore = append(e.imageAvailableSemaphore, semaphore) semaphore, _, err = e.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{}) if err != nil { return err } e.renderFinishedSemaphore = append(e.renderFinishedSemaphore, semaphore) fence, _, err := e.device.CreateFence(nil, core1_0.FenceCreateInfo{ Flags: core1_0.FenceCreateSignaled, }) if err != nil { return err } e.inFlightFence = append(e.inFlightFence, fence) } for i := 0; i < len(e.swapchainImages); i++ { e.imagesInFlight = append(e.imagesInFlight, nil) } return nil } func (e *FInstance) Init() error { help.vulkan = e err := e.createSurface() if err != nil { return err } err = e.pickPhysicalDevice() if err != nil { return err } err = e.createLogicalDevice() if err != nil { return err } err = e.createSwapchain() if err != nil { return err } err = e.createRenderPass() if err != nil { return err } err = e.createGraphicsPipeline() if err != nil { return err } err = e.createFramebuffers() if err != nil { return err } err = e.createCommandPool() if err != nil { return err } err = e.createCommandBuffers() if err != nil { return err } return e.createSyncObjects() }