| 
1488
 | 
     1 import std/sequtils
 | 
| 
 | 
     2 import std/enumutils
 | 
| 
1487
 | 
     3 import std/tables
 | 
| 
1485
 | 
     4 import std/strutils
 | 
| 
 | 
     5 import std/logging
 | 
| 
1486
 | 
     6 import std/os
 | 
| 
1485
 | 
     7 
 | 
| 
 | 
     8 include ./vkapi
 | 
| 
 | 
     9 
 | 
| 
1489
 | 
    10 # =============================================================================
 | 
| 
 | 
    11 # UTILS =======================================================================
 | 
| 
 | 
    12 # =============================================================================
 | 
| 
 | 
    13 
 | 
| 
 | 
    14 if not defined(release):
 | 
| 
 | 
    15   addHandler(newConsoleLogger())
 | 
| 
 | 
    16   addHandler(newFileLogger("svk.log"))
 | 
| 
 | 
    17 
 | 
| 
 | 
    18 # log level
 | 
| 
 | 
    19 when defined(release):
 | 
| 
 | 
    20   const LOGLEVEL {.strdefine.}: string = "Warn"
 | 
| 
 | 
    21 else:
 | 
| 
 | 
    22   const LOGLEVEL {.strdefine.}: string = "Debug"
 | 
| 
 | 
    23 setLogFilter(parseEnum[Level]("lvl" & LOGLEVEL))
 | 
| 
 | 
    24 
 | 
| 
 | 
    25 template debugAssert(arg: untyped): untyped =
 | 
| 
 | 
    26   when not defined(release):
 | 
| 
 | 
    27     assert arg
 | 
| 
 | 
    28 
 | 
| 
 | 
    29 # we will support vulkan 1.2 for maximum portability
 | 
| 
 | 
    30 const VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0)
 | 
| 
1486
 | 
    31 
 | 
| 
1488
 | 
    32 iterator items*[T: HoleyEnum](E: typedesc[T]): T =
 | 
| 
 | 
    33   for a in enumFullRange(E):
 | 
| 
 | 
    34     yield a
 | 
| 
 | 
    35 
 | 
| 
1485
 | 
    36 template checkVkResult*(call: untyped) =
 | 
| 
 | 
    37   when defined(release):
 | 
| 
 | 
    38     discard call
 | 
| 
 | 
    39   else:
 | 
| 
 | 
    40     # yes, a bit cheap, but this is only for nice debug output
 | 
| 
 | 
    41     var callstr = astToStr(call).replace("\n", "")
 | 
| 
 | 
    42     while callstr.find("  ") >= 0:
 | 
| 
 | 
    43       callstr = callstr.replace("  ", " ")
 | 
| 
 | 
    44     debug "Calling vulkan: ", callstr
 | 
| 
 | 
    45     let value = call
 | 
| 
 | 
    46     if value != VK_SUCCESS:
 | 
| 
 | 
    47       error "Vulkan error: ", astToStr(call), " returned ", $value
 | 
| 
 | 
    48       raise newException(
 | 
| 
 | 
    49         Exception, "Vulkan error: " & astToStr(call) & " returned " & $value
 | 
| 
 | 
    50       )
 | 
| 
 | 
    51 
 | 
| 
1489
 | 
    52 # =============================================================================
 | 
| 
 | 
    53 # PLATFORM TYPES ==============================================================
 | 
| 
 | 
    54 # =============================================================================
 | 
| 
 | 
    55 
 | 
| 
 | 
    56 when defined(windows):
 | 
| 
 | 
    57   type NativeWindow* = object
 | 
| 
 | 
    58     hinstance*: HINSTANCE
 | 
| 
 | 
    59     hwnd*: HWND
 | 
| 
 | 
    60     g_wpPrev*: WINDOWPLACEMENT
 | 
| 
1490
 | 
    61 
 | 
| 
1489
 | 
    62 else:
 | 
| 
 | 
    63   type NativeWindow* = object
 | 
| 
 | 
    64     display*: ptr Display
 | 
| 
 | 
    65     window*: Window
 | 
| 
 | 
    66     emptyCursor*: Cursor
 | 
| 
 | 
    67     ic*: XIC
 | 
| 
 | 
    68 
 | 
| 
 | 
    69 # =============================================================================
 | 
| 
 | 
    70 # VULKAN INSTANCE =============================================================
 | 
| 
 | 
    71 # =============================================================================
 | 
