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};
62 # Link up bitmask base types
63 foreach my $s (grep { $_->{category} eq 'enum' } values %{$data}) {
65 my $t = $data->{$s->{requires}};
66 die Dumper($s) if !defined $t;
67 die Dumper($s) if !defined $s->{fullType};
69 $t->{fullType} = $s->{fullType};
70 } elsif ($s->{bitvalues}) {
71 my $t = $data->{$s->{bitvalues}};
72 die Dumper($s) if !defined $t;
73 die Dumper($s) if !defined $s->{fullType};
75 $t->{fullType} = $s->{fullType};
76 } elsif (!defined $s->{fullType}) {
77 $s->{fullType} = 'VkFlags';
84 sub buildRequirement {
89 my $outconst = $data->{'API Constants'};
90 my $allconst = $vk->{data}->{'API Constants'};
92 # Find included types in this requirement
93 foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
94 my $d = $vk->{data}->{$c};
98 if ($d->{category} eq 'enum') {
99 # Copy all aliases across to data
100 while ($d->{alias}) {
101 $data->{$d->{name}} = $d;
102 $d = $vk->{data}->{$d->{alias}};
105 $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
106 $data->{$d->{name}} = $d;
108 $data->{$d->{name}} = $d;
113 category => 'define',
117 foreach my $c (@{$req->{enums}}) {
119 my $d = $data->{$c->{extends}};
121 if (defined($c->{value})) {
122 } elsif (defined($c->{bitpos})) {
123 $c->{value} = "".(1<<$c->{bitpos});
124 } elsif (defined($c->{extnumber})) {
125 $c->{value} = "".(1000000000
126 + 1000 * ($c->{extnumber} - 1)
128 } elsif (defined($c->{offset})) {
129 $c->{value} = $c->{dir}."".(1000000000
130 + 1000 * ($ext->{number} - 1)
132 } elsif (defined($c->{alias})) {
138 push @{$d->{items}}, $c;
139 } elsif ($c->{value}) {
140 if ($c->{value} =~ m/^"/) {
141 if (!defined $outconst->{index}->{$c->{name}}) {
142 my $v = { %$c, type=>'const char *' };
143 push @{$outconst->{items}}, $v;
144 $outconst->{index}->{$v->{name}} = $v;
147 if (!defined $outconst->{index}->{$c->{name}}) {
148 my $v = { %$c, type=>'uint32_t' };
149 push @{$outconst->{items}}, $v;
150 $outconst->{index}->{$v->{name}} = $v;
153 } elsif (!$c->{alias}) {
154 if (!defined $outconst->{index}->{$c->{name}}) {
155 my $v = $allconst->{index}->{$c->{name}};
157 die Dumper($c) if !defined $v;
159 push @{$outconst->{items}}, $v;
160 $outconst->{index}->{$c->{name}} = $v;
166 # Ideally this builds a 'view' of the features
167 # But it doesn't work properly if something is promoted and uses new names
177 map { $versions->{$_} = 1 } @$vers;
178 map { $platform->{$_} = 1 } @$plat;
180 #print Dumper($vk->{features});
182 $data->{'API Constants'} = {
183 name => 'API Constants',
184 category => 'define',
189 # add constants that the api's dont reference (only 1 so far)
190 my $outconst = $data->{'API Constants'};
191 my $allconst = $vk->{data}->{'API Constants'};
192 foreach my $v (map {$allconst->{index}->{$_}} qw(VK_UUID_SIZE)) {
193 push @{$outconst->{items}}, $v;
194 $outconst->{index}->{$v->{name}} = $v;
197 foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
198 print "Feature $feature->{name}\n" if ($vk->{sys}->{verbose});
199 foreach my $req (@{$feature->{require}}) {
200 buildRequirement($vk, $data, $req);
204 foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
205 } @{$vk->{extensions}}) {
206 foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
207 @{$extension->{require}}) {
208 print "Extension $extension->{name} $req->{feature}\n" if ($vk->{sys}->{verbose});
209 buildRequirement($vk, $data, $req, $extension);
214 #print Dumper($data);
216 # TODO: need to remove aliases here?
221 my $funcpointers = {};
224 foreach my $t (keys %{$data}) {
226 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
227 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
228 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
229 $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
230 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
231 $defines->{$v->{name}} = $v if $v->{category} eq 'define';
235 open(my $f, '>', 'features.pm');
236 print $f Dumper($data);
239 open(my $f, '>', 'vk.pm');
240 print $f Dumper($vk);
248 commands => $commands,
249 funcpointers => $funcpointers,
254 # create sizes for every struct of interest
255 foreach my $s (values %$types) {
258 if ($s->{category} eq 'struct') {
259 structSize($vk, $api, $s);
260 } elsif ($s->{category} eq 'union') {
261 unionSize($vk, $api, $s);
271 'void *' => { bitSize => 64, bitAlign => 64 },
272 'int' => { bitSize => 32, bitAlign => 32 },
273 'char' => { bitSize => 8, bitAlign => 8 },
274 'uint8_t' => { bitSize => 8, bitAlign => 8 },
275 'uint16_t' => { bitSize => 16, bitAlign => 16 },
276 'int32_t' => { bitSize => 32, bitAlign => 32 },
277 'uint32_t' => { bitSize => 32, bitAlign => 32 },
278 'int64_t' => { bitSize => 64, bitAlign => 64 },
279 'uint64_t' => { bitSize => 64, bitAlign => 64 },
280 'size_t' => { bitSize => 64, bitAlign => 64 },
281 'float' => { bitSize => 32, bitAlign => 32 },
282 'double' => { bitSize => 64, bitAlign => 64 },
283 'size_t' => { bitSize => 64, bitAlign => 64 },
284 'Window' => { bitSize => 64, bitAlign => 64 },
285 'Display' => { bitSize => 64, bitAlign => 64 },
286 'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
287 'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
288 # 'VkFlags' => { bitSize => 32, bitAlign => 32 },
289 # 'VkFlags64' => { bitSize => 64, bitAlign => 64 },
296 my $t = $api->{data}->{$m->{baseType}};
297 my $nstar = $m->{fullType} =~ tr/*/*/;
298 my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
300 my $info = $typeInfo->{'void *'};
302 # arrays and bitfields
303 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
305 } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
307 } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
308 $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
313 die Dumper($m) if $nstar > 0;
314 $info = { bitSize => $nbits, bitAlign => 1 };
316 $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
319 while ($t->{alias}) {
320 $t = $api->{data}->{$t->{alias}};
323 die Dumper($m) if !defined $t;
325 if ($t->{category} =~ m/enum|bitmask/on) {
327 die Dumper($m) if $nstar > 0;
328 $info = { bitSize => $nbits, bitAlign => 1 };
330 $t = $vk->{data}->{$t->{fullType}};
331 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
333 } elsif ($t->{category} eq 'struct') {
334 $info = structSize($vk, $api, $t) if ($nstar == 0);
335 } elsif ($t->{category} eq 'union') {
336 $info = unionSize($vk, $api, $t) if ($nstar == 0);
337 } elsif ($t->{category} eq 'handle') {
339 } elsif ($t->{category} eq 'basetype') {
340 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
341 } elsif ($t->{category} eq 'funcpointer') {
348 die Dumper($m, $t) if !defined($info);
350 #print Dumper($m, $t, $info);
351 #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
354 return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
361 return ($v + $a - 1) & ~($a - 1);
371 if (!defined($s->{bitSize})) {
372 foreach my $m (@{$s->{items}}) {
374 my $info = memberSize($vk, $api, $m);
376 $bitSize = align($bitSize, $info->{bitAlign});
378 $m->{bitOffset} = $bitSize;
379 $m->{bitSize} = $info->{bitSize};
381 $bitSize = $bitSize + $info->{bitSize};
382 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
385 $bitSize = align($bitSize, $bitAlign);
387 $s->{bitSize} = $bitSize;
388 $s->{bitAlign} = $bitAlign;
390 $bitSize = $s->{bitSize};
391 $bitAlign = $s->{bitAlign};
394 return { bitSize => $bitSize, bitAlign => $bitAlign };
404 if (!defined($s->{bitSize})) {
405 foreach my $m (@{$s->{items}}) {
407 my $info = memberSize($vk, $api, $m);
410 $m->{bitSize} = $info->{bitSize};
412 $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
413 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
416 $bitSize = align($bitSize, $bitAlign);
418 $s->{bitSize} = $bitSize;
419 $s->{bitAlign} = $bitAlign;
421 $bitSize = $s->{bitSize};
422 $bitAlign = $s->{bitAlign};
425 return { bitSize => $bitSize, bitAlign => $bitAlign };
431 my $xml = XML::Parser->new(Style => 'Tree');
432 my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
436 my $root = $doc->[1];
437 my $roota = shift @{$root};
439 my $data = $vk->{data};
440 my $alias = $vk->{alias};
441 my $extensions = $vk->{extensions};
442 my $features = $vk->{features};
444 # This destructively consumes the whole tree so must be one pass
445 while ($#{$root} >= 0) {
446 my $xt = shift @{$root};
447 my $xn = shift @{$root};
451 my $xa = shift @{$xn};
453 if ($xt eq 'types') {
454 while ($#{$xn} >= 0) {
455 my $yt = shift @{$xn};
456 my $yn = shift @{$xn};
458 next if $yt ne 'type';
462 if ($ya->{category} =~ m/struct|union/) {
463 if (!defined($ya->{alias})) {
469 while ($#{$yn} >= 0) {
470 my $mt = shift @{$yn};
471 my $mm = shift @{$yn};
473 push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
476 $data->{$s->{name}} = $s;
478 $alias->{$ya->{name}} = $ya->{alias};
479 $data->{$ya->{name}} = $ya;
481 } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
482 if (!defined($ya->{alias})) {
483 my $info = loadMember($yn);
486 $s->{name} = $info->{name};
487 $s->{type} = $info->{baseType} if defined $info->{baseType};
489 $s->{category} = 'enum' if $s->{category} eq 'bitmask';
490 analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
492 $data->{$s->{name}} = $s;
494 $ya->{category} = 'enum' if $ya->{category} eq 'bitmask';
495 $alias->{$ya->{name}} = $ya->{alias};
496 $data->{$ya->{name}} = $ya;
498 } elsif ($ya->{category} eq 'enum') {
499 $data->{$ya->{name}} = $ya;
500 } elsif ($ya->{requires} eq 'vk_platform' || $ya->{name} eq 'int') {
501 # These are just primitive types, not sure what to do with them, could auto-map them to java i suppose
502 $ya->{category} = 'platform';
503 $data->{$ya->{name}} = $ya;
505 #noisy print "Unhandled: $ya->{name}\n";
508 } elsif ($xt eq 'enums') {
509 if ($xa->{type} =~ m/enum|bitmask/o) {
510 #print "enum: $xa->{name}\n";
511 # these are forward referenced from <types> block so re-use, or just overwrite?
512 my $e = $data->{$xa->{name}};
514 $e = { %{$xa}, category => "enum" } if (!defined($e));
517 while ($#{$xn} >= 0) {
518 my $yt = shift @{$xn};
519 my $yn = shift @{$xn};
521 next if $yt ne 'enum';
523 my $ya = shift @{$yn};
525 #next if $ya->{alias};
527 push @{$e->{items}}, $ya;
530 $data->{$e->{name}} = $e;
531 } elsif ($xa->{name} eq 'API Constants') {
532 my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
534 $data->{$xa->{name}} = $d;
536 while ($#{$xn} >= 0) {
537 my $yt = shift @{$xn};
538 my $yn = shift @{$xn};
540 next if $yt ne 'enum';
542 my $ya = shift @{$yn};
544 #next if $ya->{alias};
546 push @{$d->{items}}, $ya;
547 $d->{index}->{$ya->{name}} = $ya;
550 } elsif ($xt eq 'commands') {
551 while ($#{$xn} >= 0) {
552 my $yt = shift @{$xn};
553 my $yn = shift @{$xn};
555 next if $yt ne 'command';
557 my $ya = shift @{$yn};
559 if (!defined($ya->{alias})) {
562 $cmd->{category} = 'command';
566 while ($#{$yn} >= 0) {
567 my $zt = shift @{$yn};
568 my $zn = shift @{$yn};
570 if ($zt eq 'proto') {
571 $cmd->{proto} = loadMember($zn);
572 } elsif ($zt eq 'param') {
573 push @{$cmd->{items}}, loadMember($zn);
577 my $name = $cmd->{proto}->{name};
579 # check we parsed it properly
580 if ($cmd->{proto}->{fullType} eq "") {
581 print Dumper([$ya, $yn]);
584 $cmd->{name} = $name;
586 $data->{$name} = $cmd;
588 # want forward ref or not?
589 $alias->{$ya->{name}} = $ya->{alias};
590 $data->{$ya->{name}} = $ya;
593 } elsif ($xt eq 'feature') {
596 $feature->{require} = [];
598 while ($#{$xn} >= 0) {
599 my $yt = shift @{$xn};
600 my $yn = shift @{$xn};
602 next if $yt ne 'require';
604 push @{$feature->{require}}, loadRequire($data, $alias, $yn);
607 push @{$features}, $feature;
608 } elsif ($xt eq 'extensions') {
609 while ($#{$xn} >= 0) {
610 my $yt = shift @{$xn};
611 my $yn = shift @{$xn};
613 next if $yt ne 'extension';
615 my $ext = shift @{$yn};
617 $ext->{require} = [];
619 while ($#{$yn} >= 0) {
620 my $zt = shift @{$yn};
621 my $zn = shift @{$yn};
623 next if $zt ne 'require';
625 push @{$ext->{require}}, loadRequire($data, $alias, $zn);
628 push @{$extensions}, $ext;
631 print "vulkan.pm: Ignore node: $xt\n";
636 # find an object including via alias
643 my $s = $data->{$name};
644 return $s if defined $s;
645 #print "alias $name => $alias->{$name}\n";
646 $name = $alias->{$name};
649 die "No match for type '$name'";
654 my $fullType = shift;
655 my $type = $fullType;
657 $type =~ s/const|\*|\s//gon;
659 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
661 # canonicalise spaces in c type
662 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
663 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
664 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
666 # fix brackets and trailing spaces
667 #$fullType =~ s/\( /(/go;
668 #$fullType =~ s/ \)/)/go;
669 #$fullType =~ s/ \[/[/go;
670 $fullType =~ s/^\s+|\s+$//go;
674 Name => ucfirst($name),
675 fullType => $fullType,
681 # Convert function typedef into function info
682 sub analyseFunctionPointer {
685 if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
687 my @args = split /,/,$2;
689 $s->{proto} = makeParameter('result$', $rt);
692 if ($#args != 0 || $args[0] ne 'void') {
693 foreach my $a (@args) {
694 if (my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o) {
695 push @{$s->{items}}, makeParameter($name, $fullType);
697 die "Unable to parse function pointer argument '$a'\n";
702 die "Unable to parse function pointer prototype '$s->{fullType}'\n";
704 $s->{Name} = $s->{name};
707 delete $s->{baseType};
708 delete $s->{fullType};
713 #my $x = (join '',split('\n',Dumper($nn))); $x =~ s/ +/ /g; print "load: $x\n";
714 my $m = shift @{$nn};
719 while ($#{$nn} >= 0) {
720 my $pt = shift @{$nn};
721 my $pn = shift @{$nn};
725 } elsif ($pt eq 'type') {
726 die if $pn->[1] != 0;
727 $baseType = $pn->[2];
728 $fullType .= $baseType;
729 } elsif ($pt eq 'name') {
730 die if $pn->[1] != 0;
732 } elsif ($pt eq 'enum') {
733 die if $pn->[1] != 0;
734 $fullType .= $pn->[2];
738 $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
739 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
741 # canonicalise spaces in c type
742 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
743 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
744 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
746 # fix brackets and trailing spaces
747 $fullType =~ s/\( /(/go;
748 $fullType =~ s/ \)/)/go;
749 $fullType =~ s/ \[/[/go;
750 $fullType =~ s/^\s+|\s+$//go;
751 $fullType =~ s/ :/:/go;
754 $m->{baseType} = $baseType;
755 $m->{fullType} = $fullType;
764 my $r = shift @{$nn};
770 while ($#{$nn} >= 0) {
771 my $mt = shift @{$nn};
772 my $mn = shift @{$nn};
775 my $ma = shift @{$mn};
776 push @{$r->{types}}, $ma->{name};
777 } elsif ($mt eq 'command') {
778 my $ma = shift @{$mn};
779 push @{$r->{commands}}, $ma->{name};
780 } elsif ($mt eq 'enum') {
781 my $ma = shift @{$mn};
782 push @{$r->{enums}}, $ma;
794 while ($#{$n} >= 0) {
795 my $tag = shift @{$n};
796 my $con = shift @{$n};
799 push @list, [$tag, $con];
808 while ($#{$n} >= 0) {
809 my $tag = shift @{$n};
810 my $con = shift @{$n};