Skip to content

Commit 99bfb64

Browse files
authored
Direct driver calling for no-op intercepts (#145)
Prior to this PR, layers would always intercept all known Vulkan functions, routing through a dummy pass-through implementation in the common code if per-layer code did not provide a more specialized implementation. This has a runtime cost in terms of additional dispatch indirection. With this PR, layers now only intercept functions that are required to implement the layer. These may be mandatory functions in the common code, or per-layer functions with a <user_tag> specialization. This dispatch optimization is optional and, although on by default, can still be disabled to support exploratory API tracing for tech support investigations. Fixes #14.
1 parent 34fef5a commit 99bfb64

28 files changed

+18188
-6009
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ ignore-patterns=^\.#
6464
# manipulated during runtime and thus existing member attributes cannot be
6565
# deduced by static analysis). It supports qualified module names, as well as
6666
# Unix pattern matching.
67-
ignored-modules=cairo,protos
67+
ignored-modules=cairo,protos,lglpy.timeline.protos.*
6868

6969
# Python code to execute, usually for sys.path manipulation such as
7070
# pygtk.require().

generator/generate_vulkan_common.py

Lines changed: 201 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -508,40 +508,115 @@ def generate_instance_decls(
508508
if plat_define:
509509
lines.append(f'#if defined({plat_define})\n')
510510

511-
# Declare the default implementation
511+
# Explicitly delete the generic primary template
512512
lines.append('/* See Vulkan API for documentation. */')
513-
lines.append('/* Default common code pass-through implementation. */')
514-
decl = f'VKAPI_ATTR {command.rtype} ' \
515-
f'VKAPI_CALL layer_{command.name}_default('
513+
lines.append('/* Delete the generic match-all */')
514+
decl = f'template <typename T>\n' \
515+
f'VKAPI_ATTR {command.rtype} ' \
516+
f'VKAPI_CALL layer_{command.name}('
516517
lines.append(decl)
517518

518519
for i, (ptype, pname, array) in enumerate(command.params):
519520
ending = ','
520521
if i == len(command.params) - 1:
521-
ending = ');'
522+
ending = ') = delete;'
522523
parl = f' {ptype} {pname}{array}{ending}'
523524
lines.append(parl)
524525
lines.append('')
525526

526-
# Define the default tag dispatch handler
527-
lines.append('/* Match-all template to use default implementation. */')
528-
decl = 'template <typename T>'
529-
lines.append(decl)
530-
decl = f'VKAPI_ATTR {command.rtype} VKAPI_CALL layer_{command.name}('
527+
# Define the default_tag template
528+
lines.append('/* Default common code implementation. */')
529+
decl = f'template <>\n' \
530+
f'VKAPI_ATTR {command.rtype} ' \
531+
f'VKAPI_CALL layer_{command.name}<default_tag>('
531532
lines.append(decl)
532533

533534
for i, (ptype, pname, array) in enumerate(command.params):
534535
ending = ','
535536
if i == len(command.params) - 1:
536-
ending = ''
537+
ending = ');'
537538
parl = f' {ptype} {pname}{array}{ending}'
538539
lines.append(parl)
540+
lines.append('')
539541

540-
parmfwd = ', '.join([x[1] for x in command.params])
541-
retfwd = 'return ' if command.rtype != 'void' else ''
542-
lines.append(') {')
543-
lines.append(f' {retfwd}layer_{command.name}_default({parmfwd});')
544-
lines.append('}\n')
542+
if plat_define:
543+
lines.append('#endif\n')
544+
545+
file.write('\n'.join(lines))
546+
file.write('\n')
547+
548+
file.write('// clang-format on\n')
549+
550+
551+
def generate_instance_queries(
552+
file: TextIO, mapping: VersionInfo, commands: list[Command]) -> None:
553+
'''
554+
Generate the instance intercept declarations header.
555+
556+
Args:
557+
file: The file to write.
558+
mapping: The version mapping information for the commands.
559+
commands: The list of commands read from the spec.
560+
'''
561+
# Write the copyright header to the file
562+
write_copyright_header(file)
563+
564+
file.write('#pragma once\n')
565+
file.write('\n')
566+
567+
file.write('// clang-format off\n')
568+
file.write('\n')
569+
570+
file.write('#include <vulkan/vulkan.h>\n')
571+
file.write('\n')
572+
573+
# Create a listing of API versions and API extensions
574+
for command in commands:
575+
if command.dispatch_type != 'instance':
576+
continue
577+
578+
lines = []
579+
assert command.name
580+
581+
plat_define = mapping.get_platform_define(command.name)
582+
if plat_define:
583+
lines.append(f'#if defined({plat_define})\n')
584+
585+
# Define the concept to test if user_tag specialization exists
586+
plist = []
587+
nlist = []
588+
for (ptype, pname, array) in command.params:
589+
plist.append(f'{ptype} {pname}{array}')
590+
nlist.append(pname)
591+
plist_str = ', '.join(plist)
592+
nlist_str = ', '.join(nlist)
593+
594+
lines.append('/* Test for user_tag availability. */')
595+
decl = f'template <typename T>\n' \
596+
f'concept hasLayerPtr_{command.name} = requires(\n' \
597+
f' {plist_str}\n' \
598+
f') {{\n' \
599+
f' layer_{command.name}<T>({nlist_str});\n' \
600+
f'}};'
601+
lines.append(decl)
602+
lines.append('')
603+
604+
# Define the function pointer resolution
605+
lines.append('/* Function pointer resolution. */')
606+
decl = f'constexpr PFN_{command.name} getLayerPtr_{command.name}()\n' \
607+
f'{{\n' \
608+
f' return [] <typename T>\n' \
609+
f' {{\n' \
610+
f' if constexpr(hasLayerPtr_{command.name}<T>)\n' \
611+
f' {{\n' \
612+
f' return layer_{command.name}<T>;\n' \
613+
f' }}\n' \
614+
f'\n' \
615+
f' return layer_{command.name}<default_tag>;\n' \
616+
f' }}.operator()<user_tag>();\n' \
617+
f'}}'
618+
lines.append(decl)
619+
lines.append('')
545620

546621
if plat_define:
547622
lines.append('#endif\n')
@@ -582,8 +657,9 @@ def generate_instance_defs(
582657
lines.append(f'#if defined({plat_define})\n')
583658

584659
lines.append('/* See Vulkan API for documentation. */')
585-
decl = f'VKAPI_ATTR {command.rtype} ' \
586-
f'VKAPI_CALL layer_{command.name}_default('
660+
decl = f'template <>\n' \
661+
f'VKAPI_ATTR {command.rtype} ' \
662+
f'VKAPI_CALL layer_{command.name}<default_tag>('
587663
lines.append(decl)
588664

589665
for i, (ptype, pname, array) in enumerate(command.params):
@@ -691,7 +767,8 @@ def generate_device_decls(
691767
file.write('// clang-format off\n')
692768
file.write('\n')
693769

694-
file.write('#include <vulkan/vulkan.h>\n')
770+
file.write('#include <vulkan/vulkan.h>\n\n')
771+
file.write('#include "framework/utils.hpp"\n')
695772
file.write('\n')
696773

697774
# Create a listing of API versions and API extensions
@@ -706,39 +783,36 @@ def generate_device_decls(
706783
if plat_define:
707784
lines.append(f'#if defined({plat_define})\n')
708785

786+
# Explicitly delete the generic primary template
709787
lines.append('/* See Vulkan API for documentation. */')
710-
lines.append('/* Default common code pass-through implementation. */')
711-
decl = f'VKAPI_ATTR {command.rtype} ' \
712-
f'VKAPI_CALL layer_{command.name}_default('
788+
lines.append('/* Delete the generic match-all */')
789+
decl = f'template <typename T>\n' \
790+
f'VKAPI_ATTR {command.rtype} ' \
791+
f'VKAPI_CALL layer_{command.name}('
713792
lines.append(decl)
714793

715794
for i, (ptype, pname, array) in enumerate(command.params):
716795
ending = ','
717796
if i == len(command.params) - 1:
718-
ending = ');'
797+
ending = ') = delete;'
719798
parl = f' {ptype} {pname}{array}{ending}'
720799
lines.append(parl)
721800
lines.append('')
722801

723-
# Define the default tag dispatch handler
724-
lines.append('/* Match-all template to use default implementation. */')
725-
decl = 'template <typename T>'
726-
lines.append(decl)
727-
decl = f'VKAPI_ATTR {command.rtype} VKAPI_CALL layer_{command.name}('
802+
# Define the default_tag template
803+
lines.append('/* Default common code implementation. */')
804+
decl = f'template <>\n' \
805+
f'VKAPI_ATTR {command.rtype} ' \
806+
f'VKAPI_CALL layer_{command.name}<default_tag>('
728807
lines.append(decl)
729808

730809
for i, (ptype, pname, array) in enumerate(command.params):
731810
ending = ','
732811
if i == len(command.params) - 1:
733-
ending = ''
812+
ending = ');'
734813
parl = f' {ptype} {pname}{array}{ending}'
735814
lines.append(parl)
736-
737-
parmfwd = ', '.join([x[1] for x in command.params])
738-
retfwd = 'return ' if command.rtype != 'void' else ''
739-
lines.append(') {')
740-
lines.append(f' {retfwd}layer_{command.name}_default({parmfwd});')
741-
lines.append('}\n')
815+
lines.append('')
742816

743817
if plat_define:
744818
lines.append('#endif\n')
@@ -779,9 +853,9 @@ def generate_device_defs(
779853
lines.append(f'#if defined({plat_define})\n')
780854

781855
lines.append('/* See Vulkan API for documentation. */')
782-
783-
decl = f'VKAPI_ATTR {command.rtype} ' \
784-
f'VKAPI_CALL layer_{command.name}_default('
856+
decl = f'template <>\n' \
857+
f'VKAPI_ATTR {command.rtype} ' \
858+
f'VKAPI_CALL layer_{command.name}<default_tag>('
785859
lines.append(decl)
786860

787861
for i, (ptype, pname, array) in enumerate(command.params):
@@ -815,6 +889,87 @@ def generate_device_defs(
815889
file.write(data)
816890

817891

892+
def generate_device_queries(
893+
file: TextIO, mapping: VersionInfo, commands: list[Command]) -> None:
894+
'''
895+
Generate the device intercept queries header.
896+
897+
Args:
898+
file: The file to write.
899+
mapping: The version mapping information for the commands.
900+
commands: The list of commands read from the spec.
901+
'''
902+
903+
# Write the copyright header to the file
904+
write_copyright_header(file)
905+
906+
file.write('#pragma once\n')
907+
file.write('\n')
908+
909+
file.write('// clang-format off\n')
910+
file.write('\n')
911+
912+
file.write('#include <vulkan/vulkan.h>\n\n')
913+
file.write('#include "framework/utils.hpp"\n')
914+
file.write('\n')
915+
916+
# Create a listing of API versions and API extensions
917+
for command in commands:
918+
if command.dispatch_type != 'device':
919+
continue
920+
921+
assert command.name
922+
923+
lines = []
924+
plat_define = mapping.get_platform_define(command.name)
925+
if plat_define:
926+
lines.append(f'#if defined({plat_define})\n')
927+
928+
# Define the concept to test if user_tag specialization exists
929+
plist = []
930+
nlist = []
931+
for (ptype, pname, array) in command.params:
932+
plist.append(f'{ptype} {pname}{array}')
933+
nlist.append(pname)
934+
plist_str = ', '.join(plist)
935+
nlist_str = ', '.join(nlist)
936+
937+
lines.append('/* Test for user_tag availability. */')
938+
decl = f'template <typename T>\n' \
939+
f'concept hasLayerPtr_{command.name} = requires(\n' \
940+
f' {plist_str}\n' \
941+
f') {{\n' \
942+
f' layer_{command.name}<T>({nlist_str});\n' \
943+
f'}};'
944+
lines.append(decl)
945+
lines.append('')
946+
947+
# Define the function pointer resolution
948+
lines.append('/* Function pointer resolution. */')
949+
decl = f'constexpr PFN_{command.name} getLayerPtr_{command.name}()\n' \
950+
f'{{\n' \
951+
f' return [] <typename T>\n' \
952+
f' {{\n' \
953+
f' if constexpr(hasLayerPtr_{command.name}<T>)\n' \
954+
f' {{\n' \
955+
f' return layer_{command.name}<T>;\n' \
956+
f' }}\n' \
957+
f'\n' \
958+
f' return layer_{command.name}<default_tag>;\n' \
959+
f' }}.operator()<user_tag>();\n' \
960+
f'}}'
961+
lines.append(decl)
962+
lines.append('')
963+
964+
if plat_define:
965+
lines.append('#endif\n')
966+
967+
file.write('\n'.join(lines))
968+
file.write('\n')
969+
970+
file.write('// clang-format on\n')
971+
972+
818973
def main() -> int:
819974
'''
820975
Tool main function.
@@ -867,6 +1022,10 @@ def main() -> int:
8671022
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
8681023
generate_instance_decls(handle, mapping, commands)
8691024

1025+
outfile = os.path.join(outdir, 'instance_functions_query.hpp')
1026+
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
1027+
generate_instance_queries(handle, mapping, commands)
1028+
8701029
outfile = os.path.join(outdir, 'instance_functions.cpp')
8711030
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
8721031
generate_instance_defs(handle, mapping, commands)
@@ -879,6 +1038,10 @@ def main() -> int:
8791038
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
8801039
generate_device_decls(handle, mapping, commands)
8811040

1041+
outfile = os.path.join(outdir, 'device_functions_query.hpp')
1042+
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
1043+
generate_device_queries(handle, mapping, commands)
1044+
8821045
outfile = os.path.join(outdir, 'device_functions.cpp')
8831046
with open(outfile, 'w', encoding='utf-8', newline='\n') as handle:
8841047
generate_device_defs(handle, mapping, commands)

generator/vk_codegen/device_dispatch_table.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "layer_device_functions.hpp"
1313
#endif
1414

15+
// These must be after the layer_*_functions.hpp includes
16+
#include "framework/device_functions_query.hpp"
17+
1518
/**
1619
* @brief Interception table lookup entry.
1720
*/
@@ -26,9 +29,14 @@ struct DeviceInterceptTableEntry
2629
* @brief The layer function pointer.
2730
*/
2831
PFN_vkVoidFunction function;
32+
33+
/**
34+
* @brief Did the layer provide a specialization?
35+
*/
36+
bool hasLayerSpecialization;
2937
};
3038

31-
#define ENTRY(fnc) { STR(fnc), reinterpret_cast<PFN_vkVoidFunction>(layer_##fnc<user_tag>) }
39+
#define ENTRY(fnc) { STR(fnc), reinterpret_cast<PFN_vkVoidFunction>(getLayerPtr_##fnc()), hasLayerPtr_##fnc<user_tag> }
3240

3341
/**
3442
* @brief The device dispatch table used to call the driver.

generator/vk_codegen/instance_dispatch_table.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include "layer_instance_functions.hpp"
1414
#endif
1515

16+
// These must be after the layer_*_functions.hpp includes
17+
#include "framework/device_functions_query.hpp"
18+
#include "framework/instance_functions_query.hpp"
19+
1620
/**
1721
* @brief Interception table lookup entry.
1822
*/
@@ -27,9 +31,14 @@ struct InstanceInterceptTableEntry
2731
* @brief The layer function pointer.
2832
*/
2933
PFN_vkVoidFunction function;
34+
35+
/**
36+
* @brief Did the layer provide a specialization?
37+
*/
38+
bool hasLayerSpecialization;
3039
};
3140

32-
#define ENTRY(fnc) { STR(fnc), reinterpret_cast<PFN_vkVoidFunction>(layer_##fnc<user_tag>) }
41+
#define ENTRY(fnc) { STR(fnc), reinterpret_cast<PFN_vkVoidFunction>(getLayerPtr_##fnc()), hasLayerPtr_##fnc<user_tag> }
3342

3443
/**
3544
* @brief The instance dispatch table used to call the driver.

0 commit comments

Comments
 (0)