| 
 | 
    72 
 | 
| 
1485
 | 
    73 type SVkInstance* = object
 | 
| 
 | 
    74   vkInstance: VkInstance
 | 
| 
1486
 | 
    75   debugMessenger: VkDebugUtilsMessengerEXT
 | 
| 
1489
 | 
    76   window: NativeWindow
 | 
| 
 | 
    77   vkSurface*: VkSurfaceKHR
 | 
| 
 | 
    78 
 | 
| 
 | 
    79 proc createWindow(title: string): NativeWindow
 | 
| 
1485
 | 
    80 
 | 
| 
 | 
    81 proc `=copy`(a: var SVkInstance, b: SVkInstance) {.error.}
 | 
| 
 | 
    82 
 | 
| 
 | 
    83 proc `=destroy`(a: SVkInstance) =
 | 
| 
1489
 | 
    84   debugAssert a.vkInstance.pointer == nil
 | 
| 
 | 
    85   debugAssert a.vkSurface.pointer == nil
 | 
| 
 | 
    86   debugAssert a.debugMessenger.pointer == nil
 | 
| 
 | 
    87 
 | 
| 
 | 
    88 proc destroy*(a: var SVkInstance) =
 | 
| 
1485
 | 
    89   if a.vkInstance.pointer != nil:
 | 
| 
1486
 | 
    90     if a.debugMessenger.pointer != nil:
 | 
| 
 | 
    91       vkDestroyDebugUtilsMessengerEXT(a.vkInstance, a.debugMessenger, nil)
 | 
| 
1489
 | 
    92       a.debugMessenger = VkDebugUtilsMessengerEXT(nil)
 | 
| 
 | 
    93     vkDestroySurfaceKHR(a.vkInstance, a.vkSurface, nil)
 | 
| 
 | 
    94     a.vkSurface = VkSurfaceKHR(nil)
 | 
| 
1485
 | 
    95     a.vkInstance.vkDestroyInstance(nil)
 | 
| 
1489
 | 
    96     a.vkInstance = VkInstance(nil)
 | 
| 
1485
 | 
    97 
 | 
| 
1486
 | 
    98 proc debugCallback(
 | 
| 
 | 
    99     messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT,
 | 
| 
 | 
   100     messageTypes: VkDebugUtilsMessageTypeFlagsEXT,
 | 
| 
 | 
   101     pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT,
 | 
| 
 | 
   102     userData: pointer,
 | 
| 
 | 
   103 ): VkBool32 {.cdecl.} =
 | 
| 
 | 
   104   const LOG_LEVEL_MAPPING = {
 | 
| 
 | 
   105     VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lvlDebug,
 | 
| 
 | 
   106     VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lvlInfo,
 | 
| 
 | 
   107     VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lvlWarn,
 | 
| 
 | 
   108     VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lvlError,
 | 
| 
 | 
   109   }.toTable
 | 
| 
1489
 | 
   110   log LOG_LEVEL_MAPPING[messageSeverity], "SVK-LOG: ", $pCallbackData.pMessage
 | 
| 
1486
 | 
   111   if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
 | 
| 
 | 
   112     let errorMsg = $pCallbackData.pMessage & ": " & getStackTrace()
 | 
| 
 | 
   113     raise newException(Exception, errorMsg)
 | 
| 
 | 
   114   return VK_FALSE
 | 
| 
 | 
   115 
 | 
| 
1485
 | 
   116 proc svkCreateInstance*(
 | 
| 
 | 
   117     applicationName: string,
 | 
| 
 | 
   118     enabledLayers: openArray[string] = [],
 | 
| 
1486
 | 
   119     enabledExtensions: openArray[string] =
 | 
| 
 | 
   120       if defined(release):
 | 
| 
1489
 | 
   121         if defined(windows):
 | 
| 
 | 
   122           @["VK_KHR_surface", "VK_KHR_win32_surface"]
 | 
| 
 | 
   123         else:
 | 
| 
 | 
   124           @["VK_KHR_surface", "VK_KHR_xlib_surface"]
 | 
| 
1486
 | 
   125       else:
 | 
| 
1489
 | 
   126         if defined(windows):
 | 
| 
 | 
   127           @["VK_KHR_surface", "VK_EXT_debug_utils", "VK_KHR_win32_surface"]
 | 
| 
 | 
   128         else:
 | 
| 
 | 
   129           @["VK_KHR_surface", "VK_EXT_debug_utils", "VK_KHR_xlib_surface"],
 | 
| 
1485
 | 
   130     engineName = "semicongine",
 | 
| 
 | 
   131 ): SVkInstance =
 | 
