238797e8fdd5dce55944cd1462ece9ba88d3d8ec
[panamaz] / src / notzed.vulkan / gen / generate-vulkan
1 #!/usr/bin/perl
2
3 use Data::Dumper;
4
5 use File::Path qw(make_path);
6 use File::Basename;
7
8 use strict;
9
10 use Carp 'verbose';
11 use FindBin;
12 use lib "$FindBin::Bin", 'bin/linux-amd64/lib';
13
14 use vulkan;
15 use config;
16 use code;
17
18 $SIG{__DIE__} = sub { Carp::confess( @_ ) };
19 $SIG{'INT'} = sub { Carp::confess() };
20 $Data::Dumper::Indent = 1;
21
22 # ###################################################################### #
23
24 my $enumInfo = {
25         'uint32_t' => {
26                 type => 'int'
27         },
28                 'uint64_t' => {
29                         type => 'long',
30                         suffix => 'L'
31         },
32                 'float' => {
33                         type => 'float',
34                         suffix => 'f'
35         },
36         'const char *' => {
37                 type => 'String'
38         },
39 };
40
41 # ###################################################################### #
42 my $sys = {};
43
44 $sys->{output} = 'bin/gen/notzed.vulkan/classes';
45 $sys->{package} = 'vulkan';
46 $sys->{verbose} = 0;
47
48 my $vk = new vulkan($sys);
49
50 my $api = $vk->buildFeatures(
51         [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2' ],
52         [ 'xlib',
53           #'wayland',
54           'xcb' ]);
55
56 my $structTypes = loadTypes($api, 'struct-types.api');
57 my $commandTypes = loadTypes($api, 'command-types.api');
58
59 analyseTypes($vk, $api);
60
61 # Use some heuristics to create overrides for improving the api
62
63 # - object constructor functions return values rather take pointer* values
64 # - array query functions perform the query and return the array
65 # - extension functions use a different invocation mechanism
66 # - drop any 'ignore' types from argument lists
67
68 # - [attempt to] determine which types need to be read/write/arrays
69
70 # Scan structs
71 my %defaultTemplate;
72
73 foreach my $s (values %{$api->{types}}) {
74         my $overrides = $structTypes->{overrides};
75         my $types = $structTypes->{types};
76
77         next if (defined($overrides->{$s->{name}}));
78
79         my %lengths;
80         map { $lengths{$_->{lengthfrom}}++ if $_->{lengthfrom} } @{$s->{items}};
81
82         foreach my $m (@{$s->{items}}) {
83                 my $nstar = $m->{deref} =~ tr/*/*/;
84
85                 if ($m->{lengthfor} && $nstar == 0 && $lengths{$m->{name}} == 1) {
86                         die "No type '$m->{deref}-implied'" if !defined($types->{"$m->{deref}-implied"});
87
88                         $overrides->{$s->{name}}->{$m->{name}}->{type} = "$m->{deref}-implied";
89                         print "implied: $s->{name} $m->{name} $m->{deref} $s->{index}->{$m->{lengthfor}}->{deref}\n" if $sys->{verbose};
90                 }
91
92                 if ($m->{deref} eq 'struct*-length') {
93                         $defaultTemplate{$m->{baseType}}->{array} = 1;
94                 } elsif ($m->{deref} eq 'struct[]') {
95                         $defaultTemplate{$m->{baseType}}->{array} = 1;
96                 }
97         }
98
99         $defaultTemplate{$s->{name}}->{name} = 'struct-readonly' if ($s->{returnedonly} eq 'true');
100 }
101
102 # build default overrides for commands
103 foreach my $s (values %{$api->{commands}}) {
104         my $overrides = $commandTypes->{overrides};
105         my $types = $commandTypes->{types};
106         my $first = $s->{items}->[0];
107         my $last = $s->{items}->[$#{$s->{items}}];
108         my $llast = $s->{items}->[$#{$s->{items}}-1];
109         my $result = $s->{proto};
110         my $index = $s->{index};
111
112         # check type updates anyway
113         foreach my $m (@{$s->{items}}) {
114                 if ($m->{deref} eq 'struct*-length') {
115                         $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n) && $api->{types}->{$m->{baseType}}->{returnedonly} ne 'true';
116                         $defaultTemplate{$m->{baseType}}->{array} = 1;
117                 } elsif ($m->{deref} eq 'struct*') {
118                         $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n);
119                 }
120         }
121
122         next if (defined($overrides->{$s->{name}}));
123
124         my $override = {};
125
126         # force handles to be instance types
127         if ($first->{deref} eq 'handle') {
128                 $override->{$first->{name}}->{type} = 'instance';
129         }
130
131         # extension function default template
132         if (defined($s->{extensions})) {
133                 $override->{template} = 'method-extension';
134         }
135
136         # Constructor
137         if ($last->{deref} eq 'handle*') {
138                 if (!$last->{len}) {
139                         my $t = $api->{handles}->{$last->{baseType}};
140                         print "constructor: $s->{name}\n" if $sys->{verbose};
141                         $override->{$last->{name}}->{type} =
142                                 $t->{type} eq 'VK_DEFINE_HANDLE' && $t->{name} ne 'VkInstance' ?
143                                 'dispatch*-output' : 'handle*-output';
144                 } elsif ($index->{$last->{len}}) {
145                         print "constructor?: $s->{name}\n";# if $sys->{verbose};
146                         die;
147                 } else {
148                         print "allocate-constructor?: $s->{name} $last->{len}\n";# if $sys->{verbose};
149                 }
150         }
151
152         # turn array-query functions into auto-allocate/return types
153         # ones we care about with output
154         #       handle*-length
155         #       struct*-length
156         #       uint32_t*-length
157         #       void*-length
158
159         if ($last->{deref} =~ m/-length$/ && (my $len = $index->{$last->{len}})) {
160                 if (index($len->{deref}, '*') >= 0) {
161                         my $protoa = "$result->{fullType} $s->{name}("
162                                 .join(', ', map { "$_->{fullType}" } @{$s->{items}})
163                                 .")";
164                         print "array-constructor: $protoa\n" if $sys->{verbose} > 1;
165
166                         my $otype = ($last->{deref} eq 'handle*-length' && $api->{handles}->{$last->{baseType}}->{type} eq 'VK_DEFINE_HANDLE')
167                                 ? 'dispatch*-length-query' : $last->{deref}.'-query';
168                         my $ltype = $len->{deref}.'-querylen';
169
170                         die "no template $otype" if !defined($commandTypes->{types}->{$otype});
171                         die "no template $ltype" if !defined($commandTypes->{types}->{$ltype});
172
173                         $override->{template} = defined($s->{extensions}) ? 'method-extension-query' : 'method-query';
174                         $override->{$last->{name}}->{type} = $otype;
175                         $override->{$len->{name}}->{type} = $ltype;
176                 }
177         }
178
179         # other per-item things
180         foreach my $m (@{$s->{items}}) {
181                 my $so = $structTypes->{overrides}->{$m->{baseType}};
182
183                 if ($so->{ignore}) {
184                         die "No type '$m->{deref}-ignore'" if !defined($types->{$m->{deref}.'-ignore'});
185                         $override->{$m->{name}}->{type} = $m->{deref}.'-ignore';
186                 }
187         }
188
189         $overrides->{$s->{name}} = $override if %{$override};
190 }
191
192 {
193         my $overrides = $structTypes->{overrides};
194         my $templates = $structTypes->{templates};
195
196         foreach my $k (keys %defaultTemplate) {
197                 print "what?: ".Dumper($overrides->{$k}->{template}) if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
198                 next if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
199
200                 my $t = $defaultTemplate{$k};
201                 my $name = $t->{name} || "struct-writeonly";
202
203                 $name .= "-array" if $t->{array};
204
205                 print "$name: $k\n" if $sys->{verbose} > 1;
206                 die "No override $k $name" if !$templates->{$name};
207                 $overrides->{$k}->{template} = $name;
208         }
209 }
210
211 if (0) {
212         analyseAccessors($api);
213         exit 1;
214 }
215
216 if (0) {
217         open(my $f, '>', 'types.pm');
218         print $f Dumper($commandTypes, $structTypes);
219         close $f;
220 }
221
222 if (0) {
223         open(my $f, '>', 'api.pm');
224         print $f Dumper($api);
225         close $f;
226         die;
227 }
228
229 if (0) {
230         open(my $f, '>', 'vk.pm');
231         print $f Dumper($vk);
232         close $f;
233         die;
234 }
235
236 exportVulkan($vk, $api, $structTypes);
237
238 # dump out the extension function-pointer tables
239 {
240         my $f = openOutput($sys, 'DispatchInstance');
241         my $template = $structTypes->{templates}->{dispatch};
242         my @init = ();
243         my @fieldInit = ();
244
245         foreach my $k (sort keys %{$api->{commands}}) {
246                 my $c = $api->{commands}->{$k};
247
248                 next if !defined($c->{extensions});
249
250                 push @fieldInit, code::formatTemplate($template->{'field-init'}, $c);
251                 push @init, code::formatTemplate($template->{'init'}, $c);
252
253         }
254
255         my $vars = {
256                 package => 'vulkan',
257                 Name => 'DispatchInstance',
258                 init => join("\n\t\t", @init),
259                 'field-init' => join("\n\t", @fieldInit),
260         };
261         print $f code::formatTemplateStream($template->{class}, $vars);
262
263         closeOutput($sys, 'DispatchInstance', $f);
264 }
265
266 # structs and unions
267 foreach my $k (sort keys %{$api->{types}}) {
268         my $s = $api->{data}->{$k};
269
270         die if !defined $s;
271         next if $s->{alias};
272
273         my $override = $structTypes->{overrides}->{$s->{name}};
274
275         next if $override->{ignore};
276
277         my $f = openOutput($sys, $s->{Name});
278         print $f formatStruct($vk, $api, $s);
279         closeOutput($sys, $s->{Name}, $f);
280 }
281
282 # handles
283 foreach my $k (sort keys %{$api->{handles}}) {
284         my $s = $api->{data}->{$k};
285
286         die if !defined $s;
287         next if $s->{alias};
288
289         my $f = openOutput($sys, $s->{name});
290         print $f formatHandle($vk, $api, $s);
291         closeOutput($sys, $s->{name}, $f);
292 }
293
294 # upcalls
295 foreach my $k (sort keys %{$api->{funcpointers}}) {
296         my $s = $api->{data}->{$k};
297
298         die if !defined $s;
299         next if $s->{alias};
300
301         my $override = $commandTypes->{overrides}->{$s->{name}};
302
303         next if $override->{ignore};
304
305         my $f = openOutput($sys, $s->{name});
306         print $f formatFunctionPointer($vk, $api, $s);
307         closeOutput($sys, $s->{name}, $f);
308 }
309
310 exit 0;
311
312 # ###################################################################### #
313
314 sub formatStructLayout {
315         my $types = shift;
316         my $s = shift;
317         my $offset = 0;
318         my @fields = ();
319
320         # This doens't need to worry about overrides
321
322         if ($s->{category} eq 'struct') {
323                 foreach my $m (@{$s->{items}}) {
324                         my $type = $types->{$m->{deref}};
325                         my $diff = $m->{bitOffset} - $offset;
326
327                         push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
328
329                         if ($type) {
330                                 my $v = buildVars($s, $m, $type);
331
332                                 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
333                                 $offset = $m->{bitOffset} + $m->{bitSize};
334                         } else {
335                                 push @fields, "/* Missing: $m->{deref} $m->{name} */";
336                         }
337                 }
338         } else {
339                 foreach my $m (@{$s->{items}}) {
340                         my $type = $types->{$m->{deref}};
341
342                         if ($type) {
343                                 my $v = buildVars($s, $m, $type);
344
345                                 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
346                                 $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
347                         } else {
348                                 push @fields, "/* Missing: $m->{deref} $m->{name} */";
349                         }
350                 }
351         }
352
353         my $diff = $s->{bitSize} - $offset;
354
355         push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
356
357         return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
358 }
359
360 # Experiment: see if sharing varhandles would be of any benefit
361 #  - analyse all pointer/int fields for cross-overs
362 # for vulkan 1.3:
363 #  Total fields: 3370
364 #  Unique fields: 368
365 # so very significant savings possible
366 sub analyseAccessors {
367         my $api = shift;
368         my %handles;
369         my $total;
370         my %lookat;
371
372         map { $lookat{$_} = 1 } qw(Memory.POINTER Memory.BYTE Memory.SHORT Memory.INT Memory.LONG Memory.FLOAT Memory.DOUBLE);
373
374         foreach my $s (values %{$api->{types}}) {
375                 my $override = $structTypes->{overrides}->{$s->{name}};
376
377                 next if $s->{alias};
378                 next if $override->{ignore};
379
380                 foreach my $m (@{$s->{items}}) {
381                         my $deref = $m->{deref};
382                         my $type = $structTypes->{types}->{$deref};
383                         my $v = buildVars($s, $m, $type);
384                         my $layout = code::formatTemplate($v->{layout}, $v);
385                         my $index = $m->{bitOffset} / 8;
386
387                         die if ($m->{bitoffset} & ($m->{bitSize}-1));
388
389                         if ($lookat{$layout}) {
390                                 $index = $m->{bitOffset} / $m->{bitSize};
391                         }
392
393                         my $k = sprintf "$layout \@ %03d", $index;
394
395                         $handles{$k}++;
396                         $total++;
397                 }
398         }
399
400         print "Other unique handles =\n";
401         foreach my $h (sort keys %handles) {
402                 $h =~ m/^(.*) \@/;
403                 my $x = $lookat{$1};
404                 if (!$lookat{$1}) {
405                         printf " %5d $h\n", $handles{$h};
406                 }
407         }
408         print "Unique handles of interest =\n";
409         foreach my $h (sort keys %handles) {
410                 $h =~ m/^(.*) \@/;
411                 if ($lookat{$1}) {
412                         printf " %5d $h\n", $handles{$h};
413                 }
414         }
415         my @keys = keys %handles;
416         print "Total fields: $total\n";
417         print "Unique fields: $#keys\n";
418 }
419
420 # ###################################################################### #
421
422 sub loadTypes {
423         my $api = shift;
424         my $file = shift;
425         my $config = new config({ includes=>[ $FindBin::Bin ] }, "$FindBin::Bin/$file");
426
427         my $types = {};
428         my $templates = {};
429         my $overrides = {};
430
431         foreach my $t (@{$config->{objects}}) {
432                 if ($t->{type} eq 'type') {
433                         my $nopts = $#{$t->{options}};
434                         my $type = {};
435
436                         die ("No prototype provided/found for empty type ".Dumper($t)) if ($#{$t->{items}} < 0 && $#{$t->{options}} < 0);
437
438                         # Check options / load inherited values
439                         foreach my $o (@{$t->{options}}) {
440                                 if ($o =~ m/^accessor=(.*)$/o) {
441                                         die "No template $1" if !defined($templates->{$1});
442                                         $type->{accessor} = $templates->{$1};
443                                 } elsif ($o eq 'need-frame') {
444                                         $type->{'need-frame'} = 1;
445                                 } elsif ($o eq 'need-scope') {
446                                         $type->{'need-scope'} = 1;
447                                 } elsif ($o eq 'trampoline-scope') {
448                                         $type->{'trampoline-scope'} = 1;
449                                 } elsif ($o eq 'need-alloc') {
450                                         $type->{'need-alloc'} = 1;
451                                 } elsif ($o eq 'is-instance') {
452                                         $type->{'is-instance'} = 1;
453                                 } elsif (defined($types->{$o})) {
454                                         $type = { %$type, %{$types->{$o}} };
455                                 } else {
456                                         die "Unknown option '$o' in ".Dumper($t);
457                                 }
458                         }
459
460                         # Load the fields
461                         foreach my $s (@{$t->{items}}) {
462                                 if (defined $s->{literal}) {
463                                         $type->{$s->{match}} = $s->{literal};
464                                 } elsif ($#{$s->{options}} >= 0) {
465                                         $type->{$s->{match}} = $s->{options}->[$#{$s->{options}}];
466                                 } else {
467                                         $type->{$s->{match}} = 0;
468                                 }
469
470                                 $type->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
471                         }
472
473                         # Write to all aliases
474                         foreach my $k (split /,/,$t->{name}) {
475                                 $types->{$k} = $type;
476                         }
477                 } elsif ($t->{type} eq 'code') {
478                         my $code = {
479                                 insert => {},
480                         };
481
482                         foreach my $o (@{$t->{options}}) {
483                                 if ($o =~ m/insert=(.*)/) {
484                                         foreach my $x (split /,/,$1) {
485                                                 if ($x =~ m/(.*):(.*)/) {
486                                                         die "$x $t->{name} ".Dumper($templates->{$1}) if !defined $templates->{$1}->{$2};
487                                                         $code->{insert}->{$2} = $templates->{$1}->{$2};
488                                                 }
489                                         }
490                                 } elsif ($o =~ m/^fields=(.*)/) {
491                                         $code->{fields} = $1;
492                                 } elsif (defined($templates->{$o})) {
493                                         $code = { %$code, %{$templates->{$o}} };
494                                 } else {
495                                         die ("Unknown option '$o'");
496                                 }
497                         }
498
499                         foreach my $s (@{$t->{items}}) {
500                                 if (defined  $s->{literal}) {
501                                         my $t = $s->{literal};
502
503                                         $t =~ s/^\t//gm;
504                                         $code->{$s->{match}} = $t;
505                                 } else {
506                                         delete $code->{$s->{match}};
507                                 }
508
509                                 $code->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
510                         }
511
512                         $templates->{$t->{name}} = $code;
513                 } elsif ($t->{type} eq 'override') {
514                         foreach my $s (@{$t->{items}}) {
515                                 my $c = { };
516                                 foreach my $o (@{$s->{options}}) {
517                                         if ($o =~ m/^(.*)=type:(.*)/) {
518                                                 die "No such type $s->{match} $2\n" if !defined $types->{$2};
519                                                 $c->{$1}->{type} = $2;
520                                         } elsif ($o =~ m/^(.*)=accessor:(.*)/) {
521                                                 die "No accessor template $o" if !defined($templates->{$2});
522                                                 $c->{$1}->{accessor} = $templates->{$2};
523                                         } elsif ($o =~ m/^template=(.*)/) {
524                                                 die "No template $o" if !defined($templates->{$1});
525                                                 $c->{template} = $1;
526                                         } elsif ($o eq 'ignore') {
527                                                 $c->{ignore} = 1;
528                                         }
529                                 }
530                                 $overrides->{$s->{match}} = $c;
531                         }
532                 }
533         }
534
535         # templates should probably just go in one
536         {
537                 types => $types,
538                 templates => $templates,
539                 overrides => $overrides,
540         };
541 }
542
543 sub uniq {
544   my %seen;
545   return grep { !$seen{$_}++ } @_;
546 }
547
548 # generate Vulkan.java
549 sub exportVulkan {
550         my $vk = shift;
551         my $api = shift;
552         my $structTypes = shift;
553         my $seen = {};
554         my $template = $structTypes->{templates}->{Vulkan};
555
556         my @constants = ();
557         my @defines = ();
558
559         # special case for api constants
560         {
561                 my $s = $api->{data}->{'API Constants'};
562
563                 push @constants, "// API Constants\n";
564
565                 foreach my $m (@{$s->{items}}) {
566                         next if defined($m->{alias});
567                         next if $seen->{$m->{name}}++;
568
569                         my $i = $enumInfo->{$m->{type}};
570                         my $v = $m->{value};
571
572                         # convert to java
573                         $v =~ s/[()ULF]+//gon if (!($v =~ m/^"/));
574
575                         push @constants,"public final static $i->{type} $m->{name} = $v$i->{suffix};";
576                 }
577         }
578
579         # include any defines we have a template for
580         foreach my $k (sort keys %{$api->{defines}}) {
581                 if ($template->{$k}) {
582                         push @defines, $template->{$k};
583                 } else {
584                         print "Ignored: $k\n";
585                 }
586         }
587
588         foreach my $k (sort keys %{$api->{enums}}) {
589                 my $s = $api->{data}->{$k};
590                 my $type = $s->{fullType} ? $s->{fullType} : 'VkFlags';
591                 my $i = $enumInfo->{$vk->{data}->{$type}->{type}};
592
593                 next if $s->{alias};
594
595                 push @constants, "";
596                 push @constants, "// $s->{name} $type";
597
598                 foreach my $m (@{$s->{items}}) {
599                         next if defined($m->{alias});
600                         next if $seen->{$m->{name}}++;
601
602                         my $v = $m->{value};
603
604                         $v = 1<<$m->{bitpos} if !defined($v) && defined($m->{bitpos});
605
606                         die Dumper("Ca't work out value", $m, $s) if !defined($v);
607                         push @constants, "public final static $i->{type} $m->{name} = $v$i->{suffix};";
608                 }
609         }
610
611         my $v = {
612                 package => $sys->{package},
613                 defines => join("\n\t", @defines),
614                 constants => join("\n\t", @constants),
615         };
616
617         my $f = openOutput($sys, 'Vulkan');
618
619         print $f code::formatTemplateStream($template->{class}, $v);
620
621         closeOutput($sys, 'Vulkan', $f);
622 }
623
624 # ###################################################################### #
625 # class.name to class/name.java
626 sub classToPath {
627         my $sys = shift;
628         my $name = shift;
629
630         $name = $sys->{package}.'.'.$name;
631         $name =~ s@\.@/@g;
632         $name = $sys->{output}.'/'.$name.'.java';
633         $name;
634 }
635
636 sub closeOutput {
637         my $sys = shift;
638         my $name = shift;
639         my $f = shift;
640         my $path = classToPath($sys, $name);
641
642         close($f) || die;
643         rename($path.'~', $path) || die ("rename failed: $!");
644 }
645
646 sub openOutput {
647         my $sys = shift;
648         my $name = shift;
649
650         my $path = classToPath($sys, $name);
651         my $dir = dirname($path);
652
653         make_path($dir) if (!-d $dir);
654
655         open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
656         print "writing '$path'\n" if $sys->{verbose} > 0;
657         $f;
658 }
659
660
661 # Calculate canonical derefernce types for each field and parameter
662 sub analyseFields {
663         my $vk = shift;
664         my $api = shift;
665         my $seen = shift;
666         my $s = shift;
667
668         # what about const?
669         # FIXME: bitfields
670
671         my $index = {};
672
673         map { $index->{$_->{name}} = $_ } @_;
674
675         $s->{index} = $index;
676
677         foreach my $m (@_) {
678                 my $t = $api->{data}->{$m->{baseType}};
679                 my $nstar = $m->{fullType} =~ tr/*/*/;
680                 my $type;
681                 my $array = '';
682
683                 # Check array sizes
684                 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/o) {
685                         $array = '[][]';
686                         $m->{len1} = $1;
687                         $m->{len2} = $2;
688                 } elsif ($m->{fullType} =~ m/\[(.*)\]$/o) {
689                         my $isize = $1;
690                         my $size;
691                         if ($isize =~ m/^\d+$/n) {
692                                 $size = $isize;
693                         } else {
694                                 $size = $vk->{data}->{'API Constants'}->{index}->{$isize}->{value};
695                         }
696                         $array = '[]';
697                         $m->{len1} = $size;
698                 } elsif ($m->{fullType} =~ m/[\[\]]/on) {
699                         die Dumper($m)
700                 }
701
702                 if (!defined($t)) {
703                         # will be primitive or external
704                         $type = $m->{baseType} if ($nstar == 0);
705                         $type = "$m->{baseType}*" if ($nstar == 1);
706                         $type = "$m->{baseType}**" if ($nstar == 2);
707                         die if $nstar > 2;
708                 } else {
709                         # unmap aliases
710                         while ($t->{alias}) {
711                                 print "alias $t->{name} -> $t->{alias}\n";
712                                 $t = $api->{data}->{$t->{alias}};
713                                 die if !defined $t;
714                                 $m->{baseType} = $t->{name};
715                         }
716
717                         if ($t->{category} =~ m/enum|bitmask/on) {
718                                 $t = $vk->{data}->{$t->{fullType}};
719                                 $type = $t->{type} if ($nstar == 0);
720                                 $type = "$t->{type}*" if ($nstar == 1);
721                                 die if $nstar > 1;
722                         } elsif ($t->{category} =~ m/struct|union/on) {
723                                 $m->{type} = $t->{name};
724                                 $type = 'struct' if ($nstar == 0);
725                                 $type = 'struct*' if ($nstar == 1);
726                                 $type = 'struct**' if ($nstar == 2);
727                                 die if $nstar > 2;
728                         } elsif ($t->{category} eq 'handle') {
729                                 $m->{type} = $t->{name};
730                                 #if ($t->{type} eq 'VK_DEFINE_HANDLE') {
731                                 #       $type = 'dhandle' if ($nstar == 0);
732                                 #       $type = 'dhandle*' if ($nstar == 1);
733                                 #} else {
734                                         $type = 'handle' if ($nstar == 0);
735                                         $type = 'handle*' if ($nstar == 1);
736                                 #}
737                                 die if $nstar > 1;
738                         } elsif ($t->{category} eq 'basetype') {
739                                 # ??
740                                 $type = $t->{type} if ($nstar == 0);
741                                 $type = "$t->{type}*" if ($nstar == 1);
742                                 die Dumper($m, $t) if $nstar > 1;
743                         } elsif ($t->{category} eq 'funcpointer') {
744                                 $m->{type} = $t->{name};
745                                 $type = "funcpointer" if ($nstar == 0);
746                                 die if $nstar > 0;
747                         } else {
748                                 die Dumper($m, $t);
749                         }
750                 }
751
752                 # an array type with a length
753                 if ($nstar > 0 && $m->{len}) {
754                         if ($s->{category} =~ m/struct|union/on) {
755                                 if ($m->{altlen}) {
756                                         if ($m->{altlen} =~ m/^(.*)(VK_UUID_SIZE)(.*)$/) {
757                                                 $m->{length} = $1.'Vulkan.'.$2.$3;
758                                         } elsif ($m->{altlen} =~ m/^([^a-zA-Z_]*)([a-zA-Z_]+)(.*)$/) {
759                                                 my $len = $index->{$2};
760                                                 if (defined $len) {
761                                                         $m->{length} = $1.'get'.ucfirst($2).'()'.$3;
762                                                         #$index->{$2}->{lengthfor} = $m->{name} if $index->{$2};
763                                                         $m->{lengthfrom} = $len->{name};
764                                                 }
765                                         } else {
766                                                 die "Unhandled len/altlen: ".Dumper($m);
767                                         }
768                                 } elsif ($m->{len} =~ m/(.*),null-terminated/) {
769                                         my $len = $index->{$1};
770                                         if (defined $len) {
771                                                 $m->{length} = "get$len->{Name}()";
772                                                 $m->{lengthfrom} = $len->{name};
773                                                 $len->{lengthfor} = $m->{name};
774                                         }
775                                 } elsif ($m->{len} eq 'null-terminated') {
776                                         # ignore
777                                 } elsif ($m->{len} =~ m/^(.*),(\d+)$/) {
778                                         # ignore?
779                                 } else {
780                                         my $len = $index->{$m->{len}};
781                                         if (defined $len) {
782                                                 my $cast = ($len->{fullType} eq 'uint32_t') ? '(int)' : '';
783
784                                                 die "Not simple type" if ($len->{fullType} ne $len->{baseType});
785                                                 $m->{length} = "get$len->{Name}()";
786                                                 $m->{lengthfrom} = $len->{name};
787                                                 $len->{lengthfor} = $m->{name};
788                                         } else {
789                                                 die "what?".Dumper($m);
790                                         }
791                                 }
792                         } elsif ($s->{category} eq 'command') {
793                                 if ($m->{altlen}) {
794                                         die;
795                                 } else {
796                                         $m->{length} = $m->{len} if $index->{$m->{len}};
797                                         $index->{$m->{len}}->{lengthfor} = $m->{name} if $index->{$m->{len}};
798                                 }
799                         } else {
800                                 die Dumper($s);
801                         }
802                         $type = $type.'-length' if $m->{length};
803                 }
804
805                 $seen->{$m->{fullType}} = $type.$array;
806                 $m->{deref} = $type.$array;
807
808                 # Calculate name, with some fixup hacks
809                 my $name = $m->{name};
810                 #if ($s->{type} =~ m/struct|union/on)
811                 {
812                         # Strip leading 'p' for pointers
813                         if ($name eq 'ppGeometries') { # && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
814                                 $name = 'PGeometries';
815                         } elsif ($nstar > 0 && $name =~ m/^p{$nstar}/) {
816                                 my $strip = $nstar;
817
818                                 if ($t->{category} eq 'handle' && $type ne 'handle*-length') {
819                                         $strip -= 1;
820                                 }
821
822                                 $name = substr $name, $strip;
823                         }
824
825                         $name =~ s/^pfn//o if $type eq 'funcpointer';
826                         # CamelCase
827                         $name =~ s/(?:^|_)(.)/\U$1/og;
828                 }
829                 $m->{Name} = $name;
830         }
831 }
832
833 sub analyseTypes {
834         my $vk = shift;
835         my $api = shift;
836         my $seen = {};
837
838         foreach my $s (grep { $_->{items} } values %{$api->{types}}) {
839                 analyseFields($vk, $api, $seen, $s, @{$s->{items}});
840
841                 my $name = $s->{name};
842                 $name =~ s/(?:^|_)(.)/\U$1/og;
843                 $s->{Name} = $name;
844
845                 my $first = $s->{items}->[0];
846                 my $second = $s->{items}->[1];
847
848                 if ($first->{name} eq 'sType' && $second && $second->{name} eq 'pNext') {
849                         $first->{'no-setall'} = 1;
850                         $second->{'no-setall'} = 1;
851                         print "typed: $s->{name}\n" if $sys->{verbose} > 1;
852                 } else {
853                         print "untyped: $s->{name}\n" if $sys->{verbose} > 1;
854                 }
855         }
856
857         foreach my $c (values %{$api->{funcpointers}}) {
858                 analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
859         }
860
861         foreach my $c (values %{$api->{commands}}) {
862                 $c->{proto}->{name} = 'result$';
863                 $c->{proto}->{Name} = 'result$';
864                 analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
865
866                 # collect all member functions on handles
867                 my $first = $c->{items}->[0];
868                 my $last = $c->{items}->[$#{$c->{items}}];
869                 if ($first->{deref} eq 'handle') {
870                         my $t = $api->{handles}->{$first->{baseType}};
871                         while ($t->{alias}) {
872                                 $t = $api->{handles}->{$t->{alias}};
873                         }
874                         die "No handle found ".Dumper($c) if !defined $t;
875                         push @{$t->{commands}}, $c->{name};
876                 } elsif ($c->{name} =~ m/vkEnumerateInstance|vkCreateInstance/) {
877                         push @{$api->{handles}->{'VkInstance'}->{commands}}, $c->{name};
878                 } else {
879                         die "No owner for call ".Dumper($c);
880                 }
881         }
882
883         print "Unique Types:\n";
884         my $base = {};
885         map { $base->{$_} = 1 } values %$seen;
886
887         foreach my $k (sort keys %$base) {
888                 print "$k\n";
889         }
890 }
891
892 # what else?
893 # vk? api?
894 # this way-over-evaluates, probably only call once on every field and member instead
895 sub buildVars {
896         my $s = shift;
897         my $m = shift;
898         my $type = shift;
899         my $v = { %{$m} };
900
901         foreach my $k (grep { index($_, ':') == -1 } keys %$type) {
902                 my $t = $type->{$k};
903
904                 if ($type->{"$k:eval"}) {
905                         $v->{$k} = $t;
906                         die "Eval failed: $! $@: ".Dumper($m, $type) if !defined($v->{$k});
907                 } elsif ($t) {
908                         $v->{$k} = $t;
909                 }
910         }
911
912         $v;
913 }
914
915 sub formatStructLayout {
916         my $types = shift;
917         my $s = shift;
918         my $offset = 0;
919         my @fields = ();
920
921         # This doens't need to worry about overrides
922
923         if ($s->{category} eq 'struct') {
924                 foreach my $m (@{$s->{items}}) {
925                         my $type = $types->{$m->{deref}};
926                         my $diff = $m->{bitOffset} - $offset;
927
928                         push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
929
930                         if ($type) {
931                                 my $v = buildVars($s, $m, $type);
932
933                                 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
934                                 $offset = $m->{bitOffset} + $m->{bitSize};
935                         } else {
936                                 push @fields, "/* Missing: $m->{deref} $m->{name} */";
937                         }
938                 }
939         } else {
940                 foreach my $m (@{$s->{items}}) {
941                         my $type = $types->{$m->{deref}};
942
943                         if ($type) {
944                                 my $v = buildVars($s, $m, $type);
945
946                                 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
947                                 $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
948                         } else {
949                                 push @fields, "/* Missing: $m->{deref} $m->{name} */";
950                         }
951                 }
952         }
953
954         my $diff = $s->{bitSize} - $offset;
955
956         push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
957
958         return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
959 }
960
961 sub formatAccessorIf {
962         my $s = shift;
963         my $m = shift;
964         my $info = shift;
965         my $type = shift;
966         my $field = shift;
967         my $v = shift;
968         my $accessor = $type->{accessor};
969         my $template = $accessor->{$field};
970
971         if ($template) {
972                 $template = eval($template) if ($accessor->{"$field:eval"});
973
974                 die "Error executing template $field $accessor->{$field}: $@" if (!defined($template));
975
976                 push @{$info->{$field}}, code::formatTemplate($template, $v) if $template;
977         }
978 }
979
980 sub formatStruct {
981         my $vk = shift;
982         my $api = shift;
983         my $s = shift;
984         my $templates = $structTypes->{templates};
985         my $types = $structTypes->{types};
986         my $overrides = $structTypes->{overrides};
987
988         my $override = $overrides->{$s->{name}};
989         my $tempname = $override->{template} ? $override->{template} : 'struct-writeonly';
990         my $template = $templates->{$tempname};
991         my @fields = split(/,/, $template->{fields});
992         my $info;
993
994         map { $info->{$_} = [] } @fields;
995
996         my $setall = defined $info->{'java-setall'};
997
998         # unions need multiple constructors
999         if ($setall) {
1000                 if ($s->{category} eq 'struct') {
1001                         $info->{create}->{create} = {
1002                                 'setall-arg' => [ 'SegmentAllocator alloc$' ],
1003                                 setall => [],
1004                         };
1005                 } elsif ($s->{category} eq 'union') {
1006                         foreach my $m (@{$s->{items}}) {
1007                                 $info->{create}->{"create$m->{Name}"} = {
1008                                         'setall-arg' => [ 'SegmentAllocator alloc$' ],
1009                                         setall => [],
1010                                 };
1011                         }
1012                 } else {
1013                         die;
1014                 }
1015         }
1016
1017         # Collect all fields
1018         foreach my $m (@{$s->{items}}) {
1019                 my $nstar = $m->{deref} =~ tr/*/*/;
1020                 my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
1021                 my $type = $types->{$deref};
1022
1023                 die "No type $deref ".Dumper($m, $s) if !$type;
1024
1025                 if ($type->{accessor}) {
1026                         my $v = buildVars($s, $m, $type);
1027                         my @todump;
1028
1029                         if ($m->{values}) {
1030                                 @todump = qw(init initat init-array set setat);
1031                         } else {
1032                                 @todump = qw(get getat set setat getorset getorsetat);
1033
1034                                 if ($setall && !$m->{'no-setall'}) {
1035                                         my $create = $s->{category} eq 'struct' ? $info->{create}->{create} : $info->{create}->{"create$m->{Name}"};
1036                                         formatAccessorIf($s, $m, $create, $type, 'setall-arg', $v);
1037                                         formatAccessorIf($s, $m, $create, $type, 'setall', $v);
1038                                 }
1039                         }
1040
1041                         push @{$info->{handle}}, code::formatTemplate($v->{handle}, $v)." /* $m->{name} $m->{fullType} ($m->{deref}) */" if $info->{handle} && $v->{handle};
1042                         push @{$info->{handleat}}, code::formatTemplate($v->{handleat}, $v) if $info->{handleat} && $v->{handleat};
1043
1044                         foreach my $field (@todump) {
1045                                 if ($info->{$field} && $type->{accessor}->{$field}) {
1046                                         my $t = $type->{accessor}->{$field};
1047
1048                                         $t = eval $t if $type->{accessor}->{"$field:eval"};
1049                                         die "$@" if !defined($t);
1050
1051                                         push @{$info->{$field}}, code::formatTemplate($t, $v);
1052                                 }
1053                         }
1054                 }
1055         }
1056
1057         # create constructors
1058         my $v = {
1059                 package => 'vulkan',
1060                 name => $s->{name},
1061                 Name => $s->{Name},
1062                 layout => formatStructLayout($types, $s),
1063         };
1064
1065         foreach my $field (@fields) {
1066                 $v->{$field} = join("\n", @{$info->{$field}});
1067         }
1068
1069         # build sub-components using the full $v
1070         my @createAll = ();
1071         foreach my $k (keys %{$template->{insert}}) {
1072                 my $t = $template->{insert}->{$k};
1073
1074                 if ($k eq 'create-all') {
1075                         if ($setall) {
1076                                 foreach my $kk (keys %{$info->{create}}) {
1077                                         my $create = $info->{create}->{$kk};
1078
1079                                         if ($#{$create->{'setall-arg'}} > 0) {
1080                                                 my $v = {
1081                                                         create => $kk,
1082                                                         Name => $s->{Name},
1083                                                         'java-setall-arguments' => join (', ', @{$create->{'setall-arg'}}),
1084                                                         'java-setall' => join ("\n\t\t", @{$create->{setall}}),
1085                                                 };
1086                                                 push @createAll, code::formatTemplateStream($t, $v);
1087                                         }
1088                                 }
1089                         }
1090                 } else {
1091                         $v->{$k} = code::formatTemplate($t, $v);
1092                 }
1093         }
1094         $v->{'create-all'} = join("\n", @createAll) if $setall;
1095
1096         #die Dumper($info) if $s->{name} eq 'VkMemoryOpaqueCaptureAddressAllocateInfo';
1097
1098         join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
1099                 code::formatTemplateStream($template->{class}, $v);
1100 }
1101
1102 # TODO: the template here could be mapped from types.api perhaps?
1103 # also same for the various fields, init/getset/etc.
1104 sub formatHandle {
1105         my $vk = shift;
1106         my $api = shift;
1107         my $s = shift;
1108         my $templates = $structTypes->{templates};
1109         my $overrides = $structTypes->{overrides};
1110         my $override = $overrides->{$s->{name}};
1111         my $tempname = $override->{template} ? $override->{template} : 'handle';
1112         my $template = $templates->{$tempname};
1113
1114         my $info = {
1115                 init => [],
1116                 commands => [],
1117         };
1118
1119         if (defined $s->{commands}) {
1120                 foreach my $k (sort @{$s->{commands}}) {
1121                         my $c = $api->{commands}->{$k};
1122                         push @{$info->{commands}}, formatFunction($api, $commandTypes, $c);
1123                 }
1124         }
1125
1126         my $v = {
1127                 package => 'vulkan',
1128                 name => $s->{name},
1129                 Name => $s->{Name},
1130                 init => join ("\n", @{$info->{init}}),
1131                 commands => join ("\n", @{$info->{commands}}),
1132         };
1133
1134         code::formatTemplateStream($template->{class}, $v);
1135 }
1136
1137 sub formatSignature {
1138         my $s = shift;
1139         my $types = $commandTypes->{types};
1140         my $d = '(';
1141
1142         foreach my $m (@{$s->{items}}) {
1143                 my $x = $types->{$m->{deref}};
1144
1145                 die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
1146
1147                 $d .= $x->{sig};
1148         }
1149         $d .= ')';
1150
1151         my $m = $s->{proto};
1152         my $x = $types->{$m->{deref}};
1153         die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
1154         $d .= $x->{sig};
1155 }
1156
1157 # Forms all the parameter templates
1158 sub collectFunctionInfo {
1159         my $api = shift;
1160         my $ct = shift;
1161         my $s = shift;
1162         my $types = $ct->{types};
1163         my $overrides = $ct->{overrides};
1164         my $void = $s->{proto}->{fullType} eq 'void';
1165         my $override = $overrides->{$s->{name}};
1166
1167         my @javaArgs = ();
1168         my @invokeArgs = ();
1169         my @nativeInit = ();
1170         my @queryInit = ();
1171         my @queryArgs = ();
1172
1173         my @nativeArgs = ();
1174         my @trampArgs = ();
1175
1176         my $info = {
1177                 rename => $s->{name},
1178                 name => $s->{name},
1179                 'function-descriptor' => formatFunctionDescriptor($ct, $s),
1180                 'native-result-define' => '',
1181                 'native-result-assign' => '',
1182                 'native-result' => 'void',
1183                 'trampoline-result-define' => '',
1184                 'trampoline-result-assign' => '',
1185                 'trampoline-scope' => '',
1186                 'result-test' => '',
1187                 'create-frame' => '',
1188                 'java-result' => 'void',
1189                 'java-result-assign' => '',
1190                 'result-throw' => '',
1191
1192                 'java-result-return' => 'return;',
1193                 'trampoline-result-return' => 'return;',
1194         };
1195
1196         my $hasInstance = 0;
1197         my $needFrame = 0;
1198         my $needAlloc = 0;
1199         my $needScope = 0;
1200         my $trampScope = 0;
1201
1202         my @arrayFields = qw(java-arg invoke-arg native-init query-init query-arg native-arg trampoline-arg);
1203         my @fixedFields = qw(java-result java-result-return java-result-assign);
1204
1205         map { $info->{$_} = [] } @arrayFields;
1206
1207         # Calculate parameters
1208         foreach my $m (@{$s->{items}}) {
1209                 my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
1210                 my $type = $types->{$deref};
1211
1212                 die "No type found ".Dumper($m, $s, $override) if !$type;
1213
1214                 my $v = buildVars($s, $m, $type);
1215
1216                 foreach my $field (@arrayFields) {
1217                         if ($v->{$field}) {
1218                                 push @{$info->{$field}}, code::formatTemplate($v->{$field}, $v);
1219                         } elsif ($field eq 'query-arg') {
1220                                 push @{$info->{$field}}, code::formatTemplate($v->{'invoke-arg'}, $v);
1221                         }
1222                 }
1223
1224                 foreach my $field (@fixedFields) {
1225                         $info->{$field} = code::formatTemplate($v->{$field}, $v) if ($v->{$field});
1226                 }
1227
1228                 $needScope = 1 if $type->{'need-scope'};
1229                 $needFrame = 1 if $type->{'need-frame'};
1230                 $needAlloc = 1 if $type->{'need-alloc'};
1231                 $hasInstance = 1 if $type->{'is-instance'};
1232                 $trampScope = 1 if $type->{'trampoline-scope'};
1233         }
1234
1235         $info->{'static'} = $hasInstance ? '' : 'static ';
1236
1237         # Handle default return types, others are handled by the fixedFields above
1238         if ($s->{successcodes}) {
1239                 my @codes = split(/,/,$s->{successcodes});
1240
1241                 $info->{'native-result-define'} = 'int result$;';
1242                 $info->{'native-result-assign'} = 'result$ = (int)';
1243                 $info->{'result-test'} = 'if ('.join("||", map { '(result$ == Vulkan.'.$_.')' } @codes).')';
1244                 $info->{'result-throw'} = 'throw new RuntimeException("error " + result$);';
1245
1246                 if ($#codes > 0 && $info->{'java-result'} eq 'void') {
1247                         $info->{'java-result'} = 'int';
1248                         $info->{'java-result-return'} = 'return result$;';
1249                 }
1250         } elsif ($s->{proto}->{fullType} ne 'void') {
1251                 my $m = $s->{proto};
1252                 my $type = defined($override->{$m->{name}}) ? $override->{$m->{name}}->{type} : $types->{$m->{deref}.'-return'};
1253
1254                 die "No type '$m->{deref}-return' ".Dumper($m, $s) if !defined($type);
1255                 die Dumper($m, $s) if !defined($type->{'java-result-return'});
1256
1257                 my $v = buildVars($s, $m, $type);
1258
1259                 $info->{'native-result-define'} = code::formatTemplate($v->{'native-result-define'}, $v);
1260                 $info->{'native-result-assign'} = code::formatTemplate($v->{'native-result-assign'}, $v);
1261                 #$info->{'native-result-define'}.= join("", map { "// $_\n" } split(/\n/, Dumper($m)));
1262                 $info->{'java-result'} = code::formatTemplate($v->{type}, $v);
1263                 $info->{'java-result-return'} = code::formatTemplate($v->{'java-result-return'}, $v);
1264
1265                 $info->{'native-result'} = code::formatTemplate($v->{'carrier'}, $v);
1266                 $info->{'trampoline-result-define'} = code::formatTemplate($v->{'trampoline-result-define'}, $v);
1267                 $info->{'trampoline-result-assign'} = code::formatTemplate($v->{'trampoline-result-assign'}, $v);
1268                 $info->{'trampoline-result-return'} = code::formatTemplate($v->{'trampoline-result-return'}, $v);
1269
1270                 $needScope = 1 if $type->{'need-scope'};
1271         }
1272
1273         $info->{'create-frame'} = '(Frame frame$ = Frame.frame())' if $needFrame;
1274         $info->{'trampoline-scope'} = '(ResourceScope scope$$ = ResourceScope.newConfinedScope())' if $trampScope;
1275
1276         push @{$info->{'java-arg'}}, 'SegmentAllocator alloc$' if $needAlloc;
1277         push @{$info->{'java-arg'}}, 'ResourceScope scope$' if $needScope;
1278
1279         foreach my $field (@arrayFields) {
1280                 my $with = $field =~ m/-arg$/n ? ",\n\t" : "\n\t";
1281                 $info->{$field} = join $with, @{$info->{$field}};
1282         }
1283
1284         $info->{successcodes} = $s->{successcodes} ? $s->{successcodes} : '';
1285         $info->{errorcodes} = $s->{errorcodes} ? $s->{errorcodes}: '';
1286
1287         if ($s->{category} eq 'funcpointer') {
1288                 $info->{'trampoline-signature'} = formatSignature($s);
1289         }
1290
1291         $info;
1292 }
1293
1294 sub formatFunctionPointer {
1295         my $vk = shift;
1296         my $api = shift;
1297         my $s = shift;
1298         my $template = $commandTypes->{templates}->{'funcpointer-readwrite'};
1299         my $info = collectFunctionInfo($api, $commandTypes, $s);
1300
1301         my $v = {
1302                 package => 'vulkan',
1303                 name => $s->{name},
1304                 Name => $s->{Name},
1305         };
1306
1307         foreach my $k (keys %{$template->{insert}}) {
1308                 my $t = $template->{insert}->{$k};
1309
1310                 $v->{$k} = code::formatTemplate($t, $info);
1311         }
1312
1313         code::formatTemplateStream($template->{class}, $v);
1314 }
1315
1316 sub formatFunctionDescriptor {
1317         my $ct = shift;
1318         my $s = shift;
1319         my $types = $ct->{types};
1320         my @fields = ();
1321         my $void = $s->{proto}->{fullType} eq 'void';
1322         my $override = $ct->{overrides}->{$s->{name}};
1323
1324         foreach my $m ($void ? () : $s->{proto}, @{$s->{items}}) {
1325                 my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
1326                 my $type = $types->{$deref};
1327
1328                 die "No type found ".Dumper($m, $s, $override) if !$type;
1329
1330                 my $v = buildVars($s, $m, $type);
1331
1332                 push @fields, code::formatTemplate($v->{layout}, $v)." /* $m->{deref} $m->{name} */";
1333         }
1334
1335         return ($void ? 'FunctionDescriptor.ofVoid(' : 'FunctionDescriptor.of(')
1336                 .join(",\n\t\t", @fields).')';
1337 }
1338
1339 sub formatFunction {
1340         my $api = shift;
1341         my $ct = shift;
1342         my $s = shift;
1343         my $void = $s->{proto}->{fullType} eq 'void';
1344         my $override = $ct->{overrides}->{$s->{name}};
1345         my $tempname = $override->{template} ? $override->{template} : 'method';
1346         my $template = $ct->{templates}->{$tempname};
1347
1348         my $info = collectFunctionInfo($api, $ct, $s);
1349
1350         #join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
1351         " /* template: $tempname */\n".
1352                 code::formatTemplate($template->{invoke}, $info);
1353 }