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