| 
1486
 | 
   132   putEnv("VK_LOADER_LAYERS_ENABLE", "*validation")
 | 
| 
 | 
   133   putEnv(
 | 
| 
 | 
   134     "VK_LAYER_ENABLES",
 | 
| 
 | 
   135     "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT",
 | 
| 
 | 
   136   )
 | 
| 
 | 
   137   initVulkanLoader()
 | 
| 
 | 
   138 
 | 
| 
1489
 | 
   139   var allLayers = @enabledLayers
 | 
| 
 | 
   140   when not defined(release):
 | 
| 
 | 
   141     allLayers.add "VK_LAYER_KHRONOS_validation"
 | 
| 
 | 
   142 
 | 
| 
1485
 | 
   143   let
 | 
| 
 | 
   144     appinfo = VkApplicationInfo(
 | 
| 
 | 
   145       pApplicationName: applicationName,
 | 
| 
 | 
   146       pEngineName: engineName,
 | 
| 
1486
 | 
   147       apiVersion: VULKAN_VERSION,
 | 
| 
1485
 | 
   148     )
 | 
| 
1489
 | 
   149     enabledLayersC = allocCStringArray(allLayers)
 | 
| 
1486
 | 
   150     enabledExtensionsC = allocCStringArray(enabledExtensions)
 | 
| 
1485
 | 
   151     createinfo = VkInstanceCreateInfo(
 | 
| 
 | 
   152       pApplicationInfo: addr appinfo,
 | 
| 
1489
 | 
   153       enabledLayerCount: allLayers.len.uint32,
 | 
| 
1486
 | 
   154       ppEnabledLayerNames: enabledLayersC,
 | 
| 
1485
 | 
   155       enabledExtensionCount: enabledExtensions.len.uint32,
 | 
| 
1486
 | 
   156       ppEnabledExtensionNames: enabledExtensionsC,
 | 
| 
1485
 | 
   157     )
 | 
| 
 | 
   158   checkVkResult vkCreateInstance(addr createinfo, nil, addr result.vkInstance)
 | 
| 
1486
 | 
   159 
 | 
| 
 | 
   160   enabledLayersC.deallocCStringArray()
 | 
| 
 | 
   161   enabledExtensionsC.deallocCStringArray()
 | 
| 
 | 
   162 
 | 
| 
1489
 | 
   163   # only support up to vulkan 1.2 for maximum portability
 | 
| 
1486
 | 
   164   load_VK_VERSION_1_0(result.vkInstance)
 | 
| 
 | 
   165   load_VK_VERSION_1_1(result.vkInstance)
 | 
| 
 | 
   166   load_VK_VERSION_1_2(result.vkInstance)
 | 
| 
 | 
   167 
 | 
| 
 | 
   168   for extension in enabledExtensions:
 | 
| 
 | 
   169     loadExtension(result.vkInstance, extension)
 | 
| 
1489
 | 
   170   load_VK_KHR_swapchain(result.vkInstance)
 | 
| 
1486
 | 
   171 
 | 
| 
1489
 | 
   172   var allTypes: VkDebugUtilsMessageTypeFlagsEXT
 | 
| 
 | 
   173   for t in VkDebugUtilsMessageTypeFlagBitsEXT:
 | 
| 
 | 
   174     allTypes = allTypes or t
 | 
| 
1486
 | 
   175   when not defined(release):
 | 
| 
 | 
   176     var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT(
 | 
| 
1490
 | 
   177       messageSeverity:
 | 
| 
 | 
   178         VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT or
 | 
| 
 | 
   179         VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT or
 | 
| 
 | 
   180         VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT or
 | 
| 
 | 
   181         VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
 | 
| 
 | 
   182       messageType:
 | 
| 
 | 
   183         VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT or
 | 
| 
 | 
   184         VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT or
 | 
| 
 | 
   185         VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
 | 
| 
1486
 | 
   186       pfnUserCallback: debugCallback,
 | 
| 
 | 
   187     )
 | 
| 
 | 
   188     checkVkResult vkCreateDebugUtilsMessengerEXT(
 | 
| 
 | 
   189       result.vkInstance, addr debugMessengerCreateInfo, nil, addr result.debugMessenger
 | 
| 
 | 
   190     )
 | 
| 
1489
 | 
   191 
 | 
