2 # TODO: constructors initialise sType
9 use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
26 $registry = new vulkan();
28 # pre-load 'handle' types which aren't exported by the header parser (anonymous struct typedefs)
29 foreach my $r (grep { $_->{category} eq 'handle' } values %{$registry->{handles}}) {
30 die if $registry->{alias}->{"type:$r->{name}"};
32 print "Add vkhandle: $r->{name} ($r->{type})\n";
34 name => "$r->{name}_T",
43 if ($r->{name} eq 'VkInstance') {
44 $s->{'struct:template'} = 'code:class=handle';
45 } elsif ($r->{type} eq 'VK_DEFINE_HANDLE') {
46 $s->{'struct:template'} = 'code:vulkan=handle-dynamic';
50 $api->{data}->{"struct:$r->{name}_T"} = $s;
53 # link api to registry
55 foreach my $s (values %{$api->{data}}) {
56 my $r = $registry->{index}->{"type:$s->{name}"};
58 if ($registry->{alias}->{"type:$s->{name}"} && !($s->{name} =~ m/_T$/)) {
59 # WTF will this break though
60 print "Suppress alias: $s->{name}\n";
62 push @delete, "$s->{type}:$s->{name}";
64 $s->{vkregistry} = $r if defined $r;
65 $r->{vkheader} = $s if defined $r;
69 map { delete $api->{data}->{$_} } @delete;
77 analyseFunctions($api, $registry);
78 analyseInOut($api, $registry);
79 analyseStructs($api, $registry);
85 # analyse functions and structs
86 # - try to guess which types are in-out/in-only/out-only (not very reliable)
87 # - determine which types can be used as arrays
88 # - mark function parameters which are arrays
95 foreach my $c (grep { $_->{category} =~ m/command|struct|union/ && defined $_->{vkheader} } values %{$registry->{data}}) {
96 my $d = $c->{vkheader};
99 map { $dindex->{$_->{name}} = $_ } @{$d->{items}};
101 # Check parameters -> const * are out-only, any with 'len' need array accessors
102 foreach my $m (@{$c->{items}}) {
103 my $r = $registry->{index}->{"type:$m->{baseType}"};
104 my $n = $dindex->{$m->{name}};
109 if ($r->{category} =~ m/struct|union/) {
110 $output->{$r->{name}} = $r if !($m->{fullType} =~ m/^const.*\*$/);
111 $arrays->{$r->{name}} = $r if $m->{len} || $m->{altlen} || $n->{deref} =~ m/^\[\d+/;
113 # && $m->{fullType} =~ tr/*/*/ == 1;
114 } elsif ($r->{category} eq 'handle') {
115 $n->{array} = 1 if ($m->{len} || $m->{altlen});
120 foreach my $fuckoff ('VkBaseInStructure', 'VkBaseOutStructure') {
121 delete $arrays->{$fuckoff};
122 delete $output->{$fuckoff};
126 'VkDebugUtilsMessengerCallbackDataEXT' => $registry->{index}->{'type:VkDebugUtilsMessengerCallbackDataEXT'},
129 'VkComputePipelineCreateInfo' => $registry->{index}->{'type:VkComputePipelineCreateInfo'},
133 foreach my $r (grep { $_->{category} =~ m/struct|union/on && defined $_->{vkheader} && !defined $out->{$_->{name}} && !defined $inout->{$_->{name}} } values %{$registry->{data}}) {
134 if ($r->{returnedonly}) {
135 $out->{$r->{name}} = $r;
137 $in->{$r->{name}} = $r;
141 foreach my $r (values %{$output}) {
142 print "check $r->{name}\n";
143 if ($r->{returnedonly} ne 'true') {
145 analyseInOutStruct($registry, $x, $r->{name});
146 foreach my $s (values %{$x}) {
147 print "moved in -> in-out $s->{name} due to $r->{name}\n";
148 delete $in->{$s->{name}};
149 $inout->{$s->{name}} = $s;
158 my $s = $r->{vkheader};
160 die "Missing vkheader ".Dumper($r) if !defined $s;
162 $rw = $rw."i" if $arrays->{$r->{name}};
166 $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}};
169 print join '', map { "in-out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$inout};
170 print join '', map { "in : $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$in};
171 print join '', map { " out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$out};
173 map { $set->($_, 'rw') } values %{$inout};
174 map { $set->($_, 'w') } values %{$in};
175 map { $set->($_, 'r') } values %{$out};
178 # dump all types in this type recursively into %{$record}
179 sub analyseInOutStruct {
180 my $registry = shift;
182 my $baseType = shift;
183 my $s = $registry->{index}->{"type:$baseType"};
186 # print join "\n", sort(map { $_->{name} } values %{$registry->{data}});
188 die "no match for $baseType\n" if !defined $s;
190 if (defined $s && !defined $s->{structextends} && $s->{category} =~ m/struct|union/n) {
191 $record->{$baseType} = $s;
193 foreach my $m (@{$s->{items}}) {
194 analyseInOutStruct($registry, $record, $m->{baseType});
197 if (defined $s && defined $s->{structextends}) {
198 print "extends? $baseType $s->{structextends}\n";
204 # - configure member functions
205 # - configure constructors
206 sub analyseFunctions {
208 my $registry = shift;
213 foreach my $c (grep { $_->{category} eq 'command' && defined $_->{vkheader} } values %{$registry->{data}}) {
214 my $d = $c->{vkheader};
216 # set extensions functions to use a different template
217 if ($c->{extensions}) {
218 $d->{extensions} = $c->{extensions};
219 $d->{'init:template'} = $d->{items}->[0]->{deref} eq 'u64:${VkInstance_T}'
220 ? 'code:vulkan=invoke-dynamic-init-instance' : 'code:vulkan=invoke-dynamic-init';
221 $d->{'func:template'} = 'code:vulkan=invoke-dynamic';
224 # look for constructors and member functions
225 my $first = $d->{items}->[0];
226 my $last = $d->{items}->[$#{$d->{items}}];
228 my $ffirst = $c->{items}->[0];
229 my $flast = $c->{items}->[$#{$c->{items}}];
230 my $rfirst = $registry->{index}->{"type:$ffirst->{baseType}"};
231 my $rlast = $registry->{index}->{"type:$flast->{baseType}"};
233 my $isstatic = ($rfirst->{type} ne 'VK_DEFINE_HANDLE')
234 && ($rfirst->{fullType} =~ tr/*/*/ == 0);
236 my $iscreate = ($flast->{fullType} =~ tr/*/*/==1)
237 && !($flast->{fullType} =~ m/const/)
239 && ($rlast->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
241 $d->{vkiscreate} = $iscreate;
242 $d->{vkisstatic} = $isstatic;
244 if ($iscreate && $isstatic) {
245 # fuck, all this work for vkCreateInstance()
248 $d->{return} = $last;
249 $d->{scope} = $last->{scope} = 'explicit'; # blah, for now
250 $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance');
252 print "xxxa static $flast->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n";
253 } elsif ($ffirst->{fullType} eq $ffirst->{baseType}
254 && $rfirst->{type} eq 'VK_DEFINE_HANDLE') {
256 $first->{output} = 0;
257 $first->{instance} = 1;
260 # member create function
264 $d->{return} = $last;
265 $d->{scope} = $last->{scope} = 'explicit';
266 $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance' && $rlast->{type} eq 'VK_DEFINE_HANDLE');
268 print "xxxb $ffirst->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n";
272 if ($flast->{len} && $flast->{fullType} =~ tr/*/*/ == 1
273 #&& $rlast->{type} eq 'VK_DEFINE_HANDLE')
278 # Link in successcodes (failure codes?)
279 if (defined($c->{successcodes})) {
280 $d->{success} = $d->{result};
281 $d->{success}->{success} = join ',', map { "VkConstants.$_" } split /,/,$c->{successcodes};
282 $d->{result}->{output} = 0;
284 # output return value if there is more than 1 success code
285 if ($c->{successcodes} =~ tr/,/,/ >= 1 && !defined($d->{return})) {
286 $d->{result}->{output} = 1;
296 # - which fields should be included in a set-all constructor
297 # - auto handling of arrays-with-length
298 # - handling of String[]
299 # - access modes for members
300 # - set-all constructor format hooks
303 my $registry = shift;
305 foreach my $r (grep { $_->{category} =~ m/struct|union/ && defined $_->{vkheader}} values %{$registry->{data}}) {
306 my $s = $r->{vkheader};
309 map { $index->{$_->{name}} = $_ } @{$s->{items}};
311 # # Find out which fields should be included in a 'set-all' constructor
312 # for my $m (@{$s->{items}}) {
313 # if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
314 # my $embed = $api->{data}->{"struct:$1"};
315 # # TODO: need to work out how to set the fields first
316 # #if ($#{$embed->{items}} < 6) {
317 # # $m->{'set-all-struct'} = "struct:$1";
319 # # $m->{vkset} = { type => $1 } ?
320 # } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
321 # # 1D primitive array
323 # $m->{vkset} = { select => { vkarray => 1 } };
325 # } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
326 # # 2D primitive array
327 # if (($1 * $2) <= 16) {
328 # $m->{vkset} = { select => { vkarray => 1 } };
331 # # pointers or other primitives, probably
336 # Special handling for types with 'len' specified
337 # TODO: not sure if i want this in general or just set-all constructor
338 for my $x (@{$r->{items}}) {
339 my $m = $index->{$x->{name}};
341 # if there's a len specified but ignore if set in .api file
342 if ($x->{len} && !($m->{array} || $m->{'array-size'} || $m->{'array-size-source'})) {
343 my @len = split ',', $x->{len};
345 if ($#len > 0 && $len[1] eq 'null-terminated') {
347 my $size = $index->{$len[0]};
350 $size->{implied} = "Memory.length($m->{name})";
351 $m->{select}->{vkstring} = 1;
352 } elsif ($len[0] =~ m/^[a-zA-Z]+$/) {
353 # Simple length referencing a parameter/field
354 my $size = $index->{$len[0]};
356 $size->{'array-size-source'} = $m;
357 #$size->{output} = 0;
358 $m->{'array-size'} = $size;
359 #$m->{select}->{vkstring} = 1;
360 $size->{select}->{'array-size-source'} = 1;
361 $m->{select}->{'array-size'} = 1;
363 # need to know what type it is to get correct length, array, struct[], etc
364 $size->{implied} = "Memory.length($m->{name})";
365 #$size->{implied} = "$size->{name}";
367 # use HandleArray<> rather than PointerArray<> for handles
368 if ($m->{deref} =~ m/^u64:u64:\$\{(.*)\}/) {
369 my $t = $api->{data}->{"struct:$1"}; die "no type $1" if !$t;
370 my $r = $t->{vkregistry};
372 $m->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
374 $m->{select}->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
380 # setup accessor mode (if not already set), and format hook for the set-all constructor
381 if ($r->{returnedonly} eq 'true') {
382 $s->{access} = 'r' if !$s->{vkaccess};
383 map { $_->{access} = 'r' } @{$s->{items}};
384 } elsif ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
385 $s->{access} = 'w' if !$s->{vkaccess};
386 push @{$s->{postformat}}, sub { formatConstructor(@_) };
388 $s->{access} = 'rw' if !$s->{vkaccess};
389 push @{$s->{postformat}}, sub { formatConstructor(@_) };
391 # propagate access mode.
392 map { $_->{access} = $s->{access} } @{$s->{items}};
394 if ($s->{name} eq 'VkTransformMatrix') {
395 print "yyy ".Dumper($s);
402 # TBD now in vkregistry.pm
403 sub findRegistryObject {
409 my $f = $data->{$func};
410 return $f if defined $f;
411 if ($func =~ m/^(.*)_T$/) {
413 return $f if defined $f;
415 #print "alias $func => $alias->{$func}\n";
416 $func = $alias->{$func};
422 # a basic struct, i.e. not a handle
426 my $r = $s->{vkregistry};
429 && $r->{type} ne 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
430 && $r->{type} ne 'VK_DEFINE_HANDLE';
433 # TODO: info required for these is evaluated in analyse
434 # find all functions where the first parameter is a pointer to a dispatchable handle type
435 sub matchObjectFunction {
438 my $items = $c->{items};
439 my $r = $s->{vkregistry};
441 die "vkregistry field missing ".Dumper($s) if !defined($r);
443 return $r->{type} eq 'VK_DEFINE_HANDLE'
444 #&& !$registry->{alias}->{"type:$s->{name}"}
445 && defined($items->[0])
446 && $items->[0]->{deref} eq "u64:\${$s->{name}}";
449 # a static create function
450 sub matchCreateFunction {
453 my $items = $c->{items};
454 my $r = $s->{vkregistry};
456 return ($c->{vkiscreate})
457 && ($c->{vkisstatic})
458 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}");
461 # last argument is a **handle
463 return $c->{name} =~ m/^vkCreate/
464 && $r->{type} eq 'VK_DEFINE_HANDLE'
465 && defined($items->[$#$items])
466 && $items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}";
474 if ($name eq 'pGeometries' && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
475 # small hack to fix rename conflict
476 $name = 'Geometries0';
477 } elsif ($m->{deref} =~ m/^((u64:){1,})\$\{(?<type>.*)\}$/) {
478 my $len = length($1)/4;
479 my $r = $registry->{index}->{"type:$+{type}"};
481 # strip leading 'p' for pointer, handles are void * so ignore one level
482 $len = $len - 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
483 $name = substr $name, $len;
484 } elsif ($m->{deref} =~ m/^((u64:){1,})/) {
485 # strip leading 'p' for pointer
486 $name = substr $name, length($1)/4;
490 $name =~ s/(?:^|_)(.)/\U$1/g;
495 # collect args for 'all' constructor
501 my @members = @{$s->{items}};
503 # Drop type and next fields
504 if ($#members >= 1 && $members[0]->{name} eq 'sType' && $members[1]->{name} eq 'pNext') {
505 @members = @members[2 .. $#members];
508 #print "collect $s->{name}\n";
509 foreach my $m (@members) {
512 if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
513 my $embed = $api->{data}->{"struct:$1"};
514 # TODO: need to work out how to set the fields first
515 } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
517 $n = { %{$m}, select => { vkarray => 1 } } if ($1 <= 16);
518 } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
520 $n = { %{$m}, select => { vkarray => 1 } } if (($1 * $2) <= 16);
521 } elsif ($m->{'array-size-source'} || $m->{implied}) {
522 #$n = { %{$m}, output=>0, implied => "Memory.length($m->{'array-size-source'}->{name})" };
523 $n = { %{$m}, output=>0 };
531 $n->{name} = $prefix ? $prefix.'$'.$m->{name} : $m->{name};
532 $n->{subtype} = $s->{name};
533 $n->{subname} = $m->{name};
534 $n->{localname} = $prefix;
540 # if (defined $f->{vkset}) {
543 # $a->{name} = $prefix ? $prefix.'$'.$f->{name} : $f->{name};
544 # $a->{subtype} = $s->{name};
545 # $a->{subname} = $f->{name};
546 # $a->{localname} = $prefix;
548 # $a->{select} = $f->{vkset}->{select} if defined $f->{vkset}->{select};
549 # push @{$items}, $a;
550 # } elsif ($f->{'set-all-struct'}) {
551 # die "unimplemented";
552 # my $e = $api->{data}->{$f->{'set-all-struct'}};
553 # collectAllArgs($api, $items, $e, $prefix ? $prefix.'$'.$f->{name} : $f->{name});
560 return grep { !$seen{$_}++ } @_;
563 sub format1Constructor {
570 my $info = new method($api, $c);
571 my $init = join "\n\t\t", map {
572 "MemorySegment $_\$segment = (MemorySegment)$_\$SH.invokeExact(self\$.segment);"
574 $_->{field}->{localname} ? $_->{field}->{localname} : ()
575 } @{$info->{arguments}};
577 my $setAll = join ";\n\t\t", map {
578 my $v = { %{$_->{match}}, value => $_->{field}->{name} };
579 if (defined ($_->{field}->{subtype})) {
580 $v->{name} = $_->{field}->{subtype}.'.'.$_->{field}->{subname};
581 $v->{segment} = $_->{field}->{localname}.'$segment';
583 $v->{segment} = 'self$.segment';
586 $v->{tonative} = "({type})$_->{field}->{implied}" if $_->{field}->{implied};
587 code::formatTemplate($_->{match}->{setnative}, $v);
588 } @{$info->{arguments}};
591 my $template = $api->findTemplateName('code:vulkan=create-all');
595 foreach my $l (split /\n/,Dumper($info)) {
600 $code .= code::applyTemplate($template, { %{$info->{vars}}, 'set-all' => $setAll, 'set-init' => $init});
602 push @{$res->{func}}, $code;
605 # Create an 'all-set' constructor
606 # This creates a pseudo-function and formats that
607 # This also sets up the code to initialise sType
608 sub formatConstructor {
613 my $r = $s->{vkregistry};
615 return if $s->{name} =~ m/^(VkBaseInStructure|VkBaseOutStructure)$/on;
617 if ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
618 push @{$res->{init}}, "// FIXME: array init?\n$r->{items}->[0]->{name}\$VH.set(segment, VkConstants.$r->{items}->[0]->{values});";
621 if ($s->{type} eq 'union') {
622 foreach my $u (grep { defined($_->{vkset}) } @{$s->{items}}) {
625 if ($u->{vkset}->{struct}) {
629 $a->{select} = $u->{vkset}->{select} if defined $u->{vkset}->{select};
634 type => "$s->{type}:$s->{name}",
635 deref => "u64:\$\{$s->{name}\}",
640 rename => $u->{rename},
642 format1Constructor($api, $obj, $res, $s, $c);
647 collectAllArgs($api, $args, $s, "");
649 if ($#{$args} >= 0) {
652 type => "$s->{type}:$s->{name}",
653 deref => "u64:\$\{$s->{name}\}",
660 format1Constructor($api, $obj, $res, $s, $c);