2 # TODO: array constructors initialise sType for each item
3 # FIXME: if the same len is defined for multiple targes then don't bother hiding it/setting it implicitly#
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;
51 #$api->{databytype}->{'struct'}->{"$r->{name}_T"} = $s;
54 # link api to registry
56 foreach my $s (values %{$api->{data}}) {
57 my $r = $registry->{index}->{"type:$s->{name}"};
59 if ($registry->{alias}->{"type:$s->{name}"} && !($s->{name} =~ m/_T$/)) {
60 # WTF will this break though
61 print "Suppress alias: $s->{name}\n";
63 push @delete, "$s->{type}:$s->{name}";
65 $s->{vkregistry} = $r if defined $r;
66 $r->{vkheader} = $s if defined $r;
70 map { delete $api->{data}->{$_} } @delete;
71 #map { m/^(.*):(.*)$/; delete $api->{databytype}->{$1}->{$2} } @delete;
73 # create psuedo-define of api constants
79 my $d = $registry->{data}->{'define:API Constants'};
81 name => 'api-constants',
85 foreach my $m (@{$d->{items}}) {
91 die "Unknown constant type: $m->{type}", if !defined $typeMap{$m->{type}};
93 push @{$apiConstants->{items}}, {
96 type => $typeMap{$m->{type}}
99 $api->{data}->{'define:api-constants'} = $apiConstants;
100 #$api->{databytype}->{'define'}->{'api-constants'} = $apiConstants;
102 # mark all functions for matchObjectFunction
103 foreach my $c (grep { $_->{type} eq 'func' } values %{$api->{data}}) {
104 my $items = $c->{items};
106 if ($items->[0]->{deref} =~ m/^u64:\$\{(.*)\}$/) {
107 my $type = $registry->{index}->{"type:$1"};
108 if (defined ($type) && $type->{type} eq 'VK_DEFINE_HANDLE') {
120 analyseFunctions($api, $registry);
121 analyseInOut($api, $registry);
122 analyseStructs($api, $registry);
128 # analyse functions and structs
129 # - try to guess which types are in-out/in-only/out-only (not very reliable)
130 # - determine which types can be used as arrays
131 # - mark function parameters which are arrays
134 my $registry = shift;
138 foreach my $c (grep { $_->{category} =~ m/command|struct|union/ && defined $_->{vkheader} } values %{$registry->{data}}) {
139 my $d = $c->{vkheader};
142 map { $dindex->{$_->{name}} = $_ } @{$d->{items}};
144 # Check parameters -> const * are out-only, any with 'len' need array accessors
145 foreach my $m (@{$c->{items}}) {
146 my $r = $registry->{index}->{"type:$m->{baseType}"};
147 my $n = $dindex->{$m->{name}};
152 if ($r->{category} =~ m/struct|union/) {
153 $output->{$r->{name}} = $r if !($m->{fullType} =~ m/^const.*\*$/);
154 $arrays->{$r->{name}} = $r if $m->{len} || $m->{altlen} || $n->{deref} =~ m/^\[\d+/;
156 # && $m->{fullType} =~ tr/*/*/ == 1;
157 } elsif ($r->{category} eq 'handle') {
158 $n->{array} = 1 if ($m->{len} || $m->{altlen});
163 foreach my $fuckoff ('VkBaseInStructure', 'VkBaseOutStructure') {
164 delete $arrays->{$fuckoff};
165 delete $output->{$fuckoff};
169 'VkDebugUtilsMessengerCallbackDataEXT' => $registry->{index}->{'type:VkDebugUtilsMessengerCallbackDataEXT'},
172 'VkComputePipelineCreateInfo' => $registry->{index}->{'type:VkComputePipelineCreateInfo'},
176 foreach my $r (grep { $_->{category} =~ m/struct|union/on && defined $_->{vkheader} && !defined $out->{$_->{name}} && !defined $inout->{$_->{name}} } values %{$registry->{data}}) {
177 if ($r->{returnedonly}) {
178 $out->{$r->{name}} = $r;
180 $in->{$r->{name}} = $r;
184 foreach my $r (values %{$output}) {
185 print "check $r->{name}\n";
186 if ($r->{returnedonly} ne 'true') {
188 analyseInOutStruct($registry, $x, $r->{name});
189 foreach my $s (values %{$x}) {
190 print "moved in -> in-out $s->{name} due to $r->{name}\n";
191 delete $in->{$s->{name}};
192 $inout->{$s->{name}} = $s;
201 my $s = $r->{vkheader};
203 die "Missing vkheader ".Dumper($r) if !defined $s;
205 $rw = $rw."i" if $arrays->{$r->{name}};
207 print "edit: array-struct $s->{name}\n" if $arrays->{$r->{name}};
211 $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}};
214 print join '', map { "in-out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$inout};
215 print join '', map { "in : $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$in};
216 print join '', map { " out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$out};
218 map { $set->($_, 'rw') } values %{$inout};
219 map { $set->($_, 'w') } values %{$in};
220 map { $set->($_, 'r') } values %{$out};
223 # dump all types in this type recursively into %{$record}
224 sub analyseInOutStruct {
225 my $registry = shift;
227 my $baseType = shift;
228 my $s = $registry->{index}->{"type:$baseType"};
231 # print join "\n", sort(map { $_->{name} } values %{$registry->{data}});
233 die "no match for $baseType\n" if !defined $s;
235 if (defined $s && !defined $s->{structextends} && $s->{category} =~ m/struct|union/n) {
236 $record->{$baseType} = $s;
238 foreach my $m (@{$s->{items}}) {
239 analyseInOutStruct($registry, $record, $m->{baseType});
242 if (defined $s && defined $s->{structextends}) {
243 print "extends? $baseType $s->{structextends}\n";
249 # - configure member functions
250 # - configure constructors
251 sub analyseFunctions {
253 my $registry = shift;
258 foreach my $c (grep { $_->{category} eq 'command' && defined $_->{vkheader} } values %{$registry->{data}}) {
259 my $d = $c->{vkheader};
261 # set extensions functions to use a different template
262 if ($c->{extensions}) {
263 $d->{extensions} = $c->{extensions};
264 $d->{'init:template'} = $d->{items}->[0]->{deref} eq 'u64:${VkInstance_T}'
265 ? 'code:vulkan=invoke-dynamic-init-instance' : 'code:vulkan=invoke-dynamic-init';
266 $d->{'func:template'} = 'code:vulkan=invoke-dynamic';
267 print "edit: extension: $c->{name}\n";
270 # look for constructors and member functions
271 my $first = $d->{items}->[0];
272 my $last = $d->{items}->[$#{$d->{items}}];
274 my $ffirst = $c->{items}->[0];
275 my $flast = $c->{items}->[$#{$c->{items}}];
276 my $rfirst = $registry->{index}->{"type:$ffirst->{baseType}"};
277 my $rlast = $registry->{index}->{"type:$flast->{baseType}"};
279 my $isstatic = ($rfirst->{type} ne 'VK_DEFINE_HANDLE')
280 && ($rfirst->{fullType} =~ tr/*/*/ == 0);
282 my $iscreate = ($flast->{fullType} =~ tr/*/*/==1)
283 && !($flast->{fullType} =~ m/const/)
285 && ($rlast->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
287 $d->{vkiscreate} = $iscreate;
288 $d->{vkisstatic} = $isstatic;
290 if ($iscreate && $isstatic) {
291 # fuck, all this work for vkCreateInstance()
294 $d->{return} = $last;
295 $d->{scope} = $last->{scope} = 'explicit'; # blah, for now
296 $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance');
298 print "edit: static create: $c->{name}\n";
299 } elsif ($ffirst->{fullType} eq $ffirst->{baseType}
300 && $rfirst->{type} eq 'VK_DEFINE_HANDLE') {
302 $first->{output} = 0;
303 $first->{instance} = 1;
306 print "edit: member function: $c->{name}\n";
308 # member create function
312 $d->{return} = $last;
313 $d->{scope} = $last->{scope} = 'explicit';
314 $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance' && $rlast->{type} eq 'VK_DEFINE_HANDLE');
316 print "edit: member create: $c->{name}\n";
320 if ($flast->{len} && $flast->{fullType} =~ tr/*/*/ == 1
321 #&& $rlast->{type} eq 'VK_DEFINE_HANDLE')
324 print "edit: array $c->{name} ($flast->{name})\n";
327 # Link in successcodes (failure codes?)
328 if (defined($c->{successcodes})) {
329 $d->{success} = $d->{result};
330 $d->{success}->{success} = join ',', map { "VkConstants.$_" } split /,/,$c->{successcodes};
331 $d->{result}->{output} = 0;
333 print "edit: successcodes: $c->{name}: $c->{successcodes}\n";
335 # output return value if there is more than 1 success code
336 if ($c->{successcodes} =~ tr/,/,/ >= 1 && !defined($d->{return})) {
337 $d->{result}->{output} = 1;
347 # - which fields should be included in a set-all constructor
348 # - auto handling of arrays-with-length
349 # - handling of String[]
350 # - access modes for members
351 # - set-all constructor format hooks
354 my $registry = shift;
356 foreach my $r (grep { $_->{category} =~ m/struct|union/ && defined $_->{vkheader}} values %{$registry->{data}}) {
357 my $s = $r->{vkheader};
360 map { $index->{$_->{name}} = $_ } @{$s->{items}};
362 # # Find out which fields should be included in a 'set-all' constructor
363 # for my $m (@{$s->{items}}) {
364 # if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
365 # my $embed = $api->{data}->{"struct:$1"};
366 # # TODO: need to work out how to set the fields first
367 # #if ($#{$embed->{items}} < 6) {
368 # # $m->{'set-all-struct'} = "struct:$1";
370 # # $m->{vkset} = { type => $1 } ?
371 # } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
372 # # 1D primitive array
374 # $m->{vkset} = { select => { vkarray => 1 } };
376 # } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
377 # # 2D primitive array
378 # if (($1 * $2) <= 16) {
379 # $m->{vkset} = { select => { vkarray => 1 } };
382 # # pointers or other primitives, probably
387 # Special handling for types with 'len' specified
388 # TODO: not sure if i want this in general or just set-all constructor
389 for my $x (@{$r->{items}}) {
390 my $m = $index->{$x->{name}};
392 # if there's a len specified but ignore if set in .api file
393 if ($x->{len} && !($m->{array} || $m->{'array-size'} || $m->{'array-size-source'})) {
394 my @len = split ',', $x->{len};
396 if ($#len > 0 && $len[1] eq 'null-terminated') {
398 my $size = $index->{$len[0]};
401 $size->{implied} = "Memory.length($m->{name})";
402 $m->{select}->{vkstring} = 1;
403 } elsif ($len[0] =~ m/^[a-zA-Z]+$/) {
404 # Simple length referencing a parameter/field
405 my $size = $index->{$len[0]};
407 $size->{'array-size-source'} = $m;
408 #$size->{output} = 0;
409 $m->{'array-size'} = $size;
410 #$m->{select}->{vkstring} = 1;
411 $size->{select}->{'array-size-source'} = 1;
412 $m->{select}->{'array-size'} = 1;
414 # need to know what type it is to get correct length, array, struct[], etc
415 $size->{implied} = "Memory.length($m->{name})";
416 #$size->{implied} = "$size->{name}";
418 # use HandleArray<> rather than PointerArray<> for handles
419 if ($m->{deref} =~ m/^u64:u64:\$\{(.*)\}/) {
420 my $t = $api->{data}->{"struct:$1"}; die "no type $1" if !$t;
421 my $r = $t->{vkregistry};
423 $m->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
425 $m->{select}->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
431 # setup accessor mode (if not already set), and format hook for the set-all constructor
432 if ($r->{returnedonly} eq 'true') {
433 $s->{access} = 'r' if !$s->{vkaccess};
434 map { $_->{access} = 'r' } @{$s->{items}};
435 } elsif ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
436 $s->{access} = 'w' if !$s->{vkaccess};
437 push @{$s->{postformat}}, sub { formatConstructor(@_) };
439 $s->{access} = 'rw' if !$s->{vkaccess};
440 push @{$s->{postformat}}, sub { formatConstructor(@_) };
442 # propagate access mode.
443 map { $_->{access} = $s->{access} } @{$s->{items}};
445 if ($s->{name} eq 'VkTransformMatrix') {
446 print "yyy ".Dumper($s);
453 # TBD now in vkregistry.pm
454 sub findRegistryObject {
460 my $f = $data->{$func};
461 return $f if defined $f;
462 if ($func =~ m/^(.*)_T$/) {
464 return $f if defined $f;
466 #print "alias $func => $alias->{$func}\n";
467 $func = $alias->{$func};
473 # a basic struct, i.e. not a handle
477 my $r = $s->{vkregistry};
480 && $r->{type} ne 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
481 && $r->{type} ne 'VK_DEFINE_HANDLE';
484 # TODO: info required for these is evaluated in analyse
485 # find all functions where the first parameter is a pointer to a dispatchable handle type
486 sub matchObjectFunction {
490 return $c->{vkobject} eq $s->{name};
492 # my $items = $c->{items};
493 # my $r = $s->{vkregistry};
495 # die "vkregistry field missing ".Dumper($s) if !defined($r);
497 # return $r->{type} eq 'VK_DEFINE_HANDLE'
498 # #&& !$registry->{alias}->{"type:$s->{name}"}
499 # && defined($items->[0])
500 # && $items->[0]->{deref} eq "u64:\${$s->{name}}";
503 # a static create function
504 sub matchCreateFunction {
507 my $items = $c->{items};
508 my $r = $s->{vkregistry};
510 if (($c->{vkiscreate})
511 && ($c->{vkisstatic})
512 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}")) {
513 print "match-create: $c->{name}\n";
516 return ($c->{vkiscreate})
517 && ($c->{vkisstatic})
518 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}");
521 # last argument is a **handle
523 return $c->{name} =~ m/^vkCreate/
524 && $r->{type} eq 'VK_DEFINE_HANDLE'
525 && defined($items->[$#$items])
526 && $items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}";
534 if ($name eq 'pGeometries' && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
535 # small hack to fix rename conflict
536 $name = 'Geometries0';
537 } elsif ($m->{deref} =~ m/^((u64:){1,})\$\{(?<type>.*)\}$/) {
538 my $len = length($1)/4;
539 my $r = $registry->{index}->{"type:$+{type}"};
541 # strip leading 'p' for pointer, handles are void * so ignore one level
542 $len = $len - 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
543 $name = substr $name, $len;
544 } elsif ($m->{deref} =~ m/^((u64:){1,})/) {
545 # strip leading 'p' for pointer
546 $name = substr $name, length($1)/4;
550 $name =~ s/(?:^|_)(.)/\U$1/g;
555 # collect args for 'all' constructor
561 my @members = @{$s->{items}};
563 # Drop type and next fields
564 if ($#members >= 1 && $members[0]->{name} eq 'sType' && $members[1]->{name} eq 'pNext') {
565 @members = @members[2 .. $#members];
568 #print "collect $s->{name}\n";
569 foreach my $m (@members) {
572 if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
573 my $embed = $api->{data}->{"struct:$1"};
574 # TODO: need to work out how to set the fields first
575 } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
577 $n = { %{$m}, select => { vkarray => 1 } } if ($1 <= 16);
578 } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
580 $n = { %{$m}, select => { vkarray => 1 } } if (($1 * $2) <= 16);
581 } elsif ($m->{'array-size-source'} || $m->{implied}) {
582 #$n = { %{$m}, output=>0, implied => "Memory.length($m->{'array-size-source'}->{name})" };
583 $n = { %{$m}, output=>0 };
591 $n->{name} = $prefix ? $prefix.'$'.$m->{name} : $m->{name};
592 $n->{subtype} = $s->{name};
593 $n->{subname} = $m->{name};
594 $n->{localname} = $prefix;
600 # if (defined $f->{vkset}) {
603 # $a->{name} = $prefix ? $prefix.'$'.$f->{name} : $f->{name};
604 # $a->{subtype} = $s->{name};
605 # $a->{subname} = $f->{name};
606 # $a->{localname} = $prefix;
608 # $a->{select} = $f->{vkset}->{select} if defined $f->{vkset}->{select};
609 # push @{$items}, $a;
610 # } elsif ($f->{'set-all-struct'}) {
611 # die "unimplemented";
612 # my $e = $api->{data}->{$f->{'set-all-struct'}};
613 # collectAllArgs($api, $items, $e, $prefix ? $prefix.'$'.$f->{name} : $f->{name});
620 return grep { !$seen{$_}++ } @_;
623 sub format1Constructor {
630 my $info = new method($api, $c);
631 my $init = join "\n\t\t", map {
632 "MemorySegment $_\$segment = (MemorySegment)$_\$SH.invokeExact(self\$.segment);"
634 $_->{field}->{localname} ? $_->{field}->{localname} : ()
635 } @{$info->{arguments}};
637 my $setAll = join ";\n\t\t", map {
638 my $v = { %{$_->{match}}, value => $_->{field}->{name} };
639 if (defined ($_->{field}->{subtype})) {
640 $v->{name} = $_->{field}->{subtype}.'.'.$_->{field}->{subname};
641 $v->{segment} = $_->{field}->{localname}.'$segment';
643 $v->{segment} = 'self$.segment';
646 $v->{tonative} = "({type})$_->{field}->{implied}" if $_->{field}->{implied};
647 code::formatTemplate($_->{match}->{setnative}, $v);
648 } @{$info->{arguments}};
651 my $template = $api->findTemplateName('code:vulkan=create-all');
655 foreach my $l (split /\n/,Dumper($info)) {
660 $code .= code::applyTemplate($template, { %{$info->{vars}}, 'set-all' => $setAll, 'set-init' => $init});
662 push @{$res->{func}}, $code;
665 # Create an 'all-set' constructor
666 # This creates a pseudo-function and formats that
667 # This also sets up the code to initialise sType
668 sub formatConstructor {
673 my $r = $s->{vkregistry};
675 return if $s->{name} =~ m/^(VkBaseInStructure|VkBaseOutStructure)$/on;
677 if ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
678 push @{$res->{init}}, "// FIXME: array init?\n$r->{items}->[0]->{name}\$VH.set(segment, VkConstants.$r->{items}->[0]->{values});";
681 if ($s->{type} eq 'union') {
682 foreach my $u (grep { defined($_->{vkset}) } @{$s->{items}}) {
685 if ($u->{vkset}->{struct}) {
689 $a->{select} = $u->{vkset}->{select} if defined $u->{vkset}->{select};
694 type => "$s->{type}:$s->{name}",
695 deref => "u64:\$\{$s->{name}\}",
700 rename => $u->{rename},
702 format1Constructor($api, $obj, $res, $s, $c);
707 collectAllArgs($api, $args, $s, "");
709 if ($#{$args} >= 0) {
712 type => "$s->{type}:$s->{name}",
713 deref => "u64:\$\{$s->{name}\}",
720 format1Constructor($api, $obj, $res, $s, $c);