bee1f3405c40ba188b2e72386ee5d8c7691d1051
[panamaz] / src / notzed.vkheader / gen / vkheader.pm
1 #
2 # TODO: constructors initialise sType
3 #
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         }
52
53         # link api to registry
54         my @delete = ();
55         foreach my $s (values %{$api->{data}}) {
56                 my $r = $registry->{index}->{"type:$s->{name}"};
57
58                 if ($registry->{alias}->{"type:$s->{name}"} && !($s->{name} =~ m/_T$/)) {
59                         # WTF will this break though
60                         print "Suppress alias: $s->{name}\n";
61                         $s->{output} = 0;
62                         push @delete, "$s->{type}:$s->{name}";
63                 } else {
64                         $s->{vkregistry} = $r if defined $r;
65                         $r->{vkheader} = $s if defined $r;
66                 }
67         }
68
69         map { delete $api->{data}->{$_} } @delete;
70
71         1;
72 }
73
74 sub postprocess {
75         my $api = shift;
76
77         analyseFunctions($api, $registry);
78         analyseInOut($api, $registry);
79         analyseStructs($api, $registry);
80
81         1;
82 }
83
84
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
89 sub analyseInOut {
90         my $api = shift;
91         my $registry = shift;
92         my $output = {};
93         my $arrays = {};
94
95         foreach my $c (grep { $_->{category} =~ m/command|struct|union/ && defined $_->{vkheader} } values %{$registry->{data}}) {
96                 my $d = $c->{vkheader};
97                 my $dindex = {};
98
99                 map { $dindex->{$_->{name}} = $_ } @{$d->{items}};
100
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}};
105
106                         die if !defined $n;
107                         next if !defined $r;
108
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+/;
112
113                                 # && $m->{fullType} =~ tr/*/*/ == 1;
114                         } elsif ($r->{category} eq 'handle') {
115                                 $n->{array} = 1 if ($m->{len} || $m->{altlen});
116                         }
117                 }
118         }
119
120         foreach my $fuckoff ('VkBaseInStructure', 'VkBaseOutStructure') {
121                 delete $arrays->{$fuckoff};
122                 delete $output->{$fuckoff};
123         }
124
125         my $out = {
126                 'VkDebugUtilsMessengerCallbackDataEXT' => $registry->{index}->{'type:VkDebugUtilsMessengerCallbackDataEXT'},
127         };
128         my $inout = {
129                 'VkComputePipelineCreateInfo' => $registry->{index}->{'type:VkComputePipelineCreateInfo'},
130         };
131         my $in = {};
132
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;
136                 } else {
137                         $in->{$r->{name}} = $r;
138                 }
139         }
140
141         foreach my $r (values %{$output}) {
142                 print "check $r->{name}\n";
143                 if ($r->{returnedonly} ne 'true') {
144                         my $x = {};
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;
150                         }
151
152                 }
153         }
154
155         my $set = sub  {
156                 my $r = shift;
157                 my $rw = shift;
158                 my $s = $r->{vkheader};
159
160                 die "Missing vkheader ".Dumper($r) if !defined $s;
161
162                 $rw = $rw."i" if $arrays->{$r->{name}};
163
164                 $s->{vkaccess}= $rw;
165                 $s->{access} = $rw;
166                 $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}};
167         };
168
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};
172
173         map { $set->($_, 'rw') } values %{$inout};
174         map { $set->($_, 'w') } values %{$in};
175         map { $set->($_, 'r') } values %{$out};
176 }
177
178 # dump all types in this type recursively into %{$record}
179 sub analyseInOutStruct {
180         my $registry = shift;
181         my $record = shift;
182         my $baseType = shift;
183         my $s = $registry->{index}->{"type:$baseType"};
184
185         #if (!defined $s) {
186         #       print join "\n", sort(map { $_->{name} } values %{$registry->{data}});
187         #}
188         die "no match for $baseType\n" if !defined $s;
189
190         if (defined $s && !defined $s->{structextends} && $s->{category} =~ m/struct|union/n) {
191                 $record->{$baseType} = $s;
192
193                 foreach my $m (@{$s->{items}}) {
194                         analyseInOutStruct($registry, $record, $m->{baseType});
195                 }
196         }
197         if (defined $s && defined $s->{structextends}) {
198                 print "extends? $baseType $s->{structextends}\n";
199         }
200
201 }
202
203 # analyse functions
204 # - configure member functions
205 # - configure constructors
206 sub analyseFunctions {
207         my $api = shift;
208         my $registry = shift;
209
210         # TODO: arrays etc
211
212         # Scan functions
213         foreach my $c (grep { $_->{category} eq 'command' && defined $_->{vkheader} } values %{$registry->{data}}) {
214                 my $d = $c->{vkheader};
215
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';
222                 }
223
224                 # look for constructors and member functions
225                 my $first = $d->{items}->[0];
226                 my $last = $d->{items}->[$#{$d->{items}}];
227
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}"};
232
233                 my $isstatic = ($rfirst->{type} ne 'VK_DEFINE_HANDLE')
234                         && ($rfirst->{fullType} =~ tr/*/*/ == 0);
235
236                 my $iscreate = ($flast->{fullType} =~ tr/*/*/==1)
237                         && !($flast->{fullType} =~ m/const/)
238                         && !($flast->{len})
239                         && ($rlast->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
240
241                 $d->{vkiscreate} = $iscreate;
242                 $d->{vkisstatic} = $isstatic;
243
244                 if ($iscreate && $isstatic) {
245                         # fuck, all this work for vkCreateInstance()
246                         $last->{output} = 0;
247                         $last->{return} = 1;
248                         $d->{return} = $last;
249                         $d->{scope} = $last->{scope} = 'explicit'; # blah, for now
250                         $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance');
251
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') {
255                         # member functions
256                         $first->{output} = 0;
257                         $first->{instance} = 1;
258                         $d->{static} = 0;
259
260                         # member create function
261                         if ($iscreate) {
262                                 $last->{output} = 0;
263                                 $last->{return} = 1;
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');
267
268                                 print "xxxb $ffirst->{baseType}.$c->{name} $last->{deref}  $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n";
269                         }
270                 }
271
272                 if ($flast->{len} && $flast->{fullType} =~ tr/*/*/ == 1
273                         #&& $rlast->{type} eq 'VK_DEFINE_HANDLE')
274                         ) {
275                         $last->{array} = 1;
276                 }
277
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;
283
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;
287                         }
288                 }
289         }
290
291 #       die;
292
293 }
294
295 # analyse structures
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
301 sub analyseStructs {
302         my $api = shift;
303         my $registry = shift;
304
305         foreach my $r (grep { $_->{category} =~ m/struct|union/ && defined $_->{vkheader}} values %{$registry->{data}}) {
306                 my $s = $r->{vkheader};
307                 my $index = {};
308
309                 map { $index->{$_->{name}} = $_ } @{$s->{items}};
310
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";
318                 #               #}
319                 #               # $m->{vkset} = { type => $1 } ?
320                 #       } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
321                 #               # 1D primitive array
322                 #               if ($1 <= 16) {
323                 #                       $m->{vkset} = { select => { vkarray => 1 } };
324                 #               }
325                 #       } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
326                 #               # 2D primitive array
327                 #               if (($1 * $2) <= 16) {
328                 #                       $m->{vkset} = { select => { vkarray => 1 } };
329                 #               }
330                 #       } else {
331                 #               # pointers or other primitives, probably
332                 #               $m->{vkset} = { };
333                 #       }
334                 # }
335
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}};
340
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};
344
345                                 if ($#len > 0 && $len[1] eq 'null-terminated') {
346                                         # String[], probably
347                                         my $size = $index->{$len[0]};
348
349                                         # for string[]
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]};
355
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;
362
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}";
366
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};
371
372                                                 $m->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
373
374                                                 $m->{select}->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
375                                         }
376                                 }
377                         }
378                 }
379
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(@_) };
387                 } else {
388                         $s->{access} = 'rw'  if !$s->{vkaccess};
389                         push @{$s->{postformat}}, sub { formatConstructor(@_) };
390                 }
391                 # propagate access mode.
392                 map { $_->{access} = $s->{access} } @{$s->{items}};
393
394                 if ($s->{name} eq 'VkTransformMatrix') {
395                         print "yyy ".Dumper($s);
396                         die;
397                 }
398
399         }
400 }
401
402 # TBD now in vkregistry.pm
403 sub findRegistryObject {
404         my $data = shift;
405         my $alias = shift;
406         my $func = shift;
407
408         do {
409                 my $f = $data->{$func};
410                 return $f if defined $f;
411                 if ($func =~ m/^(.*)_T$/) {
412                         $f = $data->{$1};
413                         return $f if defined $f;
414                 }
415                 #print "alias $func => $alias->{$func}\n";
416                 $func = $alias->{$func};
417         } while ($func);
418
419         return undef;
420 }
421
422 # a basic struct, i.e. not a handle
423 sub matchStruct {
424         my $s = shift;
425         my $ctx = shift;
426         my $r = $s->{vkregistry};
427
428         return defined($r)
429                 && $r->{type} ne 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
430                 && $r->{type} ne 'VK_DEFINE_HANDLE';
431 }
432
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 {
436         my $c = shift;
437         my $s = shift;
438         my $items = $c->{items};
439         my $r = $s->{vkregistry};
440
441         die "vkregistry field missing ".Dumper($s) if !defined($r);
442
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}}";
447 }
448
449 # a static create function
450 sub matchCreateFunction {
451         my $c = shift;
452         my $s = shift;
453         my $items = $c->{items};
454         my $r = $s->{vkregistry};
455
456         return ($c->{vkiscreate})
457                 && ($c->{vkisstatic})
458                 && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}");
459
460         # class is a handle
461         # last argument is a **handle
462
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}}";
467 }
468
469 sub renameField {
470         my $name = shift;
471         my $m = shift;
472         my $s = shift;
473
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}"};
480
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;
487         }
488
489         # studly-caps
490         $name =~ s/(?:^|_)(.)/\U$1/g;
491
492         return $name;
493 }
494
495 # collect args for 'all' constructor
496 sub collectAllArgs {
497         my $api = shift;
498         my $items = shift;
499         my $s = shift;
500         my $prefix = shift;
501         my @members = @{$s->{items}};
502
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];
506         }
507
508         #print "collect $s->{name}\n";
509         foreach my $m (@members) {
510                 my $n;
511
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+)\]/) {
516                         # 1D primitive array
517                         $n = { %{$m}, select => { vkarray => 1 } } if ($1 <= 16);
518                 } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
519                         # 2D primitive array
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 };
524                 } else {
525                         # everything else
526                         $n = { %{$m} };
527                 }
528
529                 if (defined($n)) {
530                         if ($prefix) {
531                                 $n->{name} = $prefix ? $prefix.'$'.$m->{name} : $m->{name};
532                                 $n->{subtype} = $s->{name};
533                                 $n->{subname} = $m->{name};
534                                 $n->{localname} = $prefix;
535                         }
536                         push @{$items}, $n;
537                 }
538         }
539
540         #       if (defined $f->{vkset}) {
541         #               my $a = { %{$f} };
542         #               if ($prefix) {
543         #                       $a->{name} = $prefix ? $prefix.'$'.$f->{name} : $f->{name};
544         #                       $a->{subtype} = $s->{name};
545         #                       $a->{subname} = $f->{name};
546         #                       $a->{localname} = $prefix;
547         #               }
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});
554         #       }
555         # }
556 }
557
558 sub uniq {
559   my %seen;
560   return grep { !$seen{$_}++ } @_;
561 }
562
563 sub format1Constructor {
564         my $api = shift;
565         my $obj = shift;
566         my $res = shift;
567         my $s = shift;
568         my $c = shift;
569
570         my $info = new method($api, $c);
571         my $init = join "\n\t\t", map {
572                 "MemorySegment $_\$segment = (MemorySegment)$_\$SH.invokeExact(self\$.segment);"
573         } uniq map {
574                 $_->{field}->{localname} ? $_->{field}->{localname} : ()
575         } @{$info->{arguments}};
576
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';
582                 } else {
583                         $v->{segment} = 'self$.segment';
584                 }
585                 # hack for string[]
586                 $v->{tonative} = "({type})$_->{field}->{implied}" if $_->{field}->{implied};
587                 code::formatTemplate($_->{match}->{setnative}, $v);
588         } @{$info->{arguments}};
589         $setAll .= ";";
590
591         my $template = $api->findTemplateName('code:vulkan=create-all');
592         my $code;
593
594         if (0) {
595                 foreach my $l (split /\n/,Dumper($info)) {
596                         $code .= "//$l\n";
597                 }
598         }
599
600         $code .= code::applyTemplate($template, { %{$info->{vars}}, 'set-all' => $setAll, 'set-init' => $init});
601
602         push @{$res->{func}}, $code;
603 }
604
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 {
609         my $api = shift;
610         my $obj = shift;
611         my $res = shift;
612         my $s = shift;
613         my $r = $s->{vkregistry};
614
615         return if $s->{name} =~ m/^(VkBaseInStructure|VkBaseOutStructure)$/on;
616
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});";
619         }
620
621         if ($s->{type} eq 'union') {
622                 foreach my $u (grep { defined($_->{vkset}) } @{$s->{items}}) {
623                         my $args = [];
624
625                         if ($u->{vkset}->{struct}) {
626                                 die "unimplemented";
627                         } else {
628                                 my $a = { %{$u} };
629                                 $a->{select} = $u->{vkset}->{select} if defined $u->{vkset}->{select};
630                                 push @{$args}, $a;
631                         }
632                         my $c = {
633                                 result => {
634                                         type => "$s->{type}:$s->{name}",
635                                         deref => "u64:\$\{$s->{name}\}",
636                                         name => 'result$',
637                                         output => 1,
638                                 },
639                                 items => $args,
640                                 rename => $u->{rename},
641                         };
642                         format1Constructor($api, $obj, $res, $s, $c);
643                 }
644         } else {
645                 my $args = [];
646
647                 collectAllArgs($api, $args, $s, "");
648
649                 if ($#{$args} >= 0) {
650                         my $c = {
651                                 result => {
652                                         type => "$s->{type}:$s->{name}",
653                                         deref => "u64:\$\{$s->{name}\}",
654                                         name => 'result$',
655                                         output => 1,
656                                 },
657                                 items => $args,
658                                 rename => "",
659                         };
660                         format1Constructor($api, $obj, $res, $s, $c);
661                 }
662         }
663 }
664
665 1;