Mercurial > games > semicongine
comparison fuhtark_test/generate_vulkan_api.sh @ 1500:91c8c3b7cbf0 main
add: futhark tests for generating vulkan api
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 26 Nov 2025 21:36:48 +0700 |
| parents | |
| children | f40d9d814c08 |
comparison
equal
deleted
inserted
replaced
| 1499:1f58458b7ef7 | 1500:91c8c3b7cbf0 |
|---|---|
| 1 #!/bin/sh -e | |
| 2 | |
| 3 # Variables | |
| 4 BASE_INCLUDE=$( realpath $( dirname $0 ) )"/include" | |
| 5 WIN_INCLUDE=$( realpath $( dirname $0 ) )"/include/winapi" | |
| 6 OUT_BIN=vulkan | |
| 7 OUTFILE=$OUT_BIN.nim | |
| 8 OUTFILE_FUTHARK=$OUT_BIN".gen.nim" | |
| 9 | |
| 10 # Convert C-headers to Nim using Futhark, if not existing yet | |
| 11 NIM_GEN_CODE='import futhark, os | |
| 12 importc: | |
| 13 outputPath "'"$OUTFILE_FUTHARK"'" | |
| 14 define VK_USE_PLATFORM_WIN32_KHR | |
| 15 define VK_USE_PLATFORM_XLIB_KHR | |
| 16 sysPath "'"$BASE_INCLUDE"'" | |
| 17 sysPath "'"$WIN_INCLUDE"'" | |
| 18 path "/usr/include/vulkan" | |
| 19 "vulkan.h"' | |
| 20 | |
| 21 if [ ! -f $OUTFILE_FUTHARK ] ; then | |
| 22 nim c --maxLoopIterationsVM:1000000000 --eval:"$NIM_GEN_CODE" | |
| 23 fi | |
| 24 | |
| 25 cp $OUTFILE_FUTHARK $OUTFILE | |
| 26 | |
| 27 # convert futhark's function definition to function pointers to allow loading functions at runtime | |
| 28 sed -i 's/^ proc \([a-zA-Z]*\*\)/ var \1: proc/' $OUTFILE | |
| 29 sed -i 's/[, ] importc: "vk[a-zA-Z]*"//' $OUTFILE | |
| 30 | |
| 31 # ensure struct-name-definitions are on single line | |
| 32 # needed for correct parsing and setting of sType struct values in the next step | |
| 33 sed -i '/struct_Vk.*inheritable,$/{N;s/\n/ /}' $OUTFILE | |
| 34 sed -i '/struct_Vk.*pure,$/{N;s/\n/ /}' $OUTFILE | |
| 35 sed -i '/struct_Vk.* {\.$/{N;s/\n/ /}' $OUTFILE | |
| 36 | |
| 37 # set default struct values for member "sType" | |
| 38 # not very clean, as we "abuse" the fact that Nim does not differentiate between camel-case and snake-case for identifiers | |
| 39 awk -i inplace '{ | |
| 40 if ( $0 ~ /^ struct_Vk.* = object/ && ! $0 ~ /VkBaseInStructure/ ) { | |
| 41 split($0, arr, "_"); | |
| 42 print $0; | |
| 43 getline; | |
| 44 if ( $0 ~ / sType\*:/ ) { | |
| 45 split($0, arr2, "##"); | |
| 46 print arr2[1] "= VK_STRUCTURE_TYPE_" substr(arr[2], 3) " ##" arr2[2]; | |
| 47 } else { | |
| 48 print; | |
| 49 } | |
| 50 } else { | |
| 51 print; | |
| 52 } | |
| 53 }' $OUTFILE | |
| 54 | |
| 55 # allow to printing Vulkan handles with Nim, as those are using pointer-types | |
| 56 cat $OUTFILE_FUTHARK | awk '/ = ptr struct_/ {print "proc `$`*(val: " $1 "): string = $(cast[int](val))"}' >> $OUTFILE | |
| 57 | |
| 58 # add some helper functions, the vulkan loader and instance creation | |
| 59 cat <<EOF >> $OUTFILE | |
| 60 | |
| 61 import std/dynlib | |
| 62 import std/strutils | |
| 63 import std/logging | |
| 64 | |
| 65 var vkInstance*: VkInstance = VkInstance(nil) | |
| 66 var vkPhysicalDevices: seq[VkPhysicalDevice] | |
| 67 | |
| 68 template checkVkResult*(call: untyped) = | |
| 69 when defined(release): | |
| 70 discard call | |
| 71 else: | |
| 72 # yes, a bit cheap, but this is only for nice debug output | |
| 73 var callstr = astToStr(call).replace("\n", "") | |
| 74 while callstr.find(" ") >= 0: | |
| 75 callstr = callstr.replace(" ", " ") | |
| 76 debug "Calling vulkan: ", callstr | |
| 77 let value = call | |
| 78 if value != VK_SUCCESS: | |
| 79 error "Vulkan error: ", astToStr(call), " returned ", \$value | |
| 80 raise newException( | |
| 81 Exception, "Vulkan error: " & astToStr(call) & " returned " & \$value | |
| 82 ) | |
| 83 | |
| 84 proc loadFunc[T](instance: VkInstance, f: var T, name: string) = | |
| 85 f = cast[T](vkGetInstanceProcAddr(instance, name)) | |
| 86 | |
| 87 proc initVulkan*() = | |
| 88 if vkGetInstanceProcAddr != nil: | |
| 89 return | |
| 90 | |
| 91 when defined(linux): | |
| 92 let vulkanLib = loadLib("libvulkan.so.1") | |
| 93 when defined(windows): | |
| 94 let vulkanLib = loadLib("vulkan-1.dll") | |
| 95 if vulkanLib == nil: | |
| 96 raise newException(Exception, "Unable to load vulkan library") | |
| 97 | |
| 98 # load function-pointer resolver function | |
| 99 vkGetInstanceProcAddr = cast[typeof(vkGetInstanceProcAddr)](checkedSymAddr(vulkanLib, "vkGetInstanceProcAddr")) | |
| 100 | |
| 101 # need to create an instance before loading other function points | |
| 102 loadFunc(vkInstance, vkCreateInstance, "vkCreateInstance") | |
| 103 let createInfo = VkInstanceCreateInfo( | |
| 104 pNext: nil, | |
| 105 flags: 0, | |
| 106 pApplicationInfo: nil, | |
| 107 enabledLayerCount: 0, | |
| 108 ppEnabledLayerNames: nil, | |
| 109 enabledExtensionCount: 0, | |
| 110 ppEnabledExtensionNames: nil, | |
| 111 ) | |
| 112 checkVkResult vkCreateInstance(addr createInfo, nil, addr vkInstance) | |
| 113 | |
| 114 # load all functions (some might be null, not checking here) | |
| 115 EOF | |
| 116 | |
| 117 cat vulkan.nim | grep -o '^ var vk[a-zA-Z]*' | grep -v vkInstance | grep -v vkGetInstanceProcAddr | grep -v vkCreateInstance | awk '{print " loadFunc(vkInstance, " $2 ", \"" $2 "\")"}' >> $OUTFILE | |
| 118 | |
| 119 cat <<EOF >> $OUTFILE | |
| 120 | |
| 121 # load and print all found devices | |
| 122 var nPhysicalDevices: uint32 | |
| 123 checkVkResult vkEnumeratePhysicalDevices(vkInstance, addr nPhysicalDevices, nil) | |
| 124 | |
| 125 if nPhysicalDevices > 0: | |
| 126 vkPhysicalDevices.setLen(nPhysicalDevices) | |
| 127 checkVkResult vkEnumeratePhysicalDevices(vkInstance, addr nPhysicalDevices, addr vkPhysicalDevices[0]) | |
| 128 vkPhysicalDevices.setLen(nPhysicalDevices) | |
| 129 echo "physical devices: ", vkPhysicalDevices | |
| 130 | |
| 131 proc destroyVulkan*() = | |
| 132 vkDestroyInstance(vkInstance, nil) | |
| 133 | |
| 134 initVulkan() | |
| 135 EOF | |
| 136 | |
| 137 nim c --run ./$OUTFILE && rm ./$OUT_BIN |
