Some work on the header version of vulkan binding.
[panamaz] / src / notzed.vkheader / gen / vkheader.pm
1 #
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#
4
5 package vkheader;
6
7 use strict;
8
9 use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
10
11 use Data::Dumper;
12 use XML::Parser;
13
14 use vulkan;
15
16 use code;
17 use method;
18
19 use Carp;
20
21 my $registry;
22
23 sub init {
24         my $api = shift;
25
26         $registry = new vulkan();
27
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}"};
31
32                 print "Add vkhandle: $r->{name} ($r->{type})\n";
33                 my $s = {
34                         name => "$r->{name}_T",
35                         rename => $r->{name},
36                         type => 'struct',
37                         size => 0,
38                         items => [],
39                         output => 1,
40                         vkregistry => $r,
41                 };
42
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';
47                 }
48
49                 $r->{vkheader} = $s;
50                 $api->{data}->{"struct:$r->{name}_T"} = $s;
51                 #$api->{databytype}->{'struct'}->{"$r->{name}_T"} = $s;
52         }
53
54         # link api to registry
55         my @delete = ();
56         foreach my $s (values %{$api->{data}}) {
57                 my $r = $registry->{index}->{"type:$s->{name}"};
58
59                 if ($registry->{alias}->{"type:$s->{name}"} && !($s->{name} =~ m/_T$/)) {
60                         # WTF will this break though
61                         print "Suppress alias: $s->{name}\n";
62                         $s->{output} = 0;
63                         push @delete, "$s->{type}:$s->{name}";
64                 } else {
65                         $s->{vkregistry} = $r if defined $r;
66                         $r->{vkheader} = $s if defined $r;
67                 }
68         }
69
70         map { delete $api->{data}->{$_} } @delete;
71         #map { m/^(.*):(.*)$/; delete $api->{databytype}->{$1}->{$2} } @delete;
72
73         # create psuedo-define of api constants
74         my %typeMap = (
75                 uint32_t => 'u32',
76                 uint64_t => 'u64',
77                 float => 'f32'
78         );
79         my $d = $registry->{data}->{'define:API Constants'};
80         my $apiConstants = {
81                 name => 'api-constants',
82                 type => 'define',
83                 items => []
84         };
85         foreach my $m (@{$d->{items}}) {
86                 my $v = $m->{value};
87
88                 $v =~ s/LL//;
89                 $v =~ s/U|F|\(|\)//g;
90
91                 die "Unknown constant type: $m->{type}", if !defined $typeMap{$m->{type}};
92
93                 push @{$apiConstants->{items}}, {
94                         name => $m->{name},
95                         value => $v,
96                         type => $typeMap{$m->{type}}
97                 };
98         }
99         $api->{data}->{'define:api-constants'} = $apiConstants;
100         #$api->{databytype}->{'define'}->{'api-constants'} = $apiConstants;
101
102         # mark all functions for matchObjectFunction
103         foreach my $c (grep { $_->{type} eq 'func' } values %{$api->{data}}) {
104                 my $items = $c->{items};
105
106                 if ($items->[0]->{deref} =~ m/^u64:\$\{(.*)\}$/) {
107                         my $type = $registry->{index}->{"type:$1"};
108                         if (defined ($type) && $type->{type} eq 'VK_DEFINE_HANDLE') {
109                                 $c->{vkobject} = $1;
110                         }
111                 }
112         }
113
114         1;
115 }
116
117 sub postprocess {
118         my $api = shift;
119
120         analyseFunctions($api, $registry);
121         analyseInOut($api, $registry);
122         analyseStructs($api, $registry);
123
124         1;
125 }
126
127
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
132 sub analyseInOut {
133         my $api = shift;
134         my $registry = shift;
135         my $output = {};
136         my $arrays = {};
137
138         foreach my $c (grep { $_->{category} =~ m/command|struct|union/ && defined $_->{vkheader} } values %{$registry->{data}}) {
139                 my $d = $c->{vkheader};
140                 my $dindex = {};
141
142                 map { $dindex->{$_->{name}} = $_ } @{$d->{items}};
143
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}};
148
149                         die if !defined $n;
150                         next if !defined $r;
151
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+/;
155
156                                 # && $m->{fullType} =~ tr/*/*/ == 1;
157                         } elsif ($r->{category} eq 'handle') {
158                                 $n->{array} = 1 if ($m->{len} || $m->{altlen});
159                         }
160                 }
161         }
162
163         foreach my $fuckoff ('VkBaseInStructure', 'VkBaseOutStructure') {
164                 delete $arrays->{$fuckoff};
165                 delete $output->{$fuckoff};
166         }
167
168         my $out = {
169                 'VkDebugUtilsMessengerCallbackDataEXT' => $registry->{index}->{'type:VkDebugUtilsMessengerCallbackDataEXT'},
170         };
171         my $inout = {
172                 'VkComputePipelineCreateInfo' => $registry->{index}->{'type:VkComputePipelineCreateInfo'},
173         };
174         my $in = {};
175
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;
179                 } else {
180                         $in->{$r->{name}} = $r;
181                 }
182         }
183
184         foreach my $r (values %{$output}) {
185                 print "check $r->{name}\n";
186                 if ($r->{returnedonly} ne 'true') {
187                         my $x = {};
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;
193                         }
194
195                 }
196         }
197
198         my $set = sub  {
199                 my $r = shift;
200                 my $rw = shift;
201                 my $s = $r->{vkheader};
202
203                 die "Missing vkheader ".Dumper($r) if !defined $s;
204
205                 $rw = $rw."i" if $arrays->{$r->{name}};
206
207                 print "edit: array-struct $s->{name}\n" if $arrays->{$r->{name}};
208
209                 $s->{vkaccess}= $rw;
210                 $s->{access} = $rw;
211                 $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}};
212         };
213
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};
217
218         map { $set->($_, 'rw') } values %{$inout};
219         map { $set->($_, 'w') } values %{$in};
220         map { $set->($_, 'r') } values %{$out};
221 }
222
223 # dump all types in this type recursively into %{$record}
224 sub analyseInOutStruct {
225         my $registry = shift;
226         my $record = shift;
227         my $baseType = shift;
228         my $s = $registry->{index}->{"type:$baseType"};
229
230         #if (!defined $s) {
231         #       print join "\n", sort(map { $_->{name} } values %{$registry->{data}});
232         #}
233         die "no match for $baseType\n" if !defined $s;
234
235         if (defined $s && !defined $s->{structextends} && $s->{category} =~ m/struct|union/n) {
236                 $record->{$baseType} = $s;
237
238                 foreach my $m (@{$s->{items}}) {
239                         analyseInOutStruct($registry, $record, $m->{baseType});
240                 }
241         }
242         if (defined $s && defined $s->{structextends}) {
243                 print "extends? $baseType $s->{structextends}\n";
244         }
245
246 }
247
248 # analyse functions
249 # - configure member functions
250 # - configure constructors
251 sub analyseFunctions {
252         my $api = shift;
253         my $registry = shift;
254
255         # TODO: arrays etc
256
257         # Scan functions
258         foreach my $c (grep { $_->{category} eq 'command' && defined $_->{vkheader} } values %{$registry->{data}}) {
259                 my $d = $c->{vkheader};
260
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";
268                 }
269
270                 # look for constructors and member functions
271                 my $first = $d->{items}->[0];
272                 my $last = $d->{items}->[$#{$d->{items}}];
273
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}"};
278
279                 my $isstatic = ($rfirst->{type} ne 'VK_DEFINE_HANDLE')
280                         && ($rfirst->{fullType} =~ tr/*/*/ == 0);
281
282                 my $iscreate = ($flast->{fullType} =~ tr/*/*/==1)
283                         && !($flast->{fullType} =~ m/const/)
284                         && !($flast->{len})
285                         && ($rlast->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
286
287                 $d->{vkiscreate} = $iscreate;
288                 $d->{vkisstatic} = $isstatic;
289
290                 if ($iscreate && $isstatic) {
291                         # fuck, all this work for vkCreateInstance()
292                         $last->{output} = 0;
293                         $last->{return} = 1;
294                         $d->{return} = $last;
295                         $d->{scope} = $last->{scope} = 'explicit'; # blah, for now
296                         $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance');
297
298                         print "edit: static create: $c->{name}\n";
299                 } elsif ($ffirst->{fullType} eq $ffirst->{baseType}
300                                  && $rfirst->{type} eq 'VK_DEFINE_HANDLE') {
301                         # member functions
302                         $first->{output} = 0;
303                         $first->{instance} = 1;
304                         $d->{static} = 0;
305
306                         print "edit: member function: $c->{name}\n";
307
308                         # member create function
309                         if ($iscreate) {
310                                 $last->{output} = 0;
311                                 $last->{return} = 1;
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');
315
316                                 print "edit: member create: $c->{name}\n";
317                         }
318                 }
319
320                 if ($flast->{len} && $flast->{fullType} =~ tr/*/*/ == 1
321                         #&& $rlast->{type} eq 'VK_DEFINE_HANDLE')
322                         ) {
323                         $last->{array} = 1;
324                         print "edit: array $c->{name} ($flast->{name})\n";
325                 }
326
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;
332
333                         print "edit: successcodes: $c->{name}: $c->{successcodes}\n";
334
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;
338                         }
339                 }
340         }
341
342 #       die;
343
344 }
345
346 # analyse structures
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
352 sub analyseStructs {
353         my $api = shift;
354         my $registry = shift;
355
356         foreach my $r (grep { $_->{category} =~ m/struct|union/ && defined $_->{vkheader}} values %{$registry->{data}}) {
357                 my $s = $r->{vkheader};
358                 my $index = {};
359
360                 map { $index->{$_->{name}} = $_ } @{$s->{items}};
361
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";
369                 #               #}
370                 #               # $m->{vkset} = { type => $1 } ?
371                 #       } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
372                 #               # 1D primitive array
373                 #               if ($1 <= 16) {
374                 #                       $m->{vkset} = { select => { vkarray => 1 } };
375                 #               }
376                 #       } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
377                 #               # 2D primitive array
378                 #               if (($1 * $2) <= 16) {
379                 #                       $m->{vkset} = { select => { vkarray => 1 } };
380                 #               }
381                 #       } else {
382                 #               # pointers or other primitives, probably
383                 #               $m->{vkset} = { };
384                 #       }
385                 # }
386
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}};
391
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};
395
396                                 if ($#len > 0 && $len[1] eq 'null-terminated') {
397                                         # String[], probably
398                                         my $size = $index->{$len[0]};
399
400                                         # for string[]
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]};
406
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;
413
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}";
417
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};
422
423                                                 $m->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
424
425                                                 $m->{select}->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
426                                         }
427                                 }
428                         }
429                 }
430
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(@_) };
438                 } else {
439                         $s->{access} = 'rw'  if !$s->{vkaccess};
440                         push @{$s->{postformat}}, sub { formatConstructor(@_) };
441                 }
442                 # propagate access mode.
443                 map { $_->{access} = $s->{access} } @{$s->{items}};
444
445                 if ($s->{name} eq 'VkTransformMatrix') {
446                         print "yyy ".Dumper($s);
447                         die;
448                 }
449
450         }
451 }
452
453 # TBD now in vkregistry.pm
454 sub findRegistryObject {
455         my $data = shift;
456         my $alias = shift;
457         my $func = shift;
458
459         do {
460                 my $f = $data->{$func};
461                 return $f if defined $f;
462                 if ($func =~ m/^(.*)_T$/) {
463                         $f = $data->{$1};
464                         return $f if defined $f;
465                 }
466                 #print "alias $func => $alias->{$func}\n";
467                 $func = $alias->{$func};
468         } while ($func);
469
470         return undef;
471 }
472
473 # a basic struct, i.e. not a handle
474 sub matchStruct {
475         my $s = shift;
476         my $ctx = shift;
477         my $r = $s->{vkregistry};
478
479         return defined($r)
480                 && $r->{type} ne 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
481                 && $r->{type} ne 'VK_DEFINE_HANDLE';
482 }
483
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 {
487         my $c = shift;
488         my $s = shift;
489
490         return $c->{vkobject} eq $s->{name};
491
492 #       my $items = $c->{items};
493 #       my $r = $s->{vkregistry};
494
495 #       die "vkregistry field missing ".Dumper($s) if !defined($r);
496
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}}";
501 }
502
503 # a static create function
504 sub matchCreateFunction {
505         my $c = shift;
506         my $s = shift;
507         my $items = $c->{items};
508         my $r = $s->{vkregistry};
509
510         if (($c->{vkiscreate})
511                 && ($c->{vkisstatic})
512                 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}")) {
513                 print "match-create: $c->{name}\n";
514         }
515
516         return ($c->{vkiscreate})
517                 && ($c->{vkisstatic})
518                 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}");
519
520         # class is a handle
521         # last argument is a **handle
522
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}}";
527 }
528
529 sub renameField {
530         my $name = shift;
531         my $m = shift;
532         my $s = shift;
533
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}"};
540
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;
547         }
548
549         # studly-caps
550         $name =~ s/(?:^|_)(.)/\U$1/g;
551
552         return $name;
553 }
554
555 # collect args for 'all' constructor
556 sub collectAllArgs {
557         my $api = shift;
558         my $items = shift;
559         my $s = shift;
560         my $prefix = shift;
561         my @members = @{$s->{items}};
562
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];
566         }
567
568         #print "collect $s->{name}\n";
569         foreach my $m (@members) {
570                 my $n;
571
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+)\]/) {
576                         # 1D primitive array
577                         $n = { %{$m}, select => { vkarray => 1 } } if ($1 <= 16);
578                 } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
579                         # 2D primitive array
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 };
584                 } else {
585                         # everything else
586                         $n = { %{$m} };
587                 }
588
589                 if (defined($n)) {
590                         if ($prefix) {
591                                 $n->{name} = $prefix ? $prefix.'$'.$m->{name} : $m->{name};
592                                 $n->{subtype} = $s->{name};
593                                 $n->{subname} = $m->{name};
594                                 $n->{localname} = $prefix;
595                         }
596                         push @{$items}, $n;
597                 }
598         }
599
600         #       if (defined $f->{vkset}) {
601         #               my $a = { %{$f} };
602         #               if ($prefix) {
603         #                       $a->{name} = $prefix ? $prefix.'$'.$f->{name} : $f->{name};
604         #                       $a->{subtype} = $s->{name};
605         #                       $a->{subname} = $f->{name};
606         #                       $a->{localname} = $prefix;
607         #               }
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});
614         #       }
615         # }
616 }
617
618 sub uniq {
619   my %seen;
620   return grep { !$seen{$_}++ } @_;
621 }
622
623 sub format1Constructor {
624         my $api = shift;
625         my $obj = shift;
626         my $res = shift;
627         my $s = shift;
628         my $c = shift;
629
630         my $info = new method($api, $c);
631         my $init = join "\n\t\t", map {
632                 "MemorySegment $_\$segment = (MemorySegment)$_\$SH.invokeExact(self\$.segment);"
633         } uniq map {
634                 $_->{field}->{localname} ? $_->{field}->{localname} : ()
635         } @{$info->{arguments}};
636
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';
642                 } else {
643                         $v->{segment} = 'self$.segment';
644                 }
645                 # hack for string[]
646                 $v->{tonative} = "({type})$_->{field}->{implied}" if $_->{field}->{implied};
647                 code::formatTemplate($_->{match}->{setnative}, $v);
648         } @{$info->{arguments}};
649         $setAll .= ";";
650
651         my $template = $api->findTemplateName('code:vulkan=create-all');
652         my $code;
653
654         if (0) {
655                 foreach my $l (split /\n/,Dumper($info)) {
656                         $code .= "//$l\n";
657                 }
658         }
659
660         $code .= code::applyTemplate($template, { %{$info->{vars}}, 'set-all' => $setAll, 'set-init' => $init});
661
662         push @{$res->{func}}, $code;
663 }
664
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 {
669         my $api = shift;
670         my $obj = shift;
671         my $res = shift;
672         my $s = shift;
673         my $r = $s->{vkregistry};
674
675         return if $s->{name} =~ m/^(VkBaseInStructure|VkBaseOutStructure)$/on;
676
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});";
679         }
680
681         if ($s->{type} eq 'union') {
682                 foreach my $u (grep { defined($_->{vkset}) } @{$s->{items}}) {
683                         my $args = [];
684
685                         if ($u->{vkset}->{struct}) {
686                                 die "unimplemented";
687                         } else {
688                                 my $a = { %{$u} };
689                                 $a->{select} = $u->{vkset}->{select} if defined $u->{vkset}->{select};
690                                 push @{$args}, $a;
691                         }
692                         my $c = {
693                                 result => {
694                                         type => "$s->{type}:$s->{name}",
695                                         deref => "u64:\$\{$s->{name}\}",
696                                         name => 'result$',
697                                         output => 1,
698                                 },
699                                 items => $args,
700                                 rename => $u->{rename},
701                         };
702                         format1Constructor($api, $obj, $res, $s, $c);
703                 }
704         } else {
705                 my $args = [];
706
707                 collectAllArgs($api, $args, $s, "");
708
709                 if ($#{$args} >= 0) {
710                         my $c = {
711                                 result => {
712                                         type => "$s->{type}:$s->{name}",
713                                         deref => "u64:\$\{$s->{name}\}",
714                                         name => 'result$',
715                                         output => 1,
716                                 },
717                                 items => $args,
718                                 rename => "",
719                         };
720                         format1Constructor($api, $obj, $res, $s, $c);
721                 }
722         }
723 }
724
725 1;