changeset 557:e2d8c02f1548

add: swapchain API, more refactoring
author Sam <sam@basx.dev>
date Wed, 01 Mar 2023 23:58:39 +0700
parents 21f15523fda0
children affa6571a2c9
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()