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