changeset 1486:0ba3f0b2be2e default tip main

did: more
author sam <sam@basx.dev>
date Sat, 03 May 2025 20:16:04 +0700
parents 6e062a84c157
children
files svk/api.nim svk/generate.nim
diffstat 2 files changed, 130 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/svk/api.nim	Sat May 03 01:03:01 2025 +0700
+++ b/svk/api.nim	Sat May 03 20:16:04 2025 +0700
@@ -1,8 +1,11 @@
 import std/strutils
 import std/logging
+import std/os
 
 include ./vkapi
 
+const VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 3, 0)
+
 template checkVkResult*(call: untyped) =
   when defined(release):
     discard call
@@ -21,36 +24,91 @@
 
 type SVkInstance* = object
   vkInstance: VkInstance
+  debugMessenger: VkDebugUtilsMessengerEXT
 
 proc `=copy`(a: var SVkInstance, b: SVkInstance) {.error.}
 
 proc `=destroy`(a: SVkInstance) =
   if a.vkInstance.pointer != nil:
+    if a.debugMessenger.pointer != nil:
+      vkDestroyDebugUtilsMessengerEXT(a.vkInstance, a.debugMessenger, nil)
     a.vkInstance.vkDestroyInstance(nil)
 
+proc debugCallback(
+    messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT,
+    messageTypes: VkDebugUtilsMessageTypeFlagsEXT,
+    pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT,
+    userData: pointer,
+): VkBool32 {.cdecl.} =
+  const LOG_LEVEL_MAPPING = {
+    VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lvlDebug,
+    VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lvlInfo,
+    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lvlWarn,
+    VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lvlError,
+  }.toTable
+  log LOG_LEVEL_MAPPING[messageSeverity]
+  if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
+    # stderr.write getStackTrace()
+    # stderr.writeLine LOG_LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}"
+    let errorMsg = $pCallbackData.pMessage & ": " & getStackTrace()
+    raise newException(Exception, errorMsg)
+  return VK_FALSE
+
 proc svkCreateInstance*(
     applicationName: string,
     enabledLayers: openArray[string] = [],
-    enabledExtensions: openArray[string] = [],
+    enabledExtensions: openArray[string] =
+      if defined(release):
+        @["VK_KHR_surface"]
+      else:
+        @["VK_KHR_surface", "VK_EXT_debug_utils"],
     engineName = "semicongine",
-    majorVersion = 1'u32,
-    minorVersion = 3'u32,
+    withSwapchain = true,
 ): SVkInstance =
+  putEnv("VK_LOADER_LAYERS_ENABLE", "*validation")
+  putEnv(
+    "VK_LAYER_ENABLES",
+    "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",
+  )
+  initVulkanLoader()
+
   let
     appinfo = VkApplicationInfo(
       pApplicationName: applicationName,
       pEngineName: engineName,
-      apiVersion: VK_MAKE_API_VERSION(0, majorVersion, minorVersion, 0),
+      apiVersion: VULKAN_VERSION,
     )
-    layersC = enabledLayers.allocCStringArray()
-    extensionsC = enabledLayers.allocCStringArray()
+    enabledLayersC = allocCStringArray(enabledLayers)
+    enabledExtensionsC = allocCStringArray(enabledExtensions)
     createinfo = VkInstanceCreateInfo(
       pApplicationInfo: addr appinfo,
       enabledLayerCount: enabledLayers.len.uint32,
-      ppEnabledLayerNames: layersC,
+      ppEnabledLayerNames: enabledLayersC,
       enabledExtensionCount: enabledExtensions.len.uint32,
-      ppEnabledExtensionNames: extensionsC,
+      ppEnabledExtensionNames: enabledExtensionsC,
     )
+  # this one we will load manually
   checkVkResult vkCreateInstance(addr createinfo, nil, addr result.vkInstance)
-  layersC.deallocCStringArray()
-  extensionsC.deallocCStringArray()
+
+  enabledLayersC.deallocCStringArray()
+  enabledExtensionsC.deallocCStringArray()
+
+  load_VK_VERSION_1_0(result.vkInstance)
+  load_VK_VERSION_1_1(result.vkInstance)
+  load_VK_VERSION_1_2(result.vkInstance)
+  load_VK_VERSION_1_3(result.vkInstance)
+
+  for extension in enabledExtensions:
+    loadExtension(result.vkInstance, extension)
+  if withSwapchain:
+    load_VK_KHR_swapchain(result.vkInstance)
+
+  when not defined(release):
+    var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT(
+      messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT.items.toSeq.toBits,
+      messageType: VkDebugUtilsMessageTypeFlagBitsEXT.items.toSeq.toBits,
+      pfnUserCallback: debugCallback,
+    )
+    checkVkResult vkCreateDebugUtilsMessengerEXT(
+      result.vkInstance, addr debugMessengerCreateInfo, nil, addr result.debugMessenger
+    )
--- a/svk/generate.nim	Sat May 03 01:03:01 2025 +0700
+++ b/svk/generate.nim	Sat May 03 20:16:04 2025 +0700
@@ -213,8 +213,15 @@
     if extendenum.attr("extends") != "":
       enums[extendenum.attr("extends")].addValue(extendenum)
 
+var extensionLoaders: seq[(string, seq[string])]
+
 for extension in extensions.findAll("extension"):
   let extNum = extension.attr("number")
+  extensionLoaders.add (extension.attr("name"), newSeq[string]())
+  for c in extension.findAll("command"):
+    if "Video" notin c.attr("name"):
+      extensionLoaders[^1][1].add c.attr("name")
+
   for extendenum in extension.findAll("enum"):
     if extendenum.attr("extends") != "":
       if extendenum.attr("extnumber") == "":
