Skip to content

Commit f72b4f7

Browse files
committed
VulkanHeaders generator: generate sType as const with default value
Also add a script to generate all the headers we need along with function loading.
1 parent 0308f7b commit f72b4f7

8 files changed

Lines changed: 297 additions & 2 deletions

File tree

libs/VulkanHeaders/GenerateAll.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# =============================================================================
2+
# Daemon-vulkan BSD Source Code
3+
# Copyright (c) 2025-2026 Reaper
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
# * Redistributions of source code must retain the above copyright
9+
# notice, this list of conditions and the following disclaimer.
10+
# * Redistributions in binary form must reproduce the above copyright
11+
# notice, this list of conditions and the following disclaimer in the
12+
# documentation and/or other materials provided with the distribution.
13+
# * Neither the name of the Reaper nor the
14+
# names of its contributors may be used to endorse or promote products
15+
# derived from this software without specific prior written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
18+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
# DISCLAIMED. IN NO EVENT SHALL REAPER BE LIABLE FOR ANY
21+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
# =============================================================================
28+
29+
from os import system, remove
30+
from sys import executable
31+
from subprocess import run
32+
33+
headers = [
34+
( "vulkan_core", "w", "" ),
35+
( "vulkan_beta", "a", "VK_ENABLE_BETA_EXTENSIONS" ),
36+
( "vulkan_win32", "a", "VK_USE_PLATFORM_WIN32_KHR" ),
37+
( "vulkan_wayland", "a", "VK_USE_PLATFORM_WAYLAND_KHR" ),
38+
( "vulkan_xlib", "a", "VK_USE_PLATFORM_XLIB_KHR" ),
39+
( "vulkan_xlib_xrandr", "a", "VK_USE_PLATFORM_XLIB_XRANDR_EXT" )
40+
]
41+
42+
with open( "FunctionDecls.h", "w" ) as f:
43+
with open( "FunctionLoaderInstance.cpp", "w" ) as f1:
44+
with open( "FunctionLoaderDevice.cpp", "w" ) as f2:
45+
print( "" )
46+
47+
vulkanLoaderPath = "../../src/engine/renderer-vulkan/VulkanLoader/"
48+
49+
for header in headers:
50+
if header[2]:
51+
run( [executable, "genvk.py", "-o", vulkanLoaderPath + "vulkan/", "-apiname", "vulkan", "-mode", header[1],
52+
"-define", header[2], header[0] + ".h"], check = True )
53+
else:
54+
run( [executable, "genvk.py", "-o", vulkanLoaderPath + "vulkan/", "-apiname", "vulkan", "-mode", header[1],
55+
header[0] + ".h"], check = True )
56+
57+
with open( "FunctionDecls.h", "r" ) as inp:
58+
with open( vulkanLoaderPath + "Vulkan.h", "w" ) as out:
59+
out.write( inp.read() )
60+
out.write( '#endif // VULKAN_LOADER_H' )
61+
62+
with open( 'VulkanLoadFunctions.cpp', mode = 'r', encoding = 'utf-8', newline = '\n' ) as inp:
63+
functionLoadStart = inp.read()
64+
65+
with open( "FunctionLoaderInstance.cpp", "r" ) as inp:
66+
with open( "FunctionLoaderDevice.cpp", "r" ) as inp2:
67+
with open( vulkanLoaderPath + "VulkanLoadFunctions.cpp", "w" ) as out:
68+
out.write( functionLoadStart )
69+
out.write( '\n\nvoid VulkanLoadInstanceFunctions( VkInstance instance ) {\n' )
70+
out.write( inp.read() )
71+
out.write( '}\n\n' )
72+
out.write( 'void VulkanLoadDeviceFunctions( VkDevice device ) {\n' )
73+
out.write( inp2.read() )
74+
out.write( '}' )
75+
76+
remove( "FunctionDecls.h" )
77+
remove( "FunctionLoaderInstance.cpp" )
78+
remove( "FunctionLoaderDevice.cpp" )