| 
 | 
   192   result.window = createWindow(applicationName)
 | 
| 
 | 
   193   when defined(windows):
 | 
| 
1490
 | 
   194     var surfaceCreateInfo =
 | 
| 
 | 
   195       VkWin32SurfaceCreateInfoKHR(hinstance: window.hinstance, hwnd: window.hwnd)
 | 
| 
 | 
   196     checkVkResult vkCreateWin32SurfaceKHR(
 | 
| 
 | 
   197       instance, addr surfaceCreateInfo, nil, addr result.vkSurface
 | 
| 
 | 
   198     )
 | 
| 
1489
 | 
   199   else:
 | 
| 
1490
 | 
   200     var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
 | 
| 
 | 
   201       dpy: result.window.display, window: result.window.window
 | 
| 
 | 
   202     )
 | 
| 
 | 
   203     checkVkResult result.vkInstance.vkCreateXlibSurfaceKHR(
 | 
| 
 | 
   204       addr surfaceCreateInfo, nil, addr result.vkSurface
 | 
| 
 | 
   205     )
 | 
| 
1489
 | 
   206 
 | 
| 
 | 
   207 # =============================================================================
 | 
| 
 | 
   208 # PHYSICAL DEVICES ============================================================
 | 
| 
 | 
   209 # =============================================================================
 | 
| 
 | 
   210 
 | 
| 
 | 
   211 type
 | 
| 
 | 
   212   SVkMemoryType* = object
 | 
| 
 | 
   213     size: uint64
 | 
| 
 | 
   214     deviceLocal: bool # fast for gpu access
 | 
| 
 | 
   215     hostCached: bool # fast for host access
 | 
| 
 | 
   216     hostVisible: bool # can use vkMapMemory
 | 
| 
1490
 | 
   217     hostCohorent: bool
 | 
| 
 | 
   218       # does *not* require vkFlushMappedMemoryRanges and vkInvalidateMappedMemoryRanges 
 | 
| 
 | 
   219 
 | 
| 
1489
 | 
   220   SVkQueueFamilies* = object
 | 
| 
 | 
   221     count: int
 | 
| 
 | 
   222     hasGraphics: bool # implies "hasTransfer"
 | 
| 
 | 
   223     hasCompute: bool # implies "hasTransfer"
 | 
| 
 | 
   224 
 | 
| 
 | 
   225   SVkPhysicalDevice* = object
 | 
| 
 | 
   226     name*: string
 | 
| 
1490
 | 
   227     discreteGPU*: bool
 | 
| 
1489
 | 
   228     vkPhysicalDevice*: VkPhysicalDevice
 | 
| 
 | 
   229     vkPhysicalDeviceFeatures*: VkPhysicalDeviceFeatures
 | 
| 
 | 
   230     vkPhysicalDeviceProperties*: VkPhysicalDeviceProperties
 | 
| 
 | 
   231     memoryTypes*: seq[SVkMemoryType]
 | 
| 
 | 
   232     queueFamily*: uint32
 | 
| 
 | 
   233 
 | 
| 
 | 
   234 proc getUsablePhysicalDevices*(instance: SVkInstance): seq[SVkPhysicalDevice] =
 | 
| 
 | 
   235   var nDevices: uint32
 | 
| 
 | 
   236   checkVkResult instance.vkInstance.vkEnumeratePhysicalDevices(addr nDevices, nil)
 | 
| 
 | 
   237   var devices = newSeq[VkPhysicalDevice](nDevices)
 | 
| 
1490
 | 
   238   checkVkResult instance.vkInstance.vkEnumeratePhysicalDevices(
 | 
| 
 | 
   239     addr nDevices, addr devices[0]
 | 
| 
 | 
   240   )
 | 
| 
1489
 | 
   241   for d in devices:
 | 
| 
 | 
   242     var dev = SVkPhysicalDevice(vkPhysicalDevice: d)
 | 
| 
 | 
   243     d.vkGetPhysicalDeviceFeatures(addr dev.vkPhysicalDeviceFeatures)
 | 
| 
 | 
   244     d.vkGetPhysicalDeviceProperties(addr dev.vkPhysicalDeviceProperties)
 | 
| 
 | 
   245 
 | 
| 
1490
 | 
   246     if dev.vkPhysicalDeviceProperties.deviceType notin
 | 
| 
 | 
   247         [VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU]:
 | 
| 
1489
 | 
   248       continue
 | 