@@ -229,6 +236,8 @@
 outFile.writeLine """
 
 import std/dynlib
+import std/strutils
+import std/tables
 
 import ../semicongine/thirdparty/winim/winim/inc/winbase
 import ../semicongine/thirdparty/winim/winim/inc/windef
@@ -311,7 +320,10 @@
     value = value[0 ..^ 4] & "'u64"
   if value[0] == '~':
     value = "not " & value[1 ..^ 1]
-  outFile.writeLine &"  {c.name}*: {c.datatype} = {value}"
+  if c.name in ["VK_TRUE", "VK_FALSE"]:
+    outFile.writeLine &"  {c.name}*: VkBool32 = VkBool32({value})"
+  else:
+    outFile.writeLine &"  {c.name}*: {c.datatype} = {value}"
 outFile.writeLine ""
 
 # generate enums ===============================================================================
@@ -335,6 +347,7 @@
 outFile.writeLine ""
 
 # generate types ===============================================================================
+var stringConverters: seq[string]
 for t in types:
   let category = t.attr("category")
   let tName = t.attr("name")
@@ -357,13 +370,14 @@
     outFile.writeLine &"  {tName}* = {a}"
   elif category == "bitmask":
     if t.len > 0 and t[0].text.startsWith("typedef"):
-      outFile.writeLine &"  {t[2][0].text}* = distinct {t[1][0].text}"
+      outFile.writeLine &"  {t[2][0].text.strip()}* = distinct {t[1][0].text.strip()}"
   elif category == "union":
     outFile.writeLine &"  {tName}* {{.union.}} = object"
     for member in t.findAll("member"):
       outFile.writeLine &"    {member.memberDecl()}"
   elif category == "handle":
-    outFile.writeLine &"  {t[2][0].text} = distinct pointer"
+    outFile.writeLine &"  {t[2][0].text.strip()} = distinct pointer"
+    stringConverters.add t[2][0].text.strip()
   elif category == "struct":
     outFile.writeLine &"  {tName}* = object"
     for member in t.findAll("member"):
@@ -451,27 +465,37 @@
   outFile.write " {.stdcall.}\n"
 
 outFile.write """
-when defined(linux):
-  let vulkanLib = loadLib("libvulkan.so.1")
-when defined(windows):
-  let vulkanLib = loadLib("vulkan-1.dll")
-if vulkanLib == nil:
-  raise newException(Exception, "Unable to load vulkan library")
-
-vkGetInstanceProcAddr = cast[proc(instance: VkInstance, pName: cstring, ): PFN_vkVoidFunction {.stdcall.}](checkedSymAddr(vulkanLib, "vkGetInstanceProcAddr"))
 
 proc loadFunc[T](instance: VkInstance, f: var T, name: string) =
   f = cast[T](vkGetInstanceProcAddr(instance, name))
 
+proc initVulkanLoader*() =
+  if vkGetInstanceProcAddr != nil:
+    return
+
+  when defined(linux):
+    let vulkanLib = loadLib("libvulkan.so.1")
+  when defined(windows):
+    let vulkanLib = loadLib("vulkan-1.dll")
+  if vulkanLib == nil:
+    raise newException(Exception, "Unable to load vulkan library")
+
+  # init two global functions
+  vkGetInstanceProcAddr = cast[proc(instance: VkInstance, pName: cstring, ): PFN_vkVoidFunction {.stdcall.}](checkedSymAddr(vulkanLib, "vkGetInstanceProcAddr"))
+
+  loadFunc(VkInstance(nil), vkCreateInstance, "vkCreateInstance")
+
 """
 
 for f in features:
   let name = f.attr("name").replace(",", "_")
   if f.attr("struct") != "":
     continue
-  outFile.writeLine &"proc loadFeature_{name}(instance: VkInstance) ="
+  outFile.writeLine &"proc load_{name}(instance: VkInstance) ="
   var hasEntries = false
   for cmd in f.findAll("command"):
+    if cmd.attr("name") == "vkCreateInstance":
+      continue
     hasEntries = true
     let cName = cmd.attr("name")
     outFile.writeLine &"  loadFunc(instance, {cName}, \"{cName}\")"
@@ -479,6 +503,32 @@
     outFile.writeLine "  discard"
   outFile.writeLine ""
 
+for (extName, commands) in extensionLoaders:
+  outFile.writeLine &"proc load_{extName}(instance: VkInstance) ="
+  for c in commands:
+    outFile.writeLine &"  loadFunc(instance, {c}, \"{c}\")"
+  if commands.len == 0:
+    outFile.writeLine &"  discard"
+outFile.writeLine ""
+
+outFile.writeLine "const EXTENSION_LOADERS = {"
+for (extName, commands) in extensionLoaders:
+  outFile.writeLine &"  \"{extName}\": load_{extName},"
+outFile.writeLine "}.toTable"
+outFile.writeLine ""
+
+outFile.writeLine "proc loadExtension*(instance: VkInstance, name: string) ="
+outFile.writeLine "  assert name in EXTENSION_LOADERS"
+outFile.writeLine "  EXTENSION_LOADERS[name](instance)"
+outFile.writeLine ""
+
+for strCon in stringConverters:
+  outFile.writeLine &"""proc `$`*(v: {strCon}): string = "0x" & cast[uint](v).toHex()"""
+outFile.writeLine ""
+
+# we preload the vkCreateInstance function, so we can create an instance
+outFile.writeLine ""
+
 outFile.close()
 
 assert execCmd("nim c " & outPath) == 0