Mercurial > games > semicongine
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fuhtark_test/generate_vulkan_api.sh Wed Nov 26 21:36:48 2025 +0700 @@ -0,0 +1,137 @@ +#!/bin/sh -e + +# Variables +BASE_INCLUDE=$( realpath $( dirname $0 ) )"/include" +WIN_INCLUDE=$( realpath $( dirname $0 ) )"/include/winapi" +OUT_BIN=vulkan +OUTFILE=$OUT_BIN.nim +OUTFILE_FUTHARK=$OUT_BIN".gen.nim" + +# Convert C-headers to Nim using Futhark, if not existing yet +NIM_GEN_CODE='import futhark, os +importc: + outputPath "'"$OUTFILE_FUTHARK"'" + define VK_USE_PLATFORM_WIN32_KHR + define VK_USE_PLATFORM_XLIB_KHR + sysPath "'"$BASE_INCLUDE"'" + sysPath "'"$WIN_INCLUDE"'" + path "/usr/include/vulkan" + "vulkan.h"' + +if [ ! -f $OUTFILE_FUTHARK ] ; then + nim c --maxLoopIterationsVM:1000000000 --eval:"$NIM_GEN_CODE" +fi + +cp $OUTFILE_FUTHARK $OUTFILE + +# convert futhark's function definition to function pointers to allow loading functions at runtime +sed -i 's/^ proc \([a-zA-Z]*\*\)/ var \1: proc/' $OUTFILE +sed -i 's/[, ] importc: "vk[a-zA-Z]*"//' $OUTFILE + +# ensure struct-name-definitions are on single line +# needed for correct parsing and setting of sType struct values in the next step +sed -i '/struct_Vk.*inheritable,$/{N;s/\n/ /}' $OUTFILE +sed -i '/struct_Vk.*pure,$/{N;s/\n/ /}' $OUTFILE +sed -i '/struct_Vk.* {\.$/{N;s/\n/ /}' $OUTFILE + +# set default struct values for member "sType" +# not very clean, as we "abuse" the fact that Nim does not differentiate between camel-case and snake-case for identifiers +awk -i inplace '{ + if ( $0 ~ /^ struct_Vk.* = object/ && ! $0 ~ /VkBaseInStructure/ ) { + split($0, arr, "_"); + print $0; + getline; + if ( $0 ~ / sType\*:/ ) { + split($0, arr2, "##"); + print arr2[1] "= VK_STRUCTURE_TYPE_" substr(arr[2], 3) " ##" arr2[2]; + } else { + print; + } + } else { + print; + } +}' $OUTFILE + +# allow to printing Vulkan handles with Nim, as those are using pointer-types +cat $OUTFILE_FUTHARK | awk '/ = ptr struct_/ {print "proc `$`*(val: " $1 "): string = $(cast[int](val))"}' >> $OUTFILE + +# add some helper functions, the vulkan loader and instance creation +cat <<EOF >> $OUTFILE + +import std/dynlib +import std/strutils +import std/logging + +var vkInstance*: VkInstance = VkInstance(nil) +var vkPhysicalDevices: seq[VkPhysicalDevice] + +template checkVkResult*(call: untyped) = + when defined(release): + discard call + else: + # yes, a bit cheap, but this is only for nice debug output + var callstr = astToStr(call).replace("\n", "") + while callstr.find(" ") >= 0: + callstr = callstr.replace(" ", " ") + debug "Calling vulkan: ", callstr + let value = call + if value != VK_SUCCESS: + error "Vulkan error: ", astToStr(call), " returned ", \$value + raise newException( + Exception, "Vulkan error: " & astToStr(call) & " returned " & \$value + ) + +proc loadFunc[T](instance: VkInstance, f: var T, name: string) = + f = cast[T](vkGetInstanceProcAddr(instance, name)) + +proc initVulkan*() = + 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") + + # load function-pointer resolver function + vkGetInstanceProcAddr = cast[typeof(vkGetInstanceProcAddr)](checkedSymAddr(vulkanLib, "vkGetInstanceProcAddr")) + + # need to create an instance before loading other function points + loadFunc(vkInstance, vkCreateInstance, "vkCreateInstance") + let createInfo = VkInstanceCreateInfo( + pNext: nil, + flags: 0, + pApplicationInfo: nil, + enabledLayerCount: 0, + ppEnabledLayerNames: nil, + enabledExtensionCount: 0, + ppEnabledExtensionNames: nil, + ) + checkVkResult vkCreateInstance(addr createInfo, nil, addr vkInstance) + + # load all functions (some might be null, not checking here) +EOF + +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 + +cat <<EOF >> $OUTFILE + + # load and print all found devices + var nPhysicalDevices: uint32 + checkVkResult vkEnumeratePhysicalDevices(vkInstance, addr nPhysicalDevices, nil) + + if nPhysicalDevices > 0: + vkPhysicalDevices.setLen(nPhysicalDevices) + checkVkResult vkEnumeratePhysicalDevices(vkInstance, addr nPhysicalDevices, addr vkPhysicalDevices[0]) + vkPhysicalDevices.setLen(nPhysicalDevices) + echo "physical devices: ", vkPhysicalDevices + +proc destroyVulkan*() = + vkDestroyInstance(vkInstance, nil) + +initVulkan() +EOF + +nim c --run ./$OUTFILE && rm ./$OUT_BIN
