Mercurial > games > semicongine
view fuhtark_test/generate_vulkan_api.sh @ 1501:f40d9d814c08 default tip
did: correct vulkan-api generator
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 26 Nov 2025 23:34:29 +0700 |
| parents | 91c8c3b7cbf0 |
| children |
line wrap: on
line source
#!/bin/sh -e # Variables VULKAN_VERSION=1.4.334 HOME_DIR=$( realpath $( dirname $0 ) ) BASE_INCLUDE="$HOME_DIR/include" WIN_INCLUDE="$HOME_DIR/include/winapi" VULKAN_INCLUDE="$HOME_DIR/Vulkan-Headers-$VULKAN_VERSION/include/vulkan" OUT_BIN=vulkan OUTFILE=$OUT_BIN.nim OUTFILE_FUTHARK=$OUT_BIN".gen.nim" # setup # sudo apt install clang libclang-dev # nimble install futhark # rm -rf Vulkan-Headers-$VULKAN_VERSION # curl -L https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$VULKAN_VERSION.tar.gz | tar -xzf - # 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 define VK_ENABLE_BETA_EXTENSIONS sysPath "'"$BASE_INCLUDE"'" sysPath "'"$WIN_INCLUDE"'" path "'"$VULKAN_INCLUDE"'" "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 # VkStructureType_1124073999 = (when declared(VkStructureType): TYPESTRUCTNAME=$( cat $OUTFILE | awk '/enum_VkStructureType\* =/ { print $3; }' ) awk -i inplace '{ if ( $0 ~ /^ struct_Vk.* = object/ && $0 !~ /VkBaseInStructure/ && $0 !~ /VkBaseOutStructure/ ) { split($0, arr, "_"); print $0; getline; if ( $0 ~ / sType\*:/ ) { split($0, arr2, "##"); print " sType*: '$TYPESTRUCTNAME' = '$TYPESTRUCTNAME'.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 import std/envvars 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 hasValidationLayer*(): bool = const val_layer = "VK_LAYER_KHRONOS_validation" var n_layers: uint32 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) if n_layers > 0: var layers = newSeq[VkLayerProperties](n_layers) checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addr layers[0]) for layer in layers: let layerName = \$(cast[cstring](addr layer.layerName[0])) if layerName.startsWith(val_layer): return true return false 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") # and this one, to check which layers are available for instance creation loadFunc(vkInstance, vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties") # extensions var extensions = @["VK_KHR_surface"] when not defined(release): extensions.add "VK_EXT_debug_utils" when defined(windows): extensions.add "VK_KHR_win32_surface" when defined(linux): extensions.add "VK_KHR_xlib_surface" let extensionsC = allocCStringArray(extensions) defer: deallocCStringArray(extensionsC) # layers var layers: seq[string] when not defined(release): if hasValidationLayer(): layers.add "VK_LAYER_KHRONOS_validation" var layersC = allocCStringArray(layers) defer: deallocCStringArray(layersC) 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", ) echo "Use instance extensions: ", extensions echo "Use layers: ", layers let createInfo = VkInstanceCreateInfo( pNext: nil, flags: 0, pApplicationInfo: nil, enabledLayerCount: layers.len.uint32, ppEnabledLayerNames: cast[ptr cstring](layersC), enabledExtensionCount: extensions.len.uint32, ppEnabledExtensionNames: cast[ptr cstring](extensionsC), ) 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
