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