Flesh out vkheader implementation, uses registry and custom callbacks to fill out...
authorNot Zed <notzed@gmail.com>
Tue, 5 Apr 2022 00:20:28 +0000 (09:50 +0930)
committerNot Zed <notzed@gmail.com>
Tue, 5 Apr 2022 00:20:28 +0000 (09:50 +0930)
Move vkregistry examples to separate module
Comment out broken Frame implementation for now, just uses locally scoped arena allocator.

34 files changed:
Makefile
src/notzed.clstatic/gen/opencl.api
src/notzed.nativez/bin/generate-api
src/notzed.nativez/classes/au/notzed/nativez/ByteArray.java
src/notzed.nativez/classes/au/notzed/nativez/Frame.java
src/notzed.nativez/classes/au/notzed/nativez/Memory.java
src/notzed.nativez/lib/api.pm
src/notzed.nativez/lib/code.api
src/notzed.nativez/lib/code.pm
src/notzed.nativez/lib/config.pm
src/notzed.nativez/lib/method.pm
src/notzed.nativez/lib/types.api
src/notzed.vkheader.test/classes/module-info.java [new file with mode: 0644]
src/notzed.vkheader.test/classes/vulkan/test/TestMandelbrot.java [moved from src/notzed.vkheader/classes/vulkan/test/TestVulkan.java with 87% similarity]
src/notzed.vkheader.test/gen/cube.frag [moved from src/notzed.vkregistry/gen/cube.frag with 100% similarity]
src/notzed.vkheader.test/gen/cube.vert [moved from src/notzed.vkregistry/gen/cube.vert with 100% similarity]
src/notzed.vkheader.test/gen/gen.make [new file with mode: 0644]
src/notzed.vkheader.test/gen/mandelbrot.comp [moved from src/notzed.vkregistry/gen/mandelbrot.comp with 100% similarity]
src/notzed.vkheader/classes/module-info.java
src/notzed.vkheader/gen/gen.make
src/notzed.vkheader/gen/vkheader.api
src/notzed.vkheader/gen/vkheader.pm
src/notzed.vkheader/gen/vulkan.pm [new file with mode: 0644]
src/notzed.vkregistry.test/classes/module-info.java [new file with mode: 0644]
src/notzed.vkregistry.test/classes/vulkan/test/Cube.java [moved from src/notzed.vkregistry/classes/vulkan/test/Cube.java with 100% similarity]
src/notzed.vkregistry.test/classes/vulkan/test/GLMaths.java [moved from src/notzed.vkregistry/classes/vulkan/test/GLMaths.java with 100% similarity]
src/notzed.vkregistry.test/classes/vulkan/test/TestCube.java [moved from src/notzed.vkregistry/classes/vulkan/test/TestCube.java with 100% similarity]
src/notzed.vkregistry.test/classes/vulkan/test/TestMandelbrot.java [moved from src/notzed.vkregistry/classes/vulkan/test/TestMandelbrot.java with 100% similarity]
src/notzed.vkregistry.test/gen/cube.frag [new file with mode: 0644]
src/notzed.vkregistry.test/gen/cube.vert [new file with mode: 0644]
src/notzed.vkregistry.test/gen/gen.make [new file with mode: 0644]
src/notzed.vkregistry.test/gen/mandelbrot.comp [new file with mode: 0644]
src/notzed.vkregistry/classes/module-info.java
src/notzed.vkregistry/gen/gen.make

index fafe48f..1bc01ce 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,24 +9,30 @@ java_MODULES = notzed.nativez \
        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
index 3bad336..e06add3 100644 (file)
@@ -155,7 +155,7 @@ library cl_khr_gl_event
        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);
index f0fee75..9f35fa4 100755 (executable)
@@ -1,5 +1,7 @@
 #!/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
@@ -21,7 +23,9 @@ use api;
 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";
