2 # Routines for working with vulkan registry
10 use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
28 my $now = clock_gettime(CLOCK_REALTIME);
32 $now = clock_gettime(CLOCK_REALTIME) - $now;
33 print "$now load registry\n";
35 # build various indices
36 my $data = $self->{data};
37 my $handles = $self->{handles};
38 my $types = $self->{types};
39 my $commands = $self->{commands};
40 my $extensions = $self->{extensions};
41 my $funcpointers = $self->{funcpointers};
43 foreach my $t (keys %{$data}) {
45 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
46 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
47 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
48 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
51 # mark extension functions?
52 foreach my $e (@{$extensions}) {
53 foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
54 my $r = $data->{$name};
58 push @{$r->{extensions}}, $e->{name};
65 sub buildRequirement {
70 my $outconst = $data->{'API Constants'};
71 my $allconst = $vk->{data}->{'API Constants'};
73 # add a couple of constants that the api's dont reference
74 push @{$outconst->{items}}, grep { $_->{name} =~ m/VK_UUID_SIZE/ } @{$allconst->{items}};
76 # Find included types in this requirement
77 foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
78 my $d = $vk->{data}->{$c};
81 if ($d->{category} eq 'enum' && !defined($d->{alias})) {
83 $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
93 foreach my $c (@{$req->{enums}}) {
95 my $d = $data->{$c->{extends}};
97 if (defined($c->{value})) {
98 } elsif (defined($c->{bitpos})) {
99 $c->{value} = "".(1<<$c->{bitpos});
100 } elsif (defined($c->{extnumber})) {
101 $c->{value} = "".(1000000000
102 + 1000 * ($c->{extnumber} - 1)
104 } elsif (defined($c->{offset})) {
105 $c->{value} = $c->{dir}."".(1000000000
106 + 1000 * ($ext->{number} - 1)
108 } elsif (defined($c->{alias})) {
114 push @{$d->{items}}, $c;
115 } elsif ($c->{value}) {
116 if ($c->{value} =~ m/^"/) {
117 push @{$outconst->{items}}, { %$c, type=>'const char *' };
119 push @{$outconst->{items}}, { %$c, type=>'uint32_t' };
121 } elsif (!$c->{alias}) {
122 my @list = grep { $_->{name} eq $c->{name} } @{$allconst->{items}};
123 die "Can't find constant '$c->{name}'".Dumper($c) if ($#list < 0);
124 push @{$outconst->{items}}, @list;
137 map { $versions->{$_} = 1 } @$vers;
138 map { $platform->{$_} = 1 } @$plat;
140 #print Dumper($vk->{features});
142 $data->{'API Constants'} = {
143 category => 'define',
147 foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
148 print "Feature $feature->{name}\n" if ($vk->{sys}->{verbose});
149 foreach my $req (@{$feature->{require}}) {
150 buildRequirement($vk, $data, $req);
154 foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
155 } @{$vk->{extensions}}) {
156 foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
157 @{$extension->{require}}) {
158 print "Extension $extension->{name} $req->{feature}\n" if ($vk->{sys}->{verbose});
159 buildRequirement($vk, $data, $req, $extension);
164 #print Dumper($data);
166 # TODO: need to remove aliases here?
172 my $funcpointers = {};
175 foreach my $t (keys %{$data}) {
177 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
178 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
179 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
180 $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
181 $bitmasks->{$v->{name}} = $v if $v->{category} eq 'bitmask';
182 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
183 $defines->{$v->{name}} = $v if $v->{category} eq 'define';
186 # link enums to their type(s)
187 foreach my $s (values %$bitmasks) {
190 if ($s->{requires}) {
191 $t = $data->{$s->{requires}};
192 while ($t && $t->{alias}) {
193 $t = $data->{$t->{alias}};
197 $t->{fullType} = $s->{baseType};
198 } elsif ($s->{name} =~ m/(.*)Flags([0-9A-Z]*)/o && defined $data->{"$1FlagBits$2"}) {
199 print "> $s->{name} $1FlagBits$2\n";
200 $t = $data->{"$1FlagBits$2"};
201 while ($t && $t->{alias}) {
202 $t = $data->{$t->{alias}};
206 $t->{fullType} = $s->{baseType};
208 $t->{fullType} = 'VkFlags';
211 foreach my $s (values %$enums) {
212 $s->{fullType} = 'VkFlags' if !defined $s->{fullType};
219 commands => $commands,
220 funcpointers => $funcpointers,
222 bitmasks => $bitmasks,
226 # create sizes for every struct of interest
227 foreach my $s (values %$types) {
230 if ($s->{category} eq 'struct') {
231 structSize($vk, $api, $s);
232 } elsif ($s->{category} eq 'union') {
233 unionSize($vk, $api, $s);
243 'void *' => { bitSize => 64, bitAlign => 64 },
244 'int' => { bitSize => 32, bitAlign => 32 },
245 'char' => { bitSize => 8, bitAlign => 8 },
246 'uint8_t' => { bitSize => 8, bitAlign => 8 },
247 'uint16_t' => { bitSize => 16, bitAlign => 16 },
248 'int32_t' => { bitSize => 32, bitAlign => 32 },
249 'uint32_t' => { bitSize => 32, bitAlign => 32 },
250 'int64_t' => { bitSize => 64, bitAlign => 64 },
251 'uint64_t' => { bitSize => 64, bitAlign => 64 },
252 'size_t' => { bitSize => 64, bitAlign => 64 },
253 'float' => { bitSize => 32, bitAlign => 32 },
254 'double' => { bitSize => 64, bitAlign => 64 },
255 'size_t' => { bitSize => 64, bitAlign => 64 },
256 'Window' => { bitSize => 64, bitAlign => 64 },
257 'Display' => { bitSize => 64, bitAlign => 64 },
258 'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
259 'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
260 # 'VkFlags' => { bitSize => 32, bitAlign => 32 },
261 # 'VkFlags64' => { bitSize => 64, bitAlign => 64 },
268 my $t = $api->{data}->{$m->{baseType}};
269 my $nstar = $m->{fullType} =~ tr/*/*/;
270 my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
272 my $info = $typeInfo->{'void *'};
274 # arrays and bitfields
275 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
277 } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
279 } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
280 $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
285 die Dumper($m) if $nstar > 0;
286 $info = { bitSize => $nbits, bitAlign => 1 };
288 $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
291 while ($t->{alias}) {
292 $t = $api->{data}->{$t->{alias}};
295 die Dumper($m) if !defined $t;
297 if ($t->{category} =~ m/enum|bitmask/on) {
299 die Dumper($m) if $nstar > 0;
300 $info = { bitSize => $nbits, bitAlign => 1 };
302 $t = $vk->{data}->{$t->{fullType}};
303 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
305 } elsif ($t->{category} eq 'struct') {
306 $info = structSize($vk, $api, $t) if ($nstar == 0);
307 } elsif ($t->{category} eq 'union') {
308 $info = unionSize($vk, $api, $t) if ($nstar == 0);
309 } elsif ($t->{category} eq 'handle') {
311 } elsif ($t->{category} eq 'basetype') {
312 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
313 } elsif ($t->{category} eq 'funcpointer') {
320 die Dumper($m, $t) if !defined($info);
322 #print Dumper($m, $t, $info);
323 #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
326 return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
333 return ($v + $a - 1) & ~($a - 1);
343 if (!defined($s->{bitSize})) {
344 foreach my $m (@{$s->{items}}) {
346 my $info = memberSize($vk, $api, $m);
348 $bitSize = align($bitSize, $info->{bitAlign});
350 $m->{bitOffset} = $bitSize;
351 $m->{bitSize} = $info->{bitSize};
353 $bitSize = $bitSize + $info->{bitSize};
354 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
357 $bitSize = align($bitSize, $bitAlign);
359 $s->{bitSize} = $bitSize;
360 $s->{bitAlign} = $bitAlign;
362 $bitSize = $s->{bitSize};
363 $bitAlign = $s->{bitAlign};
366 return { bitSize => $bitSize, bitAlign => $bitAlign };
376 if (!defined($s->{bitSize})) {
377 foreach my $m (@{$s->{items}}) {
379 my $info = memberSize($vk, $api, $m);
382 $m->{bitSize} = $info->{bitSize};
384 $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
385 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
388 $bitSize = align($bitSize, $bitAlign);
390 $s->{bitSize} = $bitSize;
391 $s->{bitAlign} = $bitAlign;
393 $bitSize = $s->{bitSize};
394 $bitAlign = $s->{bitAlign};
397 return { bitSize => $bitSize, bitAlign => $bitAlign };
403 my $xml = XML::Parser->new(Style => 'Tree');
404 my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
408 my $root = $doc->[1];
409 my $roota = shift @{$root};
411 my $data = $vk->{data};
412 my $alias = $vk->{alias};
413 my $extensions = $vk->{extensions};
414 my $features = $vk->{features};
416 # This destructively consumes the whole tree so must be one pass
417 while ($#{$root} >= 0) {
418 my $xt = shift @{$root};
419 my $xn = shift @{$root};
423 my $xa = shift @{$xn};
425 if ($xt eq 'types') {
426 while ($#{$xn} >= 0) {
427 my $yt = shift @{$xn};
428 my $yn = shift @{$xn};
430 next if $yt ne 'type';
434 if ($ya->{category} =~ m/struct|union/) {
435 if (!defined($ya->{alias})) {
441 while ($#{$yn} >= 0) {
442 my $mt = shift @{$yn};
443 my $mm = shift @{$yn};
445 push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
448 $data->{$s->{name}} = $s;
450 $alias->{$ya->{name}} = $ya->{alias};
451 $data->{$ya->{name}} = $ya;
453 } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
454 if (!defined($ya->{alias})) {
455 my $info = loadMember($yn);
458 $s->{name} = $info->{name};
459 $s->{type} = $info->{baseType} if defined $info->{baseType};
461 analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
463 $data->{$s->{name}} = $s;
465 $alias->{$ya->{name}} = $ya->{alias};
466 $data->{$ya->{name}} = $ya;
468 } elsif ($ya->{category} eq 'enum') {
469 $data->{$ya->{name}} = $ya;
470 } elsif ($ya->{requires} eq 'vk_platform' || $ya->{name} eq 'int') {
471 # These are just primitive types, not sure what to do with them, could auto-map them to java i suppose
472 $ya->{category} = 'platform';
473 $data->{$ya->{name}} = $ya;
475 #noisy print "Unhandled: $ya->{name}\n";
478 } elsif ($xt eq 'enums') {
479 if ($xa->{type} =~ m/enum|bitmask/o) {
480 # these are forward referenced from <types> block so re-use, or just overwrite?
481 my $e = $data->{$xa->{name}};
483 $e = { category => "enum", name => $xa->{name} } if (!defined($e));
487 while ($#{$xn} >= 0) {
488 my $yt = shift @{$xn};
489 my $yn = shift @{$xn};
491 next if $yt ne 'enum';
493 my $ya = shift @{$yn};
495 #next if $ya->{alias};
497 push @{$e->{items}}, $ya;
500 $data->{$xa->{name}} = $e;
501 } elsif ($xa->{name} eq 'API Constants') {
502 my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
504 $data->{$xa->{name}} = $d;
506 while ($#{$xn} >= 0) {
507 my $yt = shift @{$xn};
508 my $yn = shift @{$xn};
510 next if $yt ne 'enum';
512 my $ya = shift @{$yn};
514 #next if $ya->{alias};
516 push @{$d->{items}}, $ya;
517 $d->{index}->{$ya->{name}} = $ya;
520 } elsif ($xt eq 'commands') {
521 while ($#{$xn} >= 0) {
522 my $yt = shift @{$xn};
523 my $yn = shift @{$xn};
525 next if $yt ne 'command';
527 my $ya = shift @{$yn};
529 if (!defined($ya->{alias})) {
532 $cmd->{category} = 'command';
536 while ($#{$yn} >= 0) {
537 my $zt = shift @{$yn};
538 my $zn = shift @{$yn};
540 if ($zt eq 'proto') {
541 $cmd->{proto} = loadMember($zn);
542 } elsif ($zt eq 'param') {
543 push @{$cmd->{items}}, loadMember($zn);
547 my $name = $cmd->{proto}->{name};
549 # check we parsed it properly
550 if ($cmd->{proto}->{fullType} eq "") {
551 print Dumper([$ya, $yn]);
554 $cmd->{name} = $name;
556 $data->{$name} = $cmd;
558 # want forward ref or not?
559 $alias->{$ya->{name}} = $ya->{alias};
560 $data->{$ya->{name}} = $ya;
563 } elsif ($xt eq 'feature') {
566 $feature->{require} = [];
568 while ($#{$xn} >= 0) {
569 my $yt = shift @{$xn};
570 my $yn = shift @{$xn};
572 next if $yt ne 'require';
574 push @{$feature->{require}}, loadRequire($data, $alias, $yn);
577 push @{$features}, $feature;
578 } elsif ($xt eq 'extensions') {
579 while ($#{$xn} >= 0) {
580 my $yt = shift @{$xn};
581 my $yn = shift @{$xn};
583 next if $yt ne 'extension';
585 my $ext = shift @{$yn};
587 $ext->{require} = [];
589 while ($#{$yn} >= 0) {
590 my $zt = shift @{$yn};
591 my $zn = shift @{$yn};
593 next if $zt ne 'require';
595 push @{$ext->{require}}, loadRequire($data, $alias, $zn);
598 push @{$extensions}, $ext;
601 print "vulkan.pm: Ignore node: $xt\n";
606 # find an object including via alias
613 my $s = $data->{$name};
614 return $s if defined $s;
615 #print "alias $name => $alias->{$name}\n";
616 $name = $alias->{$name};
619 die "No match for type '$name'";
624 my $fullType = shift;
625 my $type = $fullType;
627 $type =~ s/const|\*|\s//gon;
629 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
631 # canonicalise spaces in c type
632 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
633 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
634 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
636 # fix brackets and trailing spaces
637 #$fullType =~ s/\( /(/go;
638 #$fullType =~ s/ \)/)/go;
639 #$fullType =~ s/ \[/[/go;
640 $fullType =~ s/^\s+|\s+$//go;
644 Name => ucfirst($name),
645 fullType => $fullType,
651 # Convert function typedef into function info
652 sub analyseFunctionPointer {
655 if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
657 my @args = split /,/,$2;
659 $s->{proto} = makeParameter('result$', $rt);
662 if ($#args != 0 || $args[0] ne 'void') {
663 foreach my $a (@args) {
664 if (my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o) {
665 push @{$s->{items}}, makeParameter($name, $fullType);
667 die "Unable to parse function pointer argument '$a'\n";
672 die "Unable to parse function pointer prototype '$s->{fullType}'\n";
674 $s->{Name} = $s->{name};
677 delete $s->{baseType};
678 delete $s->{fullType};
683 #my $x = (join '',split('\n',Dumper($nn))); $x =~ s/ +/ /g; print "load: $x\n";
684 my $m = shift @{$nn};
689 while ($#{$nn} >= 0) {
690 my $pt = shift @{$nn};
691 my $pn = shift @{$nn};
695 } elsif ($pt eq 'type') {
696 die if $pn->[1] != 0;
697 $baseType = $pn->[2];
698 $fullType .= $baseType;
699 } elsif ($pt eq 'name') {
700 die if $pn->[1] != 0;
702 } elsif ($pt eq 'enum') {
703 die if $pn->[1] != 0;
704 $fullType .= $pn->[2];
708 $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
709 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
711 # canonicalise spaces in c type
712 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
713 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
714 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
716 # fix brackets and trailing spaces
717 $fullType =~ s/\( /(/go;
718 $fullType =~ s/ \)/)/go;
719 $fullType =~ s/ \[/[/go;
720 $fullType =~ s/^\s+|\s+$//go;
721 $fullType =~ s/ :/:/go;
724 $m->{baseType} = $baseType;
725 $m->{fullType} = $fullType;
734 my $r = shift @{$nn};
740 while ($#{$nn} >= 0) {
741 my $mt = shift @{$nn};
742 my $mn = shift @{$nn};
745 my $ma = shift @{$mn};
746 push @{$r->{types}}, $ma->{name};
747 } elsif ($mt eq 'command') {
748 my $ma = shift @{$mn};
749 push @{$r->{commands}}, $ma->{name};
750 } elsif ($mt eq 'enum') {
751 my $ma = shift @{$mn};
752 push @{$r->{enums}}, $ma;
764 while ($#{$n} >= 0) {
765 my $tag = shift @{$n};
766 my $con = shift @{$n};
769 push @list, [$tag, $con];
778 while ($#{$n} >= 0) {
779 my $tag = shift @{$n};
780 my $con = shift @{$n};