2 # Routines for working with vulkan registry
29 # build various indices
30 my $data = $self->{data};
31 my $handles = $self->{handles};
32 my $types = $self->{types};
33 my $commands = $self->{commands};
34 my $extensions = $self->{extensions};
35 my $funcpointers = $self->{funcpointers};
37 foreach my $t (keys %{$data}) {
39 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
40 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
41 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
42 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
45 # mark extension functions?
46 foreach my $e (@{$extensions}) {
47 foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
48 my $r = $data->{$name};
52 push @{$r->{extensions}}, $e->{name};
56 # Link up bitmask base types
57 foreach my $s (grep { $_->{category} eq 'enum' } values %{$data}) {
59 my $t = $data->{$s->{requires}};
60 die Dumper($s) if !defined $t;
61 die Dumper($s) if !defined $s->{fullType};
63 $t->{fullType} = $s->{fullType};
64 } elsif ($s->{bitvalues}) {
65 my $t = $data->{$s->{bitvalues}};
66 die Dumper($s) if !defined $t;
67 die Dumper($s) if !defined $s->{fullType};
69 $t->{fullType} = $s->{fullType};
70 } elsif (!defined $s->{fullType}) {
71 $s->{fullType} = 'VkFlags';
78 sub buildRequirement {
83 my $outconst = $data->{'API Constants'};
84 my $allconst = $vk->{data}->{'API Constants'};
86 # Find included types in this requirement
87 foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
88 my $d = $vk->{data}->{$c};
92 if ($d->{category} eq 'enum') {
93 # Copy all aliases across to data
95 $data->{$d->{name}} = $d;
96 $d = $vk->{data}->{$d->{alias}};
99 $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
100 $data->{$d->{name}} = $d;
102 $data->{$d->{name}} = $d;
107 category => 'define',
111 foreach my $c (@{$req->{enums}}) {
113 my $d = $data->{$c->{extends}};
115 if (defined($c->{value})) {
116 } elsif (defined($c->{bitpos})) {
117 $c->{value} = "".(1<<$c->{bitpos});
118 } elsif (defined($c->{extnumber})) {
119 $c->{value} = "".(1000000000
120 + 1000 * ($c->{extnumber} - 1)
122 } elsif (defined($c->{offset})) {
123 $c->{value} = $c->{dir}."".(1000000000
124 + 1000 * ($ext->{number} - 1)
126 } elsif (defined($c->{alias})) {
132 push @{$d->{items}}, $c;
133 } elsif ($c->{value}) {
134 if ($c->{value} =~ m/^"/) {
135 if (!defined $outconst->{index}->{$c->{name}}) {
136 my $v = { %$c, type=>'const char *' };
137 push @{$outconst->{items}}, $v;
138 $outconst->{index}->{$v->{name}} = $v;
141 if (!defined $outconst->{index}->{$c->{name}}) {
142 my $v = { %$c, type=>'uint32_t' };
143 push @{$outconst->{items}}, $v;
144 $outconst->{index}->{$v->{name}} = $v;
147 } elsif (!$c->{alias}) {
148 if (!defined $outconst->{index}->{$c->{name}}) {
149 my $v = $allconst->{index}->{$c->{name}};
151 die Dumper($c) if !defined $v;
153 push @{$outconst->{items}}, $v;
154 $outconst->{index}->{$c->{name}} = $v;
160 # Ideally this builds a 'view' of the features
161 # But it doesn't work properly if something is promoted and uses new names
171 map { $versions->{$_} = 1 } @$vers;
172 map { $platform->{$_} = 1 } @$plat;
174 #print Dumper($vk->{features});
176 $data->{'API Constants'} = {
177 name => 'API Constants',
178 category => 'define',
183 # add constants that the api's dont reference (only 1 so far)
184 my $outconst = $data->{'API Constants'};
185 my $allconst = $vk->{data}->{'API Constants'};
186 foreach my $v (map {$allconst->{index}->{$_}} qw(VK_UUID_SIZE)) {
187 push @{$outconst->{items}}, $v;
188 $outconst->{index}->{$v->{name}} = $v;
191 foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
192 print "Feature $feature->{name}\n" if ($vk->{sys}->{verbose});
193 foreach my $req (@{$feature->{require}}) {
194 buildRequirement($vk, $data, $req);
198 foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
199 } @{$vk->{extensions}}) {
200 foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
201 @{$extension->{require}}) {
202 print "Extension $extension->{name} $req->{feature}\n" if ($vk->{sys}->{verbose});
203 buildRequirement($vk, $data, $req, $extension);
208 #print Dumper($data);
210 # TODO: need to remove aliases here?
215 my $funcpointers = {};
218 foreach my $t (keys %{$data}) {
220 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
221 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
222 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
223 $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
224 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
225 $defines->{$v->{name}} = $v if $v->{category} eq 'define';
229 open(my $f, '>', 'features.pm');
230 print $f Dumper($data);
233 open(my $f, '>', 'vk.pm');
234 print $f Dumper($vk);
242 commands => $commands,
243 funcpointers => $funcpointers,
248 # create sizes for every struct of interest
249 foreach my $s (values %$types) {
252 if ($s->{category} eq 'struct') {
253 structSize($vk, $api, $s);
254 } elsif ($s->{category} eq 'union') {
255 unionSize($vk, $api, $s);
265 'void *' => { bitSize => 64, bitAlign => 64 },
266 'int' => { bitSize => 32, bitAlign => 32 },
267 'char' => { bitSize => 8, bitAlign => 8 },
268 'uint8_t' => { bitSize => 8, bitAlign => 8 },
269 'uint16_t' => { bitSize => 16, bitAlign => 16 },
270 'int32_t' => { bitSize => 32, bitAlign => 32 },
271 'uint32_t' => { bitSize => 32, bitAlign => 32 },
272 'int64_t' => { bitSize => 64, bitAlign => 64 },
273 'uint64_t' => { bitSize => 64, bitAlign => 64 },
274 'size_t' => { bitSize => 64, bitAlign => 64 },
275 'float' => { bitSize => 32, bitAlign => 32 },
276 'double' => { bitSize => 64, bitAlign => 64 },
277 'size_t' => { bitSize => 64, bitAlign => 64 },
278 'Window' => { bitSize => 64, bitAlign => 64 },
279 'Display' => { bitSize => 64, bitAlign => 64 },
280 'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
281 'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
282 # 'VkFlags' => { bitSize => 32, bitAlign => 32 },
283 # 'VkFlags64' => { bitSize => 64, bitAlign => 64 },
290 my $t = $api->{data}->{$m->{baseType}};
291 my $nstar = $m->{fullType} =~ tr/*/*/;
292 my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
294 my $info = $typeInfo->{'void *'};
296 # arrays and bitfields
297 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
299 } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
301 } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
302 $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
307 die Dumper($m) if $nstar > 0;
308 $info = { bitSize => $nbits, bitAlign => 1 };
310 $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
313 while ($t->{alias}) {
314 $t = $api->{data}->{$t->{alias}};
317 die Dumper($m) if !defined $t;
319 if ($t->{category} =~ m/enum|bitmask/on) {
321 die Dumper($m) if $nstar > 0;
322 $info = { bitSize => $nbits, bitAlign => 1 };
324 $t = $vk->{data}->{$t->{fullType}};
325 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
327 } elsif ($t->{category} eq 'struct') {
328 $info = structSize($vk, $api, $t) if ($nstar == 0);
329 } elsif ($t->{category} eq 'union') {
330 $info = unionSize($vk, $api, $t) if ($nstar == 0);
331 } elsif ($t->{category} eq 'handle') {
333 } elsif ($t->{category} eq 'basetype') {
334 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
335 } elsif ($t->{category} eq 'funcpointer') {
342 die Dumper($m, $t) if !defined($info);
344 #print Dumper($m, $t, $info);
345 #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
348 return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
355 return ($v + $a - 1) & ~($a - 1);
365 if (!defined($s->{bitSize})) {
366 foreach my $m (@{$s->{items}}) {
368 my $info = memberSize($vk, $api, $m);
370 $bitSize = align($bitSize, $info->{bitAlign});
372 $m->{bitOffset} = $bitSize;
373 $m->{bitSize} = $info->{bitSize};
375 $bitSize = $bitSize + $info->{bitSize};
376 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
379 $bitSize = align($bitSize, $bitAlign);
381 $s->{bitSize} = $bitSize;
382 $s->{bitAlign} = $bitAlign;
384 $bitSize = $s->{bitSize};
385 $bitAlign = $s->{bitAlign};
388 return { bitSize => $bitSize, bitAlign => $bitAlign };
398 if (!defined($s->{bitSize})) {
399 foreach my $m (@{$s->{items}}) {
401 my $info = memberSize($vk, $api, $m);
404 $m->{bitSize} = $info->{bitSize};
406 $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
407 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
410 $bitSize = align($bitSize, $bitAlign);
412 $s->{bitSize} = $bitSize;
413 $s->{bitAlign} = $bitAlign;
415 $bitSize = $s->{bitSize};
416 $bitAlign = $s->{bitAlign};
419 return { bitSize => $bitSize, bitAlign => $bitAlign };
425 my $xml = XML::Parser->new(Style => 'Tree');
426 my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
430 my $root = $doc->[1];
431 my $roota = shift @{$root};
433 my $data = $vk->{data};
434 my $alias = $vk->{alias};
435 my $extensions = $vk->{extensions};
436 my $features = $vk->{features};
438 # This destructively consumes the whole tree so must be one pass
439 while ($#{$root} >= 0) {
440 my $xt = shift @{$root};
441 my $xn = shift @{$root};
445 my $xa = shift @{$xn};
447 if ($xt eq 'types') {
448 while ($#{$xn} >= 0) {
449 my $yt = shift @{$xn};
450 my $yn = shift @{$xn};
452 next if $yt ne 'type';
456 if ($ya->{category} =~ m/struct|union/) {
457 if (!defined($ya->{alias})) {
463 while ($#{$yn} >= 0) {
464 my $mt = shift @{$yn};
465 my $mm = shift @{$yn};
467 push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
470 $data->{$s->{name}} = $s;
472 $alias->{$ya->{name}} = $ya->{alias};
473 $data->{$ya->{name}} = $ya;
475 } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
476 if (!defined($ya->{alias})) {
477 my $info = loadMember($yn);
480 $s->{name} = $info->{name};
481 $s->{type} = $info->{baseType} if defined $info->{baseType};
483 $s->{category} = 'enum' if $s->{category} eq 'bitmask';
484 analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
486 $data->{$s->{name}} = $s;
488 $ya->{category} = 'enum' if $ya->{category} eq 'bitmask';
489 $alias->{$ya->{name}} = $ya->{alias};
490 $data->{$ya->{name}} = $ya;
492 } elsif ($ya->{category} eq 'enum') {
493 $data->{$ya->{name}} = $ya;
494 } elsif ($ya->{requires} eq 'vk_platform' || $ya->{name} eq 'int') {
495 # These are just primitive types, not sure what to do with them, could auto-map them to java i suppose
496 $ya->{category} = 'platform';
497 $data->{$ya->{name}} = $ya;
499 #noisy print "Unhandled: $ya->{name}\n";
502 } elsif ($xt eq 'enums') {
503 if ($xa->{type} =~ m/enum|bitmask/o) {
504 #print "enum: $xa->{name}\n";
505 # these are forward referenced from <types> block so re-use, or just overwrite?
506 my $e = $data->{$xa->{name}};
508 $e = { %{$xa}, category => "enum" } if (!defined($e));
511 while ($#{$xn} >= 0) {
512 my $yt = shift @{$xn};
513 my $yn = shift @{$xn};
515 next if $yt ne 'enum';
517 my $ya = shift @{$yn};
519 #next if $ya->{alias};
521 push @{$e->{items}}, $ya;
524 $data->{$e->{name}} = $e;
525 } elsif ($xa->{name} eq 'API Constants') {
526 my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
528 $data->{$xa->{name}} = $d;
530 while ($#{$xn} >= 0) {
531 my $yt = shift @{$xn};
532 my $yn = shift @{$xn};
534 next if $yt ne 'enum';
536 my $ya = shift @{$yn};
538 #next if $ya->{alias};
540 push @{$d->{items}}, $ya;
541 $d->{index}->{$ya->{name}} = $ya;
544 } elsif ($xt eq 'commands') {
545 while ($#{$xn} >= 0) {
546 my $yt = shift @{$xn};
547 my $yn = shift @{$xn};
549 next if $yt ne 'command';
551 my $ya = shift @{$yn};
553 if (!defined($ya->{alias})) {
556 $cmd->{category} = 'command';
560 while ($#{$yn} >= 0) {
561 my $zt = shift @{$yn};
562 my $zn = shift @{$yn};
564 if ($zt eq 'proto') {
565 $cmd->{proto} = loadMember($zn);
566 } elsif ($zt eq 'param') {
567 push @{$cmd->{items}}, loadMember($zn);
571 my $name = $cmd->{proto}->{name};
573 # check we parsed it properly
574 if ($cmd->{proto}->{fullType} eq "") {
575 print Dumper([$ya, $yn]);
578 $cmd->{name} = $name;
580 $data->{$name} = $cmd;
582 # want forward ref or not?
583 $alias->{$ya->{name}} = $ya->{alias};
584 $data->{$ya->{name}} = $ya;
587 } elsif ($xt eq 'feature') {
590 $feature->{require} = [];
592 while ($#{$xn} >= 0) {
593 my $yt = shift @{$xn};
594 my $yn = shift @{$xn};
596 next if $yt ne 'require';
598 push @{$feature->{require}}, loadRequire($data, $alias, $yn);
601 push @{$features}, $feature;
602 } elsif ($xt eq 'extensions') {
603 while ($#{$xn} >= 0) {
604 my $yt = shift @{$xn};
605 my $yn = shift @{$xn};
607 next if $yt ne 'extension';
609 my $ext = shift @{$yn};
611 $ext->{require} = [];
613 while ($#{$yn} >= 0) {
614 my $zt = shift @{$yn};
615 my $zn = shift @{$yn};
617 next if $zt ne 'require';
619 push @{$ext->{require}}, loadRequire($data, $alias, $zn);
622 push @{$extensions}, $ext;
625 print "vulkan.pm: Ignore node: $xt\n";
630 # find an object including via alias
637 my $s = $data->{$name};
638 return $s if defined $s;
639 #print "alias $name => $alias->{$name}\n";
640 $name = $alias->{$name};
643 die "No match for type '$name'";
648 my $fullType = shift;
649 my $type = $fullType;
651 $type =~ s/const|\*|\s//gon;
653 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
655 # canonicalise spaces in c type
656 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
657 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
658 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
660 # fix brackets and trailing spaces
661 #$fullType =~ s/\( /(/go;
662 #$fullType =~ s/ \)/)/go;
663 #$fullType =~ s/ \[/[/go;
664 $fullType =~ s/^\s+|\s+$//go;
668 Name => ucfirst($name),
669 fullType => $fullType,
675 # Convert function typedef into function info
676 sub analyseFunctionPointer {
679 if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
681 my @args = split /,/,$2;
683 $s->{proto} = makeParameter('result$', $rt);
686 if ($#args != 0 || $args[0] ne 'void') {
687 foreach my $a (@args) {
688 if (my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o) {
689 push @{$s->{items}}, makeParameter($name, $fullType);
691 die "Unable to parse function pointer argument '$a'\n";
696 die "Unable to parse function pointer prototype '$s->{fullType}'\n";
698 $s->{Name} = $s->{name};
701 delete $s->{baseType};
702 delete $s->{fullType};
707 #my $x = (join '',split('\n',Dumper($nn))); $x =~ s/ +/ /g; print "load: $x\n";
708 my $m = shift @{$nn};
713 while ($#{$nn} >= 0) {
714 my $pt = shift @{$nn};
715 my $pn = shift @{$nn};
719 } elsif ($pt eq 'type') {
720 die if $pn->[1] != 0;
721 $baseType = $pn->[2];
722 $fullType .= $baseType;
723 } elsif ($pt eq 'name') {
724 die if $pn->[1] != 0;
726 } elsif ($pt eq 'enum') {
727 die if $pn->[1] != 0;
728 $fullType .= $pn->[2];
732 $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
733 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
735 # canonicalise spaces in c type
736 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
737 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
738 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
740 # fix brackets and trailing spaces
741 $fullType =~ s/\( /(/go;
742 $fullType =~ s/ \)/)/go;
743 $fullType =~ s/ \[/[/go;
744 $fullType =~ s/^\s+|\s+$//go;
745 $fullType =~ s/ :/:/go;
748 $m->{baseType} = $baseType;
749 $m->{fullType} = $fullType;
758 my $r = shift @{$nn};
764 while ($#{$nn} >= 0) {
765 my $mt = shift @{$nn};
766 my $mn = shift @{$nn};
769 my $ma = shift @{$mn};
770 push @{$r->{types}}, $ma->{name};
771 } elsif ($mt eq 'command') {
772 my $ma = shift @{$mn};
773 push @{$r->{commands}}, $ma->{name};
774 } elsif ($mt eq 'enum') {
775 my $ma = shift @{$mn};
776 push @{$r->{enums}}, $ma;
788 while ($#{$n} >= 0) {
789 my $tag = shift @{$n};
790 my $con = shift @{$n};
793 push @list, [$tag, $con];
802 while ($#{$n} >= 0) {
803 my $tag = shift @{$n};
804 my $con = shift @{$n};