| 
 | 
   249     dev.name = $cast[cstring](addr dev.vkPhysicalDeviceProperties.deviceName[0])
 | 
| 
1490
 | 
   250     dev.discreteGPU =
 | 
| 
 | 
   251       dev.vkPhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
 | 
| 
1489
 | 
   252 
 | 
| 
 | 
   253     var memoryProperties: VkPhysicalDeviceMemoryProperties
 | 
| 
 | 
   254     d.vkGetPhysicalDeviceMemoryProperties(addr memoryProperties)
 | 
| 
 | 
   255     for i in 0 ..< memoryProperties.memoryTypeCount:
 | 
| 
 | 
   256       let heapI = memoryProperties.memoryTypes[i].heapIndex
 | 
| 
 | 
   257       dev.memoryTypes.add SVkMemoryType(
 | 
| 
 | 
   258         size: memoryProperties.memoryHeaps[heapI].size.uint64,
 | 
| 
1490
 | 
   259         deviceLocal:
 | 
| 
 | 
   260           VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT in
 | 
| 
 | 
   261           memoryProperties.memoryTypes[i].propertyFlags,
 | 
| 
 | 
   262         hostCached:
 | 
| 
 | 
   263           VK_MEMORY_PROPERTY_HOST_CACHED_BIT in
 | 
| 
 | 
   264           memoryProperties.memoryTypes[i].propertyFlags,
 | 
| 
 | 
   265         hostVisible:
 | 
| 
 | 
   266           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in
 | 
| 
 | 
   267           memoryProperties.memoryTypes[i].propertyFlags,
 | 
| 
 | 
   268         hostCohorent:
 | 
| 
 | 
   269           VK_MEMORY_PROPERTY_HOST_COHERENT_BIT in
 | 
| 
 | 
   270           memoryProperties.memoryTypes[i].propertyFlags,
 | 
| 
1489
 | 
   271       )
 | 
| 
 | 
   272 
 | 
| 
 | 
   273     var familyQueueCount: uint32
 | 
| 
 | 
   274     var vkQueueFamilyProperties: seq[VkQueueFamilyProperties]
 | 
| 
 | 
   275     d.vkGetPhysicalDeviceQueueFamilyProperties(addr familyQueueCount, nil)
 | 
| 
 | 
   276     vkQueueFamilyProperties.setLen(familyQueueCount)
 | 
| 
1490
 | 
   277     d.vkGetPhysicalDeviceQueueFamilyProperties(
 | 
| 
 | 
   278       addr familyQueueCount, addr vkQueueFamilyProperties[0]
 | 
| 
 | 
   279     )
 | 
| 
1489
 | 
   280     dev.queueFamily = high(uint32)
 | 
| 
 | 
   281     for i in 0 ..< familyQueueCount:
 | 
| 
 | 
   282       let hasGraphics = VK_QUEUE_GRAPHICS_BIT in vkQueueFamilyProperties[i].queueFlags
 | 
| 
 | 
   283       let hasCompute = VK_QUEUE_COMPUTE_BIT in vkQueueFamilyProperties[i].queueFlags
 | 
| 
 | 
   284       let hasPresentation = VK_FALSE
 | 
| 
1490
 | 
   285       checkVkResult dev.vkPhysicalDevice.vkGetPhysicalDeviceSurfaceSupportKHR(
 | 
| 
 | 
   286         i, instance.vkSurface, addr hasPresentation
 | 
| 
 | 
   287       )
 | 
| 
1489
 | 
   288 
 | 
| 
 | 
   289       if hasGraphics and hasCompute and bool(hasPresentation):
 | 
| 
 | 
   290         dev.queueFamily = i
 | 
| 
 | 
   291         break
 | 
| 
 | 
   292     if dev.queueFamily == high(uint32):
 | 
| 
1490
 | 
   293       raise newException(
 | 
| 
 | 
   294         Exception,
 | 
| 
 | 
   295         "Did not find queue family with graphics, compute and presentation support!",
 | 
| 
 | 
   296       )
 | 
| 
1489
 | 
   297 
 | 
| 
 | 
   298     result.add dev
 | 
| 
 | 
   299 
 | 
| 
1490
 | 
   300 # =============================================================================
 | 
| 
 | 
   301 # DEVICES =====================================================================
 | 
| 
 | 
   302 # =============================================================================
 | 
| 
 | 
   303 
 | 
| 
 | 
   304 type SVkDevice* = object
 | 
