5 use File::Path qw(make_path);
12 use lib "$FindBin::Bin", 'bin/linux-amd64/lib';
18 $SIG{__DIE__} = sub { Carp::confess( @_ ) };
19 $SIG{'INT'} = sub { Carp::confess() };
20 $Data::Dumper::Indent = 1;
22 # ###################################################################### #
44 'uint16_t' => 'short',
55 # ###################################################################### #
58 $sys->{output} = 'bin/gen/notzed.vulkan/classes';
59 $sys->{package} = 'vulkan';
62 my $vk = new vulkan($sys);
64 my $api = $vk->buildFeatures(
65 [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2' ],
70 my $structTypes = loadTypes($api, 'struct-types.api');
71 my $commandTypes = loadTypes($api, 'command-types.api');
73 analyseTypes($vk, $api);
75 # Use some heuristics to create overrides for improving the api
77 # - object constructor functions return values rather take pointer* values
78 # - array query functions perform the query and return the array
79 # - extension functions use a different invocation mechanism
80 # - drop any 'ignore' types from argument lists
82 # - [attempt to] determine which types need to be read/write/arrays
87 foreach my $s (values %{$api->{types}}) {
88 my $overrides = $structTypes->{overrides};
89 my $types = $structTypes->{types};
91 next if (defined($overrides->{$s->{name}}));
94 map { $lengths{$_->{lengthfrom}}++ if $_->{lengthfrom} } @{$s->{items}};
96 foreach my $m (@{$s->{items}}) {
97 my $nstar = $m->{deref} =~ tr/*/*/;
99 if ($m->{lengthfor} && $nstar == 0 && $lengths{$m->{name}} == 1) {
100 die "No type '$m->{deref}-implied'" if !defined($types->{"$m->{deref}-implied"});
102 $overrides->{$s->{name}}->{$m->{name}}->{type} = "$m->{deref}-implied";
103 print "implied: $s->{name} $m->{name} $m->{deref} $s->{index}->{$m->{lengthfor}}->{deref}\n" if $sys->{verbose};
106 if ($m->{deref} eq 'struct*-length') {
107 $defaultTemplate{$m->{baseType}}->{array} = 1;
108 } elsif ($m->{deref} eq 'struct[]') {
109 $defaultTemplate{$m->{baseType}}->{array} = 1;
113 $defaultTemplate{$s->{name}}->{name} = 'struct-readonly' if ($s->{returnedonly} eq 'true');
116 # build default overrides for commands
117 foreach my $s (values %{$api->{commands}}) {
118 my $overrides = $commandTypes->{overrides};
119 my $types = $commandTypes->{types};
120 my $first = $s->{items}->[0];
121 my $last = $s->{items}->[$#{$s->{items}}];
122 my $llast = $s->{items}->[$#{$s->{items}}-1];
123 my $result = $s->{proto};
124 my $index = $s->{index};
126 # check type updates anyway
127 foreach my $m (@{$s->{items}}) {
128 if ($m->{deref} eq 'struct*-length') {
129 $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n) && $api->{types}->{$m->{baseType}}->{returnedonly} ne 'true';
130 $defaultTemplate{$m->{baseType}}->{array} = 1;
131 } elsif ($m->{deref} eq 'struct*') {
132 $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n);
136 next if (defined($overrides->{$s->{name}}));
140 # force handles to be instance types
141 if ($first->{deref} eq 'handle') {
142 $override->{$first->{name}}->{type} = 'instance';
145 # extension function default template
146 if (defined($s->{extensions})) {
147 $override->{template} = 'method-extension';
151 if ($last->{deref} eq 'handle*') {
153 my $t = $api->{handles}->{$last->{baseType}};
154 print "constructor: $s->{name}\n" if $sys->{verbose};
155 $override->{$last->{name}}->{type} =
156 $t->{type} eq 'VK_DEFINE_HANDLE' && $t->{name} ne 'VkInstance' ?
157 'dispatch*-output' : 'handle*-output';
158 } elsif ($index->{$last->{len}}) {
159 print "constructor?: $s->{name}\n";# if $sys->{verbose};
162 print "allocate-constructor?: $s->{name} $last->{len}\n";# if $sys->{verbose};
166 # turn array-query functions into auto-allocate/return types
167 # ones we care about with output
173 if ($last->{deref} =~ m/-length$/ && (my $len = $index->{$last->{len}})) {
174 if (index($len->{deref}, '*') >= 0) {
175 my $protoa = "$result->{fullType} $s->{name}("
176 .join(', ', map { "$_->{fullType}" } @{$s->{items}})
178 print "array-constructor: $protoa\n" if $sys->{verbose} > 1;
180 my $otype = ($last->{deref} eq 'handle*-length' && $api->{handles}->{$last->{baseType}}->{type} eq 'VK_DEFINE_HANDLE')
181 ? 'dispatch*-length-query' : $last->{deref}.'-query';
182 my $ltype = $len->{deref}.'-querylen';
184 die "no template $otype" if !defined($commandTypes->{types}->{$otype});
185 die "no template $ltype" if !defined($commandTypes->{types}->{$ltype});
187 $override->{template} = defined($s->{extensions}) ? 'method-extension-query' : 'method-query';
188 $override->{$last->{name}}->{type} = $otype;
189 $override->{$len->{name}}->{type} = $ltype;
193 # other per-item things
194 foreach my $m (@{$s->{items}}) {
195 my $so = $structTypes->{overrides}->{$m->{baseType}};
198 die "No type '$m->{deref}-ignore'" if !defined($types->{$m->{deref}.'-ignore'});
199 $override->{$m->{name}}->{type} = $m->{deref}.'-ignore';
203 $overrides->{$s->{name}} = $override if %{$override};
207 my $overrides = $structTypes->{overrides};
208 my $templates = $structTypes->{templates};
210 foreach my $k (keys %defaultTemplate) {
211 print "what?: ".Dumper($overrides->{$k}->{template}) if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
212 next if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
214 my $t = $defaultTemplate{$k};
215 my $name = $t->{name} || "struct-writeonly";
217 $name .= "-array" if $t->{array};
219 print "$name: $k\n" if $sys->{verbose} > 1;
220 die "No override $k $name" if !$templates->{$name};
221 $overrides->{$k}->{template} = $name;
226 analyseAccessors($api);
231 open(my $f, '>', 'types.pm');
232 print $f Dumper($commandTypes, $structTypes);
237 open(my $f, '>', 'api.pm');
238 print $f Dumper($api);
244 open(my $f, '>', 'vk.pm');
245 print $f Dumper($vk);
250 exportVulkan($vk, $api, $structTypes);
252 # dump out the extension function-pointer tables
254 my $f = openOutput($sys, 'DispatchInstance');
255 my $template = $structTypes->{templates}->{dispatch};
259 foreach my $k (sort keys %{$api->{commands}}) {
260 my $c = $api->{commands}->{$k};
262 next if !defined($c->{extensions});
264 push @fieldInit, code::formatTemplate($template->{'field-init'}, $c);
265 push @init, code::formatTemplate($template->{'init'}, $c);
271 Name => 'DispatchInstance',
272 init => join("\n\t\t", @init),
273 'field-init' => join("\n\t", @fieldInit),
275 print $f code::formatTemplateStream($template->{class}, $vars);
277 closeOutput($sys, 'DispatchInstance', $f);
281 foreach my $k (sort keys %{$api->{types}}) {
282 my $s = $api->{data}->{$k};
287 my $override = $structTypes->{overrides}->{$s->{name}};
289 next if $override->{ignore};
291 my $f = openOutput($sys, $s->{Name});
292 print $f formatStruct($vk, $api, $s);
293 closeOutput($sys, $s->{Name}, $f);
297 foreach my $k (sort keys %{$api->{handles}}) {
298 my $s = $api->{data}->{$k};
303 my $f = openOutput($sys, $s->{name});
304 print $f formatHandle($vk, $api, $s);
305 closeOutput($sys, $s->{name}, $f);
309 foreach my $k (sort keys %{$api->{funcpointers}}) {
310 my $s = $api->{data}->{$k};
315 my $override = $commandTypes->{overrides}->{$s->{name}};
317 next if $override->{ignore};
319 my $f = openOutput($sys, $s->{name});
320 print $f formatFunctionPointer($vk, $api, $s);
321 closeOutput($sys, $s->{name}, $f);
326 # ###################################################################### #
328 sub formatStructLayout {
334 # This doens't need to worry about overrides
336 if ($s->{category} eq 'struct') {
337 foreach my $m (@{$s->{items}}) {
338 my $type = $types->{$m->{deref}};
339 my $diff = $m->{bitOffset} - $offset;
341 push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
344 my $v = buildVars($s, $m, $type);
346 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
347 $offset = $m->{bitOffset} + $m->{bitSize};
349 push @fields, "/* Missing: $m->{deref} $m->{name} */";
353 foreach my $m (@{$s->{items}}) {
354 my $type = $types->{$m->{deref}};
357 my $v = buildVars($s, $m, $type);
359 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
360 $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
362 push @fields, "/* Missing: $m->{deref} $m->{name} */";
367 my $diff = $s->{bitSize} - $offset;
369 push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
371 return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
374 # Experiment: see if sharing varhandles would be of any benefit
375 # - analyse all pointer/int fields for cross-overs
379 # so very significant savings possible
380 sub analyseAccessors {
386 map { $lookat{$_} = 1 } qw(Memory.POINTER Memory.BYTE Memory.SHORT Memory.INT Memory.LONG Memory.FLOAT Memory.DOUBLE);
388 foreach my $s (values %{$api->{types}}) {
389 my $override = $structTypes->{overrides}->{$s->{name}};
392 next if $override->{ignore};
394 foreach my $m (@{$s->{items}}) {
395 my $deref = $m->{deref};
396 my $type = $structTypes->{types}->{$deref};
397 my $v = buildVars($s, $m, $type);
398 my $layout = code::formatTemplate($v->{layout}, $v);
399 my $index = $m->{bitOffset} / 8;
401 die if ($m->{bitoffset} & ($m->{bitSize}-1));
403 if ($lookat{$layout}) {
404 $index = $m->{bitOffset} / $m->{bitSize};
407 my $k = sprintf "$layout \@ %03d", $index;
414 print "Other unique handles =\n";
415 foreach my $h (sort keys %handles) {
419 printf " %5d $h\n", $handles{$h};
422 print "Unique handles of interest =\n";
423 foreach my $h (sort keys %handles) {
426 printf " %5d $h\n", $handles{$h};
429 my @keys = keys %handles;
430 print "Total fields: $total\n";
431 print "Unique fields: $#keys\n";
434 # ###################################################################### #
439 my $config = new config({ includes=>[ $FindBin::Bin ] }, "$FindBin::Bin/$file");
445 foreach my $t (@{$config->{objects}}) {
446 if ($t->{type} eq 'type') {
447 my $nopts = $#{$t->{options}};
450 die ("No prototype provided/found for empty type ".Dumper($t)) if ($#{$t->{items}} < 0 && $#{$t->{options}} < 0);
452 # Check options / load inherited values
453 foreach my $o (@{$t->{options}}) {
454 if ($o =~ m/^accessor=(.*)$/o) {
455 die "No template $1" if !defined($templates->{$1});
456 $type->{accessor} = $templates->{$1};
457 } elsif ($o eq 'need-frame') {
458 $type->{'need-frame'} = 1;
459 } elsif ($o eq 'need-scope') {
460 $type->{'need-scope'} = 1;
461 } elsif ($o eq 'trampoline-scope') {
462 $type->{'trampoline-scope'} = 1;
463 } elsif ($o eq 'need-alloc') {
464 $type->{'need-alloc'} = 1;
465 } elsif ($o eq 'is-instance') {
466 $type->{'is-instance'} = 1;
467 } elsif (defined($types->{$o})) {
468 $type = { %$type, %{$types->{$o}} };
470 die "Unknown option '$o' in ".Dumper($t);
475 foreach my $s (@{$t->{items}}) {
476 if (defined $s->{literal}) {
477 $type->{$s->{match}} = $s->{literal};
478 } elsif ($#{$s->{options}} >= 0) {
479 $type->{$s->{match}} = $s->{options}->[$#{$s->{options}}];
481 $type->{$s->{match}} = 0;
484 $type->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
487 # Write to all aliases
488 foreach my $k (split /,/,$t->{name}) {
489 $types->{$k} = $type;
491 } elsif ($t->{type} eq 'code') {
496 foreach my $o (@{$t->{options}}) {
497 if ($o =~ m/insert=(.*)/) {
498 foreach my $x (split /,/,$1) {
499 if ($x =~ m/(.*):(.*)/) {
500 die "$x $t->{name} ".Dumper($templates->{$1}) if !defined $templates->{$1}->{$2};
501 $code->{insert}->{$2} = $templates->{$1}->{$2};
504 } elsif ($o =~ m/^fields=(.*)/) {
505 $code->{fields} = $1;
506 } elsif (defined($templates->{$o})) {
507 $code = { %$code, %{$templates->{$o}} };
509 die ("Unknown option '$o'");
513 foreach my $s (@{$t->{items}}) {
514 if (defined $s->{literal}) {
515 my $t = $s->{literal};
518 $code->{$s->{match}} = $t;
520 delete $code->{$s->{match}};
523 $code->{"$s->{match}:eval"} = 1 if config::optionFlag('eval', $s);
526 $templates->{$t->{name}} = $code;
527 } elsif ($t->{type} eq 'override') {
528 foreach my $s (@{$t->{items}}) {
530 foreach my $o (@{$s->{options}}) {
531 if ($o =~ m/^(.*)=type:(.*)/) {
532 die "No such type $s->{match} $2\n" if !defined $types->{$2};
533 $c->{$1}->{type} = $2;
534 } elsif ($o =~ m/^(.*)=accessor:(.*)/) {
535 die "No accessor template $o" if !defined($templates->{$2});
536 $c->{$1}->{accessor} = $templates->{$2};
537 } elsif ($o =~ m/^template=(.*)/) {
538 die "No template $o" if !defined($templates->{$1});
540 } elsif ($o eq 'expand') {
542 } elsif ($o eq 'ignore') {
546 $overrides->{$s->{match}} = $c;
551 # templates should probably just go in one
554 templates => $templates,
555 overrides => $overrides,
561 return grep { !$seen{$_}++ } @_;
564 # generate Vulkan.java
568 my $structTypes = shift;
570 my $template = $structTypes->{templates}->{Vulkan};
575 # special case for api constants
577 my $s = $api->{data}->{'API Constants'};
579 push @constants, "// API Constants\n";
581 foreach my $m (@{$s->{items}}) {
582 next if defined($m->{alias});
583 next if $seen->{$m->{name}}++;
585 my $i = $enumInfo->{$m->{type}};
589 $v =~ s/[()ULF]+//gon if (!($v =~ m/^"/));
591 push @constants,"public final static $i->{type} $m->{name} = $v$i->{suffix};";
595 # include any defines we have a template for
596 foreach my $k (sort keys %{$api->{defines}}) {
597 if ($template->{$k}) {
598 push @defines, $template->{$k};
600 print "Ignored: $k\n";
604 foreach my $k (sort keys %{$api->{enums}}) {
605 my $s = $api->{data}->{$k};
606 my $type = $s->{fullType} ? $s->{fullType} : 'VkFlags';
607 my $i = $enumInfo->{$vk->{data}->{$type}->{type}};
612 push @constants, "// $s->{name} $type";
614 foreach my $m (@{$s->{items}}) {
615 next if defined($m->{alias});
616 next if $seen->{$m->{name}}++;
620 $v = 1<<$m->{bitpos} if !defined($v) && defined($m->{bitpos});
622 die Dumper("Ca't work out value", $m, $s) if !defined($v);
623 push @constants, "public final static $i->{type} $m->{name} = $v$i->{suffix};";
628 package => $sys->{package},
629 defines => join("\n\t", @defines),
630 constants => join("\n\t", @constants),
633 my $f = openOutput($sys, 'Vulkan');
635 print $f code::formatTemplateStream($template->{class}, $v);
637 closeOutput($sys, 'Vulkan', $f);
640 # ###################################################################### #
641 # class.name to class/name.java
646 $name = $sys->{package}.'.'.$name;
648 $name = $sys->{output}.'/'.$name.'.java';
656 my $path = classToPath($sys, $name);
659 rename($path.'~', $path) || die ("rename failed: $!");
666 my $path = classToPath($sys, $name);
667 my $dir = dirname($path);
669 make_path($dir) if (!-d $dir);
671 open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
672 print "writing '$path'\n" if $sys->{verbose} > 0;
677 # Calculate canonical derefernce types for each field and parameter
689 map { $index->{$_->{name}} = $_ } @_;
691 $s->{index} = $index;
694 my $t = $api->{data}->{$m->{baseType}};
695 my $nstar = $m->{fullType} =~ tr/*/*/;
700 if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/o) {
704 } elsif ($m->{fullType} =~ m/\[(.*)\]$/o) {
707 if ($isize =~ m/^\d+$/n) {
710 $size = $vk->{data}->{'API Constants'}->{index}->{$isize}->{value};
714 } elsif ($m->{fullType} =~ m/[\[\]]/on) {
719 # will be primitive or external
720 $type = $m->{baseType} if ($nstar == 0);
721 $type = "$m->{baseType}*" if ($nstar == 1);
722 $type = "$m->{baseType}**" if ($nstar == 2);
726 while ($t->{alias}) {
727 print "alias $t->{name} -> $t->{alias}\n";
728 $t = $api->{data}->{$t->{alias}};
730 $m->{baseType} = $t->{name};
733 if ($t->{category} =~ m/enum|bitmask/on) {
734 $t = $vk->{data}->{$t->{fullType}};
735 $type = $t->{type} if ($nstar == 0);
736 $type = "$t->{type}*" if ($nstar == 1);
738 } elsif ($t->{category} =~ m/struct|union/on) {
739 $m->{type} = $t->{name};
740 $type = 'struct' if ($nstar == 0);
741 $type = 'struct*' if ($nstar == 1);
742 $type = 'struct**' if ($nstar == 2);
744 } elsif ($t->{category} eq 'handle') {
745 $m->{type} = $t->{name};
746 #if ($t->{type} eq 'VK_DEFINE_HANDLE') {
747 # $type = 'dhandle' if ($nstar == 0);
748 # $type = 'dhandle*' if ($nstar == 1);
750 $type = 'handle' if ($nstar == 0);
751 $type = 'handle*' if ($nstar == 1);
754 } elsif ($t->{category} eq 'basetype') {
756 $type = $t->{type} if ($nstar == 0);
757 $type = "$t->{type}*" if ($nstar == 1);
758 die Dumper($m, $t) if $nstar > 1;
759 } elsif ($t->{category} eq 'funcpointer') {
760 $m->{type} = $t->{name};
761 $type = "funcpointer" if ($nstar == 0);
768 if ($s->{category} eq 'struct') {
769 my $mover = $structTypes->{overrides}->{$m->{baseType}};
770 $type .= "-expand" if $nstar == 0 && $mover->{expand};
773 # an array type with a length
774 if ($nstar > 0 && $m->{len}) {
775 if ($s->{category} =~ m/struct|union/on) {
777 if ($m->{altlen} =~ m/^(.*)(VK_UUID_SIZE)(.*)$/) {
778 $m->{length} = $1.'Vulkan.'.$2.$3;
779 } elsif ($m->{altlen} =~ m/^([^a-zA-Z_]*)([a-zA-Z_]+)(.*)$/) {
780 my $len = $index->{$2};
782 $m->{length} = $1.'get'.ucfirst($2).'()'.$3;
783 #$index->{$2}->{lengthfor} = $m->{name} if $index->{$2};
784 $m->{lengthfrom} = $len->{name};
787 die "Unhandled len/altlen: ".Dumper($m);
789 } elsif ($m->{len} =~ m/(.*),null-terminated/) {
790 my $len = $index->{$1};
792 $m->{length} = "get$len->{Name}()";
793 $m->{lengthfrom} = $len->{name};
794 $len->{lengthfor} = $m->{name};
796 } elsif ($m->{len} eq 'null-terminated') {
798 } elsif ($m->{len} =~ m/^(.*),(\d+)$/) {
801 my $len = $index->{$m->{len}};
803 my $cast = ($len->{fullType} eq 'uint32_t') ? '(int)' : '';
805 die "Not simple type" if ($len->{fullType} ne $len->{baseType});
806 $m->{length} = "get$len->{Name}()";
807 $m->{lengthfrom} = $len->{name};
808 $len->{lengthfor} = $m->{name};
810 die "what?".Dumper($m);
813 } elsif ($s->{category} eq 'command') {
817 $m->{length} = $m->{len} if $index->{$m->{len}};
818 $index->{$m->{len}}->{lengthfor} = $m->{name} if $index->{$m->{len}};
823 $type = $type.'-length' if $m->{length};
826 $seen->{$m->{fullType}} = $type.$array;
827 $m->{deref} = $type.$array;
829 # Calculate name, with some fixup hacks
830 my $name = $m->{name};
831 #if ($s->{type} =~ m/struct|union/on)
833 # Strip leading 'p' for pointers
834 if ($name eq 'ppGeometries') { # && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
835 $name = 'PGeometries';
836 } elsif ($nstar > 0 && $name =~ m/^p{$nstar}/) {
839 if ($t->{category} eq 'handle' && $type ne 'handle*-length') {
843 $name = substr $name, $strip;
846 $name =~ s/^pfn//o if $type eq 'funcpointer';
848 $name =~ s/(?:^|_)(.)/\U$1/og;
859 foreach my $s (grep { $_->{items} } values %{$api->{types}}) {
860 analyseFields($vk, $api, $seen, $s, @{$s->{items}});
862 my $name = $s->{name};
863 $name =~ s/(?:^|_)(.)/\U$1/og;
866 my $first = $s->{items}->[0];
867 my $second = $s->{items}->[1];
869 if ($first->{name} eq 'sType' && $second && $second->{name} eq 'pNext') {
870 $first->{'no-setall'} = 1;
871 $second->{'no-setall'} = 1;
872 print "typed: $s->{name}\n" if $sys->{verbose} > 1;
874 print "untyped: $s->{name}\n" if $sys->{verbose} > 1;
878 foreach my $c (values %{$api->{funcpointers}}) {
879 analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
882 foreach my $c (values %{$api->{commands}}) {
883 $c->{proto}->{name} = 'result$';
884 $c->{proto}->{Name} = 'result$';
885 analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
887 # collect all member functions on handles
888 my $first = $c->{items}->[0];
889 my $last = $c->{items}->[$#{$c->{items}}];
890 if ($first->{deref} eq 'handle') {
891 my $t = $api->{handles}->{$first->{baseType}};
892 while ($t->{alias}) {
893 $t = $api->{handles}->{$t->{alias}};
895 die "No handle found ".Dumper($c) if !defined $t;
896 push @{$t->{commands}}, $c->{name};
897 } elsif ($c->{name} =~ m/vkEnumerateInstance|vkCreateInstance/) {
898 push @{$api->{handles}->{'VkInstance'}->{commands}}, $c->{name};
900 die "No owner for call ".Dumper($c);
904 print "Unique Types:\n";
906 map { $base->{$_} = 1 } values %$seen;
908 foreach my $k (sort keys %$base) {
915 # this way-over-evaluates, probably only call once on every field and member instead
922 foreach my $k (grep { index($_, ':') == -1 } keys %$type) {
925 if ($type->{"$k:eval"}) {
927 die "Eval failed: $! $@: ".Dumper($m, $type) if !defined($v->{$k});
936 sub formatStructLayout {
942 # This doens't need to worry about overrides
944 if ($s->{category} eq 'struct') {
945 foreach my $m (@{$s->{items}}) {
946 my $type = $types->{$m->{deref}};
947 my $diff = $m->{bitOffset} - $offset;
949 push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
952 my $v = buildVars($s, $m, $type);
954 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
955 $offset = $m->{bitOffset} + $m->{bitSize};
957 push @fields, "/* Missing: $m->{deref} $m->{name} */";
961 foreach my $m (@{$s->{items}}) {
962 my $type = $types->{$m->{deref}};
965 my $v = buildVars($s, $m, $type);
967 push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
968 $offset = ($m->{bitOffset} + $m->{bitSize}) if ($m->{bitOffset} + $m->{bitSize}) > $offset;
970 push @fields, "/* Missing: $m->{deref} $m->{name} */";
975 my $diff = $s->{bitSize} - $offset;
977 push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
979 return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
982 sub formatAccessorIf {
989 my $accessor = $type->{accessor};
990 my $template = $accessor->{$field};
993 $template = eval($template) if ($accessor->{"$field:eval"});
995 die "Error executing template $field $accessor->{$field}: $@" if (!defined($template));
997 push @{$info->{$field}}, code::formatTemplate($template, $v) if $template;
1005 my $templates = $structTypes->{templates};
1006 my $types = $structTypes->{types};
1007 my $overrides = $structTypes->{overrides};
1009 my $override = $overrides->{$s->{name}};
1010 my $tempname = $override->{template} ? $override->{template} : 'struct-writeonly';
1011 my $template = $templates->{$tempname};
1012 my @fields = split(/,/, $template->{fields});
1015 map { $info->{$_} = [] } @fields;
1017 my $setall = defined $info->{'java-setall'};
1019 # unions need multiple constructors
1021 if ($s->{category} eq 'struct') {
1022 $info->{create}->{create} = {
1027 } elsif ($s->{category} eq 'union') {
1028 foreach my $m (@{$s->{items}}) {
1029 $info->{create}->{"create$m->{Name}"} = {
1040 # Collect all fields
1041 foreach my $m (@{$s->{items}}) {
1042 my $nstar = $m->{deref} =~ tr/*/*/;
1043 my $mover = $override->{$m->{name}};
1044 my $deref = defined($mover->{type}) ? $mover->{type} : $m->{deref};
1045 my $type = $types->{$deref};
1047 die "No type $deref ".Dumper($m, $s) if !$type;
1048 die Dumper($mover, $type) if $mover->{accessor};
1050 if ($type->{accessor}) {
1051 my $v = buildVars($s, $m, $type);
1055 @todump = qw(init initat init-array set setat);
1057 @todump = qw(get getat set setat getorset getorsetat);
1059 if ($setall && !$m->{'no-setall'}) {
1060 my $create = $s->{category} eq 'struct' ? $info->{create}->{create} : $info->{create}->{"create$m->{Name}"};
1061 formatAccessorIf($s, $m, $create, $type, 'setall-arg', $v);
1062 formatAccessorIf($s, $m, $create, $type, 'setall', $v);
1063 formatAccessorIf($s, $m, $create, $type, 'setallat', $v);
1067 push @{$info->{handle}}, code::formatTemplate($v->{handle}, $v)." /* $m->{name} $m->{fullType} ($m->{deref}) */" if $info->{handle} && $v->{handle};
1068 push @{$info->{handleat}}, code::formatTemplate($v->{handleat}, $v) if $info->{handleat} && $v->{handleat};
1070 foreach my $field (@todump) {
1071 if ($info->{$field} && $type->{accessor}->{$field}) {
1072 my $t = $type->{accessor}->{$field};
1074 $t = eval $t if $type->{accessor}->{"$field:eval"};
1075 die "$@" if !defined($t);
1077 push @{$info->{$field}}, code::formatTemplate($t, $v);
1083 # create constructors
1085 package => 'vulkan',
1088 layout => formatStructLayout($types, $s),
1091 foreach my $field (@fields) {
1092 $v->{$field} = join("\n", @{$info->{$field}});
1095 # build sub-components using the full $v
1097 foreach my $k (keys %{$template->{insert}}) {
1098 my $t = $template->{insert}->{$k};
1100 die if (!defined($t));
1102 if ($k eq 'create-all' || $k eq 'set-all' || $k eq 'setat-all') {
1104 foreach my $kk (keys %{$info->{create}}) {
1105 my $create = $info->{create}->{$kk};
1107 if ($#{$create->{'setall-arg'}} >= 0) {
1110 set => $kk =~ s/create/set/r,
1112 'java-setall-arguments' => join (', ', @{$create->{'setall-arg'}}),
1113 'java-setall' => join ("\n\t\t", @{$create->{setall}}),
1114 'java-setallat' => join ("\n\t\t", @{$create->{setallat}}),
1116 push @createAll, code::formatTemplateStream($t, $v);
1121 $v->{$k} = code::formatTemplate($t, $v);
1124 $v->{'create-all'} = join("\n", @createAll) if $setall;
1126 #die Dumper($info, $v) if $s->{name} eq 'VkRect2D';
1128 join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
1129 code::formatTemplateStream($template->{class}, $v);
1132 # TODO: the template here could be mapped from types.api perhaps?
1133 # also same for the various fields, init/getset/etc.
1138 my $templates = $structTypes->{templates};
1139 my $overrides = $structTypes->{overrides};
1140 my $override = $overrides->{$s->{name}};
1141 my $tempname = $override->{template} ? $override->{template} : 'handle';
1142 my $template = $templates->{$tempname};
1149 if (defined $s->{commands}) {
1150 foreach my $k (sort @{$s->{commands}}) {
1151 my $c = $api->{commands}->{$k};
1152 push @{$info->{commands}}, formatFunction($api, $commandTypes, $c);
1157 package => 'vulkan',
1160 init => join ("\n", @{$info->{init}}),
1161 commands => join ("\n", @{$info->{commands}}),
1164 code::formatTemplateStream($template->{class}, $v);
1167 sub formatSignature {
1169 my $types = $commandTypes->{types};
1172 foreach my $m (@{$s->{items}}) {
1173 my $x = $types->{$m->{deref}};
1175 die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
1181 my $m = $s->{proto};
1182 my $x = $types->{$m->{deref}};
1183 die "No sig defined ".Dumper($m) if !defined($x) || !defined($x->{sig});
1187 # Forms all the parameter templates
1188 sub collectFunctionInfo {
1192 my $types = $ct->{types};
1193 my $overrides = $ct->{overrides};
1194 my $void = $s->{proto}->{fullType} eq 'void';
1195 my $override = $overrides->{$s->{name}};
1198 my @invokeArgs = ();
1199 my @nativeInit = ();
1203 my @nativeArgs = ();
1207 rename => $s->{name},
1209 'function-descriptor' => formatFunctionDescriptor($ct, $s),
1210 'native-result-define' => '',
1211 'native-result-assign' => '',
1212 'native-result' => 'void',
1213 'trampoline-result-define' => '',
1214 'trampoline-result-assign' => '',
1215 'trampoline-scope' => '',
1216 'result-test' => '',
1217 'create-frame' => '',
1218 'java-result' => 'void',
1219 'java-result-assign' => '',
1220 'result-throw' => '',
1222 'java-result-return' => 'return;',
1223 'trampoline-result-return' => 'return;',
1226 my $hasInstance = 0;
1232 my @arrayFields = qw(java-arg invoke-arg native-init query-init query-arg native-arg trampoline-arg);
1233 my @fixedFields = qw(java-result java-result-return java-result-assign);
1235 map { $info->{$_} = [] } @arrayFields;
1237 # Calculate parameters
1238 foreach my $m (@{$s->{items}}) {
1239 my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
1240 my $type = $types->{$deref};
1242 die "No type found ".Dumper($m, $s, $override) if !$type;
1244 my $v = buildVars($s, $m, $type);
1246 foreach my $field (@arrayFields) {
1248 push @{$info->{$field}}, code::formatTemplate($v->{$field}, $v);
1249 } elsif ($field eq 'query-arg') {
1250 push @{$info->{$field}}, code::formatTemplate($v->{'invoke-arg'}, $v);
1254 foreach my $field (@fixedFields) {
1255 $info->{$field} = code::formatTemplate($v->{$field}, $v) if ($v->{$field});
1258 $needScope = 1 if $type->{'need-scope'};
1259 $needFrame = 1 if $type->{'need-frame'};
1260 $needAlloc = 1 if $type->{'need-alloc'};
1261 $hasInstance = 1 if $type->{'is-instance'};
1262 $trampScope = 1 if $type->{'trampoline-scope'};
1265 $info->{'static'} = $hasInstance ? '' : 'static ';
1267 # Handle default return types, others are handled by the fixedFields above
1268 if ($s->{successcodes}) {
1269 my @codes = split(/,/,$s->{successcodes});
1271 $info->{'native-result-define'} = 'int result$;';
1272 $info->{'native-result-assign'} = 'result$ = (int)';
1273 $info->{'result-test'} = 'if ('.join("||", map { '(result$ == Vulkan.'.$_.')' } @codes).')';
1274 $info->{'result-throw'} = 'throw new RuntimeException("error " + result$);';
1276 if ($#codes > 0 && $info->{'java-result'} eq 'void') {
1277 $info->{'java-result'} = 'int';
1278 $info->{'java-result-return'} = 'return result$;';
1280 } elsif ($s->{proto}->{fullType} ne 'void') {
1281 my $m = $s->{proto};
1282 my $type = defined($override->{$m->{name}}) ? $override->{$m->{name}}->{type} : $types->{$m->{deref}.'-return'};
1284 die "No type '$m->{deref}-return' ".Dumper($m, $s) if !defined($type);
1285 die Dumper($m, $s) if !defined($type->{'java-result-return'});
1287 my $v = buildVars($s, $m, $type);
1289 $info->{'native-result-define'} = code::formatTemplate($v->{'native-result-define'}, $v);
1290 $info->{'native-result-assign'} = code::formatTemplate($v->{'native-result-assign'}, $v);
1291 #$info->{'native-result-define'}.= join("", map { "// $_\n" } split(/\n/, Dumper($m)));
1292 $info->{'java-result'} = code::formatTemplate($v->{type}, $v);
1293 $info->{'java-result-return'} = code::formatTemplate($v->{'java-result-return'}, $v);
1295 $info->{'native-result'} = code::formatTemplate($v->{'carrier'}, $v);
1296 $info->{'trampoline-result-define'} = code::formatTemplate($v->{'trampoline-result-define'}, $v);
1297 $info->{'trampoline-result-assign'} = code::formatTemplate($v->{'trampoline-result-assign'}, $v);
1298 $info->{'trampoline-result-return'} = code::formatTemplate($v->{'trampoline-result-return'}, $v);
1300 $needScope = 1 if $type->{'need-scope'};
1303 $info->{'create-frame'} = '(Frame frame$ = Frame.frame())' if $needFrame;
1304 $info->{'trampoline-scope'} = '(ResourceScope scope$$ = ResourceScope.newConfinedScope())' if $trampScope;
1306 push @{$info->{'java-arg'}}, 'SegmentAllocator alloc$' if $needAlloc;
1307 push @{$info->{'java-arg'}}, 'ResourceScope scope$' if $needScope;
1309 foreach my $field (@arrayFields) {
1310 my $with = $field =~ m/-arg$/n ? ",\n\t" : "\n\t";
1311 $info->{$field} = join $with, @{$info->{$field}};
1314 $info->{successcodes} = $s->{successcodes} ? $s->{successcodes} : '';
1315 $info->{errorcodes} = $s->{errorcodes} ? $s->{errorcodes}: '';
1317 if ($s->{category} eq 'funcpointer') {
1318 $info->{'trampoline-signature'} = formatSignature($s);
1324 sub formatFunctionPointer {
1328 my $template = $commandTypes->{templates}->{'funcpointer-readwrite'};
1329 my $info = collectFunctionInfo($api, $commandTypes, $s);
1332 package => 'vulkan',
1337 foreach my $k (keys %{$template->{insert}}) {
1338 my $t = $template->{insert}->{$k};
1340 $v->{$k} = code::formatTemplate($t, $info);
1343 code::formatTemplateStream($template->{class}, $v);
1346 sub formatFunctionDescriptor {
1349 my $types = $ct->{types};
1351 my $void = $s->{proto}->{fullType} eq 'void';
1352 my $override = $ct->{overrides}->{$s->{name}};
1354 foreach my $m ($void ? () : $s->{proto}, @{$s->{items}}) {
1355 my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
1356 my $type = $types->{$deref};
1358 die "No type found ".Dumper($m, $s, $override) if !$type;
1360 my $v = buildVars($s, $m, $type);
1362 push @fields, code::formatTemplate($v->{layout}, $v)." /* $m->{deref} $m->{name} */";
1365 return ($void ? 'FunctionDescriptor.ofVoid(' : 'FunctionDescriptor.of(')
1366 .join(",\n\t\t", @fields).')';
1369 sub formatFunction {
1373 my $void = $s->{proto}->{fullType} eq 'void';
1374 my $override = $ct->{overrides}->{$s->{name}};
1375 my $tempname = $override->{template} ? $override->{template} : 'method';
1376 my $template = $ct->{templates}->{$tempname};
1378 my $info = collectFunctionInfo($api, $ct, $s);
1380 #join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
1381 " /* template: $tempname */\n".
1382 code::formatTemplate($template->{invoke}, $info);