@@ -72,6 +76,7 @@ exit 0;
 sub formatFunction {
        my $api = shift;
        my $c = shift;
+       my $tname = shift;
        my $template = shift;
        my $info = new method($api, $c);
 
@@ -81,14 +86,23 @@ sub formatFunction {
 
        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 {
@@ -125,21 +139,28 @@ sub formatItems {
                $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;
        }
@@ -180,27 +201,6 @@ sub formatLibrary {
        }
 }
 
-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 {
@@ -209,7 +209,7 @@ 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;
 
@@ -238,6 +238,7 @@ sub formatStruct {
                ctx => $s,
                library => [],
                func => [],
+               init => [],
                define => [],
                enum => [],
                call => [],
@@ -247,6 +248,11 @@ sub formatStruct {
 
        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),
@@ -254,19 +260,20 @@ sub formatStruct {
                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
@@ -279,7 +286,7 @@ sub exportLibraries {
                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 => [],
@@ -304,7 +311,7 @@ sub exportLibraries {
                        calls => join("\n", @{$res->{call}}),
                };
 
-               export($api, $obj->{name}, code::applyTemplate($library, $vars));
+               export($api, $obj->{name}, code::applyTemplate($library, $vars, 1));
        }
 }
 
@@ -343,6 +350,7 @@ sub exportStructs {
                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));
@@ -352,6 +360,7 @@ sub exportStructs {
        # 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));
        }
 }
index bfe21da..0abf605 100644 (file)
@@ -91,4 +91,8 @@ public class ByteArray extends AbstractList<Byte> implements Pointer {
        public void setAtIndex(long index, byte value) {
                segment.set(Memory.BYTE, index, value);
        }
+
+       public String toUtf8String() {
+               return segment.getUtf8String(0L);
+       }
 }
index 560ba5a..a694770 100644 (file)
@@ -34,62 +34,84 @@ import static jdk.incubator.foreign.ValueLayout.OfAddress;
  */
 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);
@@ -196,4 +218,29 @@ public class Frame implements AutoCloseable, SegmentAllocator {
                        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;
+               }
+       }
 }
index c935f22..1484708 100644 (file)
@@ -45,6 +45,10 @@ public class Memory {
                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))
@@ -122,6 +126,14 @@ public class Memory {
                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;
        }
index 636bb41..c758616 100644 (file)
@@ -1,4 +1,7 @@
 
+# 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;
@@ -7,6 +10,7 @@ use File::Path qw(make_path);
 use File::Basename;
 use Data::Dumper;
 use List::Util qw(first);
+use Carp 'verbose';
 
 use config;
 
@@ -55,7 +59,8 @@ my %defaultTable = (
                items => [],
                options => [],
                regex => qr/^func:<default>$/,
-               type => 'func'
+               type => 'func',
+               'func:template' => 'code:method=invoke'
        },
        'enum:<default>' => {
                name => '<default>',
@@ -96,9 +101,27 @@ sub new {
                $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: $! $@";
                }
        }
 
@@ -127,6 +150,12 @@ sub new {
 
        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 = {};
@@ -138,8 +167,6 @@ sub new {
                push @{$self->{types}}, initType($self, $copyIndex, $obj);
        }
 
-       bless $self, $class;
-
        return $self;
 }
 
@@ -173,7 +200,7 @@ sub openOutput {
        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;
 }
 
@@ -190,9 +217,10 @@ sub initType {
        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};
        }
 
@@ -231,7 +259,7 @@ sub findType {
        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 = %+;
@@ -243,6 +271,41 @@ sub findType {
        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;
@@ -399,30 +462,15 @@ sub addDependencies {
        }
 }
 
-# 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}};
 
@@ -448,7 +496,7 @@ sub findDependencies {
                        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}";
 
@@ -461,9 +509,15 @@ sub findDependencies {
                        }
                } 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);
