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