diff --git a/cmd/main.go b/cmd/main.go index 689663f..7be8baf 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,10 @@ func main() { } vulkan := instance.New("test", win, window.CreateLoaderFromSDL) vulkan.EnumeratePhysicalDevices() + err = vulkan.Init() + if err != nil { + zlog.Infof("err", err) + } for i := 1; i < 100; i++ { w.Tick() } diff --git a/engine/render/vulkan/instance/ext_windows.go b/engine/render/vulkan/instance/ext_windows.go index b9d5645..2812990 100644 --- a/engine/render/vulkan/instance/ext_windows.go +++ b/engine/render/vulkan/instance/ext_windows.go @@ -1,17 +1,6 @@ package instance -import ( - "github.com/vkngwrapper/extensions/v2/ext_debug_utils" - "github.com/vkngwrapper/extensions/v2/khr_get_physical_device_properties2" - "github.com/vkngwrapper/extensions/v2/khr_surface" -) +import "github.com/vkngwrapper/extensions/v2/khr_swapchain" var validationLayers = []string{"VK_LAYER_KHRONOS_validation"} -var extensions = []string{ - khr_surface.ExtensionName, - ext_debug_utils.ExtensionName, - khr_get_physical_device_properties2.ExtensionName, - - "VK_EXT_debug_report", - "VK_EXT_metal_surface", -} +var deviceExtensions = []string{khr_swapchain.ExtensionName} diff --git a/engine/render/vulkan/instance/instance.go b/engine/render/vulkan/instance/instance.go index bd9040e..bcddaea 100644 --- a/engine/render/vulkan/instance/instance.go +++ b/engine/render/vulkan/instance/instance.go @@ -7,26 +7,37 @@ import ( "github.com/vkngwrapper/core/v2" "github.com/vkngwrapper/core/v2/common" "github.com/vkngwrapper/core/v2/core1_0" + "github.com/vkngwrapper/extensions/v2/khr_surface" + "github.com/vkngwrapper/extensions/v2/khr_swapchain" "zworld/engine/window" ) -var ( - engineName = "goworld" - layers = []string{ - "VK_LAYER_KHRONOS_validation", - //"VK_LAYER_LUNARG_api_dump", - } -) - type T interface { EnumeratePhysicalDevices() []core1_0.PhysicalDevice Destroy() + Init() error Ptr() core1_0.Instance } type FLoaderCreate func() *core.VulkanLoader type FInstance struct { - loader *core.VulkanLoader - ptr core1_0.Instance + loader *core.VulkanLoader + win *window.FSDLWindow + ptr core1_0.Instance + 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 } func New(appName string, win *window.FSDLWindow, LoaderCreate FLoaderCreate) T { @@ -35,30 +46,39 @@ func New(appName string, win *window.FSDLWindow, LoaderCreate FLoaderCreate) T { APIVersion: common.APIVersion(common.CreateVersion(1, 1, 0)), ApplicationName: appName, ApplicationVersion: common.CreateVersion(0, 1, 0), - EngineName: "goworld", + EngineName: engineName, EngineVersion: common.CreateVersion(0, 2, 1), } - help.getCreateInstanceExtension(instanceOptions, loader, win) + err := help.getCreateInstanceExtension(instanceOptions, loader, win) + if err != nil { + return nil + } + err = help.getCreateInstanceLayer(instanceOptions, loader, win) + if err != nil { + return nil + } handle, _, err := loader.CreateInstance(nil, *instanceOptions) if err != nil { panic(err) } return &FInstance{ - ptr: handle, + ptr: handle, + loader: loader, + win: win, } } -func (i *FInstance) Ptr() core1_0.Instance { - return i.ptr +func (e *FInstance) Ptr() core1_0.Instance { + return e.ptr } -func (i *FInstance) Destroy() { - i.ptr.Destroy(nil) - i.ptr = nil +func (e *FInstance) Destroy() { + e.ptr.Destroy(nil) + e.ptr = nil } -func (i *FInstance) EnumeratePhysicalDevices() []core1_0.PhysicalDevice { - r, _, err := i.ptr.EnumeratePhysicalDevices() +func (e *FInstance) EnumeratePhysicalDevices() []core1_0.PhysicalDevice { + r, _, err := e.ptr.EnumeratePhysicalDevices() if err != nil { panic(err) } diff --git a/engine/render/vulkan/instance/instance_init.go b/engine/render/vulkan/instance/instance_init.go new file mode 100644 index 0000000..02dc1e9 --- /dev/null +++ b/engine/render/vulkan/instance/instance_init.go @@ -0,0 +1,200 @@ +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" +) + +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) 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 + } + return nil +} diff --git a/engine/render/vulkan/instance/type.go b/engine/render/vulkan/instance/type.go index 2d6dd43..e627087 100644 --- a/engine/render/vulkan/instance/type.go +++ b/engine/render/vulkan/instance/type.go @@ -2,4 +2,7 @@ package instance const enableValidationLayers = true -var help FVulkanHelp +var ( + help FVulkanHelp + engineName = "goworld" +) diff --git a/engine/render/vulkan/instance/vulkan_help.go b/engine/render/vulkan/instance/vulkan_help.go index 0d80c87..e31d2ad 100644 --- a/engine/render/vulkan/instance/vulkan_help.go +++ b/engine/render/vulkan/instance/vulkan_help.go @@ -5,10 +5,27 @@ import ( "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_surface" "zworld/engine/window" ) -type FVulkanHelp struct{} +type FVulkanHelp struct { + vulkan *FInstance +} +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 +} func (h *FVulkanHelp) getCreateInstanceExtension(instanceOptions *core1_0.InstanceCreateInfo, loader *core.VulkanLoader, win *window.FSDLWindow) error { // Add extensions @@ -56,3 +73,130 @@ func (h *FVulkanHelp) getCreateInstanceLayer(instanceOptions *core1_0.InstanceCr } return nil } + +func (h *FVulkanHelp) findQueueFamilies(device core1_0.PhysicalDevice) (QueueFamilyIndices, error) { + indices := QueueFamilyIndices{} + e := h.vulkan + queueFamilies := device.QueueFamilyProperties() + + for queueFamilyIdx, queueFamily := range queueFamilies { + if (queueFamily.QueueFlags & core1_0.QueueGraphics) != 0 { + indices.GraphicsFamily = new(int) + *indices.GraphicsFamily = queueFamilyIdx + } + + supported, _, err := e.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 (h *FVulkanHelp) 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 (h *FVulkanHelp) querySwapChainSupport(device core1_0.PhysicalDevice) (SwapChainSupportDetails, error) { + e := h.vulkan + var details SwapChainSupportDetails + var err error + + details.Capabilities, _, err = e.surface.PhysicalDeviceSurfaceCapabilities(device) + if err != nil { + return details, err + } + + details.Formats, _, err = e.surface.PhysicalDeviceSurfaceFormats(device) + if err != nil { + return details, err + } + + details.PresentModes, _, err = e.surface.PhysicalDeviceSurfacePresentModes(device) + return details, err +} +func (h *FVulkanHelp) isDeviceSuitable(device core1_0.PhysicalDevice) bool { + indices, err := h.findQueueFamilies(device) + if err != nil { + return false + } + + extensionsSupported := h.checkDeviceExtensionSupport(device) + + var swapChainAdequate bool + if extensionsSupported { + swapChainSupport, err := h.querySwapChainSupport(device) + if err != nil { + return false + } + + swapChainAdequate = len(swapChainSupport.Formats) > 0 && len(swapChainSupport.PresentModes) > 0 + } + + return indices.IsComplete() && extensionsSupported && swapChainAdequate +} + +func (h *FVulkanHelp) 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 (h *FVulkanHelp) chooseSwapPresentMode(availablePresentModes []khr_surface.PresentMode) khr_surface.PresentMode { + for _, presentMode := range availablePresentModes { + if presentMode == khr_surface.PresentModeMailbox { + return presentMode + } + } + + return khr_surface.PresentModeFIFO +} + +func (h *FVulkanHelp) chooseSwapExtent(capabilities *khr_surface.SurfaceCapabilities) core1_0.Extent2D { + if capabilities.CurrentExtent.Width != -1 { + return capabilities.CurrentExtent + } + e := h.vulkan + widthInt, heightInt := e.win.GetInstance().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} +} diff --git a/go.mod b/go.mod index 850ab0b..f44f0be 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/kjk/flex v0.0.0-20171203210503-ed34d6b6a425 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/vkngwrapper/integrations/sdl2/v2 v2.1.0 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index cf48f9e..c128e57 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/vkngwrapper/core/v2 v2.2.1 h1:8xw2tuIXAeyNQj4mnDA7BHO6T6f7ba08UbJZB7U github.com/vkngwrapper/core/v2 v2.2.1/go.mod h1:EWABLJZGHa8nyeO4Bh9eR/V862HAz+Fvk5DitkOvYF4= github.com/vkngwrapper/extensions/v2 v2.2.0 h1:2ZP+Nom2EbefqgR2EPherJRS836wSWPoXeOLvV7aUuY= github.com/vkngwrapper/extensions/v2 v2.2.0/go.mod h1:55exjYwTUyQVNS/zhrC/Or/c2CA4Q9Cj/88Tu9EqlJ0= +github.com/vkngwrapper/integrations/sdl2/v2 v2.1.0 h1:7XZItFXzMeg6NDsvTXLQz65QD93t+rUuLwXbHbXsmmc= +github.com/vkngwrapper/integrations/sdl2/v2 v2.1.0/go.mod h1:Qwh+0jymwQHlXQQgVYB77m6O49ShV37nR75RBJ/oRvI= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=