2 # Routines for working with vulkan registry
10 use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
26 my $now = clock_gettime(CLOCK_REALTIME);
30 $now = clock_gettime(CLOCK_REALTIME) - $now;
31 print "$now load registry\n";
33 # build various indices
34 my $data = $self->{data};
35 my $handles = $self->{handles};
36 my $types = $self->{types};
37 my $commands = $self->{commands};
38 my $extensions = $self->{extensions};
39 my $funcpointers = $self->{funcpointers};
41 foreach my $t (keys %{$data}) {
43 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
44 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
45 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
46 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
49 # mark extension functions?
50 foreach my $e (@{$extensions}) {
51 foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
52 my $r = $data->{$name};
56 push @{$r->{extensions}}, $e;
60 #print Dumper($self->{types});
62 # FIXME: link extensions up
63 # my $r = findData($data, $alias, "type:$ma->{name}");
64 # die "cann't find $ma->{name}" if !defined $r;
65 # push @{$r->{extensions}}, $ext;
66 # push @{$r->{commands}}, $ma->{name};
71 sub buildRequirement {
76 my $outconst = $data->{'API Constants'};
77 my $allconst = $vk->{data}->{'API Constants'};
79 # add a couple of constants that the api's dont reference
80 push @{$outconst->{items}}, grep { $_->{name} =~ m/VK_UUID_SIZE/ } @{$allconst->{items}};
82 #print " $req->{comment}\n";
83 #print Dumper($req->{types});
84 foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
85 my $d = $vk->{data}->{$c};
87 #print Dumper({ d=> $d, c => $c });
91 if ($d->{category} eq 'enum' && !defined($d->{alias})) {
93 $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
97 #print "Alias: $d->{alias}\n" if defined($d->{alias});
99 print "Ignored: ".Dumper($c);
102 foreach my $c (@{$req->{enums}}) {
104 my $d = $data->{$c->{extends}};
106 if (defined($c->{value})) {
107 } elsif (defined($c->{bitpos})) {
108 $c->{value} = "".(1<<$c->{bitpos});
109 } elsif (defined($c->{extnumber})) {
110 $c->{value} = "".(1000000000
111 + 1000 * ($c->{extnumber} - 1)
113 } elsif (defined($c->{offset})) {
114 $c->{value} = $c->{dir}."".(1000000000
115 + 1000 * ($ext->{number} - 1)
117 } elsif (defined($c->{alias})) {
123 push @{$d->{items}}, $c;
124 } elsif ($c->{value}) {
125 if ($c->{value} =~ m/^"/) {
126 push @{$outconst->{items}}, { %$c, type=>'const char *' };
128 push @{$outconst->{items}}, { %$c, type=>'uint32_t' };
130 } elsif (!$c->{alias}) {
131 my @list = grep { $_->{name} eq $c->{name} } @{$allconst->{items}};
132 die "Can't find constant '$c->{name}'".Dumper($c) if ($#list < 0);
133 push @{$outconst->{items}}, @list;
146 map { $versions->{$_} = 1 } @$vers;
147 map { $platform->{$_} = 1 } @$plat;
149 #print Dumper($vk->{features});
151 $data->{'API Constants'} = {
152 category => 'define',
156 foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
157 print "Feature $feature->{name}\n";
158 foreach my $req (@{$feature->{require}}) {
159 buildRequirement($vk, $data, $req);
163 foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
164 } @{$vk->{extensions}}) {
165 foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
166 @{$extension->{require}}) {
167 print "Extension $extension->{name} $req->{feature}\n";
168 buildRequirement($vk, $data, $req, $extension);
173 #print Dumper($data);
175 # TODO: need to remove aliases here?
181 my $funcpointers = {};
183 foreach my $t (keys %{$data}) {
185 $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
186 $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
187 $commands->{$v->{name}} = $v if $v->{category} eq 'command';
188 $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
189 $bitmasks->{$v->{name}} = $v if $v->{category} eq 'bitmask';
190 $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
193 # link enums to their type(s)
194 foreach my $s (values %$bitmasks) {
197 if ($s->{requires}) {
198 $t = $data->{$s->{requires}};
199 while ($t && $t->{alias}) {
200 $t = $data->{$t->{alias}};
204 $t->{fullType} = $s->{baseType};
205 } elsif ($s->{name} =~ m/(.*)Flags([0-9A-Z]*)/o && defined $data->{"$1FlagBits$2"}) {
206 print "> $s->{name} $1FlagBits$2\n";
207 $t = $data->{"$1FlagBits$2"};
208 while ($t && $t->{alias}) {
209 $t = $data->{$t->{alias}};
213 $t->{fullType} = $s->{baseType};
215 $t->{fullType} = 'VkFlags';
218 foreach my $s (values %$enums) {
219 $s->{fullType} = 'VkFlags' if !defined $s->{fullType};
223 # Have to actually map the types too
227 foreach my $c (values %{$types}) {
230 while ($c->{alias}) {
232 $c = $vk->{data}->{$c->{alias}};
234 print " -> $c->{name}\n";
237 map {delete $types->{$_} } (keys %$del);
238 map {$data->{$_->{name}} = $_; $enums->{$_->{name}} = $_ } (values %$add);
241 # check type sare included?
245 foreach my $e (values %{$enums}) {
248 while ($e->{alias}) {
250 $del->{$e->{name}} = $e;
251 $e = $vk->{data}->{$e->{alias}};
254 print " -> $e->{name}\n";
255 $add->{$e->{name}} = $e;
258 map {delete $enums->{$_} } (keys %$del);
259 map {$data->{$_->{name}} = $_; $enums->{$_->{name}} = $_ } (values %$add);
267 commands => $commands,
268 funcpointers => $funcpointers,
270 bitmasks => $bitmasks,
273 # create sizes for every struct of interest
274 foreach my $s (values %$types) {
277 if ($s->{category} eq 'struct') {
278 structSize($vk, $api, $s);
279 } elsif ($s->{category} eq 'union') {
280 unionSize($vk, $api, $s);
290 'void *' => { bitSize => 64, bitAlign => 64 },
291 'int' => { bitSize => 32, bitAlign => 32 },
292 'char' => { bitSize => 8, bitAlign => 8 },
293 'uint8_t' => { bitSize => 8, bitAlign => 8 },
294 'uint16_t' => { bitSize => 16, bitAlign => 16 },
295 'int32_t' => { bitSize => 32, bitAlign => 32 },
296 'uint32_t' => { bitSize => 32, bitAlign => 32 },
297 'int64_t' => { bitSize => 64, bitAlign => 64 },
298 'uint64_t' => { bitSize => 64, bitAlign => 64 },
299 'size_t' => { bitSize => 64, bitAlign => 64 },
300 'float' => { bitSize => 32, bitAlign => 32 },
301 'double' => { bitSize => 64, bitAlign => 64 },
302 'size_t' => { bitSize => 64, bitAlign => 64 },
303 'Window' => { bitSize => 64, bitAlign => 64 },
304 'Display' => { bitSize => 64, bitAlign => 64 },
305 'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
306 'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
307 # 'VkFlags' => { bitSize => 32, bitAlign => 32 },
308 # 'VkFlags64' => { bitSize => 64, bitAlign => 64 },
311 # fuck how can i parameterise this shit?
312 # ?create a 'deref' thing that all these things can work from?
313 # this should probably do it too
318 my $t = $api->{data}->{$m->{baseType}};
319 my $nstar = $m->{fullType} =~ tr/*/*/;
320 my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
322 my $info = $typeInfo->{'void *'};
324 # arrays and bitfields
325 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
327 } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
329 } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
330 $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
335 die Dumper($m) if $nstar > 0;
336 $info = { bitSize => $nbits, bitAlign => 1 };
338 $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
341 while ($t->{alias}) {
342 $t = $api->{data}->{$t->{alias}};
345 die Dumper($m) if !defined $t;
347 if ($t->{category} =~ m/enum|bitmask/on) {
349 die Dumper($m) if $nstar > 0;
350 $info = { bitSize => $nbits, bitAlign => 1 };
352 $t = $vk->{data}->{$t->{fullType}};
353 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
355 } elsif ($t->{category} eq 'struct') {
356 $info = structSize($vk, $api, $t) if ($nstar == 0);
357 } elsif ($t->{category} eq 'union') {
358 $info = unionSize($vk, $api, $t) if ($nstar == 0);
359 } elsif ($t->{category} eq 'handle') {
361 } elsif ($t->{category} eq 'basetype') {
362 $info = $typeInfo->{$t->{type}} if ($nstar == 0);
363 } elsif ($t->{category} eq 'funcpointer') {
370 die Dumper($m, $t) if !defined($info);
372 #print Dumper($m, $t, $info);
373 #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
376 return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
383 return ($v + $a - 1) & ~($a - 1);
393 if (!defined($s->{bitSize})) {
394 foreach my $m (@{$s->{items}}) {
396 my $info = memberSize($vk, $api, $m);
398 $bitSize = align($bitSize, $info->{bitAlign});
400 $m->{bitOffset} = $bitSize;
401 $m->{bitSize} = $info->{bitSize};
403 $bitSize = $bitSize + $info->{bitSize};
404 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
407 $bitSize = align($bitSize, $bitAlign);
409 $s->{bitSize} = $bitSize;
410 $s->{bitAlign} = $bitAlign;
412 $bitSize = $s->{bitSize};
413 $bitAlign = $s->{bitAlign};
416 return { bitSize => $bitSize, bitAlign => $bitAlign };
426 if (!defined($s->{bitSize})) {
427 foreach my $m (@{$s->{items}}) {
429 my $info = memberSize($vk, $api, $m);
432 $m->{bitSize} = $info->{bitSize};
434 $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
435 $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
438 $bitSize = align($bitSize, $bitAlign);
440 $s->{bitSize} = $bitSize;
441 $s->{bitAlign} = $bitAlign;
443 $bitSize = $s->{bitSize};
444 $bitAlign = $s->{bitAlign};
447 return { bitSize => $bitSize, bitAlign => $bitAlign };
453 my $xml = XML::Parser->new(Style => 'Tree');
454 my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
458 my $root = $doc->[1];
459 my $roota = shift @{$root};
461 my $data = $vk->{data};
462 my $alias = $vk->{alias};
463 my $extensions = $vk->{extensions};
464 my $features = $vk->{features};
466 # This destructively consumes the whole tree so must be one pass
467 while ($#{$root} >= 0) {
468 my $xt = shift @{$root};
469 my $xn = shift @{$root};
473 my $xa = shift @{$xn};
475 if ($xt eq 'types') {
476 while ($#{$xn} >= 0) {
477 my $yt = shift @{$xn};
478 my $yn = shift @{$xn};
480 next if $yt ne 'type';
484 if ($ya->{category} =~ m/struct|union/) {
485 if (!defined($ya->{alias})) {
491 while ($#{$yn} >= 0) {
492 my $mt = shift @{$yn};
493 my $mm = shift @{$yn};
495 push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
498 $data->{$s->{name}} = $s;
500 $alias->{$ya->{name}} = $ya->{alias};
501 $data->{$ya->{name}} = $ya;
503 } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
504 if (!defined($ya->{alias})) {
505 my $info = loadMember($yn);
508 $s->{name} = $info->{name};
509 $s->{type} = $info->{baseType} if defined $info->{baseType};
511 analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
513 $data->{$s->{name}} = $s;
515 $alias->{$ya->{name}} = $ya->{alias};
516 $data->{$ya->{name}} = $ya;
518 } elsif ($ya->{category} eq 'enum') {
519 $data->{$ya->{name}} = $ya;
520 } elsif ($ya->{requires} || $ya->{name} eq 'int') {
521 # ?? wtf to do with this
522 $ya->{category} = 'platform';
523 $data->{$ya->{name}} = $ya;
526 } elsif ($xt eq 'enums') {
527 if ($xa->{type} =~ m/enum|bitmask/o) {
528 # these are forward referenced from <types> block so re-use, or just overwrite?
529 my $e = $data->{$xa->{name}};
531 $e = { category => "enum", name => $xa->{name} } if (!defined($e));
535 while ($#{$xn} >= 0) {
536 my $yt = shift @{$xn};
537 my $yn = shift @{$xn};
539 next if $yt ne 'enum';
541 my $ya = shift @{$yn};
543 #next if $ya->{alias};
545 push @{$e->{items}}, $ya;
548 $data->{$xa->{name}} = $e;
549 } elsif ($xa->{name} eq 'API Constants') {
550 my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
552 $data->{$xa->{name}} = $d;
554 while ($#{$xn} >= 0) {
555 my $yt = shift @{$xn};
556 my $yn = shift @{$xn};
558 next if $yt ne 'enum';
560 my $ya = shift @{$yn};
562 #next if $ya->{alias};
564 push @{$d->{items}}, $ya;
565 $d->{index}->{$ya->{name}} = $ya;
568 } elsif ($xt eq 'commands') {
569 while ($#{$xn} >= 0) {
570 my $yt = shift @{$xn};
571 my $yn = shift @{$xn};
573 next if $yt ne 'command';
575 my $ya = shift @{$yn};
577 if (!defined($ya->{alias})) {
580 $cmd->{category} = 'command';
584 while ($#{$yn} >= 0) {
585 my $zt = shift @{$yn};
586 my $zn = shift @{$yn};
588 if ($zt eq 'proto') {
589 $cmd->{proto} = loadMember($zn);
590 } elsif ($zt eq 'param') {
591 push @{$cmd->{items}}, loadMember($zn);
595 my $name = $cmd->{proto}->{name};
597 # check we parsed it properly
598 if ($cmd->{proto}->{fullType} eq "") {
599 print Dumper([$ya, $yn]);
602 $cmd->{name} = $name;
604 $data->{$name} = $cmd;
606 # want forward ref or not?
607 $alias->{$ya->{name}} = $ya->{alias};
608 $data->{$ya->{name}} = $ya;
611 } elsif ($xt eq 'feature') {
614 $feature->{require} = [];
616 while ($#{$xn} >= 0) {
617 my $yt = shift @{$xn};
618 my $yn = shift @{$xn};
620 next if $yt ne 'require';
622 push @{$feature->{require}}, loadRequire($data, $alias, $yn);
625 push @{$features}, $feature;
626 } elsif ($xt eq 'extensions') {
627 while ($#{$xn} >= 0) {
628 my $yt = shift @{$xn};
629 my $yn = shift @{$xn};
631 next if $yt ne 'extension';
633 my $ext = shift @{$yn};
635 $ext->{require} = [];
637 while ($#{$yn} >= 0) {
638 my $zt = shift @{$yn};
639 my $zn = shift @{$yn};
641 next if $zt ne 'require';
643 push @{$ext->{require}}, loadRequire($data, $alias, $zn);
646 push @{$extensions}, $ext;
649 print "vulkan.pm: Ignore node: $xt\n";
654 # find an object including via alias
661 my $s = $data->{$name};
662 return $s if defined $s;
663 #print "alias $name => $alias->{$name}\n";
664 $name = $alias->{$name};
667 die "No match for type '$name'";
672 my $fullType = shift;
673 my $type = $fullType;
675 $type =~ s/const|\*|\s//gon;
677 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
679 # canonicalise spaces in c type
680 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
681 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
682 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
684 # fix brackets and trailing spaces
685 #$fullType =~ s/\( /(/go;
686 #$fullType =~ s/ \)/)/go;
687 #$fullType =~ s/ \[/[/go;
688 $fullType =~ s/^\s+|\s+$//go;
692 Name => ucfirst($name),
693 fullType => $fullType,
699 # Convert function typedef into function info
700 sub analyseFunctionPointer {
703 if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
705 my @args = split /,/,$2;
707 $s->{proto} = makeParameter('result$', $rt);
710 foreach my $a (@args) {
711 my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o;
713 push @{$s->{items}}, makeParameter($name, $fullType);
718 $s->{Name} = $s->{name};
720 delete $s->{baseType};
721 delete $s->{fullType};
726 #my $x = (join '',split('\n',Dumper($nn))); $x =~ s/ +/ /g; print "load: $x\n";
727 my $m = shift @{$nn};
732 while ($#{$nn} >= 0) {
733 my $pt = shift @{$nn};
734 my $pn = shift @{$nn};
738 } elsif ($pt eq 'type') {
739 die if $pn->[1] != 0;
740 $baseType = $pn->[2];
741 $fullType .= $baseType;
742 } elsif ($pt eq 'name') {
743 die if $pn->[1] != 0;
745 } elsif ($pt eq 'enum') {
746 die if $pn->[1] != 0;
747 $fullType .= $pn->[2];
751 $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
752 $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
754 # canonicalise spaces in c type
755 #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
756 $fullType =~ s/(?<! )\*/ */go; # insert a space before * if there isn't one
757 $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
759 # fix brackets and trailing spaces
760 $fullType =~ s/\( /(/go;
761 $fullType =~ s/ \)/)/go;
762 $fullType =~ s/ \[/[/go;
763 $fullType =~ s/^\s+|\s+$//go;
764 $fullType =~ s/ :/:/go;
767 $m->{baseType} = $baseType;
768 $m->{fullType} = $fullType;
777 my $r = shift @{$nn};
783 while ($#{$nn} >= 0) {
784 my $mt = shift @{$nn};
785 my $mn = shift @{$nn};
788 my $ma = shift @{$mn};
789 push @{$r->{types}}, $ma->{name};
790 } elsif ($mt eq 'command') {
791 my $ma = shift @{$mn};
792 push @{$r->{commands}}, $ma->{name};
793 } elsif ($mt eq 'enum') {
794 my $ma = shift @{$mn};
795 push @{$r->{enums}}, $ma;
807 while ($#{$n} >= 0) {
808 my $tag = shift @{$n};
809 my $con = shift @{$n};
812 push @list, [$tag, $con];
821 while ($#{$n} >= 0) {
822 my $tag = shift @{$n};
823 my $con = shift @{$n};