@@ -528,6 +582,7 @@ sub loadAPIFile {
 }
 
 sub parseRename {
+       my $api = shift;
        my $how = shift;
        my $rename = $renameTable{'identity'};
 
@@ -539,6 +594,11 @@ sub parseRename {
                        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 {
@@ -556,10 +616,6 @@ sub preprocess {
        # 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}});
@@ -605,18 +661,22 @@ sub analyseAPI {
        # 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);
                        }
                }
 
@@ -625,7 +685,7 @@ sub analyseAPI {
                        my $match = $inc->{match};
                        my $mode = $defmode;
 
-                       if ($inc->{match} =~ m/^(.*):(.*)$/) {
+                       if ($inc->{match} =~ m/^(.*?):(.*)$/) {
                                $match = $2;
                                $mode = $1;
                        }
@@ -633,6 +693,12 @@ sub analyseAPI {
                        $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$/;
                        }
@@ -640,7 +706,7 @@ sub analyseAPI {
                        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);
                                }
                        }
 
@@ -707,17 +773,19 @@ sub processField {
        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);
@@ -727,7 +795,7 @@ sub processField {
        }
 
        $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
@@ -750,7 +818,7 @@ sub processTypeFunc {
                        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;
@@ -768,36 +836,44 @@ sub processFunc {
        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);
@@ -877,7 +953,7 @@ sub processType {
 
                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}}) {
@@ -891,13 +967,13 @@ sub processType {
                        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);
        }
 
@@ -910,10 +986,11 @@ sub processLibrary {
        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
@@ -937,6 +1014,7 @@ sub processLibrary {
                        }
                } 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);
index c5ff6e9..3c8a185 100644 (file)
@@ -162,6 +162,7 @@ public class {rename} implements Pointer {
 
        private {rename}(MemorySegment segment) {
                this.segment = segment;
+{init}
        }
 
        public static {rename} create(MemorySegment segment) {
@@ -211,6 +212,7 @@ public class {rename} implements Pointer, Array<{rename}> {
 
        private {rename}(MemorySegment segment) {
                this.segment = segment;
+{init}
        }
 
        public static {rename} create(MemorySegment segment) {
@@ -233,6 +235,10 @@ public class {rename} implements Pointer, Array<{rename}> {
                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();
@@ -284,6 +290,7 @@ public class {rename} implements Pointer {
        private {rename}(MemoryAddress address, ResourceScope scope) {
                this.address = address;
                this.scope = scope;
+{init}
        }
 
        public static {rename} create(MemoryAddress address, ResourceScope scope) {
@@ -337,6 +344,7 @@ code getset {
        }
        }}
        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
@@ -357,6 +365,34 @@ code getset {
        }}
 }
 
+# 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) {
index bfd8d6d..9383602 100644 (file)
@@ -200,6 +200,7 @@ sub formatDefine {
 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 {
@@ -207,30 +208,52 @@ sub formatEnum {
                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;
@@ -242,13 +265,16 @@ sub formatTemplate {
 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 {
index 3b4cdff..6fb67a3 100644 (file)
@@ -86,13 +86,14 @@ sub loadControlFile {
                                        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;
@@ -100,6 +101,7 @@ sub loadControlFile {
                                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') {
index 2476803..c835f98 100644 (file)
@@ -19,6 +19,9 @@ sub fieldScope {
 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$/) {
@@ -146,7 +149,7 @@ sub new {
                $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'} = '';
index 671a7bd..0f219b5 100644 (file)
@@ -1,8 +1,7 @@
 # -*- 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?
@@ -74,29 +73,38 @@ type /^\[(?<length>\d+)u64:\$\{(\w+)\}\]$/ {
 }
 
 # 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
@@ -111,12 +119,12 @@ type /^\[(?<length0>\d+)\[(?<length1>\d+)(?<ctype>[uif]\d+)\]\]$/
        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"
        }}
 }
 
@@ -230,7 +238,8 @@ type /^u64:\$\{(\w+)\}$/ copy=<pointer> {
 # *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
diff --git a/src/notzed.vkheader.test/classes/module-info.java b/src/notzed.vkheader.test/classes/module-info.java
new file mode 100644 (file)
index 0000000..489e41b
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.vkheader.test {
+       requires notzed.vkheader;
+
+       requires java.desktop;
+}
@@ -61,19 +61,9 @@ import au.notzed.nativez.*;
 
 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();
 
@@ -116,7 +106,7 @@ public class TestVulkan {
                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);
@@ -126,16 +116,26 @@ public class TestVulkan {
                                | 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,
@@ -145,18 +145,17 @@ public class TestVulkan {
                                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;
@@ -164,21 +163,15 @@ public class TestVulkan {
 
                        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) {
@@ -200,26 +193,24 @@ public class TestVulkan {
                                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);
                }
        }
 
@@ -238,10 +229,9 @@ public class TestVulkan {
                                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);
 
@@ -249,7 +239,7 @@ public class TestVulkan {
                                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);
 
@@ -277,10 +267,9 @@ public class TestVulkan {
 
                        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,
@@ -290,10 +279,9 @@ public class TestVulkan {
                        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);
@@ -317,7 +305,6 @@ public class TestVulkan {
                                descriptorSets.getAtIndex(0),
                                0,
                                0,
-                               1,
                                VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
                                null,
                                bufferInfo,
@@ -338,7 +325,7 @@ public class TestVulkan {
                                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);
@@ -349,10 +336,9 @@ public class TestVulkan {
                                0,
                                1,
                                layout_table,
-                               0,
                                null);
 
-                       pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null);
+                       pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null, scope);
 
                        /* Create pipeline */
                        VkComputePipelineCreateInfo pipeline = VkComputePipelineCreateInfo.create(frame,
@@ -365,7 +351,7 @@ public class TestVulkan {
 
                        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);
                }
@@ -380,7 +366,7 @@ public class TestVulkan {
                                0,
                                computeQueueIndex);
 
-                       commandPool = device.vkCreateCommandPool(poolinfo, null);
+                       commandPool = device.vkCreateCommandPool(poolinfo, null, scope);
 
                        VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
                                commandPool,
@@ -388,7 +374,8 @@ public class TestVulkan {
                                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,
@@ -417,8 +404,8 @@ public class TestVulkan {
                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;
@@ -426,7 +413,8 @@ public class TestVulkan {
                        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 */
@@ -436,7 +424,7 @@ public class TestVulkan {
                        int res;
                        do {
                                res = device.vkWaitForFences(1, fences, VK_TRUE, 1000000);
-                       } while (res == VkResult.VK_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
 
                        device.vkDestroyFence(fence, null);
                }
@@ -546,7 +534,7 @@ public class TestVulkan {
 
        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);
 
@@ -558,7 +546,7 @@ public class TestVulkan {
        }
 
        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());
 
@@ -573,7 +561,7 @@ public class TestVulkan {
                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;
@@ -588,6 +576,7 @@ public class TestVulkan {
 
                init_instance();
                init_debug();
+
                init_device();
 
                dst = init_buffer(dstBufferSize,
@@ -614,6 +603,6 @@ public class TestVulkan {
        public static void main(String[] args) throws Throwable {
                System.loadLibrary("vulkan");
 
-               new TestVulkan().demo();
+               new TestMandelbrot().demo();
        }
 }
diff --git a/src/notzed.vkheader.test/gen/gen.make b/src/notzed.vkheader.test/gen/gen.make
new file mode 100644 (file)
index 0000000..9099112
--- /dev/null
@@ -0,0 +1,18 @@
+
+
+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 $@ $<
index 012bfac..ed4a816 100644 (file)
@@ -2,5 +2,5 @@
 module notzed.vkheader {
        requires transitive notzed.nativez;
 
-       requires java.desktop;
+       exports vulkan;
 }
index cf30e7d..54da390 100644 (file)
@@ -1,4 +1,3 @@
 
-
 notzed.vkheader_API = vkheader
 notzed.vkheader_APIFLAGS = -t vulkan -Isrc/notzed.vkheader/gen
index 4c93262..27b5f11 100644 (file)
 # -*- 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}
@@ -55,5 +253,48 @@ code vulkan-method {
                }
                {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}
+}
+}}
 }
index 4411aa3..bee1f34 100644 (file)
+#
+# 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;
diff --git a/src/notzed.vkheader/gen/vulkan.pm b/src/notzed.vkheader/gen/vulkan.pm
new file mode 100644 (file)
index 0000000..cc5ec98
--- /dev/null
@@ -0,0 +1,352 @@
+
+# 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;
diff --git a/src/notzed.vkregistry.test/classes/module-info.java b/src/notzed.vkregistry.test/classes/module-info.java
new file mode 100644 (file)
index 0000000..7d1e258
--- /dev/null
@@ -0,0 +1,7 @@
+
+module notzed.vkregistry.test {
+       requires notzed.vkregistry;
+       requires notzed.xlib;
+
+       requires java.desktop;
+}
diff --git a/src/notzed.vkregistry.test/gen/cube.frag b/src/notzed.vkregistry.test/gen/cube.frag
new file mode 100644 (file)
index 0000000..de24544
--- /dev/null
@@ -0,0 +1,8 @@
+#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;
+}
diff --git a/src/notzed.vkregistry.test/gen/cube.vert b/src/notzed.vkregistry.test/gen/cube.vert
new file mode 100644 (file)
index 0000000..5d21e1e
--- /dev/null
@@ -0,0 +1,14 @@
+#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;
+}
diff --git a/src/notzed.vkregistry.test/gen/gen.make b/src/notzed.vkregistry.test/gen/gen.make
new file mode 100644 (file)
index 0000000..a9f07aa
--- /dev/null
@@ -0,0 +1,17 @@
+
+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 $@ $<
diff --git a/src/notzed.vkregistry.test/gen/mandelbrot.comp b/src/notzed.vkregistry.test/gen/mandelbrot.comp
new file mode 100644 (file)
index 0000000..6a45590
--- /dev/null
@@ -0,0 +1,60 @@
+#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;
+}
index b8ea86d..fa090a5 100644 (file)
@@ -1,9 +1,6 @@
 
 module notzed.vkregistry {
        requires transitive notzed.nativez;
-       requires transitive notzed.xlib;
-
-       requires java.desktop;
 
        exports vulkan;
 }
index e502e01..ff42ddd 100644 (file)
@@ -1,25 +1,8 @@
 
-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 $@ $<