libs/VulkanHeaders/Globals.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# =============================================================================
2+
# Daemon-vulkan BSD Source Code
3+
# Copyright (c) 2025-2026 Reaper
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
# * Redistributions of source code must retain the above copyright
9+
# notice, this list of conditions and the following disclaimer.
10+
# * Redistributions in binary form must reproduce the above copyright
11+
# notice, this list of conditions and the following disclaimer in the
12+
# documentation and/or other materials provided with the distribution.
13+
# * Neither the name of the Reaper nor the
14+
# names of its contributors may be used to endorse or promote products
15+
# derived from this software without specific prior written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
18+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
# DISCLAIMED. IN NO EVENT SHALL REAPER BE LIABLE FOR ANY
21+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
# =============================================================================
28+
29+
def init():
30+
global headerText
31+
global functionDefinitionsText
32+
33+
global functionLoadInstanceText
34+
global functionLoadDeviceText
35+
36+
headerText = ''
37+
functionDefinitionsText = ''
38+
39+
functionLoadInstanceText = ''
40+
functionLoadDeviceText = ''

libs/VulkanHeaders/Vulkan.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Auto-generated, do not modify
2+
3+
#include "Vulkan.h"
4+

libs/VulkanHeaders/Vulkan.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Auto-generated, do not modify
2+
3+
#ifndef VULKAN_LOADER_H
4+
#define VULKAN_LOADER_H
5+
6+
#ifdef _MSC_VER
7+
#define VK_USE_PLATFORM_WIN32_KHR
8+
#endif
9+
10+
#define VK_ENABLE_BETA_EXTENSIONS
11+
12+
#include "vulkan/vulkan.h"
13+
#include "vulkan/vk_enum_string_helper.h"
14+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Auto-generated, do not modify
2+
3+
#ifdef _MSC_VER
4+
#include <windows.h>
5+
#else
6+
#include <dlfcn.h>
7+
#endif
8+
9+
#include "Vulkan.h"
10+
11+
#include "VulkanLoadFunctions.h"
12+
13+
#ifdef _MSC_VER
14+
HMODULE libVulkan;
15+
#else
16+
void* libVulkan;
17+
#endif
18+
19+
void VulkanLoaderInit() {
20+
#ifdef _MSC_VER
21+
libVulkan = LoadLibrary( "vulkan-1.dll" );
22+
vkGetInstanceProcAddr = ( PFN_vkGetInstanceProcAddr ) GetProcAddress( libVulkan, "vkGetInstanceProcAddr" );
23+
#else
24+
libVulkan = dlopen( "libvulkan.so", RTLD_NOW );
25+
vkGetInstanceProcAddr = dlsym( libVulkan, "vkGetInstanceProcAddr" );
26+
#endif
27+
28+
vkEnumerateInstanceVersion = ( PFN_vkEnumerateInstanceVersion ) vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceVersion" );
29+
30+
vkEnumerateInstanceExtensionProperties = ( PFN_vkEnumerateInstanceExtensionProperties ) vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceExtensionProperties" );
31+
32+
vkEnumerateInstanceLayerProperties = ( PFN_vkEnumerateInstanceLayerProperties ) vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceLayerProperties" );
33+
34+
vkCreateInstance = ( PFN_vkCreateInstance ) vkGetInstanceProcAddr( nullptr, "vkCreateInstance" );
35+
}
36+
37+
void VulkanLoaderFree() {
38+
#ifdef _MSC_VER
39+
FreeLibrary( libVulkan );
40+
#else
41+
dlclose( libVulkan );
42+
#endif
43+
}

libs/VulkanHeaders/generator.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from spec_tools.util import getElemName, getElemType
2323

24+
import Globals
2425

2526
def write(*args, **kwargs):
2627
file = kwargs.pop('file', sys.stdout)
@@ -1124,6 +1125,14 @@ def makeCParamDecl(self, param, aligncol):
11241125
text = noneStr(elem.text)
11251126
tail = noneStr(elem.tail)
11261127

1128+
if text == "sType":
1129+
sType = param.get( "values" )
1130+
1131+
if sType:
1132+
text += " = " + sType
1133+
elif text == "VkStructureType":
1134+
text = "const VkStructureType"
1135+
11271136
if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
11281137
# OpenXR-specific macro insertion - but not in apiinc for the spec
11291138
tail = self.genOpts.conventions.make_voidpointer_alias(tail)
@@ -1173,6 +1182,9 @@ def getCParamTypeLength(self, param):
11731182
for elem in param:
11741183
text = noneStr(elem.text)
11751184
tail = noneStr(elem.tail)
1185+
1186+
if text == "VkStructureType":
1187+
text = "const VkStructureType"
11761188

