notzed.apistatic notzed.apiobject \
notzed.ffmpeg \
notzed.clstatic \
- notzed.xlib notzed.vkregistry
+ notzed.xlib \
+ notzed.vkregistry notzed.vkregistry.test \
+ notzed.vkheader notzed.vkheader.test
native_MODULES = notzed.api
notzed.apistatic_JDEPMOD = notzed.nativez notzed.api
notzed.apiobject_JDEPMOD = notzed.nativez notzed.api
notzed.ffmpeg_JDEPMOD = notzed.nativez
notzed.clstatic_JDEPMOD = notzed.nativez
-notzed.vkregistry_JDEPMOD = notzed.nativez notzed.xlib
+notzed.vkregistry_JDEPMOD = notzed.nativez
+notzed.vkregistry.test_JDEPMOD = notzed.vkregistry notzed.xlib
notzed.vkheader_JDEPMOD = notzed.nativez
+notzed.vkheader.test_JDEPMOD = notzed.vkheader
notzed.apistatic_JMAIN = api.test.TestAPI
notzed.apiobject_JMAIN = api.test.TestAPI
notzed.ffmpeg_JMAIN = ffmpeg.test.TestFFMPEG
notzed.clstatic_JMAIN = opencl.test.clinfo
-notzed.vkregistry_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube
+notzed.vkregistry.test_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube
+notzed.vkheader.test_JMAIN = vulkan.test.TestMandelbrot
$(foreach module,$(java_MODULES),$(eval $(module)_JMAINFLAGS=--enable-native-access=notzed.nativez,$(module)))
-notzed.vkregistry_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.vkregistry
+notzed.vkregistry.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.vkregistry
+notzed.vkheader.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.vkheader
include java.make
success:errcode_ret=CL.CL_SUCCESS {
code:<inline> {{
- public {name} create(cl_platform_id platform, ResourceScope scope) {
+ public cl_khr_gl_event create(cl_platform_id platform, ResourceScope scope) {
// OpenCL 1.2+ only
return create((s) -> CL.clGetExtensionFunctionAddressForPlatform(platform, s), scope);
//return create(CL::clGetExtensionFunctionAddress, scope);
#!/usr/bin/perl
+# TODO: formatLibrary - code:<inline> has no template applied, no vars available to do so
+
# usage
# -t package target package
# -d directory output root
use code;
use method;
-$SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
+$SIG{__DIE__} = sub { Carp::confess( @_ ) };
+$SIG{'INT'} = sub { Carp::confess() };
+
$Data::Dumper::Indent = 1;
my $apidef = "api.api";
sub formatFunction {
my $api = shift;
my $c = shift;
+ my $tname = shift;
my $template = shift;
my $info = new method($api, $c);
my $code;
- #foreach my $l (split /\n/,Dumper($info->{vars}).Dumper($info->{arguments})) {
- # $code .= "// $l\n";
- #}
+ # allow per-function override
+ $template = $api->findTemplateName($c->{$tname}) if defined $c->{$tname};
+
+ if (defined($template)) {
+ if ($tname eq 'func:template') {
+ foreach my $l (split /\n/,Dumper($c)) {
+ $code .= "// $l\n";
+ }
+ }
- $code .= code::applyTemplate($template, $info->{vars});
- $code =~ s/^\s*\n//osgm;
+ $code .= code::applyTemplate($template, $info->{vars});
+ $code =~ s/^\s*\n//osgm;
- return $code;
+ return $code;
+ } else {
+ return ();
+ }
}
sub formatCall {
$res->{seen}->{"$_->{type}:$_->{name}"}++ == 0
} $api->findMatches($inc, $res->{ctx});
- if ($inc->{type} eq 'func') {
+ if ($obj->{name} eq 'VkInstance') {
+ print Dumper($obj, $inc, \@list);
+ die;
+ }
+
+ if ($inc->{type} eq 'func' && $inc->{literal}) {
+ push @{$res->{func}}, $inc->{literal};
+ } elsif ($inc->{type} eq 'func') {
my $def = $api->{index}->{'func:<default>'};
- my $func = api::optionValue('func:template', 'code:method=invoke', $inc, $res->{template});
- my $init = api::optionValue('init:template', undef, $inc, $res->{template});
- my $funct = findTemplateName($api, $func);
- my $initt = findTemplateName($api, $init) if defined $init;
+ my $func = api::optionValue('func:template', 'code:method=invoke', $inc, $res->{template}, $def);
+ my $init = api::optionValue('init:template', undef, $inc, $res->{template}, $def);
+ my $funct = $api->findTemplateName($func);
+ my $initt = $api->findTemplateName($init) if defined $init;
- push @{$res->{func}}, map { formatFunction($api, $_, $funct) } @list;
- push @{$res->{init}}, map { formatFunction($api, $_, $initt) } @list if defined($initt);
+ push @{$res->{func}}, map { formatFunction($api, $_, 'func:template', $funct) } @list;
+ push @{$res->{init}}, map { formatFunction($api, $_, 'init:template', $initt) } @list;
} elsif ($inc->{type} eq 'define') {
push @{$res->{define}}, map { code::formatDefine($api, $_) } @list;
} elsif ($inc->{type} eq 'enum') {
- push @{$res->{enum}}, map { code::formatEnum($api, $_) } @list;
+ push @{$res->{enum}}, map { code::formatEnum($api, $_, $res->{seen}) } @list;
} elsif ($inc->{type} eq 'call') {
- push @{$res->{enum}}, map { formatCall($api, $_) } @list;
+ push @{$res->{call}}, map { formatCall($api, $_) } @list;
} else {
die;
}
}
}
-sub findTemplateName {
- my $api = shift;
- my $name = shift;
-
- if ($name =~ m/^(.+)=(.+)$/) {
- my $template = api::findItem($api->{index}->{$1}, $2);
- return $template if defined $template;
- }
- die "can't find template '$name'\n";
-}
-
-sub findTemplate {
- my $api = shift;
- my $obj = shift;
- my $s = shift;
- my $data = $api->{data};
- my $def = $api->{index}->{"$s->{type}:<default>"};
- my $name = api::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
-
- return findTemplateName($api, $name);
-}
# TODO: embedded structs
sub formatStruct {
my $s = shift;
my $data = $api->{data};
my $seen = {};
- my $structTemplate = findTemplate($api, $obj, $s);
+ my $structTemplate = $api->findTemplate($obj, $s);
my @members = code::scanFields($api, $s);
my @membersOutput = grep { $_->{field}->{output} } @members;
ctx => $s,
library => [],
func => [],
+ init => [],
define => [],
enum => [],
call => [],
formatLibrary($api, $obj, $res);
+ # handle per-struct format functions
+ foreach my $fmt (@{$s->{postformat}}) {
+ $fmt->($api, $obj, $res, $s);
+ }
+
my $vars = {
%{$api->{vars}},
layout => code::formatStructLayout($api, $s, \@members),
name => $s->{name},
varhandles => $varhandles,
accessors => $accessors,
+ init => join("\n", @{$res->{init}}),
methods => join("\n", @{$res->{func}}),
defines => join("\n", @{$res->{define}}),
enums => join("\n", @{$res->{enum}}),
};
my $code;
- foreach my $l (split /\n/,Dumper($s, \@members)) {
+ foreach my $l (split /\n/,Dumper({ template => $api->queryTemplate($obj, $s), struct => $s }, \@members)) {
$code .= "//$l\n";
}
$api->{output}->{"$s->{type}:$s->{name}"} = 1;
- return $code.code::applyTemplate($structTemplate, $vars);
+ return $code.code::applyTemplate($structTemplate, $vars, 1);
}
# output all libraries
next if $api->{output}->{"$obj->{type}:$obj->{name}"};
next if $obj->{output} != 1;
- my $library = findTemplateName($api, api::optionValue('template', 'code:class=library', $obj, $def));
+ my $library = $api->findTemplateName(api::optionValue('template', 'code:class=library', $obj, $def));
my $res = {
ctx => $obj,
library => [],
calls => join("\n", @{$res->{call}}),
};
- export($api, $obj->{name}, code::applyTemplate($library, $vars));
+ export($api, $obj->{name}, code::applyTemplate($library, $vars, 1));
}
}
print "gen ".($#list+1)." $obj->{type} $obj->{name}\n" if ($api->{vars}->{verbose} > 0);
foreach my $s (@list) {
next if $api->{output}->{"$s->{type}:$s->{name}"};
+
print " $s->{name}\n" if ($api->{vars}->{verbose} > 1);
export($api, $s->{rename}, formatClass($api, $obj, $s));
# then anything else left using the default outputs
foreach my $s (grep { $_->{output} && ($_->{type} =~ m/struct|union|call/) && !$api->{output}->{"$_->{type}:$_->{name}"} } @{$api->{api}}) {
my $obj = $data->{"$s->{type}:<default>"};
+ print "* $s->{name}\n" if ($api->{vars}->{verbose} > 1);
export($api, $s->{rename}, formatClass($api, $obj, $s));
}
}
public void setAtIndex(long index, byte value) {
segment.set(Memory.BYTE, index, value);
}
+
+ public String toUtf8String() {
+ return segment.getUtf8String(0L);
+ }
}
*/
public class Frame implements AutoCloseable, SegmentAllocator {
- private final long tos;
- private Stack stack;
- private ResourceScope overflow;
+ private final ResourceScope scope;
+ private final SegmentAllocator alloc;
- Frame(long tos, Stack stack) {
- this.tos = tos;
- this.stack = stack;
+ Frame() {
+ this.scope = ResourceScope.newConfinedScope();
+ this.alloc = SegmentAllocator.newNativeArena(scope);
}
- private static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create());
- private static final ThreadLocal<Stack> stacks = ThreadLocal.withInitial(() -> new Stack(scope));
-
public static Frame frame() {
- return stacks.get().createFrame();
- }
-
- private static class Stack {
- private final MemorySegment stack;
- private long sp;
- private Thread thread = Thread.currentThread();
-
- Stack(ResourceScope scope) {
- stack = MemorySegment.allocateNative(4096, 4096, scope);
- sp = 4096;
- }
- Frame createFrame() {
- return new Frame(sp, this);
- }
+ return new Frame();
}
@Override
public MemorySegment allocate(long size, long alignment) {
- if (stack.thread != Thread.currentThread())
- throw new IllegalStateException();
- if (alignment != Long.highestOneBit(alignment))
- throw new IllegalArgumentException();
- if (stack.sp >= size) {
- stack.sp = (stack.sp - size) & ~(alignment - 1);
- return stack.stack.asSlice(stack.sp, size).fill((byte)0);
- } else {
- // or arena allocator?
- if (overflow == null)
- overflow = ResourceScope.newConfinedScope();
- return MemorySegment.allocateNative(size, alignment, overflow);
- }
+ return alloc.allocate(size, alignment);
}
@Override
public void close() {
- stack.sp = tos;
- stack = null;
- if (overflow != null) {
- overflow.close();
- overflow = null;
- }
- }
+ scope.close();
+ }
+
+ // private final long tos;
+ // private Stack stack;
+ // private ResourceScope overflow;
+
+ // Frame(long tos, Stack stack) {
+ // this.tos = tos;
+ // this.stack = stack;
+ // }
+
+ // private static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create());
+ // private static final ThreadLocal<Stack> stacks = ThreadLocal.withInitial(() -> new Stack(scope));
+
+ // public static Frame frame() {
+ // //return stacks.get().createFrame();
+ // }
+
+ // private static class Stack {
+ // private final MemorySegment stack;
+ // private long sp;
+ // private Thread thread = Thread.currentThread();
+
+ // Stack(ResourceScope scope) {
+ // stack = MemorySegment.allocateNative(4096, 4096, scope);
+ // sp = 4096;
+ // }
+ // Frame createFrame() {
+ // return new Frame(sp, this);
+ // }
+ // }
+
+ // @Override
+ // public MemorySegment allocate(long size, long alignment) {
+ // if (stack.thread != Thread.currentThread())
+ // throw new IllegalStateException();
+ // if (alignment != Long.highestOneBit(alignment))
+ // throw new IllegalArgumentException();
+ // if (stack.sp >= size) {
+ // stack.sp = (stack.sp - size) & ~(alignment - 1);
+ // return stack.stack.asSlice(stack.sp, size).fill((byte)0);
+ // } else {
+ // // or arena allocator?
+ // if (overflow == null)
+ // overflow = ResourceScope.newConfinedScope();
+ // return MemorySegment.allocateNative(size, alignment, overflow);
+ // }
+ // }
+
+ // @Override
+ // public void close() {
+ // stack.sp = tos;
+ // stack = null;
+ // if (overflow != null) {
+ // overflow.close();
+ // overflow = null;
+ // }
+ // }
public MemorySegment allocateInt() {
return allocate(Memory.INT);
return Memory.NULL;
}
}
+
+ public static MemorySegment copy(MemorySegment ctx, String string) {
+ if (string != null) {
+ SegmentAllocator alloc = SegmentAllocator.nativeAllocator(ctx.scope());
+
+ return alloc.allocateUtf8String(string);
+ } else {
+ return Memory.NULL;
+ }
+ }
+
+ public static MemorySegment copy(MemorySegment ctx, String[] array) {
+ System.out.printf("copy array %s\n", array != null ? array.length : "");
+ if (array != null) {
+ SegmentAllocator alloc = SegmentAllocator.nativeAllocator(ctx.scope());
+ MemorySegment list = alloc.allocateArray(Memory.POINTER, array.length);
+ for (int i = 0; i < array.length; i++) {
+ System.out.printf(" [%d] '%s'\n", i, array[i]);
+ list.setAtIndex(Memory.POINTER, i, alloc.allocateUtf8String(array[i]));
+ }
+ return list;
+ } else {
+ return Memory.NULL;
+ }
+ }
}
return sharedScope;
}
+ public static MethodHandle downcall(FunctionDescriptor desc) {
+ return CLinker.systemCLinker().downcallHandle(desc);
+ }
+
public static MethodHandle downcall(String name, FunctionDescriptor desc) {
return SymbolLookup.loaderLookup().lookup(name)
.map(sym -> CLinker.systemCLinker().downcallHandle(sym, desc))
return list != null ? list.length() : 0;
}
+ public static long length(MemorySegment list) {
+ return list != null ? list.byteSize() : 0;
+ }
+
+ public static long length(Object[] list) {
+ return list != null ? list.length : 0;
+ }
+
public static long size(MemorySegment s) {
return s != null ? s.byteSize() : 0;
}
+# TODO: define more & consistent stage processing hooks, currently implicit require init() and stage.postprocess
+# TODO: code:<inline> etc should probably instead be code:<type> where <type> is one of the template fields - methods, init, etc.
+
package api;
use strict;
use File::Basename;
use Data::Dumper;
use List::Util qw(first);
+use Carp 'verbose';
use config;
items => [],
options => [],
regex => qr/^func:<default>$/,
- type => 'func'
+ type => 'func',
+ 'func:template' => 'code:method=invoke'
},
'enum:<default>' => {
name => '<default>',
$self->{data} = { %{$self->{data}}, %{$info} };
}
+ # FIXME: fix the list names in export.cc and export-defines instead
+ # set all the member list names to be consistent
+ foreach my $s (values %{$self->{data}}) {
+ foreach my $n ('fields', 'arguments', 'values') {
+ if ($s->{$n}) {
+ $s->{items} = $s->{$n};
+ delete $s->{$n};
+ }
+ }
+ }
+
+ bless $self, $class;
+
+ # handle requires and init modules
foreach my $p (@{$conf->{pragmas}}) {
if ($p->[0] eq '%require') {
require $p->[1];
+ my $n = $p->[1];
+ $n =~ s/.pm$/::init(\$self)/;
+ print "$n\n";
+ eval $n || die "module init failed: $! $@";
}
}
postprocess($self);
+ foreach my $p (@{$conf->{pragmas}}) {
+ if ($p->[0] eq '%stage.postprocess') {
+ eval "$p->[1](\$self)" || die "$p->[0] $p->[1] failed: $! $@";
+ }
+ }
+
# form the types
# TODO: could match the regexes to every possible type in the api and create a direct index
my $copyIndex = {};
push @{$self->{types}}, initType($self, $copyIndex, $obj);
}
- bless $self, $class;
-
return $self;
}
make_path($dir) if (!-d $dir);
open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
- print "writing '$path'\n";
+ #print "writing '$path'\n";
$f;
}
my $type = {};
$type->{options} = [ @{$obj->{options}} ];
+ $type->{file} = $obj->{file};
# FIXME: per-item options, set etc?
- foreach my $inc (@{$obj->{items}}) {
+ foreach my $inc (grep { defined $_->{literal} } @{$obj->{items}}) {
$type->{items}->{$inc->{match}} = $inc->{literal};
}
my @list;
foreach my $type (@{$api->{types}}) {
- my $select = !defined($type->{select}) || defined($m->{$type->{select}});
+ my $select = !defined($type->{select}) || defined($m->{select}->{$type->{select}});
if ($select && ($deref =~ m/$type->{regex}/)) {
my %match = %+;
undef;
}
+sub findTemplateName {
+ my $api = shift;
+ my $name = shift;
+
+ if ($name =~ m/^(.+)=(.+)$/) {
+ my $template = findItem($api->{index}->{$1}, $2);
+ return $template if defined $template;
+ }
+ die "can't find template '$name'\n";
+}
+
+sub queryTemplate {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $sname = $s->{"$s->{type}:template"};
+ my $def = $api->{index}->{"$s->{type}:<default>"};
+ my $name = $sname ? $sname : api::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
+
+ return $name;
+}
+
+sub findTemplate {
+ my $api = shift;
+ my $obj = shift;
+ my $s = shift;
+ my $sname = $s->{"$s->{type}:template"};
+ my $def = $api->{index}->{"$s->{type}:<default>"};
+ my $name = $sname ? $sname : api::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
+
+ print "template: $s->{name} -> $name\n" if ($api->{vars}->{verbose} > 1);
+
+ return findTemplateName($api, $name);
+}
+
# find value of first option of the given name
sub optionValue {
my $name = shift;
}
}
-# use either {match} or {regex} to get all matches for a data type
+# use either {match} or {regex} or {matcher} to get all matches for a data type
sub findMatches {
my $api = shift;
my $inc = shift;
my $ctx = shift;
my $data = $api->{data};
- if ($inc->{match} eq 'func:<matcher>') {
- # or just last option?
- my $code;
-
- if (defined($inc->{literal})) {
- $code = 'sub { '.$inc->{literal}.' }';
- } else {
- my @options = @{$inc->{options}};
- $code = 'sub { '.$options[$#options].'(@_) }';
- }
-
- my $match = eval $code;
-
- if (!defined($match)) {
- die "unable to parse match function $inc->{match} $! $@";
- }
- grep { $match->($_, $ctx) } grep { $_->{type} eq $inc->{type} } values %$data;
+ if ($inc->{matcher}) {
+ grep { $inc->{matcher}->($_, $ctx) } grep { $_->{type} eq $inc->{type} } values %$data;
} else {
my $s = $data->{$inc->{match}};
foreach my $inc (@{$obj->{items}}) {
next if ($inc->{type} eq 'library');
- #print "? $inc->{regex}\n";
+ print "? $inc->{match}\n";
foreach my $s (findMatches($api, $inc, $obj)) {
my $n = "$s->{type}:$s->{name}";
}
} elsif ($obj->{type} =~ m/^(struct|union|call|func|enum|define)$/) {
#foreach my $n (grep { $_ =~ m/$obj->{regex}/ } keys %data) {
+
+ print "? $obj->{name}\n";
+
+
foreach my $s (findMatches($api, $obj, $obj)) {
my $n = "$s->{type}:$s->{name}";
+ print "+ $n\n";
+
$seen{$n}++;
$s->{output} = 1;
addDependencies($api, $obj, $s, $setdeps);
}
sub parseRename {
+ my $api = shift;
my $how = shift;
my $rename = $renameTable{'identity'};
my $rx = qr/$1/;
my $rp = $2;
$rename = sub { my $s=shift; $s = $old->($s); $s =~ s/$rx/$rp/; return $s;};
+ } elsif ($n =~ m@^func:(.*)$@) {
+ $rename = eval "sub { $1(\@_) }";
+ if (!defined($rename)) {
+ die "Unable to parse rename function '$1'";
+ }
} elsif ($new) {
$rename = sub { my $s=shift; $s = $old->($s); return $new->($s); };
} else {
# Find any anonymous types and add them in
my %anonymous = ();
foreach my $s (values %{$api->{data}}) {
- # FIXME: fix the list names in export.cc and export-defines
- $s->{items} = $s->{fields} if $s->{fields};
- $s->{items} = $s->{arguments} if $s->{arguments};
- $s->{items} = $s->{values} if $s->{values};
foreach my $m (grep { $_->{type} =~ m/struct:|union:/} @{$s->{items}}) {
$anonymous{$m->{type}} = 1 if !defined($api->{data}->{$m->{type}});
# Note that type:name regexes always start at the beginning
foreach my $obj (@{$api->{api}}) {
+ $obj->{match} = "$obj->{type}:$obj->{name}";
+
if ($obj->{name} =~ m@^/(.*)/$@) {
$obj->{regex} = qr/^$obj->{type}:$1/;
+ } elsif ($obj->{name} =~ m@^<invoke=(.*)>$@) {
+ $obj->{matcher} = eval "sub { $1(\@_) }";
+ die "unable to parse match function $obj->{name} $! $@" if !defined($obj->{matcher});
} else {
$obj->{regex} = qr/^$obj->{type}:$obj->{name}$/;
}
- $obj->{match} = "$obj->{type}:$obj->{name}";
foreach my $opt (@{$obj->{options}}) {
if ($opt =~ m/^(.+:rename)=(.*)$/) {
- $obj->{$1} = parseRename($2);
+ $obj->{$1} = parseRename($api, $2);
} elsif ($opt =~ m/^rename=(.*)$/) {
- $obj->{"$obj->{type}:rename"} = parseRename($1);
+ $obj->{"$obj->{type}:rename"} = parseRename($api, $1);
}
}
my $match = $inc->{match};
my $mode = $defmode;
- if ($inc->{match} =~ m/^(.*):(.*)$/) {
+ if ($inc->{match} =~ m/^(.*?):(.*)$/) {
$match = $2;
$mode = $1;
}
$inc->{type} = $mode;
if ($match =~ m@^/(.*)/$@) {
$inc->{regex} = $mode ne 'field' ? qr/$mode:$1/ : qr/$1/;
+ } elsif ($match =~ m@^<invoke>$@) {
+ $inc->{matcher} = eval "sub { $inc->{literal} }";
+ die "unable to parse match function $inc->{literal} $! $@" if !defined($inc->{matcher});
+ } elsif ($match =~ m@^<invoke=(.*)>$@) {
+ $inc->{matcher} = eval "sub { $1(\@_) }";
+ die "unable to parse match function $inc->{match} $! $@" if !defined($inc->{matcher});
} else {
$inc->{regex} = $mode ne 'field' ? qr/^$mode:$match$/ : qr/^$match$/;
}
foreach my $opt (@{$inc->{options}}) {
if ($opt =~ m/^rename=(.*)$/) {
#print "option $opt ".Dumper($inc);
- $inc->{"$mode:rename"} = parseRename($1);
+ $inc->{"$mode:rename"} = parseRename($api, $1);
}
}
my $def = $api->{index}->{"$s->{type}:<default>"};
#print "process $s->{type}:$s->{name}.$m->{name}\n";
- #print " $m->{name}\n";
+ print " $m->{name}\n" if ($api->{vars}->{verbose} > 1);
foreach my $flag (@itemFlags) {
my $value = optionFlag("$flag", $inc);
$value = optionFlag("$flag:$m->{name}", $obj, $def) if !defined($value);
$m->{$flag} = $value if (defined($value));
+ $m->{select}->{$flag} = 1 if (defined($value));
}
foreach my $option (@itemOptions) {
my $value = optionValue("$option", undef, $inc);
$value = optionValue("$option:$m->{name}", undef, $obj, $def) if !defined($value);
$m->{$option} = $value if (defined($value));
+ $m->{select}->{$option} = 1 if (defined($value));
}
foreach my $option (@itemAnyOptions) {
my $value = optionValue("$option", undef, $inc);
}
$m->{output} = 1;
- $m->{rename} = (first { defined $_ } $inc->{'field:rename'}, $obj->{'field:rename'}, $def->{'field:rename'}, $renameTable{identity})->($m->{name});
+ $m->{rename} = (first { defined $_ } $inc->{'field:rename'}, $obj->{'field:rename'}, $def->{'field:rename'}, $renameTable{identity})->($m->{name}, $m, $s);
}
# process func or call main type
processField($api, $obj, $inc, $s, $m);
}
- $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+ $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name}, $s);
$s->{access} = optionValue('access', '', $obj, $def);
$v = optionValue('onsuccess', undef, $obj, $def);
$s->{onsuccess} = $v if defined $v;
my $obj = shift;
my $inc = shift;
my $s = shift;
+ my @params = (defined($s->{result}) ? $s->{result} : (), @{$s->{items}});
my $index = -1;
+ my $lindex = -2 - $#params;
my $def = $api->{index}->{"$s->{type}:<default>"};
my $v;
print "process $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
foreach my $m (defined($s->{result}) ? $s->{result} : (), @{$s->{items}}) {
- #print " $m->{name}\n";
+ #print "[$index] [$lindex] $m->{name}\n";
foreach my $flag (@itemFlags) {
my $value = optionFlag("$flag:$m->{name}", $inc, $obj, $def);
$value = optionFlag("$flag:$index", $inc, $obj, $def) if !defined($value);
+ $value = optionFlag("$flag:$lindex", $inc, $obj, $def) if !defined($value);
$m->{$flag} = $value if (defined($value));
+ $m->{select}->{$flag} = 1 if (defined($value));
}
foreach my $option (@itemOptions) {
my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
$value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
+ $value = optionValue("$option:$lindex", undef, $inc, $obj, $def) if !defined($value);
$m->{$option} = $value if (defined($value));
+ $m->{select}->{$option} = 1 if (defined($value));
}
foreach my $option (@itemAnyOptions) {
my $value = optionValue("$option:$m->{name}", undef, $inc, $obj, $def);
$value = optionValue("$option:$index", undef, $inc, $obj, $def) if !defined($value);
+ $value = optionValue("$option:$lindex", undef, $inc, $obj, $def) if !defined($value);
$value = optionValue("$option", undef, $inc, $obj, $def) if !defined($value);
$m->{$option} = $value if (defined($value));
}
$m->{output} = 1;
$index++;
+ $lindex++;
}
- $s->{rename} = (first { defined $_ } $inc->{"$s->{type}:rename"}, $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+ $s->{rename} = (first { defined $_ } $inc->{"$s->{type}:rename"}, $obj->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name}, $s);
$s->{access} = optionValue('access', '', $inc, $obj, $def);
$v = optionValue('onsuccess', undef, $inc, $obj, $def);
$s->{onsuccess} = $v if defined ($v);
next if ($seen->{"$s->{type}:$s->{name}"}++);
- print "process type $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
+ print "process type $obj->{match} $s->{type}:$s->{name}\n" if ($api->{vars}->{verbose} > 1);
# process the struct/union fields first
foreach my $inc (grep { $_->{type} eq 'field' } @{$obj->{items}}) {
processFields($api, $memberseen, $obj, undef, $s, @{$s->{items}});
}
- $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $def->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name});
+ $s->{rename} = (first { defined $_ } $obj->{"$s->{type}:rename"}, $def->{"$s->{type}:rename"}, $renameTable{identity})->($s->{name}, $s);
# finish off
postProcessType($s);
# handle other types included/mark them no-output
- $seen->{"$s->{type}:$s->{name}"} = 0;
+ #$seen->{"$s->{type}:$s->{name}"} = 0;
processLibrary($api, $seen, $obj, $s);
}
my $ctx = shift;
my $data = $api->{data};
- return if ($seen->{"$lib->{type}:$lib->{name}"}++);
+ #return if ($seen->{"$ctx->{type}:$ctx->{name}"}++);
- print "process library $lib->{type}:$lib->{name}\n";
+ print "process library $ctx->{type}:$ctx->{name}\n" if ($api->{vars}->{verbose} > 0);
#print 'lib='.Dumper($lib);
+ #print Dumper($api->{api}); die;
#print 'lib.options='.Dumper($lib->{options});
# TODO: embedded types
}
} elsif ($inc->{type} eq 'library') {
foreach my $l (grep { "$_->{type}:$_->{name}" =~ m/$inc->{regex}/ } @{$api->{api}}) {
+ print " -> $l->{name}\n";
# included libraries are never output directly
$l->{output} = 0;
processLibrary($api, $seen, $l, $ctx);
private {rename}(MemorySegment segment) {
this.segment = segment;
+{init}
}
public static {rename} create(MemorySegment segment) {
private {rename}(MemorySegment segment) {
this.segment = segment;
+{init}
}
public static {rename} create(MemorySegment segment) {
return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize() * length, scope)) : null;
}
+ public static {rename} createArray(long length, SegmentAllocator alloc) {
+ return create(alloc.allocateArray(LAYOUT, length));
+ }
+
@Override
public final MemoryAddress address() {
return segment.address();
private {rename}(MemoryAddress address, ResourceScope scope) {
this.address = address;
this.scope = scope;
+{init}
}
public static {rename} create(MemoryAddress address, ResourceScope scope) {
}
}}
geti set=value={getnative} set=segment=segment {{
+ # TODO: final static VarHandle {name}$VI = MemoryLayout.sequenceLayout(LAYOUT).varHandle(PathElement.sequenceElement(), PathElement.groupElement("{name}"));
public {type} get{rename}AtIndex(long index) {
// option a: resolve an offset segment and asme as above with set=segment=segment
// option b: an indexed varhandle
}}
}
+# set requires allocations, not sure if it should be Frame or Scope?
+code getset-frame {
+ get set=value={getnative} {{
+ public {type} get{rename}() {
+ return {tojava};
+ }
+ }}
+ geti set=value={getnative} set=segment=segment {{
+ public {type} get{rename}AtIndex(long index) {
+ // option a: resolve an offset segment and asme as above with set=segment=segment
+ // option b: an indexed varhandle
+ MemorySegment segment = segment().asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize());
+ return {tojava};
+ }
+ }}
+ set set=value=value {{
+ public void set{rename}(Frame frame$, {type} value) {
+ {setnative};
+ }
+ }}
+ seti set=value=value set=segment=segment {{
+ public void set{rename}AtIndex(Frame frame$, long index, {type} value) {
+ MemorySegment segment = segment().asSlice(index * LAYOUT.byteSize(), LAYOUT.byteSize());
+ {setnative};
+ }
+ }}
+}
+
code getsetelement {
get set=index=i {{
public {typei} get{rename}Element(long i) {
sub formatEnum {
my $api = shift;
my $s = shift;
+ my $seen = shift;
my $type = $defineType{$s->{value_type}};
my $d = join "\n\t", "// enum $s->{name}", map {
my $value = $_->{value};
"public static final $type $name = $typePrefix{$type}$value$typeSuffix{$type};";
+ } grep {
+ $seen->{"value:$_->{name}"}++ == 0
} @{$s->{items}};
return "\t$d\n";
}
+# somewhat faster
+# dunno how to ignore comments tho
sub formatTemplate {
my $template = shift;
my $vars = shift;
my $prefix = shift;
my $result;
- #print "apply-template vars=".Dumper($vars);
-
- while ($template =~ m/^(.*?)\{([\w-]+)\}(.*)$/s) {
+ while ($template =~ m/^(.*?)\{([\w-]+)\}/spo) {
$result .= $1;
- #print " $2 -> $vars->{$2}\n";
+ if (defined $vars->{$2}) {
+ $template = $vars->{$2}.${^POSTMATCH};
+ } else {
+ $result .= "{$2}";
+ $template = ${^POSTMATCH};
+ }
+ }
+ $result .= $template;
+ $result =~ s/^/$prefix/gm;
+ $result;
+}
+
+# Format a template without recursive application
+sub formatTemplateStream {
+ my $template = shift;
+ my $vars = shift;
+ my $prefix = shift;
+ my $result;
+
+ while ($template =~ m/^(.*?)\{([\w-]+)\}/spo) {
+ $result .= $1;
if (defined $vars->{$2}) {
- $template = $vars->{$2}.$3;
+ $result .= $vars->{$2};
} else {
$result .= "{$2}";
- $template = $3;
}
+ $template = ${^POSTMATCH};
}
$result .= $template;
$result =~ s/^/$prefix/gm;
sub applyTemplate {
my $code = shift;
my $match = shift;
+ my $stream = shift;
my $vars = \%{$match};
+ #print "template: $code->{match}\n";
+
foreach my $set (api::optionValuesAll('set', $code)) {
$vars->{$1} = $2 if ($set =~ m/^(\w+)=(.*)/);
}
- formatTemplate($code->{literal}, $vars);
+ $stream ? formatTemplateStream($code->{literal}, $vars) : formatTemplate($code->{literal}, $vars);
}
sub bitfieldSize {
push @{$self->{pragmas}}, \@pragma;
}
} elsif ($tokeniser->{type} eq 'token') {
- $target = { type => $t, options => [], items => [] };
+ $target = { type => $t, options => [], items => [], file => "$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}" };
push @$list, $target;
$state = 1;
} else {
die("$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}:$tokeniser->{colno}: expected type token");
}
} elsif ($state == 1) {
+ # token [token]
if ($tokeniser->{type} eq 'token') {
$target->{name} = $t;
$state = 2;
die("$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}:$tokeniser->{colno}: expected match token");
}
} elsif ($state == 2) {
+ # token token token* [ '{' | literal ]
if ($t eq '{') {
$state = 3;
} elsif ($tokeniser->{type} eq 'literal') {
sub fieldScopeAction {
my $m = shift;
+ # TBH this just isn't going to work, scopes are so limited in use
+ return ();
+
if ($m->{field}->{scope} =~ m/^explicit,(.*)$/) {
return code::formatTemplate("scope\$.addCloseAction(() -> $1;", { %{$m->{match}}, value => 'res$' });
} elsif ($m->{field}->{scope} =~ m/^explicit$/) {
$info->{'java-result-return'} = 'return res$;';
$info->{'trampoline-result-define'} = apply('{type} res$ = ', $result);
- $info->{'trampoline-result-return'} = code::formatTemplate('return {tonative};', { %{$result->{match}}, value => 'res$' });
+ $info->{'trampoline-result-return'} = code::formatTemplate('return ({type}){tonative};', { %{$result->{match}}, value => 'res$' });
} else {
$info->{'java-result'} = 'void';
$info->{'java-result-assign'} = '';
# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
#
-# TODO: add a 'mode' to type matching
-# this woudld allow per-field overrides, e.g. char * field use String or MemorySegment or ByteArray depending on option
+# TODO: use select=array rather than inline logic to select alternatives? maybe?
# special case for bitfields
# TODO: well this is bit of a mess, maybe should use <init> like code:method?
}
# 1d array of (struct or union or function?)
-type /^\[(?<length>\d+)\$\{(\w+)\}\]$/ {
+type /^\[(?<length>\d+)\$\{(\w+)\}\]$/ template=code:getbyvalue {
type {{ "$data->{$m->{type}}->{rename}" }}
layout {{ "MemoryLayout.sequenceLayout({length}, {type}.LAYOUT)" }}
+
+ getsegment {{ '(MemorySegment){name}$SH.invokeExact({segment})' }}
+ getnative {{ '{type}.create({getsegment})' }}
+
+ varhandle {{
+ 'final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));'."\n".
+ '//final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement());'."\n"
+ }}
+
}
# 1d array of primitive
type /^\[(?<length>\d+)(?<ctype>[uif]\d+)\]$/
copy=<pointer> template=code:getbyvalue,code:getsetelement {
- layout {{ "MemoryLayout.sequenceLayout({length}, Memory.".uc($typeSizes{$match->{ctype}}).")" }}
- type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
- tojava {{ "{type}.createArray({value}, $match->{length}, {scope})" }}
- tonative {{ "(Addressable)Memory.address({value}" }}
-
- getnative {{ "{type}.create((MemorySegment)$m->{name}\$SH.invokeExact({segment}))" }}
+ layout {{ "MemoryLayout.sequenceLayout({length}, Memory.".uc($typeSizes{$match->{ctype}}).")" }}
+ type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ tojava {{ "{type}.createArray({value}, $match->{length}, {scope})" }}
+ tonative {{ "(Addressable)Memory.address({value})" }}
+ lea {{ "(MemorySegment)$m->{name}\$SH.invokeExact({segment})" }}
+ getnative {{ "{type}.create({lea})" }}
setnative;
- varhandle {{
+ varhandle {{
"final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
"final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement());\n"
}}
- typei {{ "$typeSizes{$match->{ctype}}" }}
- getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index})" }}
- setnativei {{ "$m->{name}\$EH.set({segment}, {index}, ({typei}){value})" }}
+ typei {{ "$typeSizes{$match->{ctype}}" }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index})" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index}, ({typei}){value})" }}
}
# 2d array of primitive
setnative;
typei {{ "$typeSizes{$match->{ctype}}" }}
- getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index0}, {index1}))" }}
- setnativei {{ "$m->{name}\$EH.set({segment}, {index0}, {index1}, ({typei}){value}))" }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index0}, {index1})" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index0}, {index1}, ({typei}){value})" }}
varhandle {{
"final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
- "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement(), MemotyLayout.PathElement.sequenceElement());\n"
+ "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());\n"
}}
}
# *void with size
type /^u64:v$/ select=array-size copy=<pointer> {
type {{ 'MemorySegment' }}
- tojava {{ "MemorySegment.ofAddress({value}, $m->{'array-size'}->{name} * 8, {scope})" }}
+ length {{ 'get'.($m->{'array-size'}->{rename}).'()' }}
+ tojava {{ "MemorySegment.ofAddress({value}, {length}, {scope})" }}
}
# *void
--- /dev/null
+
+module notzed.vkheader.test {
+ requires notzed.vkheader;
+
+ requires java.desktop;
+}
import vulkan.*;
-import static vulkan.VkBufferUsageFlagBits.*;
-import static vulkan.VkMemoryPropertyFlagBits.*;
-import static vulkan.VkSharingMode.*;
-import static vulkan.VkDescriptorType.*;
-import static vulkan.VkShaderStageFlagBits.*;
-import static vulkan.VkCommandBufferLevel.*;
-import static vulkan.VkCommandBufferUsageFlagBits.*;
-import static vulkan.VkPipelineBindPoint.*;
-
-import static vulkan.VkDebugUtilsMessageSeverityFlagBitsEXT.*;
-import static vulkan.VkDebugUtilsMessageTypeFlagBitsEXT.*;
-
-public class TestVulkan {
+import static vulkan.VkConstants.*;
+
+public class TestMandelbrot {
static final boolean debug = true;
ResourceScope scope = ResourceScope.newSharedScope();
if (!debug)
return;
try (Frame frame = Frame.frame()) {
- NativeSymbol cb = PFN_vkDebugUtilsMessengerCallbackEXT.of((severity, flags, data) -> {
+ var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data, dummy) -> {
System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
return 0;
}, scope);
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
- cb.address(),
+ cb,
null);
- logger = instance.vkCreateDebugUtilsMessengerEXT(info, null);
+ logger = instance.vkCreateDebugUtilsMessengerEXT(info, null, scope);
}
//typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
}
+ void dumpStructure(GroupLayout layout, MemorySegment s) {
+ System.out.println(s.address());
+ for (MemoryLayout m: layout.memberLayouts()) {
+ if (m instanceof jdk.incubator.foreign.ValueLayout) {
+ var vh = layout.varHandle(jdk.incubator.foreign.MemoryLayout.PathElement.groupElement(m.name().get()));
+ System.out.printf(" %s: %s\n", m.name().get(), vh.get(s));
+ }
+ }
+ }
+
void init_instance() throws Exception {
try (Frame frame = Frame.frame()) {
VkInstanceCreateInfo info = VkInstanceCreateInfo.create(frame,
debug ? new String[] { "VK_EXT_debug_utils" } : null
);
- instance = VkInstance.vkCreateInstance(info, null);
+ instance = VkInstance.vkCreateInstance(info, null, scope);
}
}
void init_device() throws Exception {
try (Frame frame = Frame.frame()) {
- IntArray count$h = IntArray.create(frame, 1);
HandleArray<VkPhysicalDevice> devs;
int count;
int res;
- devs = instance.vkEnumeratePhysicalDevices();
+ devs = instance.vkEnumeratePhysicalDevices(frame, scope);
int best = 0;
int devid = -1;
for (int i=0;i<devs.length();i++) {
VkPhysicalDevice dev = devs.getAtIndex(i);
- VkQueueFamilyProperties famprops;
-
- // TODO: change to return the allocated array directly
- dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, null);
- famprops = VkQueueFamilyProperties.createArray(frame, count$h.getAtIndex(0));
- dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, famprops);
-
- int family_count = count$h.getAtIndex(0);
+ VkQueueFamilyProperties famprops = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+ int family_count = (int)famprops.length();
for (int j=0;j<family_count;j++) {
int score = 0;
- if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_COMPUTE_BIT) != 0)
+ if ((famprops.getQueueFlagsAtIndex(j) & VK_QUEUE_COMPUTE_BIT) != 0)
score += 1;
- if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_GRAPHICS_BIT) == 0)
+ if ((famprops.getQueueFlagsAtIndex(j) & VK_QUEUE_GRAPHICS_BIT) == 0)
score += 1;
if (score > best) {
frame,
0,
queueid,
- 1,
qpri);
VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
frame,
0,
- 1,
qinfo,
null,
null,
null);
- device = physicalDevice.vkCreateDevice(devinfo, null);
+ device = physicalDevice.vkCreateDevice(devinfo, null, scope);
System.out.printf("device = %s\n", device.address());
// NOTE: app scope
- deviceMemoryProperties = VkPhysicalDeviceMemoryProperties.create(scope);
+ deviceMemoryProperties = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
physicalDevice.vkGetPhysicalDeviceMemoryProperties(deviceMemoryProperties);
- computeQueue = device.vkGetDeviceQueue(queueid, 0);
+ computeQueue = device.vkGetDeviceQueue(queueid, 0, scope);
}
}
dataSize,
usage,
VK_SHARING_MODE_EXCLUSIVE,
- 0,
null);
- VkBuffer buffer = device.vkCreateBuffer(buf_info, null);
+ VkBuffer buffer = device.vkCreateBuffer(buf_info, null, scope);
device.vkGetBufferMemoryRequirements(buffer, req);
req.getSize(),
find_memory_type(deviceMemoryProperties, req.getMemoryTypeBits(), properties));
- VkDeviceMemory memory = device.vkAllocateMemory(alloc, null);
+ VkDeviceMemory memory = device.vkAllocateMemory(alloc, null, scope);
device.vkBindBufferMemory(buffer, memory, 0);
VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(frame,
0,
- 1,
layout_binding);
- descriptorSetLayout = device.vkCreateDescriptorSetLayout(descriptor_layout, null);
+ descriptorSetLayout = device.vkCreateDescriptorSetLayout(descriptor_layout, null, scope);
/* Create descriptor pool */
VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame,
VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame,
0,
1,
- 1,
type_count);
- descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, null);
+ descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, null, scope);
/* Allocate from pool */
HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
descriptorSets.getAtIndex(0),
0,
0,
- 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
null,
bufferInfo,
mandelbrot_cs.length() * 4,
mandelbrot_cs);
- mandelbrotShader = device.vkCreateShaderModule(vsInfo, null);
+ mandelbrotShader = device.vkCreateShaderModule(vsInfo, null, scope);
/* Link shader to layout */
HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
0,
1,
layout_table,
- 0,
null);
- pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null);
+ pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null, scope);
/* Create pipeline */
VkComputePipelineCreateInfo pipeline = VkComputePipelineCreateInfo.create(frame,
stage.setStage(VK_SHADER_STAGE_COMPUTE_BIT);
stage.setModule(mandelbrotShader);
- stage.setName(frame, mandelbrot_entry);
+ stage.setName(mandelbrot_entry);
device.vkCreateComputePipelines(null, 1, pipeline, null, computePipeline);
}
0,
computeQueueIndex);
- commandPool = device.vkCreateCommandPool(poolinfo, null);
+ commandPool = device.vkCreateCommandPool(poolinfo, null, scope);
VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
commandPool,
1);
// should it take a scope?
- commandBuffers = device.vkAllocateCommandBuffers(cmdinfo);
+ commandBuffers = VkCommandBuffer.createArray(instance, 1, (SegmentAllocator)scope, scope);
+ device.vkAllocateCommandBuffers(cmdinfo, commandBuffers);
/* Fill command buffer with commands for later operation */
VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.create(frame,
try (Frame frame = Frame.frame()) {
VkSubmitInfo submitInfo = VkSubmitInfo.create(frame);
- submitInfo.setCommandBufferCount(0, 1);
- submitInfo.setCommandBuffers(0, commandBuffers);
+ submitInfo.setCommandBufferCount(1);
+ submitInfo.setCommandBuffers(commandBuffers);
/* Create fence to mark the task completion */
VkFence fence;
VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame);
// maybe this should take a HandleArray<Fence> rather than being a constructor
- fence = device.vkCreateFence(fenceInfo, null);
+ // FIXME: some local scope
+ fence = device.vkCreateFence(fenceInfo, null, scope);
fences.set(0, fence);
/* Await completion */
int res;
do {
res = device.vkWaitForFences(1, fences, VK_TRUE, 1000000);
- } while (res == VkResult.VK_TIMEOUT);
+ } while (res == VK_TIMEOUT);
device.vkDestroyFence(fence, null);
}
IntArray loadSPIRV0(String name) throws IOException {
// hmm any way to just load this directly?
- try (InputStream is = TestVulkan.class.getResourceAsStream(name)) {
+ try (InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
ByteBuffer bb = ByteBuffer.allocateDirect(8192).order(ByteOrder.nativeOrder());
int length = Channels.newChannel(is).read(bb);
}
IntArray loadSPIRV(String name) throws IOException {
- try (InputStream is = TestVulkan.class.getResourceAsStream(name)) {
+ try (InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
MemorySegment seg = ((SegmentAllocator)scope).allocateArray(Memory.INT, 2048);
int length = Channels.newChannel(is).read(seg.asByteBuffer());
VkMemoryType mtypes = memory.getMemoryTypes();
for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
- if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlags(i) & query) == query))
+ if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlagsAtIndex(i) & query) == query))
return i;
}
return -1;
init_instance();
init_debug();
+
init_device();
dst = init_buffer(dstBufferSize,
public static void main(String[] args) throws Throwable {
System.loadLibrary("vulkan");
- new TestVulkan().demo();
+ new TestMandelbrot().demo();
}
}
--- /dev/null
+
+
+bin/status/notzed.vkheader.test.classes: \
+ bin/modules/notzed.vkheader.test/vulkan/test/mandelbrot.bin \
+ bin/modules/notzed.vkheader.test/vulkan/test/cube_vs.bin \
+ bin/modules/notzed.vkheader.test/vulkan/test/cube_fs.bin
+
+bin/modules/notzed.vkheader.test/vulkan/test/mandelbrot.bin: src/notzed.vkheader.test/gen/mandelbrot.comp
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vkheader.test/vulkan/test/cube_vs.bin: src/notzed.vkheader.test/gen/cube.vert
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vkheader.test/vulkan/test/cube_fs.bin: src/notzed.vkheader.test/gen/cube.frag
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
module notzed.vkheader {
requires transitive notzed.nativez;
- requires java.desktop;
+ exports vulkan;
}
-
notzed.vkheader_API = vkheader
notzed.vkheader_APIFLAGS = -t vulkan -Isrc/notzed.vkheader/gen
# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+# FIXME: struct init for an array of struct needs to set the sType fields if appropriate
+# FIXME: extension function pointers resolved and stored per-object, should just be stored on the vkinstance or something
+
+# these are for the set-all constructors
+
+# TODO: the set String and String[] things should be the same,
+# either use the segment scope to allocate or take the segment as an argument
+# probably the former
+
+# **char, i.e. char *array[]
+type /^u64:u64:i8$/ copy=<pointer>
+# select=vkstring template=code:getset-frame
+{
+ length {{ 'get'.($m->{'array-size'}->{rename}).'()' }}
+ type {{ 'String[]' }}
+ tojava {{ 'Memory.toStringArray({value}, {length})' }}
+# tonative {{ 'frame$.copy({value})' }}
+ tonative {{ 'Frame.copy({segment}, {value}).address()' }}
+ typei {{ 'String' }}
+}
+
+# *char, i.e. string
+type /^u64:(?<ctype>i8)$/ copy=<pointer> {
+ type {{ 'String' }}
+ tonative {{ '(Addressable)({value} != null ? frame$.copy({value}) : MemoryAddress.NULL)' }}
+
+ setnative {{ '{name}$VH.set({segment}, Frame.copy({segment}, {value}).address())' }}
+# copynative {{ 'SegmentAllocator.nativeAllocator({segment}.scope()).allocateUtf8String({value})' }}
+# copynative {{ '((SegmentAllocator){segment}).allocateUtf8String({value})' }}
+# copynative {{ 'frame$.copy({value})' }}
+
+ tojava {{ '({value}).getUtf8String(0L)' }}
+}
+
+# 1d array of primitive
+type /^\[(?<length>\d+)(?<ctype>[uif]\d+)\]$/ select=vkarray
+ copy=<pointer> template=code:getbyvalue,code:getsetelement {
+ layout {{ "MemoryLayout.sequenceLayout({length}, {layouti})" }}
+# type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ type {{ $typeSizes{$match->{ctype}}."[]" }}
+ tojava {{ "{type}.createArray({value}, $match->{length}, {scope})" }}
+ tonative {{ "(Addressable)Memory.address({value})" }}
+ lea {{ "(MemorySegment)$m->{name}\$SH.invokeExact({segment})" }}
+ getnative {{ "({lea}).toArray({layouti})" }}
+ setnative {{ '({lea}).copyFrom(MemorySegment.ofArray({value}))' }}
+ varhandle {{
+ "final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
+ "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement());\n"
+ }}
+
+ typei {{ "$typeSizes{$match->{ctype}}" }}
+ layouti {{ 'Memory.'.uc($typeSizes{$match->{ctype}}) }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index})" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index}, ({typei}){value})" }}
+}
+
+# 2d array of primitive
+type /^\[(?<length0>\d+)\[(?<length1>\d+)(?<ctype>[uif]\d+)\]\]$/ select=vkarray
+ copy=<pointer> template=code:getbyvalue,code:getsetelement2d {
+ layout {{ "MemoryLayout.sequenceLayout({length0}, MemoryLayout.sequenceLayout({length1}, Memory.".uc($typeSizes{$match->{ctype}})."))" }}
+# type {{ ucfirst($typeSizes{$match->{ctype}})."Array" }}
+ type {{ $typeSizes{$match->{ctype}}."[]" }}
+ tojava {{ "{type}.create({value})" }}
+ tonative {{ "(Addressable)Memory.address({value})" }}
+
+ lea {{ "(MemorySegment)$m->{name}\$SH.invokeExact({segment})" }}
+ getnative {{ "({lea}).toArray({layouti})" }}
+ setnative {{ '({lea}).copyFrom(MemorySegment.ofArray({value}))' }}
+
+ typei {{ "$typeSizes{$match->{ctype}}" }}
+ layouti {{ 'Memory.'.uc($typeSizes{$match->{ctype}}) }}
+ getnativei {{ "({typei})$m->{name}\$EH.get({segment}, {index0}, {index1})" }}
+ setnativei {{ "$m->{name}\$EH.set({segment}, {index0}, {index1}, ({typei}){value})" }}
+
+ varhandle {{
+ "final static MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n".
+ "final static VarHandle $m->{name}\$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());\n"
+ }}
+}
+
+# this passes instance to the create functions
+type /^u64:u64:\$\{(\w+)\}$/ select=vkinstance copy=<pointer> {
+ length {{ $m->{'array-size'} ? 'get'.($m->{'array-size'}->{rename}).'()' : 'Long.MAX_VALUE' }}
+ type {{ !$m->{array} ? 'PointerArray' : 'HandleArray<{typei}>' }}
+ tojava {{
+ !$m->{array}
+ ? 'PointerArray.createArray({value}, {length}, {scope})'
+ : 'HandleArray.createArray({value}, {length}, (a, s) -> {typei}.create(instance, a, s), {scope})'
+ }}
+ # tojavai ... ?
+ carrieri {{ "MemoryAddress" }}
+ typei {{ "$data->{$m->{type}}->{rename}" }}
+ tojavai {{ "{typei}.create(instance, {value}, {scope})" }}
+}
+
%include types.api;
%include code.api;
%require vkheader.pm;
-struct <default> rename=s/_T$// {
+%stage.postprocess vkheader::postprocess;
+
+# field rename function
+# field:remname=func:[function]
+# [function] is a defined function that will take:
+# function(name, member, struct)
+
+# TODO: make rename func and <matcher> syntax the same
+# perhaps field:rename=<invoke=vkheader::renameField>
+# func:<invoke=vkheader::matchObjectFunction>
+
+struct <default> rename=s/_T$// field:rename=func:vkheader::renameField access=rw default=all {
}
struct VkInstance_T {
- func:vkGetInstanceProcAddr raw:result$ instance:0;
+ # override this otherwise it wants to return PFN_vkVoidFunction
+ func:vkGetInstanceProcAddr {{
+ static final MethodHandle vkGetInstanceProcAddr$FH = Memory.downcall("vkGetInstanceProcAddr", FunctionDescriptor.of(Memory.POINTER, Memory.POINTER, Memory.POINTER));
+ public NativeSymbol vkGetInstanceProcAddr(String pName) {
+ MemoryAddress result$;
+ try (Frame frame$ = Frame.frame()) {
+ result$ = (MemoryAddress)vkGetInstanceProcAddr$FH.invokeExact((jdk.incubator.foreign.Addressable)address(), (Addressable)(pName != null ? frame$.copy(pName) : MemoryAddress.NULL));
+ return NativeSymbol.ofAddress(pName, result$, scope());
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }}
+ func:<invoke=vkheader::matchCreateFunction>;
+ func:<invoke=vkheader::matchObjectFunction>;
- func:<matcher> instance:0 {{
- my $s = shift;
- return defined($s->{items}->[0]) && $s->{items}->[0]->{deref} eq 'u64:${VkInstance_T}';
- }}
+ # a few helpers
+ code:<inline> {{
+ public HandleArray<VkPhysicalDevice> vkEnumeratePhysicalDevices(SegmentAllocator alloc, ResourceScope scope) {
+ try (Frame frame = Frame.frame()) {
+ IntArray count = IntArray.createArray(1, frame);
+ vkEnumeratePhysicalDevices(count, null);
+ var list = VkPhysicalDevice.createArray(this, count.get(0), alloc, scope);
+ vkEnumeratePhysicalDevices(count, list);
+ return list;
+ }
+ }
+ }}
+}
+
+struct VkDevice_T {
+ func:<invoke=vkheader::matchCreateFunction>;
+ func:<invoke=vkheader::matchObjectFunction>;
+
+ code:<inline> {{
+ public MemorySegment vkMapMemory(VkDeviceMemory memory, long offset, long size, int flags, ResourceScope scope$) {
+ try (Frame frame = Frame.frame()) {
+ PointerArray result = PointerArray.createArray(1, frame);
+
+ vkMapMemory(memory, offset, size, flags, result);
+
+ // possibly can make this readonly etc based on flags
+ return MemorySegment.ofAddress(result.getAtIndex(0), size, scope$);
+ }
+ }
+ }}
- # another version for extension functions - uses a dynamic func resolution
- #func:<match-function> instance:0 func:template=code:vulkan-method=invoke-dynamic {{
- #}}
+}
+
+struct VkPhysicalDevice_T {
+ func:<invoke=vkheader::matchCreateFunction>;
+ func:<invoke=vkheader::matchObjectFunction>;
+
+ code:<inline> {{
+ public VkQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties(SegmentAllocator alloc) {
+ try (Frame frame = Frame.frame()) {
+ IntArray count = IntArray.createArray(1, frame);
+ vkGetPhysicalDeviceQueueFamilyProperties(count, null);
+ VkQueueFamilyProperties props = VkQueueFamilyProperties.createArray(count.getAtIndex(0), alloc);
+ vkGetPhysicalDeviceQueueFamilyProperties(count, props);
+ return props;
+ }
+ }
+ }}
}
struct /Vk/ {
- func:<matcher> instance:0 vkheader::matchObjectFunction;
+ func:<invoke=vkheader::matchCreateFunction>;
+ func:<invoke=vkheader::matchObjectFunction>;
+}
+
+call <default> access=rw {
+}
+
+call PFN_vkDebugUtilsMessengerCallbackEXT access=w {
+}
+
+call PFN_vkGetDeviceProcAddr access=r {
}
-call /PFN_vk/ access=rw {
+#call /PFN_vk/ access=rw {
+#}
+
+func vkCreateInstance {
+ result$ success=VK_SUCCESS,1;
+ pInstance return;
+}
+
+library VkConstants {
+ enum://;
+ define:extensions;
}
enum // {
}
-code vulkan-method {
+define extensions vkheader.h {
+ /^VK_.*_(SPEC_VERSION|EXTENSION_NAME)$/ include;
+}
+
+code vulkan {
- invoke-dynamic {{
- MethodHandle {name}$FH;
+ create-all {{
+ public static {java-result} create{rename}(Frame frame$, {java-arguments}) {
+ try {
+ {java-result} self$ = {java-result}.create(frame$);
+ {set-init}
+ {set-all}
+ return self$;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }}
+
+ invoke-dynamic-init-instance {{ {name}$NS = vkGetInstanceProcAddr("{name}"); }}
+ invoke-dynamic-init {{ {name}$NS = instance.vkGetInstanceProcAddr("{name}"); }}
+
+ invoke-dynamic {{
+ final NativeSymbol {name}$NS;
+ final static MethodHandle {name}$DH = Memory.downcall({function-descriptor});
public {java-result} {rename}({java-arguments}) {
{native-output-define}
{native-result-define}
try {create-frame}{
- if ({name}$FH == null)
- {name}$FH = Memory.downcall("{name}", vkGetInstanceProcAddr("{name}"), {function-descriptor}, scope());
-
{native-output-init}
- {native-result-assign}{name}$FH.invokeExact({native-call});
+ {native-result-assign}{name}$DH.invokeExact({name}$NS, {native-call});
{native-output-copy}
{result-test}{
{java-result-assign}
}
{result-throw}
}
- }}
+ }}
+
+ handle-dynamic {{
+package {package};
+import jdk.incubator.foreign.*;
+import java.lang.invoke.*;
+import au.notzed.nativez.*;
+
+public class {rename} implements Pointer {
+
+ MemoryAddress address;
+ ResourceScope scope;
+ VkInstance instance;
+
+ private {rename}(VkInstance instance, MemoryAddress address, ResourceScope scope) {
+ this.address = address;
+ this.scope = scope;
+ this.instance = instance;
+ {init}
+ }
+
+ public static {rename} create(VkInstance instance, MemoryAddress address, ResourceScope scope) {
+ return MemoryAddress.NULL != address ? new {rename}(instance, address, scope) : null;
+ }
+
+ public static HandleArray<{rename}> createArray(VkInstance instance, long count, SegmentAllocator alloc, ResourceScope scope) {
+ return HandleArray.createArray(count, alloc, (a, s)-> create(instance, a, s), scope);
+ }
+
+ @Override
+ public MemoryAddress address() {
+ return address;
+ }
+
+ @Override
+ public ResourceScope scope() {
+ return scope;
+ }
+
+ {defines}
+ {enums}
+ {methods}
+}
+}}
}
+#
+# TODO: constructors initialise sType
+#
package vkheader;
+use strict;
+
+use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
+
use Data::Dumper;
+use XML::Parser;
-#
-my $handles = {
- VkInstance => {
- objecttype => 'VK_OBJECT_TYPE_INSTANCE',
- },
- VkPhysicalDevice => {
- objecttype => 'VK_OBJECT_TYPE_PHYSICAL_DEVICE',
- },
- VkDevice => {
- objecttype => 'VK_OBJECT_TYPE_DEVICE',
- },
- VkQueue => {
- objecttype => 'VK_OBJECT_TYPE_QUEUE',
- },
- VkCommandBuffer => {
- objecttype => 'VK_OBJECT_TYPE_COMMAND_BUFFER',
- },
-};
+use vulkan;
+
+use code;
+use method;
+
+use Carp;
+
+my $registry;
+
+sub init {
+ my $api = shift;
+
+ $registry = new vulkan();
+
+ # pre-load 'handle' types which aren't exported by the header parser (anonymous struct typedefs)
+ foreach my $r (grep { $_->{category} eq 'handle' } values %{$registry->{handles}}) {
+ die if $registry->{alias}->{"type:$r->{name}"};
+
+ print "Add vkhandle: $r->{name} ($r->{type})\n";
+ my $s = {
+ name => "$r->{name}_T",
+ rename => $r->{name},
+ type => 'struct',
+ size => 0,
+ items => [],
+ output => 1,
+ vkregistry => $r,
+ };
+
+ if ($r->{name} eq 'VkInstance') {
+ $s->{'struct:template'} = 'code:class=handle';
+ } elsif ($r->{type} eq 'VK_DEFINE_HANDLE') {
+ $s->{'struct:template'} = 'code:vulkan=handle-dynamic';
+ }
+
+ $r->{vkheader} = $s;
+ $api->{data}->{"struct:$r->{name}_T"} = $s;
+ }
+
+ # link api to registry
+ my @delete = ();
+ foreach my $s (values %{$api->{data}}) {
+ my $r = $registry->{index}->{"type:$s->{name}"};
+
+ if ($registry->{alias}->{"type:$s->{name}"} && !($s->{name} =~ m/_T$/)) {
+ # WTF will this break though
+ print "Suppress alias: $s->{name}\n";
+ $s->{output} = 0;
+ push @delete, "$s->{type}:$s->{name}";
+ } else {
+ $s->{vkregistry} = $r if defined $r;
+ $r->{vkheader} = $s if defined $r;
+ }
+ }
+
+ map { delete $api->{data}->{$_} } @delete;
+
+ 1;
+}
+
+sub postprocess {
+ my $api = shift;
+
+ analyseFunctions($api, $registry);
+ analyseInOut($api, $registry);
+ analyseStructs($api, $registry);
+
+ 1;
+}
+
+
+# analyse functions and structs
+# - try to guess which types are in-out/in-only/out-only (not very reliable)
+# - determine which types can be used as arrays
+# - mark function parameters which are arrays
+sub analyseInOut {
+ my $api = shift;
+ my $registry = shift;
+ my $output = {};
+ my $arrays = {};
+
+ foreach my $c (grep { $_->{category} =~ m/command|struct|union/ && defined $_->{vkheader} } values %{$registry->{data}}) {
+ my $d = $c->{vkheader};
+ my $dindex = {};
+
+ map { $dindex->{$_->{name}} = $_ } @{$d->{items}};
+
+ # Check parameters -> const * are out-only, any with 'len' need array accessors
+ foreach my $m (@{$c->{items}}) {
+ my $r = $registry->{index}->{"type:$m->{baseType}"};
+ my $n = $dindex->{$m->{name}};
+
+ die if !defined $n;
+ next if !defined $r;
+
+ if ($r->{category} =~ m/struct|union/) {
+ $output->{$r->{name}} = $r if !($m->{fullType} =~ m/^const.*\*$/);
+ $arrays->{$r->{name}} = $r if $m->{len} || $m->{altlen} || $n->{deref} =~ m/^\[\d+/;
+
+ # && $m->{fullType} =~ tr/*/*/ == 1;
+ } elsif ($r->{category} eq 'handle') {
+ $n->{array} = 1 if ($m->{len} || $m->{altlen});
+ }
+ }
+ }
+
+ foreach my $fuckoff ('VkBaseInStructure', 'VkBaseOutStructure') {
+ delete $arrays->{$fuckoff};
+ delete $output->{$fuckoff};
+ }
+
+ my $out = {
+ 'VkDebugUtilsMessengerCallbackDataEXT' => $registry->{index}->{'type:VkDebugUtilsMessengerCallbackDataEXT'},
+ };
+ my $inout = {
+ 'VkComputePipelineCreateInfo' => $registry->{index}->{'type:VkComputePipelineCreateInfo'},
+ };
+ my $in = {};
+
+ foreach my $r (grep { $_->{category} =~ m/struct|union/on && defined $_->{vkheader} && !defined $out->{$_->{name}} && !defined $inout->{$_->{name}} } values %{$registry->{data}}) {
+ if ($r->{returnedonly}) {
+ $out->{$r->{name}} = $r;
+ } else {
+ $in->{$r->{name}} = $r;
+ }
+ }
+
+ foreach my $r (values %{$output}) {
+ print "check $r->{name}\n";
+ if ($r->{returnedonly} ne 'true') {
+ my $x = {};
+ analyseInOutStruct($registry, $x, $r->{name});
+ foreach my $s (values %{$x}) {
+ print "moved in -> in-out $s->{name} due to $r->{name}\n";
+ delete $in->{$s->{name}};
+ $inout->{$s->{name}} = $s;
+ }
+
+ }
+ }
+
+ my $set = sub {
+ my $r = shift;
+ my $rw = shift;
+ my $s = $r->{vkheader};
+
+ die "Missing vkheader ".Dumper($r) if !defined $s;
+
+ $rw = $rw."i" if $arrays->{$r->{name}};
+
+ $s->{vkaccess}= $rw;
+ $s->{access} = $rw;
+ $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}};
+ };
+
+ print join '', map { "in-out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$inout};
+ print join '', map { "in : $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$in};
+ print join '', map { " out: $_->{name}".($arrays->{$_->{name}} ? " array\n":"\n") } values %{$out};
+
+ map { $set->($_, 'rw') } values %{$inout};
+ map { $set->($_, 'w') } values %{$in};
+ map { $set->($_, 'r') } values %{$out};
+}
+
+# dump all types in this type recursively into %{$record}
+sub analyseInOutStruct {
+ my $registry = shift;
+ my $record = shift;
+ my $baseType = shift;
+ my $s = $registry->{index}->{"type:$baseType"};
+
+ #if (!defined $s) {
+ # print join "\n", sort(map { $_->{name} } values %{$registry->{data}});
+ #}
+ die "no match for $baseType\n" if !defined $s;
+
+ if (defined $s && !defined $s->{structextends} && $s->{category} =~ m/struct|union/n) {
+ $record->{$baseType} = $s;
+
+ foreach my $m (@{$s->{items}}) {
+ analyseInOutStruct($registry, $record, $m->{baseType});
+ }
+ }
+ if (defined $s && defined $s->{structextends}) {
+ print "extends? $baseType $s->{structextends}\n";
+ }
+
+}
+
+# analyse functions
+# - configure member functions
+# - configure constructors
+sub analyseFunctions {
+ my $api = shift;
+ my $registry = shift;
+
+ # TODO: arrays etc
+
+ # Scan functions
+ foreach my $c (grep { $_->{category} eq 'command' && defined $_->{vkheader} } values %{$registry->{data}}) {
+ my $d = $c->{vkheader};
+
+ # set extensions functions to use a different template
+ if ($c->{extensions}) {
+ $d->{extensions} = $c->{extensions};
+ $d->{'init:template'} = $d->{items}->[0]->{deref} eq 'u64:${VkInstance_T}'
+ ? 'code:vulkan=invoke-dynamic-init-instance' : 'code:vulkan=invoke-dynamic-init';
+ $d->{'func:template'} = 'code:vulkan=invoke-dynamic';
+ }
+
+ # look for constructors and member functions
+ my $first = $d->{items}->[0];
+ my $last = $d->{items}->[$#{$d->{items}}];
+
+ my $ffirst = $c->{items}->[0];
+ my $flast = $c->{items}->[$#{$c->{items}}];
+ my $rfirst = $registry->{index}->{"type:$ffirst->{baseType}"};
+ my $rlast = $registry->{index}->{"type:$flast->{baseType}"};
+
+ my $isstatic = ($rfirst->{type} ne 'VK_DEFINE_HANDLE')
+ && ($rfirst->{fullType} =~ tr/*/*/ == 0);
+
+ my $iscreate = ($flast->{fullType} =~ tr/*/*/==1)
+ && !($flast->{fullType} =~ m/const/)
+ && !($flast->{len})
+ && ($rlast->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
+
+ $d->{vkiscreate} = $iscreate;
+ $d->{vkisstatic} = $isstatic;
+
+ if ($iscreate && $isstatic) {
+ # fuck, all this work for vkCreateInstance()
+ $last->{output} = 0;
+ $last->{return} = 1;
+ $d->{return} = $last;
+ $d->{scope} = $last->{scope} = 'explicit'; # blah, for now
+ $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance');
+
+ print "xxxa static $flast->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n";
+ } elsif ($ffirst->{fullType} eq $ffirst->{baseType}
+ && $rfirst->{type} eq 'VK_DEFINE_HANDLE') {
+ # member functions
+ $first->{output} = 0;
+ $first->{instance} = 1;
+ $d->{static} = 0;
+
+ # member create function
+ if ($iscreate) {
+ $last->{output} = 0;
+ $last->{return} = 1;
+ $d->{return} = $last;
+ $d->{scope} = $last->{scope} = 'explicit';
+ $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance' && $rlast->{type} eq 'VK_DEFINE_HANDLE');
+
+ print "xxxb $ffirst->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n";
+ }
+ }
+
+ if ($flast->{len} && $flast->{fullType} =~ tr/*/*/ == 1
+ #&& $rlast->{type} eq 'VK_DEFINE_HANDLE')
+ ) {
+ $last->{array} = 1;
+ }
+
+ # Link in successcodes (failure codes?)
+ if (defined($c->{successcodes})) {
+ $d->{success} = $d->{result};
+ $d->{success}->{success} = join ',', map { "VkConstants.$_" } split /,/,$c->{successcodes};
+ $d->{result}->{output} = 0;
+
+ # output return value if there is more than 1 success code
+ if ($c->{successcodes} =~ tr/,/,/ >= 1 && !defined($d->{return})) {
+ $d->{result}->{output} = 1;
+ }
+ }
+ }
+
+# die;
+
+}
+# analyse structures
+# - which fields should be included in a set-all constructor
+# - auto handling of arrays-with-length
+# - handling of String[]
+# - access modes for members
+# - set-all constructor format hooks
+sub analyseStructs {
+ my $api = shift;
+ my $registry = shift;
+
+ foreach my $r (grep { $_->{category} =~ m/struct|union/ && defined $_->{vkheader}} values %{$registry->{data}}) {
+ my $s = $r->{vkheader};
+ my $index = {};
+
+ map { $index->{$_->{name}} = $_ } @{$s->{items}};
+
+ # # Find out which fields should be included in a 'set-all' constructor
+ # for my $m (@{$s->{items}}) {
+ # if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
+ # my $embed = $api->{data}->{"struct:$1"};
+ # # TODO: need to work out how to set the fields first
+ # #if ($#{$embed->{items}} < 6) {
+ # # $m->{'set-all-struct'} = "struct:$1";
+ # #}
+ # # $m->{vkset} = { type => $1 } ?
+ # } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
+ # # 1D primitive array
+ # if ($1 <= 16) {
+ # $m->{vkset} = { select => { vkarray => 1 } };
+ # }
+ # } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
+ # # 2D primitive array
+ # if (($1 * $2) <= 16) {
+ # $m->{vkset} = { select => { vkarray => 1 } };
+ # }
+ # } else {
+ # # pointers or other primitives, probably
+ # $m->{vkset} = { };
+ # }
+ # }
+
+ # Special handling for types with 'len' specified
+ # TODO: not sure if i want this in general or just set-all constructor
+ for my $x (@{$r->{items}}) {
+ my $m = $index->{$x->{name}};
+
+ # if there's a len specified but ignore if set in .api file
+ if ($x->{len} && !($m->{array} || $m->{'array-size'} || $m->{'array-size-source'})) {
+ my @len = split ',', $x->{len};
+
+ if ($#len > 0 && $len[1] eq 'null-terminated') {
+ # String[], probably
+ my $size = $index->{$len[0]};
+
+ # for string[]
+ $size->{implied} = "Memory.length($m->{name})";
+ $m->{select}->{vkstring} = 1;
+ } elsif ($len[0] =~ m/^[a-zA-Z]+$/) {
+ # Simple length referencing a parameter/field
+ my $size = $index->{$len[0]};
+
+ $size->{'array-size-source'} = $m;
+ #$size->{output} = 0;
+ $m->{'array-size'} = $size;
+ #$m->{select}->{vkstring} = 1;
+ $size->{select}->{'array-size-source'} = 1;
+ $m->{select}->{'array-size'} = 1;
+
+ # need to know what type it is to get correct length, array, struct[], etc
+ $size->{implied} = "Memory.length($m->{name})";
+ #$size->{implied} = "$size->{name}";
+
+ # use HandleArray<> rather than PointerArray<> for handles
+ if ($m->{deref} =~ m/^u64:u64:\$\{(.*)\}/) {
+ my $t = $api->{data}->{"struct:$1"}; die "no type $1" if !$t;
+ my $r = $t->{vkregistry};
+
+ $m->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
+
+ $m->{select}->{array} = 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
+ }
+ }
+ }
+ }
+
+ # setup accessor mode (if not already set), and format hook for the set-all constructor
+ if ($r->{returnedonly} eq 'true') {
+ $s->{access} = 'r' if !$s->{vkaccess};
+ map { $_->{access} = 'r' } @{$s->{items}};
+ } elsif ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
+ $s->{access} = 'w' if !$s->{vkaccess};
+ push @{$s->{postformat}}, sub { formatConstructor(@_) };
+ } else {
+ $s->{access} = 'rw' if !$s->{vkaccess};
+ push @{$s->{postformat}}, sub { formatConstructor(@_) };
+ }
+ # propagate access mode.
+ map { $_->{access} = $s->{access} } @{$s->{items}};
+
+ if ($s->{name} eq 'VkTransformMatrix') {
+ print "yyy ".Dumper($s);
+ die;
+ }
+
+ }
+}
+
+# TBD now in vkregistry.pm
+sub findRegistryObject {
+ my $data = shift;
+ my $alias = shift;
+ my $func = shift;
+
+ do {
+ my $f = $data->{$func};
+ return $f if defined $f;
+ if ($func =~ m/^(.*)_T$/) {
+ $f = $data->{$1};
+ return $f if defined $f;
+ }
+ #print "alias $func => $alias->{$func}\n";
+ $func = $alias->{$func};
+ } while ($func);
+
+ return undef;
+}
+
+# a basic struct, i.e. not a handle
+sub matchStruct {
+ my $s = shift;
+ my $ctx = shift;
+ my $r = $s->{vkregistry};
+
+ return defined($r)
+ && $r->{type} ne 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'
+ && $r->{type} ne 'VK_DEFINE_HANDLE';
+}
+
+# TODO: info required for these is evaluated in analyse
# find all functions where the first parameter is a pointer to a dispatchable handle type
sub matchObjectFunction {
+ my $c = shift;
my $s = shift;
- my $ctx = shift;
+ my $items = $c->{items};
+ my $r = $s->{vkregistry};
+
+ die "vkregistry field missing ".Dumper($s) if !defined($r);
+
+ return $r->{type} eq 'VK_DEFINE_HANDLE'
+ #&& !$registry->{alias}->{"type:$s->{name}"}
+ && defined($items->[0])
+ && $items->[0]->{deref} eq "u64:\${$s->{name}}";
+}
+
+# a static create function
+sub matchCreateFunction {
+ my $c = shift;
+ my $s = shift;
+ my $items = $c->{items};
+ my $r = $s->{vkregistry};
+
+ return ($c->{vkiscreate})
+ && ($c->{vkisstatic})
+ && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}");
+
+ # class is a handle
+ # last argument is a **handle
- return defined($handles->{"$ctx->{rename}"})
- && defined($s->{items}->[0])
- && $s->{items}->[0]->{deref} eq "u64:\${$ctx->{name}}";
+ return $c->{name} =~ m/^vkCreate/
+ && $r->{type} eq 'VK_DEFINE_HANDLE'
+ && defined($items->[$#$items])
+ && $items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}";
}
-sub dummy {
- die;
+sub renameField {
+ my $name = shift;
+ my $m = shift;
+ my $s = shift;
+
+ if ($name eq 'pGeometries' && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
+ # small hack to fix rename conflict
+ $name = 'Geometries0';
+ } elsif ($m->{deref} =~ m/^((u64:){1,})\$\{(?<type>.*)\}$/) {
+ my $len = length($1)/4;
+ my $r = $registry->{index}->{"type:$+{type}"};
+
+ # strip leading 'p' for pointer, handles are void * so ignore one level
+ $len = $len - 1 if ($r->{type} =~ m/^(VK_DEFINE_HANDLE|VK_DEFINE_NON_DISPATCHABLE_HANDLE)$/n);
+ $name = substr $name, $len;
+ } elsif ($m->{deref} =~ m/^((u64:){1,})/) {
+ # strip leading 'p' for pointer
+ $name = substr $name, length($1)/4;
+ }
+
+ # studly-caps
+ $name =~ s/(?:^|_)(.)/\U$1/g;
+
+ return $name;
+}
+
+# collect args for 'all' constructor
+sub collectAllArgs {
+ my $api = shift;
+ my $items = shift;
+ my $s = shift;
+ my $prefix = shift;
+ my @members = @{$s->{items}};
+
+ # Drop type and next fields
+ if ($#members >= 1 && $members[0]->{name} eq 'sType' && $members[1]->{name} eq 'pNext') {
+ @members = @members[2 .. $#members];
+ }
+
+ #print "collect $s->{name}\n";
+ foreach my $m (@members) {
+ my $n;
+
+ if ($m->{deref} =~ m/^\$\{(.*)\}$/ && defined($api->{data}->{"struct:$1"})) {
+ my $embed = $api->{data}->{"struct:$1"};
+ # TODO: need to work out how to set the fields first
+ } elsif ($m->{deref} =~ m/\[(\d+)([uif]\d+)\]/) {
+ # 1D primitive array
+ $n = { %{$m}, select => { vkarray => 1 } } if ($1 <= 16);
+ } elsif ($m->{deref} =~ m/\[(\d+)\[(\d+)([uif]\d+)\]\]/) {
+ # 2D primitive array
+ $n = { %{$m}, select => { vkarray => 1 } } if (($1 * $2) <= 16);
+ } elsif ($m->{'array-size-source'} || $m->{implied}) {
+ #$n = { %{$m}, output=>0, implied => "Memory.length($m->{'array-size-source'}->{name})" };
+ $n = { %{$m}, output=>0 };
+ } else {
+ # everything else
+ $n = { %{$m} };
+ }
+
+ if (defined($n)) {
+ if ($prefix) {
+ $n->{name} = $prefix ? $prefix.'$'.$m->{name} : $m->{name};
+ $n->{subtype} = $s->{name};
+ $n->{subname} = $m->{name};
+ $n->{localname} = $prefix;
+ }
+ push @{$items}, $n;
+ }
+ }
+
+ # if (defined $f->{vkset}) {
+ # my $a = { %{$f} };
+ # if ($prefix) {
+ # $a->{name} = $prefix ? $prefix.'$'.$f->{name} : $f->{name};
+ # $a->{subtype} = $s->{name};
+ # $a->{subname} = $f->{name};
+ # $a->{localname} = $prefix;
+ # }
+ # $a->{select} = $f->{vkset}->{select} if defined $f->{vkset}->{select};
+ # push @{$items}, $a;
+ # } elsif ($f->{'set-all-struct'}) {
+ # die "unimplemented";
+ # my $e = $api->{data}->{$f->{'set-all-struct'}};
+ # collectAllArgs($api, $items, $e, $prefix ? $prefix.'$'.$f->{name} : $f->{name});
+ # }
+ # }
+}
+
+sub uniq {
+ my %seen;
+ return grep { !$seen{$_}++ } @_;
+}
+
+sub format1Constructor {
+ my $api = shift;
+ my $obj = shift;
+ my $res = shift;
+ my $s = shift;
+ my $c = shift;
+
+ my $info = new method($api, $c);
+ my $init = join "\n\t\t", map {
+ "MemorySegment $_\$segment = (MemorySegment)$_\$SH.invokeExact(self\$.segment);"
+ } uniq map {
+ $_->{field}->{localname} ? $_->{field}->{localname} : ()
+ } @{$info->{arguments}};
+
+ my $setAll = join ";\n\t\t", map {
+ my $v = { %{$_->{match}}, value => $_->{field}->{name} };
+ if (defined ($_->{field}->{subtype})) {
+ $v->{name} = $_->{field}->{subtype}.'.'.$_->{field}->{subname};
+ $v->{segment} = $_->{field}->{localname}.'$segment';
+ } else {
+ $v->{segment} = 'self$.segment';
+ }
+ # hack for string[]
+ $v->{tonative} = "({type})$_->{field}->{implied}" if $_->{field}->{implied};
+ code::formatTemplate($_->{match}->{setnative}, $v);
+ } @{$info->{arguments}};
+ $setAll .= ";";
+
+ my $template = $api->findTemplateName('code:vulkan=create-all');
+ my $code;
+
+ if (0) {
+ foreach my $l (split /\n/,Dumper($info)) {
+ $code .= "//$l\n";
+ }
+ }
+
+ $code .= code::applyTemplate($template, { %{$info->{vars}}, 'set-all' => $setAll, 'set-init' => $init});
+
+ push @{$res->{func}}, $code;
+}
+
+# Create an 'all-set' constructor
+# This creates a pseudo-function and formats that
+# This also sets up the code to initialise sType
+sub formatConstructor {
+ my $api = shift;
+ my $obj = shift;
+ my $res = shift;
+ my $s = shift;
+ my $r = $s->{vkregistry};
+
+ return if $s->{name} =~ m/^(VkBaseInStructure|VkBaseOutStructure)$/on;
+
+ if ($#{$r->{items}} >= 0 && $r->{items}->[0]->{baseType} eq 'VkStructureType') {
+ push @{$res->{init}}, "// FIXME: array init?\n$r->{items}->[0]->{name}\$VH.set(segment, VkConstants.$r->{items}->[0]->{values});";
+ }
+
+ if ($s->{type} eq 'union') {
+ foreach my $u (grep { defined($_->{vkset}) } @{$s->{items}}) {
+ my $args = [];
+
+ if ($u->{vkset}->{struct}) {
+ die "unimplemented";
+ } else {
+ my $a = { %{$u} };
+ $a->{select} = $u->{vkset}->{select} if defined $u->{vkset}->{select};
+ push @{$args}, $a;
+ }
+ my $c = {
+ result => {
+ type => "$s->{type}:$s->{name}",
+ deref => "u64:\$\{$s->{name}\}",
+ name => 'result$',
+ output => 1,
+ },
+ items => $args,
+ rename => $u->{rename},
+ };
+ format1Constructor($api, $obj, $res, $s, $c);
+ }
+ } else {
+ my $args = [];
+
+ collectAllArgs($api, $args, $s, "");
+
+ if ($#{$args} >= 0) {
+ my $c = {
+ result => {
+ type => "$s->{type}:$s->{name}",
+ deref => "u64:\$\{$s->{name}\}",
+ name => 'result$',
+ output => 1,
+ },
+ items => $args,
+ rename => "",
+ };
+ format1Constructor($api, $obj, $res, $s, $c);
+ }
+ }
}
1;
--- /dev/null
+
+# Routines for working with vulkan registry
+
+package vulkan;
+
+use strict;
+
+use Data::Dumper;
+use XML::Parser;
+use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
+
+sub new {
+ my $class = shift;
+
+ my $now = clock_gettime(CLOCK_REALTIME);
+
+ my $xml = XML::Parser->new(Style => 'Tree');
+ my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
+
+ #print Dumper($doc);
+
+ my $root = $doc->[1];
+ my $roota = shift @{$root};
+
+ my $data = {};
+ my $alias = {};
+ my $extensions = {};
+
+ # This destructively consumes the whole tree so must be one pass
+ while ($#{$root} >= 0) {
+ my $xt = shift @{$root};
+ my $xn = shift @{$root};
+
+ next if $xt eq '0';
+
+ my $xa = shift @{$xn};
+
+ if ($xt eq 'types') {
+ while ($#{$xn} >= 0) {
+ my $yt = shift @{$xn};
+ my $yn = shift @{$xn};
+
+ next if $yt ne 'type';
+
+ my $ya = $yn->[0];
+
+ if ($ya->{category} =~ m/struct|union/) {
+ if (!defined($ya->{alias})) {
+ my $s = $ya;
+
+ $s->{items} = [];
+
+ shift @{$yn};
+ while ($#{$yn} >= 0) {
+ my $mt = shift @{$yn};
+ my $mm = shift @{$yn};
+
+ push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
+ }
+
+ $data->{"type:$s->{name}"} = $s;
+ } else {
+ $alias->{"type:$ya->{name}"} = "type:$ya->{alias}";
+ }
+ } elsif ($ya->{category} eq "handle") {
+ if (!defined($ya->{alias})) {
+ my $info = loadMember($yn);
+ my $s = $ya;
+
+ $s->{name} = $info->{name};
+ $s->{type} = $info->{baseType};
+
+ $data->{"type:$s->{name}"} = $s;
+
+ # vulkan.h uses xx_T for handles
+ $alias->{"type:$s->{name}_T"} = "type:$s->{name}";
+ } else {
+ $alias->{"type:$ya->{name}"} = "type:$ya->{alias}";
+ }
+ } elsif ($ya->{category} =~ m/^(basetype|funcpointer|bitmask)$/n) {
+ if (!defined($ya->{alias})) {
+ my $info = loadMember($yn);
+ my $s = $ya;
+
+ $s->{name} = $info->{name},
+ $s->{type} => $info->{baseType} if defined $info->{baseType};
+
+ $data->{"type:$s->{name}"} = $s;
+ } else {
+ $alias->{"type:$ya->{name}"} = "type:$ya->{alias}";
+ }
+ } elsif ($ya->{category} eq 'enum') {
+ $data->{"type:$ya->{name}"} = $ya;
+ } elsif ($ya->{requires} eq 'vk_platform') {
+ $ya->{category} = 'platform';
+ $data->{"type:$ya->{name}"} = $ya;
+ }
+ }
+ } elsif ($xt eq 'enums') {
+ # TODO: load them all
+ if ($xa->{type} =~ m/enum|bitmask/o) {
+ $data->{"type:$xa->{name}"} = { category => $xa->{type}, name => $xa->{name} };
+ } elsif ($xa->{name} eq 'API Constants') {
+ }
+ } elsif ($xt eq 'commands') {
+ while ($#{$xn} >= 0) {
+ my $yt = shift @{$xn};
+ my $yn = shift @{$xn};
+
+ next if $yt ne 'command';
+
+ my $ya = shift @{$yn};
+
+ if (!defined($ya->{alias})) {
+ my $cmd = $ya;
+
+ $cmd->{category} = 'command';
+ $cmd->{items} = [];
+ $cmd->{proto} = {};
+
+ while ($#{$yn} >= 0) {
+ my $zt = shift @{$yn};
+ my $zn = shift @{$yn};
+
+ if ($zt eq 'proto') {
+ $cmd->{proto} = loadMember($zn);
+ } elsif ($zt eq 'param') {
+ push @{$cmd->{items}}, loadMember($zn);
+ }
+ }
+
+ my $name = $cmd->{proto}->{name};
+
+ # check we parsed it properly
+ if ($cmd->{proto}->{fullType} eq "") {
+ print Dumper([$ya, $yn]);
+ die();
+ }
+ $cmd->{name} = $name;
+
+ $data->{"type:$name"} = $cmd;
+ } else {
+ # want forward ref or not?
+ $alias->{"type:$ya->{name}"} = "type:$ya->{alias}";
+ }
+ }
+ } elsif ($xt eq 'feature') {
+ my $feature = $xa;
+
+ $feature->{require} = [];
+
+ while ($#{$xn} >= 0) {
+ my $yt = shift @{$xn};
+ my $yn = shift @{$xn};
+
+ next if $yt ne 'require';
+
+ push @{$feature->{require}}, loadRequire($data, $alias, $yn);
+ }
+ } elsif ($xt eq 'extensions') {
+ while ($#{$xn} >= 0) {
+ my $yt = shift @{$xn};
+ my $yn = shift @{$xn};
+
+ next if $yt ne 'extension';
+
+ my $ext = shift @{$yn};
+
+ $ext->{require} = [];
+
+ while ($#{$yn} >= 0) {
+ my $zt = shift @{$yn};
+ my $zn = shift @{$yn};
+
+ next if $zt ne 'require';
+
+ push @{$ext->{require}}, loadRequire($data, $alias, $zn);
+ }
+
+ $extensions->{$ext->{name}} = $ext;
+ }
+ } else {
+ #print "vulkan.pm: Ignore node: $xt\n";
+ }
+ }
+
+ # build various indices
+ my $index = {};
+
+ map { $index->{$_} = $data->{$_} } keys %{$data};
+ map { $index->{$_} = findData($index, $alias, $_) } keys %{$alias};
+
+ my $handles = {};
+ my $types = {};
+ my $commands = {};
+
+ # these include aliases, should it?
+ foreach my $t (keys %{$index}) {
+ my $v = $data->{$t};
+ $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
+ $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
+ $commands->{$v->{name}} = $v if $v->{category} eq 'command';
+ }
+
+ # mark extension functions
+ foreach my $e (values %{$extensions}) {
+ foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
+ my $r = $index->{"type:$name"};
+
+ die if !defined($r);
+
+ push @{$r->{extensions}}, $e;
+ }
+ }
+
+ # FIXME: link extensions up
+ # my $r = findData($data, $alias, "type:$ma->{name}");
+ # die "cann't find $ma->{name}" if !defined $r;
+ # push @{$r->{extensions}}, $ext;
+ # push @{$r->{commands}}, $ma->{name};
+
+ my $self = {
+ data => $data,
+ alias => $alias,
+ index => $index,
+ extensions => $extensions,
+ handles => $handles,
+ types => $types,
+ commands => $commands,
+ };
+
+ $now = clock_gettime(CLOCK_REALTIME) - $now;
+ print "$now load registry\n";
+
+ bless $self, $class;
+ $self;
+}
+
+# find an object including via alias
+sub findData {
+ my $data = shift;
+ my $alias = shift;
+ my $name = shift;
+
+ do {
+ my $s = $data->{$name};
+ return $s if defined $s;
+ #print "alias $name => $alias->{$name}\n";
+ $name = $alias->{$name};
+ } while ($name);
+
+ die "No match for type '$name'";
+}
+
+sub loadMember {
+ my $nn = shift;
+ #my $x = (join '',split('\n',Dumper($nn))); $x =~ s/ +/ /g; print "load: $x\n";
+ my $m = shift @{$nn};
+ my $baseType = "";
+ my $fullType = "";
+ my $name = "";
+
+ while ($#{$nn} >= 0) {
+ my $pt = shift @{$nn};
+ my $pn = shift @{$nn};
+
+ if ($pt eq '0') {
+ $fullType .= $pn;
+ } elsif ($pt eq 'type') {
+ die if $pn->[1] != 0;
+ $baseType = $pn->[2];
+ $fullType .= $baseType;
+ } elsif ($pt eq 'name') {
+ die if $pn->[1] != 0;
+ $name = $pn->[2];
+ } elsif ($pt eq 'enum') {
+ die if $pn->[1] != 0;
+ $fullType .= $pn->[2];
+ }
+ }
+
+ # canonicalise spaces in c type
+ $fullType =~ s/(?<!const)\s+//g; # strip all spaces except those following const
+ $fullType =~ s/(?<! )\*/ */g; # insert a space before * if there isn't one
+ $fullType =~ s/(?<=\*)(\S)/ \1/g;# insert a space after * if there isn't one
+ #$fullType =~ s/^\s+|\s+$//g;
+
+ $m->{name} = $name;
+ $m->{baseType} = $baseType;
+ $m->{fullType} = $fullType;
+
+ $m;
+}
+
+sub loadRequire {
+ my $data = shift;
+ my $alias = shift;
+ my $nn = shift;
+ my $r = shift @{$nn};
+
+ $r->{enums} = [];
+ $r->{types} = [];
+ $r->{commands} = [];
+
+ while ($#{$nn} >= 0) {
+ my $mt = shift @{$nn};
+ my $mn = shift @{$nn};
+
+ if ($mt eq 'type') {
+ my $ma = shift @{$mn};
+ push @{$r->{types}}, $ma->{name};
+ } elsif ($mt eq 'command') {
+ my $ma = shift @{$mn};
+ push @{$r->{commands}}, $ma->{name};
+ } elsif ($mt eq 'enum') {
+ my $ma = shift @{$mn};
+ push @{$r->{enums}}, $ma;
+ }
+ }
+
+ $r;
+}
+
+sub findElements {
+ my $n = shift;
+ my $name = shift;
+ my @list;
+
+ while ($#{$n} >= 0) {
+ my $tag = shift @{$n};
+ my $con = shift @{$n};
+
+ if ($tag eq $name) {
+ push @list, [$tag, $con];
+ }
+ }
+ @list;
+}
+
+sub scanElements {
+ my $n = shift;
+
+ while ($#{$n} >= 0) {
+ my $tag = shift @{$n};
+ my $con = shift @{$n};
+
+ print "$#{$n} ";
+ print "tag $tag\n";
+ }
+}
+
+1;
--- /dev/null
+
+module notzed.vkregistry.test {
+ requires notzed.vkregistry;
+ requires notzed.xlib;
+
+ requires java.desktop;
+}
--- /dev/null
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (location = 0) in vec4 color;
+layout (location = 0) out vec4 outColor;
+void main() {
+ outColor = color;
+}
--- /dev/null
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (std140, binding = 0) uniform bufferVals {
+ mat4 mvp;
+} data;
+layout (location = 0) in vec4 pos;
+layout (location = 1) in vec4 inColor;
+layout (location = 0) out vec4 outColor;
+
+void main() {
+ outColor = inColor;
+ gl_Position = data.mvp * pos;
+}
--- /dev/null
+
+bin/status/notzed.vkregistry.test.classes: \
+ bin/modules/notzed.vkregistry.test/vulkan/test/mandelbrot.bin \
+ bin/modules/notzed.vkregistry.test/vulkan/test/cube_vs.bin \
+ bin/modules/notzed.vkregistry.test/vulkan/test/cube_fs.bin
+
+bin/modules/notzed.vkregistry.test/vulkan/test/mandelbrot.bin: src/notzed.vkregistry.test/gen/mandelbrot.comp
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vkregistry.test/vulkan/test/cube_vs.bin: src/notzed.vkregistry.test/gen/cube.vert
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vkregistry.test/vulkan/test/cube_fs.bin: src/notzed.vkregistry.test/gen/cube.frag
+ mkdir -p $(@D)
+ glslangValidator --target-env vulkan1.0 -V -o $@ $<
--- /dev/null
+#version 450
+
+#define WIDTH (1920*1)
+#define HEIGHT (1080*1)
+#define LWS_X 8
+#define LWS_Y 8
+#define LIMIT 10000
+
+layout (local_size_x = LWS_X, local_size_y = LWS_Y, local_size_z = 1 ) in;
+
+layout(std430, binding = 0) buffer buf {
+ uint imageData[];
+};
+
+void main() {
+
+ /*
+ In order to fit the work into workgroups, some unnecessary threads are launched.
+ We terminate those threads here.
+ */
+ if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+ return;
+
+ float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+ float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+ /*
+ What follows is code for rendering the mandelbrot set.
+ */
+ vec2 uv = vec2(x, (y - 0.5) * (12.0 / 19.0) + 0.5);
+ float n = 0.0;
+ vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(4.0);
+ vec2 z = vec2(0.0);
+ const int M = LIMIT;
+
+ for (int i = 0; i<M; i++) {
+ z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+
+ if (dot(z, z) > 4)
+ break;
+ n++;
+ }
+
+ // we use a simple cosine palette to determine color:
+ // http://iquilezles.org/www/articles/palettes/palettes.htm
+ float t = float(n) * 500.0 / float(M);
+ vec3 d = vec3(0.5, 0.5, 0.5);
+ vec3 e = vec3(0.5, 0.5, 0.5);
+ vec3 f = vec3(1.0, 1.0, 1.0);
+ vec3 g = vec3(0.00, 0.33, 0.67);
+
+ vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+
+ if (n == M)
+ color = vec4(0, 0, 0, 1);
+
+ // store the rendered mandelbrot set into a storage buffer:
+ imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x] = packUnorm4x8(color);
+ //imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color;
+}
module notzed.vkregistry {
requires transitive notzed.nativez;
- requires transitive notzed.xlib;
-
- requires java.desktop;
exports vulkan;
}
-G=src/notzed.vkregistry/gen
-
bin/status/notzed.vkregistry.classes: \
- bin/status/notzed.vkregistry.export \
- bin/modules/notzed.vkregistry/vulkan/test/mandelbrot.bin \
- bin/modules/notzed.vkregistry/vulkan/test/cube_vs.bin \
- bin/modules/notzed.vkregistry/vulkan/test/cube_fs.bin
+ bin/status/notzed.vkregistry.export
-bin/status/notzed.vkregistry.export: $(G)/export-vulkan $(G)/VkDevice-part.java $(G)/VkInstance-part.java
- $(G)/export-vulkan -t vulkan -d bin/gen/notzed.vkregistry/classes
+bin/status/notzed.vkregistry.export: src/notzed.vkregistry/gen/export-vulkan src/notzed.vkregistry/gen/VkDevice-part.java src/notzed.vkregistry/gen/VkInstance-part.java
+ src/notzed.vkregistry/gen/export-vulkan -t vulkan -d bin/gen/notzed.vkregistry/classes
mkdir -p $(@D)
touch $@
-
-bin/modules/notzed.vkregistry/vulkan/test/mandelbrot.bin: $(G)/mandelbrot.comp
- mkdir -p $(@D)
- glslangValidator --target-env vulkan1.0 -V -o $@ $<
-
-bin/modules/notzed.vkregistry/vulkan/test/cube_vs.bin: $(G)/cube.vert
- mkdir -p $(@D)
- glslangValidator --target-env vulkan1.0 -V -o $@ $<
-
-bin/modules/notzed.vkregistry/vulkan/test/cube_fs.bin: $(G)/cube.frag
- mkdir -p $(@D)
- glslangValidator --target-env vulkan1.0 -V -o $@ $<