| 
 | 
   305   vkDevice: VkDevice
 | 
| 
 | 
   306   vkQueue: VkQueue
 | 
| 
 | 
   307 
 | 
| 
 | 
   308 proc `=copy`(a: var SVkDevice, b: SVkDevice) {.error.}
 | 
| 
 | 
   309 
 | 
| 
 | 
   310 proc `=destroy`(a: SVkDevice) =
 | 
| 
 | 
   311   debugAssert a.vkDevice.pointer == nil
 | 
| 
 | 
   312   debugAssert a.vkQueue.pointer == nil
 | 
| 
 | 
   313 
 | 
| 
 | 
   314 proc destroy*(a: var SVkDevice) =
 | 
| 
 | 
   315   if a.vkDevice.pointer != nil:
 | 
| 
 | 
   316     vkDestroyDevice(a.vkDevice, nil)
 | 
| 
 | 
   317     a.vkDevice = VkDevice(nil)
 | 
| 
 | 
   318     a.vkQueue = VkQueue(nil)
 | 
| 
 | 
   319 
 | 
| 
 | 
   320 proc svkCreateDevice*(
 | 
| 
 | 
   321     instance: SVkInstance, physicalDevice: SVkPhysicalDevice
 | 
| 
 | 
   322 ): SVkDevice =
 | 
| 
 | 
   323   let
 | 
| 
 | 
   324     priority = cfloat(1)
 | 
| 
 | 
   325     queueInfo = VkDeviceQueueCreateInfo(
 | 
| 
 | 
   326       queueFamilyIndex: physicalDevice.queueFamily,
 | 
| 
 | 
   327       queueCount: 1,
 | 
| 
 | 
   328       pQueuePriorities: addr priority,
 | 
| 
 | 
   329     )
 | 
| 
 | 
   330     deviceExtensions = @["VK_KHR_swapchain"]
 | 
| 
 | 
   331     deviceExtensionsC = allocCStringArray(deviceExtensions)
 | 
| 
 | 
   332     enabledFeatures = VkPhysicalDeviceFeatures(
 | 
| 
 | 
   333       fillModeNonSolid: VK_TRUE,
 | 
| 
 | 
   334       depthClamp: VK_TRUE,
 | 
| 
 | 
   335       wideLines: VK_TRUE,
 | 
| 
 | 
   336       largePoints: VK_TRUE,
 | 
| 
 | 
   337     )
 | 
| 
 | 
   338     vk8bitExt = VkPhysicalDevice8BitStorageFeatures(
 | 
| 
 | 
   339       storageBuffer8BitAccess: VK_TRUE,
 | 
| 
 | 
   340       uniformAndStorageBuffer8BitAccess: VK_TRUE,
 | 
| 
 | 
   341       storagePushConstant8: VK_TRUE,
 | 
| 
 | 
   342     )
 | 
| 
 | 
   343     vk16bitExt = VkPhysicalDevice16BitStorageFeatures(
 | 
| 
 | 
   344       storageBuffer16BitAccess: VK_TRUE,
 | 
| 
 | 
   345       uniformAndStorageBuffer16BitAccess: VK_TRUE,
 | 
| 
 | 
   346       storagePushConstant16: VK_TRUE,
 | 
| 
 | 
   347       storageInputOutput16: VK_FALSE,
 | 
| 
 | 
   348       pNext: addr vk8bitExt,
 | 
| 
 | 
   349     )
 | 
| 
 | 
   350     createDeviceInfo = VkDeviceCreateInfo(
 | 
| 
 | 
   351       queueCreateInfoCount: 1,
 | 
| 
 | 
   352       pQueueCreateInfos: addr queueInfo,
 | 
| 
 | 
   353       enabledLayerCount: 0,
 | 
| 
 | 
   354       ppEnabledLayerNames: nil,
 | 
| 
 | 
   355       enabledExtensionCount: 1,
 | 
| 
 | 
   356       ppEnabledExtensionNames: deviceExtensionsC,
 | 
| 
 | 
   357       pEnabledFeatures: addr enabledFeatures,
 | 
| 
 | 
   358       pNext: addr vk16bitExt,
 | 
| 
 | 
   359     )
 | 
| 
 | 
   360   checkVkResult vkCreateDevice(
 | 
| 
 | 
   361     physicalDevice = physicalDevice.vkPhysicalDevice,
 | 
| 
 | 
   362     pCreateInfo = addr createDeviceInfo,
 | 
| 
 | 
   363     pAllocator = nil,
 | 
| 
 | 
   364     pDevice = addr result.vkDevice,
 | 
| 
 | 
   365   )
 | 