11771189
if self.should_insert_may_alias_macro and self.genOpts.conventions.is_voidpointer_alias(elem.tag, text, tail):
11781190
# OpenXR-specific macro insertion
@@ -1351,14 +1363,27 @@ def makeCDecls(self, cmd):
13511363
# Leading text
13521364
pdecl += noneStr(proto.text)
13531365
tdecl += noneStr(proto.text)
1366+
13541367
# For each child element, if it is a <name> wrap in appropriate
13551368
# declaration. Otherwise append its contents and tail contents.
1369+
functionName = None
1370+
deviceFunction = True
13561371
for elem in proto:
13571372
text = noneStr(elem.text)
13581373
tail = noneStr(elem.tail)
1374+
13591375
if elem.tag == 'name':
13601376
pdecl += self.makeProtoName(text, tail)
13611377
tdecl += self.makeTypedefName(text, tail)
1378+
1379+
Globals.headerText += 'extern PFN_' + text + ' ' + text + ';\n\n'
1380+
Globals.functionDefinitionsText += 'PFN_' + text + ' ' + text + ';\n\n'
1381+
1382+
if not text.endswith( ( 'vkGetInstanceProcAddr', 'vkEnumerateInstanceVersion', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateInstanceLayerProperties', 'vkCreateInstance', 'vkDestroyInstance' ) ):
1383+
functionName = text
1384+
1385+
if text == 'vkGetDeviceProcAddr':
1386+
deviceFunction = False
13621387
else:
13631388
pdecl += text + tail
13641389
tdecl += text + tail
@@ -1376,6 +1401,22 @@ def makeCDecls(self, cmd):
13761401
# self.indentFuncPointer
13771402
# self.alignFuncParam
13781403
n = len(params)
1404+
1405+
if n > 0 and functionName:
1406+
for p in params:
1407+
for elem in p:
1408+
if deviceFunction and noneStr( elem.text ).endswith( ( 'VkInstance', 'VkPhysicalDevice', 'VkPhysicalDeviceGroup' ) ):
1409+
deviceFunction = False
1410+
break
1411+
1412+
if not deviceFunction:
1413+
break
1414+
1415+
if deviceFunction:
1416+
Globals.functionLoadDeviceText += '\t' + functionName + ' = ( PFN_' + functionName + ' ) vkGetDeviceProcAddr( device, "' + functionName + '" );\n\n'
1417+
else:
1418+
Globals.functionLoadInstanceText += '\t' + functionName + ' = ( PFN_' + functionName + ' ) vkGetInstanceProcAddr( instance, "' + functionName + '" );\n\n'
1419+
13791420
# Indented parameters
13801421
if n > 0:
13811422
indentdecl = '(\n'

libs/VulkanHeaders/genvk.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,10 @@ def genTarget(args):
11051105
help='generate MISRA C++-friendly headers')
11061106
parser.add_argument('--iscts', action='store_true', dest='isCTS',
11071107
help='Specify if this should generate CTS compatible code')
1108+
parser.add_argument('-mode', action='store',
1109+
help='w - write, a - append')
1110+
parser.add_argument('-define', action='store',
1111+
help='Pe-processor define to use')
11081112

11091113
args = parser.parse_args()
11101114

@@ -1148,12 +1152,16 @@ def genTarget(args):
11481152
logDiag('* Dumping registry to regdump.txt')
11491153
reg.dumpReg(filehandle=open('regdump.txt', 'w', encoding='utf-8'))
11501154

1155+
if not args.mode in ( 'w', 'a' ):
1156+
print( 'Unknown mode: ' + args.mode + '\nAvailable modes: w, a' )
1157+
exit( -1 )
1158+
11511159
# Finally, use the output generator to create the requested target
11521160
if args.debug:
11531161
pdb.run('reg.apiGen()')
11541162
else:
11551163
startTimer(args.time)
1156-
reg.apiGen()
1164+
reg.apiGen( args.registry.removesuffix( 'vk.xml' ), args.directory, args.mode, args.define )
11571165
endTimer(args.time, f"* Time to generate {options.filename} =")
11581166

11591167
if not args.quiet:

libs/VulkanHeaders/reg.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from generator import GeneratorOptions, OutputGenerator, noneStr, write
1616
from apiconventions import APIConventions
1717

18+
import Globals
19+
1820
def apiNameMatch(str, supported):
1921
"""Return whether a required api name matches a pattern specified for an
2022
XML <feature> 'api' attribute or <extension> 'supported' attribute.
@@ -1656,7 +1658,7 @@ def tagValidExtensionStructs(self):
16561658
for parent in self.validextensionstructs:
16571659
self.validextensionstructs[parent].sort()
16581660

1659-
def apiGen(self):
1661+
def apiGen(self, inputDir, outputDir, mode, define):
16601662
"""Generate interface for specified versions using the current
16611663
generator and generator options"""
16621664

@@ -1844,6 +1846,18 @@ def apiGen(self):
18441846
# generated.
18451847
self.gen.logMsg('diag', 'PASS 3: GENERATE INTERFACES FOR FEATURES')
18461848
self.gen.beginFile(self.genOpts)
1849+
1850+
headerStart = ''
1851+
functionLoadStart = ''
1852+
if mode == "w":
1853+
with open( inputDir + 'Vulkan.h', mode = 'r', encoding = 'utf-8', newline = '\n' ) as inp:
1854+
headerStart = inp.read()
1855+
1856+
with open( inputDir + 'VulkanLoadFunctions.cpp', mode = 'r', encoding = 'utf-8', newline = '\n' ) as inp:
1857+
functionLoadStart = inp.read()
1858+
1859+
Globals.init()
1860+
18471861
for f in features:
18481862
self.gen.logMsg('diag', 'PASS 3: Generating interface for',
18491863
f.name)
@@ -1870,6 +1884,59 @@ def apiGen(self):
18701884
for s in self.syncpipelinedict:
18711885
self.generateSyncPipeline(self.syncpipelinedict[s])
18721886
self.gen.endFile()
1887+
1888+
outputDir = outputDir.rstrip( '/' ).rsplit( '/', 1 )[0] + '/'
1889+
1890+
indent = "\t" if define else ""
1891+
1892+
with open( 'FunctionDecls.h', mode = mode, encoding = 'utf-8', newline = '\n' ) as out:
1893+
if define:
1894+
out.write( "#if defined( " + define + " )\n" )
1895+
1896+
out.write( headerStart + indent + Globals.headerText )
1897+
1898+
if define:
1899+
out.write( "#endif\n\n" )
1900+
1901+
with open( outputDir + 'Vulkan.cpp', mode = mode, encoding = 'utf-8', newline = '\n' ) as out:
1902+
if mode == "w":
1903+
out.write( '// Auto-generated, do not modify\n\n' )
1904+
out.write( '#include "Vulkan.h"\n\n' )
1905+
1906+
if define:
1907+
out.write( "#if defined( " + define + " )\n" )
1908+
1909+
out.write( indent + Globals.functionDefinitionsText )
1910+
1911+
if define:
1912+
out.write( "#endif\n\n" )
1913+
1914+
#with open( outputDir + 'VulkanLoadFunctions.cpp', mode = mode, encoding = 'utf-8', newline = '\n' ) as out:
1915+
# out.write( functionLoadStart )
1916+
# out.write( '\n\nvoid VulkanLoadInstanceFunctions( VkInstance instance ) {\n' )
1917+
# out.write( Globals.functionLoadInstanceText )
1918+
# out.write( '}\n\n' )
1919+
# out.write( 'void VulkanLoadDeviceFunctions( VkDevice device ) {\n' )
1920+
# out.write( Globals.functionLoadDeviceText )
1921+
# out.write( '}' )
1922+
1923+
with open( 'FunctionLoaderInstance.cpp', mode = mode, encoding = 'utf-8', newline = '\n' ) as out:
1924+
if define:
1925+
out.write( "#if defined( " + define + " )\n" )
1926+
1927+
out.write( Globals.functionLoadInstanceText )
1928+
1929+
if define:
1930+
out.write( "#endif\n\n" )
1931+
1932+
with open( 'FunctionLoaderDevice.cpp', mode = mode, encoding = 'utf-8', newline = '\n' ) as out:
1933+
if define:
1934+
out.write( "#if defined( " + define + " )\n" )
1935+
1936+
out.write( Globals.functionLoadDeviceText )
1937+
1938+
if define:
1939+
out.write( "#endif\n\n" )
18731940

18741941
def apiReset(self):
18751942
"""Reset type/enum/command dictionaries before generating another API.

0 commit comments

Comments
 (0)