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