| 
 | 
   366   deviceExtensionsC.deallocCStringArray()
 | 
| 
 | 
   367   result.vkDevice.vkGetDeviceQueue(physicalDevice.queueFamily, 0, addr result.vkQueue)
 | 
| 
 | 
   368 
 | 
| 
 | 
   369 # =============================================================================
 | 
| 
 | 
   370 # PLATFORM UTILS ==============================================================
 | 
| 
 | 
   371 # =============================================================================
 | 
| 
 | 
   372 
 | 
| 
1489
 | 
   373 when defined(windows):
 | 
| 
 | 
   374   proc createWindow(title: string): NativeWindow =
 | 
| 
 | 
   375     result.hInstance = HINSTANCE(GetModuleHandle(nil))
 | 
| 
 | 
   376     var
 | 
| 
 | 
   377       windowClassName = T"EngineWindowClass"
 | 
| 
 | 
   378       windowName = T(title)
 | 
| 
 | 
   379       windowClass = WNDCLASSEX(
 | 
| 
 | 
   380         cbSize: UINT(WNDCLASSEX.sizeof),
 | 
| 
 | 
   381         lpfnWndProc: windowHandler,
 | 
| 
 | 
   382         hInstance: result.hInstance,
 | 
| 
 | 
   383         lpszClassName: windowClassName,
 | 
| 
 | 
   384         hcursor: currentCursor,
 | 
| 
 | 
   385       )
 | 
| 
 | 
   386 
 | 
| 
 | 
   387     if (RegisterClassEx(addr(windowClass)) == 0):
 | 
| 
 | 
   388       raise newException(Exception, "Unable to register window class")
 | 
| 
 | 
   389 
 | 
| 
 | 
   390     result.hwnd = CreateWindowEx(
 | 
| 
 | 
   391       DWORD(0),
 | 
| 
 | 
   392       windowClassName,
 | 
| 
 | 
   393       windowName,
 | 
| 
 | 
   394       DWORD(WS_OVERLAPPEDWINDOW),
 | 
| 
 | 
   395       CW_USEDEFAULT,
 | 
| 
 | 
   396       CW_USEDEFAULT,
 | 
| 
 | 
   397       CW_USEDEFAULT,
 | 
| 
 | 
   398       CW_USEDEFAULT,
 | 
| 
 | 
   399       HMENU(0),
 | 
| 
 | 
   400       HINSTANCE(0),
 | 
| 
 | 
   401       result.hInstance,
 | 
| 
 | 
   402       nil,
 | 
| 
 | 
   403     )
 | 
| 
 | 
   404 
 | 
| 
 | 
   405     result.g_wpPrev.length = UINT(sizeof(WINDOWPLACEMENT))
 | 
| 
 | 
   406     discard result.hwnd.ShowWindow(SW_SHOW)
 | 
| 
 | 
   407     discard result.hwnd.SetForegroundWindow()
 | 
| 
 | 
   408     discard result.hwnd.SetFocus()
 | 
| 
 | 
   409 
 | 
| 
 | 
   410   proc destroyWindow*(window: NativeWindow) =
 | 
| 
 | 
   411     DestroyWindow(window.hwnd)
 | 
| 
 | 
   412 
 | 
| 
 | 
   413 else:
 | 
| 
 | 
   414   import ../semicongine/thirdparty/x11/xkblib
 | 
| 
 | 
   415   import ../semicongine/thirdparty/x11/xutil
 | 
| 
 | 
   416 
 | 
| 
 | 
   417   var deleteMessage* {.hint[GlobalVar]: off.}: x.Atom # one internal use, not serious
 | 
| 
 | 
   418 
 | 
| 
 | 
   419   proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} =
 | 
| 
 | 
   420     logging.error "Xlib: " & $event[]
 | 
| 
 | 
   421 
 | 
| 
 | 
   422   proc createWindow(title: string): NativeWindow =
 | 
| 
 | 
   423     doAssert XInitThreads() != 0
 | 
| 
 | 
   424     let display = XOpenDisplay(nil)
 | 
| 
 | 
   425     if display == nil:
 | 
| 
 | 
   426       quit "Failed to open display"
 | 
| 
 | 
   427     discard XSetErrorHandler(XErrorLogger)
 | 
