Mercurial > games > semicongine
changeset 96:b9fc90de1450
add: swapchain API, more refactoring
author | Sam <sam@basx.dev> |
---|---|
date | Wed, 01 Mar 2023 23:58:39 +0700 |
parents | 8011e4d6372d |
children | 110ed3ee5df8 |
files | src/semicongine/platform/linux/surface.nim src/semicongine/vulkan.nim src/semicongine/vulkan/device.nim src/semicongine/vulkan/instance.nim src/semicongine/vulkan/physicaldevice.nim src/semicongine/vulkan/surface.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim |
diffstat | 8 files changed, 286 insertions(+), 144 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/platform/linux/surface.nim Wed Mar 01 00:01:06 2023 +0700 +++ b/src/semicongine/platform/linux/surface.nim Wed Mar 01 23:58:39 2023 +0700 @@ -1,12 +1,11 @@ import ../../vulkan/api -import ../../vulkan/instance import ../../platform/window -proc createNativeSurface*(instance: Instance, window: NativeWindow): VkSurfaceKHR = - assert instance.vk.valid +proc createNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = + assert instance.valid var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, dpy: cast[ptr api.Display](window.display), window: cast[api.Window](window.window), ) - checkVkResult vkCreateXlibSurfaceKHR(instance.vk, addr(surfaceCreateInfo), nil, addr(result)) + checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))
--- a/src/semicongine/vulkan.nim Wed Mar 01 00:01:06 2023 +0700 +++ b/src/semicongine/vulkan.nim Wed Mar 01 23:58:39 2023 +0700 @@ -4,12 +4,15 @@ import ./vulkan/instance export instance -import ./vulkan/surface -export surface +import ./vulkan/physicaldevice +export physicaldevice import ./vulkan/device export device +import ./vulkan/swapchain +export swapchain + import ./vulkan/buffer export buffer
--- a/src/semicongine/vulkan/device.nim Wed Mar 01 00:01:06 2023 +0700 +++ b/src/semicongine/vulkan/device.nim Wed Mar 01 23:58:39 2023 +0700 @@ -5,118 +5,36 @@ import ./api import ./utils import ./instance -import ./surface +import ./physicaldevice type - PhysicalDevice* = object - vk*: VkPhysicalDevice Device* = object physicalDevice*: PhysicalDevice vk*: VkDevice queues*: Table[QueueFamily, Queue] - QueueFamily* = object - properties*: VkQueueFamilyProperties - index*: uint32 - flags*: seq[VkQueueFlagBits] - presentation: bool - # presentation is related to a specific surface, saving it here if provided during querying - surface: Option[Surface] Queue* = object vk*: VkQueue - -proc getPhysicalDevices*(instance: Instance): seq[PhysicalDevice] = - assert instance.vk.valid - var nDevices: uint32 - checkVkResult vkEnumeratePhysicalDevices(instance.vk, addr(nDevices), nil) - var devices = newSeq[VkPhysicalDevice](nDevices) - checkVkResult vkEnumeratePhysicalDevices(instance.vk, addr(nDevices), devices.toCPointer) - for i in 0 ..< nDevices: - result.add PhysicalDevice(vk: devices[i]) - -proc getExtensions*(device: PhysicalDevice): seq[string] = - assert device.vk.valid - var extensionCount: uint32 - checkVkResult vkEnumerateDeviceExtensionProperties(device.vk, nil, addr(extensionCount), nil) - if extensionCount > 0: - var extensions = newSeq[VkExtensionProperties](extensionCount) - checkVkResult vkEnumerateDeviceExtensionProperties(device.vk, nil, addr(extensionCount), extensions.toCPointer) - for extension in extensions: - result.add(cleanString(extension.extensionName)) - -proc getSurfaceFormats*(device: PhysicalDevice, surface: Surface): seq[VkSurfaceFormatKHR] = - assert device.vk.valid - assert surface.vk.valid - var n_formats: uint32 - checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device.vk, surface.vk, addr(n_formats), nil) - result = newSeq[VkSurfaceFormatKHR](n_formats) - checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device.vk, surface.vk, addr(n_formats), result.toCPointer) - -proc getSurfacePresentModes*(device: PhysicalDevice, surface: Surface): seq[VkPresentModeKHR] = - assert device.vk.valid - assert surface.vk.valid - var n_modes: uint32 - checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device.vk, surface.vk, addr(n_modes), nil) - result = newSeq[VkPresentModeKHR](n_modes) - checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device.vk, surface.vk, addr(n_modes), result.toCPointer) - -proc getQueueFamilies*(device: PhysicalDevice): seq[QueueFamily] = - assert device.vk.valid - var nQueuefamilies: uint32 - vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies, nil) - var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) - vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies , queuFamilies.toCPointer) - for i in 0 ..< nQueuefamilies: - result.add QueueFamily( - properties: queuFamilies[i], - index: i, - flags: queuFamilies[i].queueFlags.toEnums, - presentation: VkBool32(false), - ) - -proc getQueueFamilies*(device: PhysicalDevice, surface: Surface): seq[QueueFamily] = - assert device.vk.valid - assert surface.vk.valid - var nQueuefamilies: uint32 - vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies, nil) - var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) - vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies , queuFamilies.toCPointer) - for i in 0 ..< nQueuefamilies: - var presentation = VkBool32(false) - checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(device.vk, i, surface.vk, addr presentation) - result.add QueueFamily( - properties: queuFamilies[i], - index: i, - flags: queuFamilies[i].queueFlags.toEnums, - surface: if presentation: some(surface) else: none(Surface), - presentation: presentation, - ) - -proc filterForGraphicsPresentationQueues*(families: seq[QueueFamily]): seq[QueueFamily] = - var hasGraphics = false - var hasPresentation = false - var queues: Table[uint32, QueueFamily] - for family in families: - if VK_QUEUE_GRAPHICS_BIT in family.flags: - queues[family.index] = family - hasGraphics = true - if family.presentation: - queues[family.index] = family - hasPresentation = true - if hasGraphics and hasPresentation: - return queues.values.toSeq + presentation: bool + graphics: bool proc createDevice*( + instance: Instance, physicalDevice: PhysicalDevice, - enabledLayers: openArray[string], - enabledExtensions: openArray[string], - queueFamilies: openArray[QueueFamily], + enabledLayers: seq[string], + enabledExtensions: seq[string], + queueFamilies: seq[QueueFamily], ): Device = + assert instance.vk.valid assert physicalDevice.vk.valid assert queueFamilies.len > 0 + result.physicalDevice = physicalDevice + var allExtensions = enabledExtensions & @["VK_KHR_swapchain"] + for extension in allExtensions: + instance.vk.loadExtension(extension) var enabledLayersC = allocCStringArray(enabledLayers) - enabledExtensionsC = allocCStringArray(enabledExtensions) + enabledExtensionsC = allocCStringArray(allExtensions) priority = 1'f32 var deviceQueues: Table[QueueFamily, VkDeviceQueueCreateInfo] for family in queueFamilies: @@ -134,7 +52,7 @@ pQueueCreateInfos: queueList.toCPointer, enabledLayerCount: uint32(enabledLayers.len), ppEnabledLayerNames: enabledLayersC, - enabledExtensionCount: uint32(enabledExtensions.len), + enabledExtensionCount: uint32(allExtensions.len), ppEnabledExtensionNames: enabledExtensionsC, pEnabledFeatures: nil, ) @@ -147,19 +65,21 @@ ) deallocCStringArray(enabledLayersC) deallocCStringArray(enabledExtensionsC) - for queueFamily in deviceQueues.keys: + for family in deviceQueues.keys: var queue: VkQueue - vkGetDeviceQueue(result.vk, queueFamily.index, 0, addr queue) - result.queues[queueFamily] = Queue(vk: queue) + vkGetDeviceQueue(result.vk, family.index, 0, addr queue) + result.queues[family] = Queue(vk: queue, presentation: family.hasPresentation(physicalDevice.surface), graphics: family.hasGraphics()) func firstGraphicsQueue*(device: Device): Option[Queue] = + assert device.vk.valid for family, queue in device.queues: - if VK_QUEUE_GRAPHICS_BIT in family.flags: + if queue.graphics: return some(queue) -func firstPresentationQueue*(device: Device): Option[Queue] = +proc firstPresentationQueue*(device: Device): Option[Queue] = + assert device.vk.valid for family, queue in device.queues: - if family.presentation: + if queue.presentation: return some(queue) proc destroy*(device: var Device) =
--- a/src/semicongine/vulkan/instance.nim Wed Mar 01 00:01:06 2023 +0700 +++ b/src/semicongine/vulkan/instance.nim Wed Mar 01 23:58:39 2023 +0700 @@ -1,13 +1,19 @@ import std/strformat +import std/enumutils +import std/sequtils import ./api import ./utils import ../platform/vulkanExtensions +import ../platform/window +import ../platform/surface type Instance* = object vk*: VkInstance + window*: NativeWindow + surface*: VkSurfaceKHR Debugger* = object instance*: Instance messenger*: VkDebugUtilsMessengerEXT @@ -37,6 +43,7 @@ result.add(cleanString(layer.layerName)) proc createInstance*( + window: NativeWindow, vulkanVersion: uint32, instanceExtensions: seq[string], layers: seq[string], @@ -72,10 +79,14 @@ deallocCStringArray(instanceExtensionsC) for extension in requiredExtensions: result.vk.loadExtension($extension) + result.surface = result.vk.createNativeSurface(window) proc destroy*(instance: var Instance) = assert instance.vk.valid + assert instance.surface.valid # needs to happen after window is trashed as the driver might have a hook registered for the window destruction + instance.vk.vkDestroySurfaceKHR(instance.surface, nil) + instance.surface.reset instance.vk.vkDestroyInstance(nil) instance.vk.reset() @@ -96,8 +107,8 @@ ): Debugger = assert instance.vk.valid result.instance = instance - var severityLevelBits = high(VkDebugUtilsMessageSeverityFlagsEXT) - var typeBits = high(VkDebugUtilsMessageTypeFlagsEXT) + var severityLevelBits = VkDebugUtilsMessageSeverityFlagBitsEXT.items.toSeq.toBits + var typeBits = VkDebugUtilsMessageTypeFlagBitsEXT.items.toSeq.toBits if severityLevels.len > 0: severityLevelBits = toBits severityLevels if types.len > 0:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/physicaldevice.nim Wed Mar 01 23:58:39 2023 +0700 @@ -0,0 +1,163 @@ +import std/enumerate +import std/options +import std/tables +import std/sequtils + +import ./api +import ./utils +import ./instance + +type + PhysicalDevice* = object + vk*: VkPhysicalDevice + name*: string + devicetype*: VkPhysicalDeviceType + surface*: VkSurfaceKHR + QueueFamily* = object + device: PhysicalDevice + properties*: VkQueueFamilyProperties + index*: uint32 + flags*: seq[VkQueueFlagBits] + +proc getProperties*(device: PhysicalDevice): VkPhysicalDeviceProperties = + assert device.vk.valid + device.vk.vkGetPhysicalDeviceProperties(addr result) + +proc getPhysicalDevices*(instance: Instance): seq[PhysicalDevice] = + assert instance.vk.valid + assert instance.surface.valid + var nDevices: uint32 + checkVkResult vkEnumeratePhysicalDevices(instance.vk, addr(nDevices), nil) + var devices = newSeq[VkPhysicalDevice](nDevices) + checkVkResult vkEnumeratePhysicalDevices(instance.vk, addr(nDevices), devices.toCPointer) + for i in 0 ..< nDevices: + var device = PhysicalDevice(vk: devices[i], surface: instance.surface) + let props = device.getProperties() + device.name = props.deviceName.cleanString() + device.devicetype = props.deviceType + result.add device + +proc getExtensions*(device: PhysicalDevice): seq[string] = + assert device.vk.valid + var extensionCount: uint32 + checkVkResult vkEnumerateDeviceExtensionProperties(device.vk, nil, addr(extensionCount), nil) + if extensionCount > 0: + var extensions = newSeq[VkExtensionProperties](extensionCount) + checkVkResult vkEnumerateDeviceExtensionProperties(device.vk, nil, addr(extensionCount), extensions.toCPointer) + for extension in extensions: + result.add(cleanString(extension.extensionName)) + +proc getFeatures*(device: PhysicalDevice): VkPhysicalDeviceFeatures = + assert device.vk.valid + device.vk.vkGetPhysicalDeviceFeatures(addr result) + +proc getSurfaceCapabilities*(device: PhysicalDevice): VkSurfaceCapabilitiesKHR = + assert device.vk.valid + assert device.surface.valid + checkVkResult device.vk.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.surface, addr(result)) + +proc getSurfaceFormats*(device: PhysicalDevice): seq[VkSurfaceFormatKHR] = + assert device.vk.valid + assert device.surface.valid + var n_formats: uint32 + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device.vk, device.surface, addr(n_formats), nil) + result = newSeq[VkSurfaceFormatKHR](n_formats) + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device.vk, device.surface, addr(n_formats), result.toCPointer) + +func filterSurfaceFormat*( + formats: seq[VkSurfaceFormatKHR], + imageFormat = VK_FORMAT_B8G8R8A8_SRGB, + colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR +): VkSurfaceFormatKHR = + for format in formats: + if format.format == imageFormat and format.colorSpace == colorSpace: + return format + +proc filterSurfaceFormat*(device: PhysicalDevice): VkSurfaceFormatKHR = + assert device.vk.valid + assert device.surface.valid + device.getSurfaceFormats().filterSurfaceFormat() + +proc getSurfacePresentModes*(device: PhysicalDevice): seq[VkPresentModeKHR] = + assert device.vk.valid + assert device.surface.valid + var n_modes: uint32 + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device.vk, device.surface, addr(n_modes), nil) + result = newSeq[VkPresentModeKHR](n_modes) + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device.vk, device.surface, addr(n_modes), result.toCPointer) + +proc getQueueFamilies*(device: PhysicalDevice): seq[QueueFamily] = + assert device.vk.valid + var nQueuefamilies: uint32 + vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies, nil) + var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) + vkGetPhysicalDeviceQueueFamilyProperties(device.vk, addr nQueuefamilies , queuFamilies.toCPointer) + for i in 0 ..< nQueuefamilies: + result.add QueueFamily( + device: device, + properties: queuFamilies[i], + index: i, + flags: queuFamilies[i].queueFlags.toEnums, + ) + +proc hasGraphics*(family: QueueFamily): bool = + VK_QUEUE_GRAPHICS_BIT in family.flags +proc hasPresentation*(family: QueueFamily, surface: VkSurfaceKHR): bool = + assert surface.valid + var presentation = VkBool32(false) + checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(family.device.vk, family.index, surface, addr presentation) + return presentation + +proc filterForGraphicsPresentationQueues*(device: PhysicalDevice): seq[QueueFamily] = + var hasGraphics = false + var hasPresentation = false + var queues: Table[uint32, QueueFamily] + for family in device.getQueueFamilies(): + if family.hasGraphics: + queues[family.index] = family + hasGraphics = true + if family.hasPresentation(device.surface): + queues[family.index] = family + hasPresentation = true + if hasGraphics and hasPresentation: + return queues.values.toSeq + +proc filterGraphics(families: seq[QueueFamily]): seq[QueueFamily] = + for family in families: + if family.hasGraphics: + result.add family + +proc filterPresentation(families: seq[QueueFamily], surface: VkSurfaceKHR): seq[QueueFamily] = + assert surface.valid + for family in families: + if family.hasPresentation(surface): + result.add family + +proc rateGraphics*(device: PhysicalDevice): float = + assert device.vk.valid + assert device.surface.valid + if device.getQueueFamilies().filterGraphics().filterPresentation(device.surface).len == 0: + return -1 + if not ("VK_KHR_swapchain" in device.getExtensions()): + return -1 + const deviceTypeMap = [ + VK_PHYSICAL_DEVICE_TYPE_OTHER, + VK_PHYSICAL_DEVICE_TYPE_CPU, + VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, + VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, + ] + for (i, devicetype) in enumerate(deviceTypeMap): + if device.devicetype == devicetype: + result = float(i) + +proc filterBestGraphics*(devices: seq[PhysicalDevice]): PhysicalDevice = + var bestVal = -1'f + for device in devices: + assert device.vk.valid + assert device.surface.valid + let rating = device.rateGraphics() + if rating > bestVal: + bestVal = rating + result = device + assert bestVal >= 0
--- a/src/semicongine/vulkan/surface.nim Wed Mar 01 00:01:06 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -import ./api -import ./instance - -import ../platform/window -import ../platform/surface - -type - Surface* = object - vk*: VkSurfaceKHR - instance: Instance - -proc createSurface*(instance: Instance, window: NativeWindow): Surface = - assert instance.vk.valid - result.instance = instance - result.vk = instance.createNativeSurface(window) - -proc destroy*(surface: var Surface) = - assert surface.vk.valid - assert surface.instance.vk.valid - surface.instance.vk.vkDestroySurfaceKHR(surface.vk, nil) - surface.vk.reset
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/swapchain.nim Wed Mar 01 23:58:39 2023 +0700 @@ -0,0 +1,51 @@ +import std/options + +import ./api +import ./device +import ./physicaldevice + +type + Swapchain = object + vk*: VkSwapchainKHR + device*: Device + + +proc createSwapchain*(device: Device, surfaceFormat: VkSurfaceFormatKHR, nBuffers=3'u32, presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR): (Swapchain, VkResult) = + assert device.vk.valid + assert device.physicalDevice.vk.valid + var capabilities = device.physicalDevice.getSurfaceCapabilities() + + var imageCount = nBuffers + # following is according to vulkan specs + if presentationMode in [VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR]: + imageCount = 1 + else: + imageCount = max(imageCount, capabilities.minImageCount) + if capabilities.maxImageCount != 0: + imageCount = min(imageCount, capabilities.maxImageCount) + + var createInfo = VkSwapchainCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + surface: device.physicalDevice.surface, + minImageCount: imageCount, + imageFormat: surfaceFormat.format, + imageColorSpace: surfaceFormat.colorSpace, + imageExtent: capabilities.currentExtent, + imageArrayLayers: 1, + imageUsage: toBits [VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT], + # VK_SHARING_MODE_CONCURRENT no supported currently + imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, + preTransform: capabilities.currentTransform, + compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, # only used for blending with other windows, can be opaque + presentMode: presentationMode, + clipped: true, + ) + var + swapchain = Swapchain(device: device) + createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk)) + + return (swapchain, createResult) + +proc destroy*(swapchain: var Swapchain) = + assert swapchain.vk.valid + swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil)
--- a/tests/test_vulkan_wrapper.nim Wed Mar 01 00:01:06 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Wed Mar 01 23:58:39 2023 +0700 @@ -1,3 +1,6 @@ +import std/tables +import std/options + import semicongine/vulkan import semicongine/platform/window @@ -12,43 +15,56 @@ echo " " & extension # create instance - var instance = createInstance( + var thewindow = createWindow("Test") + var instance = thewindow.createInstance( vulkanVersion=VK_MAKE_API_VERSION(0, 1, 3, 0), instanceExtensions= @["VK_EXT_debug_utils"], layers= @["VK_LAYER_KHRONOS_validation"] ) var debugger = instance.createDebugMessenger() - # create surface - var thewindow = createWindow("Test") - var surface = instance.createSurface(thewindow) - # diagnostic output echo "Devices" for device in instance.getPhysicalDevices(): echo " " & $device + echo " Rating: " & $device.rateGraphics() echo " Extensions" for extension in device.getExtensions(): echo " " & $extension + echo " Properties" + echo " " & $device.getProperties() + echo " Features" + echo " " & $device.getFeatures() echo " Queue families" for queueFamily in device.getQueueFamilies(): echo " " & $queueFamily echo " Surface present modes" - for mode in device.getSurfacePresentModes(surface): + for mode in device.getSurfacePresentModes(): echo " " & $mode echo " Surface formats" - for format in device.getSurfaceFormats(surface): + for format in device.getSurfaceFormats(): echo " " & $format # create devices - var devices: seq[Device] - for physicalDevice in instance.getPhysicalDevices(): - devices.add physicalDevice.createDevice([], [], physicalDevice.getQueueFamilies(surface).filterForGraphicsPresentationQueues()) + let selectedPhysicalDevice = instance.getPhysicalDevices().filterBestGraphics() + var device = instance.createDevice( + selectedPhysicalDevice, + @[], + @[], + selectedPhysicalDevice.filterForGraphicsPresentationQueues() + ) + + echo "Created device ", device.physicalDevice.name + var (swapchain, res) = device.createSwapchain(device.physicalDevice.getSurfaceFormats().filterSurfaceFormat()) + if res != VK_SUCCESS: + raise newException(Exception, "Unable to create swapchain") + + echo "All successfull" + echo "Start cleanup" # cleanup - surface.destroy() - for device in devices.mitems: - device.destroy() + swapchain.destroy() + device.destroy() debugger.destroy() instance.destroy()