| 
 | 
   428 
 | 
| 
 | 
   429     let screen = display.XDefaultScreen()
 | 
| 
 | 
   430     let rootWindow = display.XRootWindow(screen)
 | 
| 
 | 
   431     let vis = display.XDefaultVisual(screen)
 | 
| 
 | 
   432     discard display.XkbSetDetectableAutoRepeat(true, nil)
 | 
| 
 | 
   433     var
 | 
| 
 | 
   434       attribs: XWindowAttributes
 | 
| 
 | 
   435       width = cuint(800)
 | 
| 
 | 
   436       height = cuint(600)
 | 
| 
 | 
   437     doAssert display.XGetWindowAttributes(rootWindow, addr(attribs)) != 0
 | 
| 
 | 
   438 
 | 
| 
 | 
   439     var attrs = XSetWindowAttributes(
 | 
| 
 | 
   440       event_mask:
 | 
| 
 | 
   441         FocusChangeMask or KeyPressMask or KeyReleaseMask or ExposureMask or
 | 
| 
1490
 | 
   442         VisibilityChangeMask or StructureNotifyMask or ButtonMotionMask or
 | 
| 
 | 
   443         ButtonPressMask or ButtonReleaseMask
 | 
| 
1489
 | 
   444     )
 | 
| 
 | 
   445     let window = display.XCreateWindow(
 | 
| 
 | 
   446       rootWindow,
 | 
| 
 | 
   447       (attribs.width - cint(width)) div 2,
 | 
| 
 | 
   448       (attribs.height - cint(height)) div 2,
 | 
| 
 | 
   449       width,
 | 
| 
 | 
   450       height,
 | 
| 
 | 
   451       0,
 | 
| 
 | 
   452       display.XDefaultDepth(screen),
 | 
| 
 | 
   453       InputOutput,
 | 
| 
 | 
   454       vis,
 | 
| 
 | 
   455       CWEventMask,
 | 
| 
 | 
   456       addr(attrs),
 | 
| 
 | 
   457     )
 | 
| 
 | 
   458     doAssert display.XSetStandardProperties(window, title, "window", 0, nil, 0, nil) != 0
 | 
| 
 | 
   459     # get an input context, to allow encoding of key-events to characters
 | 
| 
 | 
   460 
 | 
| 
 | 
   461     let im = XOpenIM(display, nil, nil, nil)
 | 
| 
 | 
   462     assert im != nil
 | 
| 
 | 
   463     let ic = im.XCreateIC(XNInputStyle, XIMPreeditNothing or XIMStatusNothing, nil)
 | 
| 
 | 
   464     assert ic != nil
 | 
| 
 | 
   465 
 | 
| 
 | 
   466     doAssert display.XMapWindow(window) != 0
 | 
| 
 | 
   467 
 | 
| 
 | 
   468     deleteMessage = display.XInternAtom("WM_DELETE_WINDOW", XBool(false))
 | 
| 
 | 
   469     doAssert display.XSetWMProtocols(window, addr(deleteMessage), 1) != 0
 | 
| 
 | 
   470 
 | 
| 
 | 
   471     var data = "\0".cstring
 | 
| 
 | 
   472     var pixmap = display.XCreateBitmapFromData(window, data, 1, 1)
 | 
| 
 | 
   473     var color: XColor
 | 
| 
 | 
   474     var empty_cursor =
 | 
| 
 | 
   475       display.XCreatePixmapCursor(pixmap, pixmap, addr(color), addr(color), 0, 0)
 | 
| 
 | 
   476     doAssert display.XFreePixmap(pixmap) != 0
 | 
| 
 | 
   477 
 | 
| 
 | 
   478     discard display.XSync(0)
 | 
| 
 | 
   479 
 | 
| 
 | 
   480     # wait until window is shown
 | 
| 
 | 
   481     var ev: XEvent
 | 
| 
 | 
   482     while ev.theType != MapNotify:
 | 
| 
 | 
   483       discard display.XNextEvent(addr(ev))
 | 
| 
 | 
   484 
 | 
| 
1490
 | 
   485     result =
 | 
| 
 | 
   486       NativeWindow(display: display, window: window, emptyCursor: empty_cursor, ic: ic)
 | 
| 
1489
 | 
   487 
 | 
| 
 | 
   488   proc destroyWindow*(window: NativeWindow) =
 | 
| 
 | 
   489     doAssert XDestroyWindow(window.display, window.window) != 0
 |