Added vulkan cube demo.
authorNot Zed <notzed@gmail.com>
Sat, 26 Mar 2022 11:59:26 +0000 (22:29 +1030)
committerNot Zed <notzed@gmail.com>
Sat, 26 Mar 2022 11:59:26 +0000 (22:29 +1030)
A few bug fixes and improvements in both code generators.

24 files changed:
Makefile
src/notzed.nativez/classes/au/notzed/nativez/Frame.java
src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java
src/notzed.nativez/lib/code.api
src/notzed.nativez/lib/method.pm
src/notzed.nativez/lib/types.api
src/notzed.nativez/native/export.cc
src/notzed.vkheader/gen/vkheader.api
src/notzed.vkregistry/classes/module-info.java
src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java
src/notzed.vkregistry/classes/vulkan/test/Cube.java [new file with mode: 0644]
src/notzed.vkregistry/classes/vulkan/test/GLMaths.java [new file with mode: 0644]
src/notzed.vkregistry/classes/vulkan/test/TestCube.java [new file with mode: 0644]
src/notzed.vkregistry/classes/vulkan/test/TestMandelbrot.java [moved from src/notzed.vkregistry/classes/vulkan/test/TestVulkan.java with 94% similarity]
src/notzed.vkregistry/gen/VkDevice-part.java
src/notzed.vkregistry/gen/VkInstance-part.java
src/notzed.vkregistry/gen/cube.frag [new file with mode: 0644]
src/notzed.vkregistry/gen/cube.vert [new file with mode: 0644]
src/notzed.vkregistry/gen/export-vulkan
src/notzed.vkregistry/gen/gen.make
src/notzed.xlib/classes/module-info.java [new file with mode: 0644]
src/notzed.xlib/gen/gen.make [new file with mode: 0644]
src/notzed.xlib/gen/xlib.api [new file with mode: 0644]
src/notzed.xlib/gen/xlib.h [new file with mode: 0644]

index 8d2d841..fafe48f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,22 +5,28 @@ dist_EXTRA=README COPYING
 
 include config.make
 
-java_MODULES = notzed.nativez notzed.apistatic notzed.apiobject notzed.ffmpeg notzed.clstatic notzed.vkregistry notzed.vkheader
+java_MODULES = notzed.nativez \
+       notzed.apistatic notzed.apiobject \
+       notzed.ffmpeg \
+       notzed.clstatic \
+       notzed.xlib notzed.vkregistry
 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.vkregistry_JDEPMOD = notzed.nativez notzed.xlib
 notzed.vkheader_JDEPMOD = notzed.nativez
 
 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.TestVulkan
+notzed.vkregistry_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube
 
 $(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
+
 include java.make
index e45032e..560ba5a 100644 (file)
@@ -29,7 +29,8 @@ import static jdk.incubator.foreign.ValueLayout.OfAddress;
  * </pre>
  * Any memory allocated is freed when the frame is closed.
  * <p>
- * This is quite a bit faster than using an arena allocator.
+ * This is quite a bit faster than using an arena allocator ... but it also
+ * removes most scope guarantees.
  */
 public class Frame implements AutoCloseable, SegmentAllocator {
 
index 3f7d299..f7be38d 100644 (file)
@@ -61,6 +61,13 @@ public class HandleArray<T extends Pointer> extends AbstractList<T> implements P
                return create(MemorySegment.ofAddress(address, size * Memory.POINTER.byteSize(), scope), create);
        }
 
+       public static <T extends Pointer> HandleArray<T> create(SegmentAllocator alloc, BiFunction<MemoryAddress,ResourceScope,T> create, T... values) {
+               HandleArray<T> array = create(alloc.allocateArray(Memory.POINTER, values.length), create);
+               for (int i=0;i<values.length;i++)
+                       array.setAtIndex(i, values[i]);
+               return array;
+       }
+
        @Override
        public final MemoryAddress address() {
                return segment.address();
index a9c8c23..c5ff6e9 100644 (file)
@@ -84,8 +84,10 @@ code method {
                }
                Trampoline trampoline = ({native-arguments}) -> {
                        // frame?  scope?
-                       {trampoline-result-define}target$.call({java-call});
-                       {trampoline-result-return}
+                       try (ResourceScope upcallScope$ = ResourceScope.newConfinedScope()) {
+                               {trampoline-result-define}target$.call({java-call});
+                               {trampoline-result-return}
+                       }
                };
                return new FunctionPointer<>(
                        Memory.upcall(
@@ -227,6 +229,10 @@ public class {rename} implements Pointer, Array<{rename}> {
                return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope)) : null;
        }
 
+       public static {rename} createArray(MemoryAddress address, long length, ResourceScope scope) {
+               return MemoryAddress.NULL != address ? create(MemorySegment.ofAddress(address, LAYOUT.byteSize() * length, scope)) : null;
+       }
+
        @Override
        public final MemoryAddress address() {
                return segment.address();
index 9cf6f24..2476803 100644 (file)
@@ -79,8 +79,8 @@ sub new {
                }
        } @members;
 
-       # for java upcalls
-       $info->{'java-call'} = join ', ', map { code::formatTemplate('{tojava}', { %{$_->{match}}, value=>'{name}' }) } grep { $_->{field}->{output} } @members;
+       # for java upcalls, they have an explicit scope always
+       $info->{'java-call'} = join ', ', map { code::formatTemplate('{tojava}', { %{$_->{match}}, value=>'{name}', scope=>'upcallScope$' }) } grep { $_->{field}->{output} } @members;
 
        $info->{'java-signature'} = code::formatFunctionSignature($api, [$result, @members]);
        $info->{'function-descriptor'} = code::formatFunctionDescriptor($api, [$result, @members]);
index 633d554..671a7bd 100644 (file)
@@ -195,7 +195,7 @@ type /^u64:(?<ctype>[ui]8)$/ select=segment copy=<pointer> {
 # *i8 with no flag = String
 type /^u64:(?<ctype>i8)$/ copy=<pointer> {
        type            {{ 'String' }}
-       tonative        {{ '(Addressable)frame$.copy({value})' }}
+       tonative        {{ '(Addressable)({value} != null ? frame$.copy({value}) : MemoryAddress.NULL)' }}
 
        setnative       {{ '{name}$VH.set({segment}, {copynative})' }}
        copynative      {{ 'SegmentAllocator.nativeAllocator(segment.scope()).allocateUtf8String({value})' }}
@@ -213,6 +213,14 @@ type /^u64:(?<ctype>[uif]\d+)$/ copy=<pointer> {
        tojavai         {{ "({typei}){value}" }}
 }
 
+# *type  struct?  handle? with size
+# maybe should be handled by getbyvalue
+type /^u64:\$\{(\w+)\}$/ select=array-size copy=<pointer> {
+       type            {{ "$data->{$m->{type}}->{rename}" }}
+       length          {{ 'get'.($m->{'array-size'}->{rename}).'()' }}
+       tojava          {{ "$data->{$m->{type}}->{rename}.createArray({value}, {length}, {scope})" }}
+}
+
 # *type  struct?  handle?
 type /^u64:\$\{(\w+)\}$/ copy=<pointer> {
        type            {{ "$data->{$m->{type}}->{rename}" }}
@@ -222,7 +230,7 @@ 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}, {scope})" }}
+       tojava          {{ "MemorySegment.ofAddress({value}, $m->{'array-size'}->{name} * 8, {scope})" }}
 }
 
 # *void
index ac87eb3..d8b183b 100644 (file)
@@ -704,12 +704,6 @@ static void export_type(tree type, const char *names) {
                                return;
                        names = IDENTIFIER_POINTER(name);
                }
-               if (hash_lookup(&dumped, names)) {
-                       if (debug_level > 1)
-                               fprintf(stderr, " - type already output, skipping");
-                       return;
-               }
-               hash_put(&dumped, node_alloc(type, names));
 
                deftype = TREE_TYPE(type);
                target = target_type(deftype);
@@ -719,6 +713,15 @@ static void export_type(tree type, const char *names) {
 
                switch (TREE_CODE(target)) {
                case FUNCTION_TYPE: {
+                       // TODO: should be able to call export_type(target, names) here, except code is slightly different
+
+                       if (hash_lookup(&dumped, names)) {
+                               if (debug_level > 1)
+                                       fprintf(stderr, " - type already output, skipping");
+                               return;
+                       }
+                       hash_put(&dumped, node_alloc(type, names));
+
                        // function pointer typdef
                        // I don't know if i even want this
                        generate("'call:%s' => { name => '%s', type => 'call',", names, names);
@@ -750,6 +753,14 @@ static void export_type(tree type, const char *names) {
                        break;
                }
                case ENUMERAL_TYPE: {
+                       if (hash_lookup(&dumped, names)) {
+                               if (debug_level > 1)
+                                       fprintf(stderr, " - type already output, skipping");
+                               return;
+                       }
+                       hash_put(&dumped, node_alloc(type, names));
+
+                       // TODO: should be able to call export_type(target, names) here
                        // TODO: typedef of anonymous enumerations may be repeated
                        // TODO: this could be detected in the frontend - e.g. don't include any
                        // TODO: anon enum values if any are already there
@@ -772,8 +783,17 @@ static void export_type(tree type, const char *names) {
                        generate("]},\n");
                        break;
                }
-               case RECORD_TYPE: // forward declaration or opaque types
+               case RECORD_TYPE:
                case UNION_TYPE:
+                       if (TYPE_FIELDS(target)) {
+                               // 'typedef struct { } foo;'
+                               export_type(target, names);
+                       } else {
+                               // forward declaration or opaque types
+                               if (debug_level)
+                                       fprintf(stderr, "forward ignored %s: %s\n", ZTREE_CODE(target), names);
+                       }
+                       break;
                case VOID_TYPE:
                case INTEGER_TYPE:
                case REAL_TYPE:
@@ -889,8 +909,11 @@ static void export_type(tree type, const char *names) {
                                return;
                        names = IDENTIFIER_POINTER(name);
                }
-               if (hash_lookup(&dumped, names))
+               if (hash_lookup(&dumped, names)) {
+                       if (debug_level > 1)
+                               fprintf(stderr, "already exported: %-16s %s\n", ZTREE_CODE(type), names);
                        return;
+               }
                hash_put(&dumped, node_alloc(type, names));
 
                if (debug_level > 1)
index 2c67a19..4c93262 100644 (file)
@@ -21,10 +21,13 @@ struct VkInstance_T {
        #}}
 }
 
-struct // {
+struct /Vk/ {
        func:<matcher> instance:0 vkheader::matchObjectFunction;
 }
 
+call /PFN_vk/ access=rw {
+}
+
 enum // {
 }
 
index ccc933f..b8ea86d 100644 (file)
@@ -1,6 +1,7 @@
 
 module notzed.vkregistry {
        requires transitive notzed.nativez;
+       requires transitive notzed.xlib;
 
        requires java.desktop;
 
index f3f596e..93b9838 100644 (file)
@@ -11,7 +11,7 @@ public final class VkPhysicalDeviceGroupProperties implements Pointer {
        VkPhysicalDeviceGroupProperties(MemorySegment segment, DispatchInstance instanceDispatch) {
                this.segment = segment;
                this.instanceDispatch = instanceDispatch;
-               segment.set(Memory.INT, 0, VkStructureType.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES);
+               segment.set(Memory.INT, 0, VkConstants.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES);
        }
        public final MemoryAddress address() { return segment.address(); }
        public final ResourceScope scope() { return segment.scope(); }
diff --git a/src/notzed.vkregistry/classes/vulkan/test/Cube.java b/src/notzed.vkregistry/classes/vulkan/test/Cube.java
new file mode 100644 (file)
index 0000000..e8f824e
--- /dev/null
@@ -0,0 +1,55 @@
+
+package vulkan.test;
+
+//struct Vertex {
+//    float posX, posY, posZ, posW;  // Position data
+//    float r, g, b, a;              // Color
+//};
+
+class Cube {
+       static final int dataStride = 8 * 4;
+       static final float[] data = new float[] {
+               // red face
+               -1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+               // green face
+               -1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+               // blue face
+               -1, 1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               -1, -1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+               // yellow face
+               1, 1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               1, -1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+               // magenta face
+               1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               -1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+               // cyan face
+               1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+               -1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+       };
+}
diff --git a/src/notzed.vkregistry/classes/vulkan/test/GLMaths.java b/src/notzed.vkregistry/classes/vulkan/test/GLMaths.java
new file mode 100644 (file)
index 0000000..3414740
--- /dev/null
@@ -0,0 +1,131 @@
+
+package vulkan.test;
+
+import static java.lang.Math.*;
+import java.util.Arrays;
+
+public class GLMaths {
+       public static void identity4f(float []matrix) {
+               Arrays.fill(matrix, 0.0f);
+               for (int i = 0; i < 4; i++)
+                       matrix[i * 4 + i] = 1.0f;
+       }
+
+       public static float length3f(float [] a) {
+               float sum = 0;
+               for (int i = 0; i < 3; i++)
+                       sum += a[i] * a[i];
+               return (float)sqrt(sum);
+       }
+
+       public static void sub3f(float [] c, float [] a, float [] b) {
+               for (int i = 0; i < 3; i++)
+                       c[i] = a[i] - b[i];
+       }
+
+       public static void norm3f(float [] vec) {
+               float fix = 1.0f / length3f(vec);
+               for (int i = 0; i < 3; i++)
+                       vec[i] *= fix;
+       }
+
+       public static void cross3f(float [] c, float [] a, float [] b) {
+               c[0] = a[1] * b[2] - a[2] * b[1];
+               c[1] = a[2] * b[0] - a[0] * b[2];
+               c[2] = a[0] * b[1] - a[1] * b[0];
+       }
+
+       public static float dot3f(float [] a, float [] b) {
+               return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+       }
+
+       public static float [] mult4x4f(float [] c, float [] b, float [] a) {
+               c[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
+               c[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
+               c[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
+               c[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
+
+               c[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
+               c[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
+               c[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
+               c[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
+
+               c[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
+               c[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
+               c[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
+               c[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
+
+               c[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
+               c[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
+               c[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
+               c[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
+
+               return c;
+       }
+
+       public static void lookAt(float []mat, float []eye, float []centre, float []up) {
+               float forward[] = new float[3], side[] = new float[3], u[] = new float[3];
+
+               sub3f(forward, centre, eye);
+               norm3f(forward);
+               cross3f(side, forward, up);
+               norm3f(side);
+               cross3f(u, side, forward);
+
+               mat[0] = side[0];
+               mat[4] = side[1];
+               mat[8] = side[2];
+
+               mat[1] = u[0];
+               mat[5] = u[1];
+               mat[9] = u[2];
+
+               mat[2] = -forward[0];
+               mat[6] = -forward[1];
+               mat[10] = -forward[2];
+
+               mat[12] = -dot3f(side, eye);
+               mat[13] = -dot3f(u, eye);
+               mat[14] = dot3f(forward, eye);
+
+               mat[3] = 0.0f;
+               mat[7] = 0.0f;
+               mat[11] = 0.0f;
+
+               mat[15] = 1.0f;
+       }
+
+       public static void frustum(float []mat, float left, float right, float bottom, float top, float znear, float zfar) {
+               float temp, temp2, temp3, temp4;
+
+               temp = 2.0f * znear;
+               temp2 = right - left;
+               temp3 = top - bottom;
+               temp4 = zfar - znear;
+               mat[0] = temp / temp2;
+               mat[1] = 0.0f;
+               mat[2] = 0.0f;
+               mat[3] = 0.0f;
+               mat[4] = 0.0f;
+               mat[5] = temp / temp3;
+               mat[6] = 0.0f;
+               mat[7] = 0.0f;
+               mat[8] = (right + left) / temp2;
+               mat[9] = (top + bottom) / temp3;
+               mat[10] = (-zfar - znear) / temp4;
+               mat[11] = -1.0f;
+               mat[12] = 0.0f;
+               mat[13] = 0.0f;
+               mat[14] = (-temp * zfar) / temp4;
+               mat[15] = 0.0f;
+       }
+
+       public static void perspective(float []mat, float fovy, float aspect, float znear, float zfar) {
+               float ymax, xmax;
+
+               ymax = znear * (float)tan(fovy * 0.5f);
+               xmax = ymax * aspect;
+
+               frustum(mat, -xmax, xmax, -ymax, ymax, znear, zfar);
+       }
+}
diff --git a/src/notzed.vkregistry/classes/vulkan/test/TestCube.java b/src/notzed.vkregistry/classes/vulkan/test/TestCube.java
new file mode 100644 (file)
index 0000000..6e7ec57
--- /dev/null
@@ -0,0 +1,1135 @@
+ /*
+The MIT License (MIT)
+
+Copyright (C) 2017 Eric Arnebäck
+Copyright (C) 2019 Michael Zucchi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+ */
+
+/*
+ * This is a Java conversion of a C conversion of this:
+ * https://github.com/Erkaman/vulkan_minimal_compute
+ *
+ * It's been simplified a bit and converted to the 'zvk' api.
+ */
+
+package vulkan.test;
+
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.image.MemoryImageSource;
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Collections;
+
+import java.lang.invoke.*;
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.PathElement;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.VkConstants.*;
+
+import xlib.*;
+import static xlib.XLib.*;
+import static vulkan.test.GLMaths.*;
+
+public class TestCube {
+       static final boolean debug = true;
+
+       final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
+       final static int NUM_DESCRIPTOR_SETS = 1;
+
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int width = 800;
+       int height = 800;
+       float projection[] = new float[16];
+       float view[] = new float[16];
+       float model[] = new float[16];
+       float clip[] = new float[] {
+               1.0f, 0.0f, 0.0f, 0.0f,
+               0.0f, -1.0f, 0.0f, 0.0f,
+               0.0f, 0.0f, 0.5f, 0.0f,
+               0.0f, 0.0f, 0.5f, 1.0f
+       };
+       float mvp[] = new float[16];
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+       VkPhysicalDeviceMemoryProperties memory_properties;
+       VkPhysicalDeviceFeatures device_features;
+
+       int present_queue_index;
+       int graphics_queue_index;
+
+       VkDevice device;
+       VkSwapchainKHR chain;
+
+       VkQueue graphics_queue;
+       VkQueue present_queue;
+
+       int chainImageFormat;
+       HandleArray<VkImage> chainImage;
+       HandleArray<VkImageView> chainImageView;
+
+       int depthFormat;
+       VkImage depthImage;
+       VkImageView depthView;
+       VkDeviceMemory depthMemory;
+
+       VkCommandPool cmd_pool;
+       HandleArray<VkCommandBuffer> cmd;
+
+       BufferMemory uniform;
+       VkPipelineLayout pipeline_layout;
+
+       VkDescriptorSetLayout desc_layout;
+       VkDescriptorPool desc_pool;
+       HandleArray<VkDescriptorSet> desc_set = VkDescriptorSet.createArray(1, (SegmentAllocator)scope);
+
+
+       VkRenderPass render_pass;
+       HandleArray<VkFramebuffer> framebuffers;
+
+       BufferMemory vertex;
+       HandleArray<VkBuffer> vertexBuffer = VkBuffer.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputBindingDescription vi_binding = VkVertexInputBindingDescription.createArray(1, (SegmentAllocator)scope);
+       VkVertexInputAttributeDescription vi_attribs = VkVertexInputAttributeDescription.createArray(2, (SegmentAllocator)scope);
+
+       IntArray cube_vs;
+       IntArray cube_fs;
+       HandleArray<VkShaderModule> shader = VkShaderModule.createArray(2, (SegmentAllocator)scope);
+
+       HandleArray<VkPipeline> pipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+       VkSemaphore chainSemaphore;
+       VkFence drawFence;
+
+       record BufferMemory (VkBuffer buffer, VkDeviceMemory memory, long size) {
+               public void free(VkDevice device) {
+                       device.vkFreeMemory(memory, null);
+                       device.vkDestroyBuffer(buffer, null);
+               }
+       }
+
+       VkDebugUtilsMessengerEXT logger;
+
+       void init_debug() throws Exception {
+               if (!debug)
+                       return;
+               try (Frame frame = Frame.frame()) {
+                       NativeSymbol cb = PFN_vkDebugUtilsMessengerCallbackEXT.of((severity, flags, data) -> {
+                                       System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+                                       return 0;
+                               }, scope);
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(frame,
+                               0,
+                               VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+                               | 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(),
+                               null);
+
+                       logger = instance.vkCreateDebugUtilsMessengerEXT(info, null);
+               }
+
+               //typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
+
+       }
+
+       void init_instance() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkInstanceCreateInfo info = VkInstanceCreateInfo.create(frame,
+                               0,
+                               VkApplicationInfo.create(frame, "cube", 1, "cube-engine", 2, VK_MAKE_API_VERSION(0, 1, 0, 0)),
+                               new String[] { "VK_LAYER_KHRONOS_validation" },
+                               new String[] { "VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils" }
+                               );
+
+                       instance = VkInstance.vkCreateInstance(info, null);
+                       System.out.printf("instance = %s\n", instance);
+               }
+       }
+
+       XDisplay display;
+       long window;
+       long wm_delete_window;
+       VkSurfaceKHR surface;
+
+       void init_surface() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       XInitThreads();
+                       display = XOpenDisplay(null);
+                       long visualMask = VisualScreenMask;
+                       IntArray numberOfVisuals = IntArray.createArray(1, frame);
+                       XVisualInfo vInfoTemplate = XVisualInfo.create(frame);
+
+                       vInfoTemplate.setScreen(DefaultScreen(display));
+
+                       XVisualInfo visualInfo = XGetVisualInfo(display, visualMask, vInfoTemplate, numberOfVisuals);
+                       long colormap = XCreateColormap(display, RootWindow(display, vInfoTemplate.getScreen()), visualInfo.getVisual(), AllocNone);
+
+                       XSetWindowAttributes windowAttributes = XSetWindowAttributes.create(frame);
+
+                       windowAttributes.setColormap(colormap);
+                       windowAttributes.setBackgroundPixel(0xffffffff);
+                       windowAttributes.setBorderPixel(0);
+                       windowAttributes.setEventMask(KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask);
+
+                       window = XCreateWindow(display, RootWindow(display, vInfoTemplate.getScreen()),
+                               0, 0, width, height,
+                               0, visualInfo.getDepth(), InputOutput, visualInfo.getVisual(),
+                               CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, windowAttributes);
+
+                       XSelectInput(display, window, ExposureMask | KeyPressMask);
+                       XMapWindow(display, window);
+                       XFlush(display);
+                       wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+
+                       VkXlibSurfaceCreateInfoKHR surfaceinfo = VkXlibSurfaceCreateInfoKHR.create(frame,
+                               0,
+                               display.address(),
+                               window);
+
+                       surface = instance.vkCreateXlibSurfaceKHR(surfaceinfo, null);
+                       System.out.printf("surface: %s\n", surface);
+               }
+       }
+
+       void init_device() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       IntArray count$h = IntArray.create(frame, 1);
+                       IntArray present$h = IntArray.create(frame, 1);
+                       HandleArray<VkPhysicalDevice> devs;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices();
+
+                       // Search for device and queue indices
+                       int devid = -1;
+                       int present_queue = -1;
+                       int graphics_queue = -1;
+                       for (int i = 0; i < devs.length(); i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops;
+
+                               // TODO: change to return the allocated array directly
+                               dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, null);
+                               famprops = VkQueueFamilyProperties.createArray(count$h.getAtIndex(0), frame);
+                               dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, famprops);
+
+                               for (int j = 0; j < famprops.length(); j++) {
+                                       boolean present;
+
+                                       dev.vkGetPhysicalDeviceSurfaceSupportKHR(j, surface, present$h);
+                                       present = present$h.get(0) != 0;
+
+                                       if (present && present_queue == -1)
+                                               present_queue = j;
+                                       if ((famprops.getQueueFlags(j) & VK_QUEUE_GRAPHICS_BIT) != 0) {
+                                               graphics_queue = j;
+                                               if (present) {
+                                                       present_queue = j;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (present_queue != -1 && graphics_queue != -1) {
+                                       devid = i;
+                                       break;
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       physicalDevice = devs.getAtIndex(devid);
+                       present_queue_index = present_queue;
+                       graphics_queue_index = graphics_queue;
+
+                       // NOTE: app scope
+                       memory_properties = VkPhysicalDeviceMemoryProperties.create(scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(memory_properties);
+                       device_features = VkPhysicalDeviceFeatures.create(scope);
+                       physicalDevice.vkGetPhysicalDeviceFeatures(device_features);
+
+                       FloatArray qpri = FloatArray.create(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               frame,
+                               0,
+                               graphics_queue,
+                               1,
+                               qpri);
+                       String [] extensions = {
+                               "VK_KHR_swapchain"
+                       };
+                       VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame);
+                       features.setDepthClamp(1);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               frame,
+                               0,
+                               1,
+                               qinfo,
+                               null,
+                               extensions,
+                               features);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, null);
+
+                       System.out.printf("device = %s\n", device);
+
+                       /* ************************************************************** */
+                       int format;
+                       int formatCount;
+                       physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, count$h, null);
+                       formatCount = count$h.getAtIndex(0);
+                       VkSurfaceFormatKHR surfFormats = VkSurfaceFormatKHR.createArray(formatCount, frame);
+                       physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, count$h, surfFormats);
+                       // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+                       // the surface has no preferred format.  Otherwise, at least one
+                       // supported format will be returned.
+                       if (formatCount == 1 && surfFormats.getFormat(0) == VK_FORMAT_UNDEFINED) {
+                               format = VK_FORMAT_B8G8R8A8_UNORM;
+                       } else {
+                               format = surfFormats.getFormat(0);
+                       }
+
+                       VkSurfaceCapabilitiesKHR surfCapabilities = VkSurfaceCapabilitiesKHR.create(frame);
+
+                       physicalDevice.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, surfCapabilities);
+
+                       physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, count$h, null);
+                       IntArray presentModes = IntArray.createArray(count$h.get(0), frame);
+                       physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, count$h, presentModes);
+
+                       VkExtent2D swapchainExtent;
+                       // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
+                       if (surfCapabilities.getCurrentExtent().getWidth() == 0xFFFFFFFF) {
+                               // If the surface size is undefined, the size is set to
+                               // the size of the images requested.
+                               swapchainExtent = VkExtent2D.create(frame,
+                                       clampi(width, surfCapabilities.getMinImageExtent().getWidth(), surfCapabilities.getMaxImageExtent().getWidth()),
+                                       clampi(height, surfCapabilities.getMinImageExtent().getHeight(), surfCapabilities.getMaxImageExtent().getHeight()));
+                       } else {
+                               // If the surface size is defined, the swap chain size must match
+                               swapchainExtent = surfCapabilities.getCurrentExtent();
+                       }
+                       int compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+                       int compositeAlphaFlags[] = {
+                               VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+                               VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
+                       };
+                       for (int flag: compositeAlphaFlags) {
+                               if ((surfCapabilities.getSupportedCompositeAlpha() & flag) != 0) {
+                                       compositeAlpha = flag;
+                                       break;
+                               }
+                       }
+
+                       VkSwapchainCreateInfoKHR chaininfo = VkSwapchainCreateInfoKHR.create(frame,
+                               0,
+                               surface,
+                               surfCapabilities.getMinImageCount(),
+                               format,
+                               VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+                               1, //.imageArrayLayers = 1,
+                               VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               // assumes queues are same.
+                               0,
+                               null,
+                               (surfCapabilities.getSupportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0
+                               ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfCapabilities.getCurrentTransform(),
+                               compositeAlpha,
+                               VK_PRESENT_MODE_FIFO_KHR,
+                               1,
+                               null);
+                       chaininfo.getImageExtent().setWidth(swapchainExtent.getWidth());
+                       chaininfo.getImageExtent().setHeight(swapchainExtent.getHeight());
+
+                       chain = device.vkCreateSwapchainKHR(chaininfo, null);
+
+                       int chainImageCount;
+                       device.vkGetSwapchainImagesKHR(chain, count$h, null);
+                       chainImageCount = count$h.get(0);
+                       chainImage = VkImage.createArray(chainImageCount, (SegmentAllocator)scope);
+                       chainImageView = VkImageView.createArray(chainImageCount, (SegmentAllocator)scope);
+
+                       device.vkGetSwapchainImagesKHR(chain, count$h, chainImage);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(frame,
+                               0,
+                               null,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               format);
+                       VkComponentMapping components = viewinfo.getComponents();
+                       components.setR(VK_COMPONENT_SWIZZLE_R);
+                       components.setG(VK_COMPONENT_SWIZZLE_G);
+                       components.setB(VK_COMPONENT_SWIZZLE_B);
+                       components.setA(VK_COMPONENT_SWIZZLE_A);
+                       VkImageSubresourceRange subresourceRange = viewinfo.getSubresourceRange();
+                       subresourceRange.setAspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
+                       subresourceRange.setLevelCount(1);
+                       subresourceRange.setLayerCount(1);
+
+                       for (int i = 0; i < chainImageCount; i++) {
+                               viewinfo.setImage(chainImage.get(i));
+
+                               chainImageView.setAtIndex(i, device.vkCreateImageView(viewinfo, null));
+                       }
+
+                       chainImageFormat = format;
+               }
+       }
+
+       void init_device_queue() {
+               graphics_queue = device.vkGetDeviceQueue(graphics_queue_index, 0);
+               if (graphics_queue_index == present_queue_index) {
+                       present_queue = graphics_queue;
+               } else {
+                       present_queue = device.vkGetDeviceQueue(present_queue_index, 0);
+               }
+       }
+
+       void init_command() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(frame,
+                               0,
+                               graphics_queue_index);
+
+                       cmd_pool = device.vkCreateCommandPool(poolinfo, null);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
+                               cmd_pool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1);
+
+                       cmd = device.vkAllocateCommandBuffers(cmdinfo);
+               }
+       }
+
+       // parameterise as init_image?
+       void init_depth() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       int format = VK_FORMAT_D16_UNORM;
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkImageCreateInfo imageinfo = VkImageCreateInfo.create(frame, 0,
+                               VK_IMAGE_TYPE_2D,
+                               format,
+                               1,
+                               1,
+                               NUM_SAMPLES,
+                               0,
+                               VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0, null,
+                               VK_IMAGE_LAYOUT_UNDEFINED);
+                       imageinfo.getExtent().setWidth(width);
+                       imageinfo.getExtent().setHeight(height);
+                       imageinfo.getExtent().setDepth(1);
+
+                       depthImage = device.vkCreateImage(imageinfo, null);
+
+                       device.vkGetImageMemoryRequirements(depthImage, req);
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
+
+                       depthMemory = device.vkAllocateMemory(alloc, null);
+
+                       device.vkBindImageMemory(depthImage, depthMemory, 0);
+
+                       VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(frame, 0,
+                               depthImage,
+                               VK_IMAGE_VIEW_TYPE_2D,
+                               VK_FORMAT_D16_UNORM);
+
+                       VkComponentMapping components = viewinfo.getComponents();
+                       components.setR(VK_COMPONENT_SWIZZLE_R);
+                       components.setG(VK_COMPONENT_SWIZZLE_G);
+                       components.setB(VK_COMPONENT_SWIZZLE_B);
+                       components.setA(VK_COMPONENT_SWIZZLE_A);
+                       VkImageSubresourceRange subresourceRange = viewinfo.getSubresourceRange();
+                       subresourceRange.setAspectMask(VK_IMAGE_ASPECT_DEPTH_BIT);
+                       subresourceRange.setLevelCount(1);
+                       subresourceRange.setLayerCount(1);
+
+                       depthView = device.vkCreateImageView(viewinfo, null);
+
+                       depthFormat = format;
+               }
+       }
+
+       void init_uniform() throws Exception {
+               uniform = init_buffer(mvp.length * 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(mvp));
+       }
+
+       void init_descriptor() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(frame,
+                               0,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_VERTEX_BIT,
+                               null);
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(frame,
+                               0,
+                               1,
+                               layout_binding);
+
+                       desc_layout = device.vkCreateDescriptorSetLayout(descriptor_layout, null);
+                       layout_table.setAtIndex(0, desc_layout);
+
+                       VkPipelineLayoutCreateInfo pipeline_info = VkPipelineLayoutCreateInfo.create(frame,
+                               0,
+                               1,
+                               layout_table,
+                               0,
+                               null);
+
+                       pipeline_layout = device.vkCreatePipelineLayout(pipeline_info, null);
+
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               1);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame,
+                               0,
+                               1,
+                               1,
+                               type_count);
+
+                       desc_pool = device.vkCreateDescriptorPool(descriptor_pool, null);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(frame,
+                               desc_pool,
+                               1,
+                               layout_table);
+
+                       device.vkAllocateDescriptorSets(alloc_info, desc_set);
+
+                       VkDescriptorBufferInfo uniformInfo = VkDescriptorBufferInfo.create(frame, uniform.buffer, 0, uniform.size);
+                       VkWriteDescriptorSet writes = VkWriteDescriptorSet.create(frame,
+                               desc_set.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                               null,
+                               uniformInfo,
+                               null);
+
+                       device.vkUpdateDescriptorSets(1, writes, 0, null);
+               }
+       }
+
+       void init_render() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkAttachmentDescription attachments = VkAttachmentDescription.createArray(2, frame);
+
+                       attachments.setFormat(chainImageFormat);
+                       attachments.setSamples(NUM_SAMPLES);
+                       attachments.setLoadOp(VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOp(VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+                       attachments.setFlags(0);
+
+                       attachments.setFormat(1, depthFormat);
+                       attachments.setSamples(1, NUM_SAMPLES);
+                       attachments.setLoadOp(1, VK_ATTACHMENT_LOAD_OP_CLEAR);
+                       attachments.setStoreOp(1, VK_ATTACHMENT_STORE_OP_STORE);
+                       attachments.setStencilLoadOp(1, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+                       attachments.setStencilStoreOp(1, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+                       attachments.setInitialLayout(1, VK_IMAGE_LAYOUT_UNDEFINED);
+                       attachments.setFinalLayout(1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+                       attachments.setFlags(1, 0);
+
+                       VkAttachmentReference color_reference = VkAttachmentReference.create(frame,
+                               0,
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+                       VkAttachmentReference depth_reference = VkAttachmentReference.create(frame,
+                               1,
+                               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+                       VkSubpassDescription subpass = VkSubpassDescription.create(frame,
+                               0,
+                               VK_PIPELINE_BIND_POINT_GRAPHICS,
+                               0, null,
+                               1, color_reference,
+                               null,
+                               depth_reference,
+                               0, null);
+
+                       VkRenderPassCreateInfo rp_info = VkRenderPassCreateInfo.create(frame,
+                               0,
+                               (int)attachments.length(), attachments,
+                               1, subpass,
+                               0, null);
+
+                       render_pass = device.vkCreateRenderPass(rp_info, null);
+               }
+       }
+
+       void init_framebuffer() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       HandleArray<VkImageView> attachments = VkImageView.createArray(2, frame);
+
+                       attachments.setAtIndex(1, depthView);
+
+                       VkFramebufferCreateInfo fb_info = VkFramebufferCreateInfo.create(frame,
+                               0,
+                               render_pass,
+                               2,
+                               attachments,
+                               width,
+                               height,
+                               1);
+
+                       framebuffers = VkFramebuffer.createArray(chainImage.length(), (SegmentAllocator)scope);
+                       for (int i = 0; i < chainImage.size(); i++) {
+                               attachments.setAtIndex(0, chainImageView.get(i));
+                               framebuffers.setAtIndex(i, device.vkCreateFramebuffer(fb_info, null));
+                               System.out.printf("framebuffer[%d] = %s\n", i, framebuffers.getAtIndex(i));
+                       }
+               }
+       }
+
+       void init_vertexbuffer() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       vertex = init_buffer(Cube.data.length * 4, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(Cube.data));
+
+                       vertexBuffer.setAtIndex(0, vertex.buffer);
+
+                       /* ***************************************** */
+                       vi_binding.setBinding(0);
+                       vi_binding.setInputRate(VK_VERTEX_INPUT_RATE_VERTEX);
+                       vi_binding.setStride(Cube.dataStride);
+
+                       vi_attribs.setBinding(0);
+                       vi_attribs.setLocation(0);
+                       vi_attribs.setFormat(VK_FORMAT_R32G32B32A32_SFLOAT);
+                       vi_attribs.setOffset(0);
+                       vi_attribs.setBinding(1, 0);
+                       vi_attribs.setLocation(1, 1);
+                       vi_attribs.setFormat(1, VK_FORMAT_R32G32B32A32_SFLOAT);
+                       vi_attribs.setOffset(1, 16);
+               }
+       }
+
+       void init_pipeline() throws Exception {
+               int res;
+               try (Frame frame = Frame.frame()) {
+                       IntArray dynamicStateEnables = IntArray.create(frame,
+                               VK_DYNAMIC_STATE_VIEWPORT,
+                               VK_DYNAMIC_STATE_SCISSOR);
+
+                       VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo.create(frame,
+                               0, dynamicStateEnables.size(), dynamicStateEnables);
+
+                       VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.create(frame,
+                               0,
+                               1, vi_binding,
+                               2, vi_attribs);
+
+                       VkPipelineInputAssemblyStateCreateInfo ia = VkPipelineInputAssemblyStateCreateInfo.create(frame,
+                               0,
+                               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+                               0);
+
+                       VkPipelineRasterizationStateCreateInfo rs = VkPipelineRasterizationStateCreateInfo.create(frame,
+                               0,
+                               VK_TRUE,
+                               VK_FALSE,
+                               VK_POLYGON_MODE_FILL,
+                               VK_CULL_MODE_BACK_BIT,
+                               VK_FRONT_FACE_CLOCKWISE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f,
+                               0.0f,
+                               1.0f);
+
+                       VkPipelineColorBlendAttachmentState att_state = VkPipelineColorBlendAttachmentState.create(frame,
+                               VK_FALSE,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_FACTOR_ZERO,
+                               VK_BLEND_OP_ADD,
+                               0xf);
+
+                       VkPipelineColorBlendStateCreateInfo cb = VkPipelineColorBlendStateCreateInfo.create(frame,
+                               0,
+                               VK_FALSE,
+                               VK_LOGIC_OP_NO_OP,
+                               1, att_state,
+                               1.0f, 1.0f, 1.0f, 1.0f);
+
+                       VkPipelineViewportStateCreateInfo vp = VkPipelineViewportStateCreateInfo.create(frame,
+                               0,
+                               1, null,
+                               1, null);
+
+                       VkPipelineDepthStencilStateCreateInfo ds = VkPipelineDepthStencilStateCreateInfo.create(frame,
+                               0,
+                               VK_TRUE,
+                               VK_TRUE,
+                               VK_COMPARE_OP_LESS_OR_EQUAL,
+                               VK_FALSE,
+                               VK_FALSE,
+                               0.0f,
+                               0.0f);
+                       VkStencilOpState back = ds.getBack();
+
+                       back.setFailOp(VK_STENCIL_OP_KEEP);
+                       back.setPassOp(VK_STENCIL_OP_KEEP);
+                       back.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       back.setCompareMask(0);
+                       back.setReference(0);
+                       back.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       back.setWriteMask(0);
+
+                       VkStencilOpState front = ds.getFront();
+
+                       front.setFailOp(VK_STENCIL_OP_KEEP);
+                       front.setPassOp(VK_STENCIL_OP_KEEP);
+                       front.setCompareOp(VK_COMPARE_OP_ALWAYS);
+                       front.setCompareMask(0);
+                       front.setReference(0);
+                       front.setDepthFailOp(VK_STENCIL_OP_KEEP);
+                       front.setWriteMask(0);
+
+                       VkPipelineMultisampleStateCreateInfo ms = VkPipelineMultisampleStateCreateInfo.create(frame,
+                               0,
+                               NUM_SAMPLES,
+                               0, //.sampleShadingEnable = VK_FALSE,
+                               0.0f,
+                               null,
+                               0, //.alphaToCoverageEnable = VK_FALSE,
+                               0 //.alphaToOneEnable = VK_FALSE,
+                               );
+
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(frame,
+                               0,
+                               cube_vs.length() * 4,
+                               cube_vs);
+                       VkShaderModuleCreateInfo fsInfo = VkShaderModuleCreateInfo.create(frame,
+                               0,
+                               cube_fs.length() * 4,
+                               cube_fs);
+
+                       shader.setAtIndex(0, device.vkCreateShaderModule(vsInfo, null));
+                       shader.setAtIndex(1, device.vkCreateShaderModule(fsInfo, null));
+
+                       VkPipelineShaderStageCreateInfo shaderStages = VkPipelineShaderStageCreateInfo.createArray(2, (SegmentAllocator)scope);
+
+                       shaderStages.setStage(VK_SHADER_STAGE_VERTEX_BIT);
+                       shaderStages.setName(frame, "main");
+                       shaderStages.setModule(shader.get(0));
+
+                       shaderStages.setStage(1, VK_SHADER_STAGE_FRAGMENT_BIT);
+                       shaderStages.setName(frame, 1, "main");
+                       shaderStages.setModule(1, shader.get(1));
+
+                       VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(frame,
+                               0,
+                               2, shaderStages,
+                               vi,
+                               ia,
+                               null,
+                               vp,
+                               rs,
+                               ms,
+                               ds,
+                               cb,
+                               dynamicState,
+                               pipeline_layout,
+                               render_pass,
+                               0,
+                               null,
+                               0);
+
+                       res = device.vkCreateGraphicsPipelines(null, 1, pipeline, null, this.pipeline);
+
+                       VkSemaphoreCreateInfo seminfo = VkSemaphoreCreateInfo.create(frame, 0);
+                       chainSemaphore = device.vkCreateSemaphore(seminfo, null);
+
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame, 0);
+                       drawFence = device.vkCreateFence(fenceInfo, null);
+               }
+       }
+
+
+       void execute_begin_command_buffer() throws Exception {
+               /* DEPENDS on init_command() */
+               try (Frame frame = Frame.frame()) {
+                       VkCommandBufferBeginInfo cmd_buf_info = VkCommandBufferBeginInfo.create(frame,
+                               0,
+                               null);
+
+                       cmd.getAtIndex(0).vkBeginCommandBuffer(cmd_buf_info);
+               }
+       }
+
+       void execute_end_command_buffer() throws Exception {
+               cmd.getAtIndex(0).vkEndCommandBuffer();
+       }
+
+       final static long FENCE_TIMEOUT = 100000000;
+
+       void execute_queue_command_buffer() throws Exception {
+               int res;
+
+               /* Queue the command buffer for execution */
+               try (Frame frame = Frame.frame()) {
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame, 0);
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+                       fences.setAtIndex(0, device.vkCreateFence(fenceInfo, null));
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(frame,
+                               0, null,
+                               pipe_stage_flags,
+                               1, cmd,
+                               0, null);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       do {
+                               res = device.vkWaitForFences( 1, fences, 1, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       device.vkDestroyFence(fences.getAtIndex(0), null);
+               }
+       }
+
+       void cmd_viewport() {
+               try (Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkViewport viewport = VkViewport.create(frame,
+                               0, 0, width, height, 0.0f, 1.0f);
+                       cmd.vkCmdSetViewport(0, 1, viewport);
+               }
+       }
+
+       void cmd_scissors() {
+               try (Frame frame = Frame.frame()) {
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+                       VkRect2D scissor = VkRect2D.create(frame);
+                       VkExtent2D extent = scissor.getExtent();
+
+                       extent.setWidth(width);
+                       extent.setHeight(height);
+
+                       cmd.vkCmdSetScissor(0, 1, scissor);
+               }
+       }
+
+       void cmd_paint() throws Exception {
+               int res;
+               try (Frame frame = Frame.frame()) {
+                       int chainIndex;
+                       IntArray chainIndices = IntArray.createArray(1, frame);
+                       VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+
+                       device.vkAcquireNextImageKHR(chain, ~0L, chainSemaphore, null, chainIndices);
+                       chainIndex = chainIndices.getAtIndex(0);
+                       LongArray offsets = LongArray.createArray(1, frame);
+
+                       VkClearValue clear_values = VkClearValue.createArray(2, frame);
+                       FloatArray col = clear_values.getColor().getFloat32();
+                       col.setAtIndex(0, 0.2f);
+                       col.setAtIndex(1, 0.2f);
+                       col.setAtIndex(2, 0.2f);
+                       col.setAtIndex(3, 0.2f);
+                       VkClearDepthStencilValue depthStencil = clear_values.getDepthStencil(1);
+                       depthStencil.setDepth(1.0f);
+                       depthStencil.setStencil(0);
+
+                       System.out.printf("render framebuffer[%d] = %s\n", chainIndex, framebuffers.getAtIndex(chainIndex));
+
+                       VkRenderPassBeginInfo rp_begin = VkRenderPassBeginInfo.create(frame,
+                               render_pass,
+                               framebuffers.getAtIndex(chainIndex),
+                               2,
+                               clear_values);
+                       VkExtent2D extent = rp_begin.getRenderArea().getExtent();
+                       extent.setWidth(width);
+                       extent.setHeight(height);
+
+                       cmd.vkCmdBeginRenderPass(rp_begin, VK_SUBPASS_CONTENTS_INLINE);
+
+                       cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.getAtIndex(0));
+                       cmd.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, NUM_DESCRIPTOR_SETS, desc_set, 0, null);
+                       cmd.vkCmdBindVertexBuffers(0, 1, vertexBuffer, offsets);
+
+                       cmd_viewport();
+                       cmd_scissors();
+
+                       cmd.vkCmdDraw(12 * 3, 1, 0, 0);
+                       cmd.vkCmdEndRenderPass();
+
+                       cmd.vkEndCommandBuffer();
+
+                       IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+                       HandleArray<VkSemaphore> semaphores = VkSemaphore.create(frame, chainSemaphore);
+
+                       VkSubmitInfo submit_info = VkSubmitInfo.create(frame,
+                               1, semaphores,
+                               pipe_stage_flags,
+                               1, this.cmd,
+                               0, null);
+
+                       HandleArray<VkFence> fences = VkFence.create(frame, drawFence);
+
+                       // Queue the command buffer for execution
+                       device.vkResetFences(1, fences);
+
+                       graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+                       // Make sure command buffer is finished before presenting
+                       do {
+                               res = device.vkWaitForFences(1, fences, VK_TRUE, FENCE_TIMEOUT);
+                       } while (res == VK_TIMEOUT);
+
+                       // Now present the image in the window
+                       HandleArray<VkSwapchainKHR> chains = VkSwapchainKHR.create(frame, chain);
+                       VkPresentInfoKHR present = VkPresentInfoKHR.create(frame,
+                               0, null,
+                               1, chains,
+                               chainIndices,
+                               null);
+
+                       present_queue.vkQueuePresentKHR(present);
+               }
+       }
+
+       /**
+        * Buffers are created in three steps:
+        * 1) create buffer, specifying usage and size
+        * 2) allocate memory based on memory requirements
+        * 3) bind memory
+        *
+        */
+       BufferMemory init_buffer(long dataSize, int usage, int properties, MemorySegment init) throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+                       VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(frame,
+                               0,
+                               dataSize,
+                               usage,
+                               VK_SHARING_MODE_EXCLUSIVE,
+                               0,
+                               null);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, null);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+                               req.getSize(),
+                               find_memory_type(memory_properties, req.getMemoryTypeBits(), properties));
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, null);
+
+                       if (init != null) {
+                               MemorySegment mem = device.vkMapMemory(memory, 0, dataSize, 0, scope);
+                               mem.copyFrom(init);
+                               device.vkUnmapMemory(memory);
+                       }
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory, dataSize);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyFence(drawFence, null);
+               device.vkDestroySemaphore(chainSemaphore, null);
+
+               device.vkDestroyPipeline(pipeline.getAtIndex(0), null);
+               for (int i=0;i<shader.size();i++)
+                       device.vkDestroyShaderModule(shader.getAtIndex(i), null);
+
+               vertex.free(device);
+               uniform.free(device);
+
+               for (int i=0;i<framebuffers.size();i++)
+                       device.vkDestroyFramebuffer(framebuffers.getAtIndex(i), null);
+
+               device.vkDestroyRenderPass(render_pass, null);
+
+               device.vkDestroyDescriptorPool(desc_pool, null);
+               device.vkDestroyPipelineLayout(pipeline_layout, null);
+               device.vkDestroyDescriptorSetLayout(desc_layout, null);
+
+               device.vkDestroyImageView(depthView, null);
+               device.vkFreeMemory(depthMemory, null);
+               device.vkDestroyImage(depthImage, null);
+
+               for (int i = 0; i < chainImageView.size(); i++)
+                       device.vkDestroyImageView(chainImageView.getAtIndex(i), null);
+
+               device.vkDestroySwapchainKHR(chain, null);
+
+               device.vkDestroyCommandPool(cmd_pool, null);
+               device.vkDestroyDevice(null);
+
+               instance.vkDestroySurfaceKHR(surface, null);
+
+               if (logger != null)
+                       instance.vkDestroyDebugUtilsMessengerEXT(logger, null);
+               instance.vkDestroyInstance(null);
+       }
+
+       IntArray loadSPIRV0(String name) throws IOException {
+               // hmm any way to just load this directly?
+               try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+                       ByteBuffer bb = ByteBuffer.allocateDirect(8192).order(ByteOrder.nativeOrder());
+                       int length = Channels.newChannel(is).read(bb);
+
+                       bb.position(0);
+                       bb.limit(length);
+
+                       return IntArray.create(MemorySegment.ofByteBuffer(bb));
+               }
+       }
+
+       IntArray loadSPIRV(String name) throws IOException {
+               try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+                       MemorySegment seg = ((SegmentAllocator)scope).allocateArray(Memory.INT, 2048);
+                       int length = Channels.newChannel(is).read(seg.asByteBuffer());
+
+                       return IntArray.create(seg.asSlice(0, length));
+               }
+       }
+       IntArray loadSPIRV(String name, SegmentAllocator alloc) throws IOException {
+               try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+                       MemorySegment seg = alloc.allocateArray(Memory.INT, 2048);
+                       int length = Channels.newChannel(is).read(seg.asByteBuffer());
+
+                       return IntArray.create(seg.asSlice(0, length));
+               }
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlags(i) & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) {
+               return (variant << 29) | (major << 22) | (minor << 12) | patch;
+       }
+
+       static int clampi(int v, int min, int max) {
+               return v < min ? min : v < max ? v : max;
+       }
+
+       void init_matrices() {
+               float eye[] = new float[] {-5, 3, -10};
+               float centre[] = new float[] {0, 0, 0};
+               float up[] = new float[] {0, -1, 0};
+               float t0[] = new float[16], t1[] = new float[16];
+
+               perspective(projection, (float)(Math.PI * 0.25), 1.0f, 0.1f, 100.0f);
+               lookAt(view, eye, centre, up);
+               identity4f(model);
+               mult4x4f(t0, clip, projection);
+               mult4x4f(t1, t0, view);
+               mult4x4f(mvp, t1, model);
+       }
+
+       void demo() throws Exception {
+               cube_vs = loadSPIRV("cube_vs.bin");
+               cube_fs = loadSPIRV("cube_fs.bin");
+
+               init_matrices();
+
+               init_instance();
+               init_debug();
+
+               init_surface();
+               init_device();
+               init_device_queue();
+               init_command();
+               init_depth();
+               init_uniform();
+
+               init_descriptor();
+               init_render();
+               init_framebuffer();
+               init_vertexbuffer();
+               init_pipeline();
+
+               execute_begin_command_buffer();
+
+               cmd_paint();
+
+               System.out.println("behold the prize!");
+               Thread.sleep(2000);
+
+               shutdown();
+       }
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+               System.loadLibrary("X11");
+
+               new TestCube().demo();
+       }
+}
@@ -60,20 +60,9 @@ import jdk.incubator.foreign.MemoryLayout.PathElement;
 import au.notzed.nativez.*;
 
 import vulkan.*;
+import static vulkan.VkConstants.*;
 
-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 {
+public class TestMandelbrot {
        static final boolean debug = true;
        ResourceScope scope = ResourceScope.newSharedScope();
 
@@ -168,7 +157,7 @@ public class TestVulkan {
 
                                // TODO: change to return the allocated array directly
                                dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, null);
-                               famprops = VkQueueFamilyProperties.createArray(frame, count$h.getAtIndex(0));
+                               famprops = VkQueueFamilyProperties.createArray(count$h.getAtIndex(0), frame);
                                dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, famprops);
 
                                int family_count = count$h.getAtIndex(0);
@@ -176,9 +165,9 @@ public class TestVulkan {
                                for (int j=0;j<family_count;j++) {
                                        int score = 0;
 
-                                       if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_COMPUTE_BIT) != 0)
+                                       if ((famprops.getQueueFlags(j) & VK_QUEUE_COMPUTE_BIT) != 0)
                                                score += 1;
-                                       if ((famprops.getQueueFlags(j) & VkQueueFlagBits.VK_QUEUE_GRAPHICS_BIT) == 0)
+                                       if ((famprops.getQueueFlags(j) & VK_QUEUE_GRAPHICS_BIT) == 0)
                                                score += 1;
 
                                        if (score > best) {
@@ -436,7 +425,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 +535,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 +547,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());
 
@@ -614,6 +603,6 @@ public class TestVulkan {
        public static void main(String[] args) throws Throwable {
                System.loadLibrary("vulkan");
 
-               new TestVulkan().demo();
+               new TestMandelbrot().demo();
        }
 }
index 7b6d5e0..a9ac4f1 100644 (file)
@@ -10,7 +10,7 @@ ResourceScope deviceScope = ResourceScope.newSharedScope();
                        Memory.POINTER.withName("pAllocateInfo"),
                        Memory.POINTER.withName("pCommandBuffers")));
        /**
-        * VkResult vkAllocateCommandBuffers ( VkDevice const VkCommandBufferAllocateInfo* VkCommandBuffer* )
+        * VkConstants vkAllocateCommandBuffers ( VkDevice const VkCommandBufferAllocateInfo* VkCommandBuffer* )
         * Success Codes: VK_SUCCESS
         *   Error Codes: VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY
         */
@@ -25,7 +25,7 @@ ResourceScope deviceScope = ResourceScope.newSharedScope();
                                (Addressable)address(),
                                (Addressable)Memory.address(pAllocateInfo),
                                (Addressable)Memory.address(pCommandBuffers));
-                       if (res$code == VkResult.VK_SUCCESS) return pCommandBuffers;
+                       if (res$code == VkConstants.VK_SUCCESS) return pCommandBuffers;
                } catch (Throwable t) { throw new RuntimeException(t); }
                throw new Exception(String.format("failcode %d", res$code));
        }
@@ -41,7 +41,7 @@ ResourceScope deviceScope = ResourceScope.newSharedScope();
                        Memory.INT.withName("flags"),
                        Memory.POINTER.withName("ppData")));
        /**
-        * VkResult vkMapMemory ( VkDevice VkDeviceMemory VkDeviceSize VkDeviceSize VkMemoryMapFlags void** )
+        * VkConstants vkMapMemory ( VkDevice VkDeviceMemory VkDeviceSize VkDeviceSize VkMemoryMapFlags void** )
         * Success Codes: VK_SUCCESS
         *   Error Codes: VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_MEMORY_MAP_FAILED
         */
@@ -56,7 +56,7 @@ ResourceScope deviceScope = ResourceScope.newSharedScope();
                                (long)size,
                                (int)flags,
                                (Addressable)Memory.address(ppData));
-                       if (res$code == VkResult.VK_SUCCESS) return MemorySegment.ofAddress(ppData.getAtIndex(Memory.POINTER, 0), size, scope);
+                       if (res$code == VkConstants.VK_SUCCESS) return MemorySegment.ofAddress(ppData.getAtIndex(Memory.POINTER, 0), size, scope);
                } catch (Throwable t) { throw new RuntimeException(t); }
                throw new Exception(String.format("failcode %d", res$code));
        }
index b2de68a..0894d69 100644 (file)
@@ -10,7 +10,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                        Memory.POINTER.withName("pPhysicalDeviceCount"),
                        Memory.POINTER.withName("pPhysicalDevices")));
        /**
-        * VkResult vkEnumeratePhysicalDevices ( VkInstance uint32_t* VkPhysicalDevice* )
+        * VkConstants vkEnumeratePhysicalDevices ( VkInstance uint32_t* VkPhysicalDevice* )
         */
        public HandleArray<VkPhysicalDevice> vkEnumeratePhysicalDevices()throws Exception {
                int res$code;
@@ -22,7 +22,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                                (Addressable)count.address(),
                                (Addressable)MemoryAddress.NULL);
 
-                       if (res$code == VkResult.VK_SUCCESS) {
+                       if (res$code == VkConstants.VK_SUCCESS) {
                                HandleArray<VkPhysicalDevice> devices =
                                        HandleArray.create(
                                                MemorySegment.allocateNative(count.get(Memory.INT, 0) * Memory.POINTER.byteSize(), Memory.POINTER.bitAlignment(), instanceScope),
@@ -33,7 +33,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                                        (Addressable)count.address(),
                                        (Addressable)devices.address());
 
-                               if (res$code == VkResult.VK_SUCCESS)
+                               if (res$code == VkConstants.VK_SUCCESS)
                                        return devices;
                        }
                } catch (Throwable t) { throw new RuntimeException(t); }
@@ -48,7 +48,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                        Memory.POINTER.withName("pPhysicalDeviceGroupCount"),
                        Memory.POINTER.withName("pPhysicalDeviceGroupProperties")));
        /**
-        * VkResult vkEnumeratePhysicalDeviceGroups ( VkInstance uint32_t* VkPhysicalDeviceGroupProperties* )
+        * VkConstants vkEnumeratePhysicalDeviceGroups ( VkInstance uint32_t* VkPhysicalDeviceGroupProperties* )
         */
        public VkPhysicalDeviceGroupProperties vkEnumeratePhysicalDeviceGroups() throws Exception {
                int res$code;
@@ -60,7 +60,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                                (Addressable)count.address(),
                                (Addressable)MemoryAddress.NULL);
 
-                       if (res$code == VkResult.VK_SUCCESS) {
+                       if (res$code == VkConstants.VK_SUCCESS) {
                                VkPhysicalDeviceGroupProperties properties =
                                        new VkPhysicalDeviceGroupProperties(
                                                MemorySegment.allocateNative(count.get(Memory.INT, 0) * VkPhysicalDeviceGroupProperties.LAYOUT.byteSize(), 64, instanceScope),
@@ -71,7 +71,7 @@ ResourceScope instanceScope = ResourceScope.newSharedScope();
                                        (Addressable)count.address(),
                                        (Addressable)properties.address());
 
-                               if (res$code == VkResult.VK_SUCCESS)
+                               if (res$code == VkConstants.VK_SUCCESS)
                                        return properties;
                        }
 
diff --git a/src/notzed.vkregistry/gen/cube.frag b/src/notzed.vkregistry/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/gen/cube.vert b/src/notzed.vkregistry/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;
+}
index 375dff2..35c6128 100755 (executable)
@@ -7,6 +7,7 @@
 # TODO: things that take a Memory.*Array probably don't need to also take a count, if one is defined in the registry
 # TODO: the api constants, where to?
 # TODO: vkDestroyDevice and vkDestroyInstance should close the ResourceScope they have
+# TODO: inlined array accessor should use $SH method to get segment
 
 use Data::Dumper;
 use File::Path qw(make_path);
@@ -50,6 +51,14 @@ my %accessMode = (
     'VkCommandBufferAllocateInfo' => 3
     );
 
+# functions which return last argument via a *
+# most are auto-detected in the setup phase
+my %functionCreate = (
+       # don't really help much
+       #vkAcquireNextImageKHR => { create => 1, static => 0 },
+       #vkAcquireNextImage2KHR => { create => 1, static => 0 }
+       );
+
 
 # unused but this documents all INSTANCE creation functions
 %dynamicInit = (
@@ -177,6 +186,11 @@ sub addRequire {
     }
 }
 
+# ##################################################################### #
+
+# convert the shitty xml loader format into something easier to use
+# ... types
+
 foreach $x (grep  { $_->isa('types') } @{$registry->{Kids}}) {
     foreach $t (grep { $_->isa('type') } @{$x->{Kids}}) {
                if (!defined($t->{alias})) {
@@ -299,6 +313,7 @@ foreach $x (grep  { $_->isa('types') } @{$registry->{Kids}}) {
     }
 }
 
+# ... enums
 foreach $x (grep  { $_->isa('enums') } @{$registry->{Kids}}) {
     if ($x->{type} eq "enum") {
                my @members = ();
@@ -356,6 +371,7 @@ foreach $x (grep  { $_->isa('enums') } @{$registry->{Kids}}) {
     }
 }
 
+# ... commands
 foreach $x (grep  { $_->isa('commands') } @{$registry->{Kids}}) {
     foreach $y (grep  { $_->isa('command') } @{$x->{Kids}}) {
                if (!defined($y->{alias})) {
@@ -392,6 +408,8 @@ foreach $x (grep  { $_->isa('commands') } @{$registry->{Kids}}) {
     }
 }
 
+# parse feature descriptions
+
 foreach $x (grep  { $_->isa('feature') } @{$registry->{Kids}}) {
     my %feature = (
                api => $x->{api},
@@ -410,6 +428,7 @@ foreach $x (grep  { $_->isa('feature') } @{$registry->{Kids}}) {
     push @features, \%feature;
 }
 
+# and extensions
 foreach $x (grep  { $_->isa('extensions') } @{$registry->{Kids}}) {
     foreach $y (grep  { $_->isa('extension') } @{$x->{Kids}}) {
                my %extension = (
@@ -621,6 +640,7 @@ foreach $y (keys %functionSets) {
     }
 }
 
+# ##################################################################### #
 
 # TODO: unused
 sub findType {
@@ -779,7 +799,7 @@ sub functionJavaType {
                if (defined($type->{name})) {
                        return $type->{name};
                } else {
-                       return "MemorySegment";
+                       return "Addressable";
                }
     } else {
                # FIXME: This loops to handle a double-alias for some flag types
@@ -1081,6 +1101,10 @@ sub formatCreatePrototype {
     # TODO: don't include return type if it's success
     #$desc = ($cmd->{successcodes} eq 'VK_SUCCESS') ? $desc = 'void' : $jrtype;
     $desc = $params[$#params]->{baseType};
+
+       # HACK: for integer returns
+       $desc = 'int' if $desc eq 'uint32_t';
+
     $desc .= " $proto->{name}(";
 
     # TODO: static or not
@@ -1190,7 +1214,7 @@ sub exportFunction {
     if ($cmd->{successcodes}) {
                my $returnStatement = ($cmd->{successcodes} eq 'VK_SUCCESS') ? 'return' : 'return res$code';
                foreach $r (split /,/, $cmd->{successcodes}) {
-                       print $f "\t\t\tif (res\$code == VkResult.$r) $returnStatement;\n";
+                       print $f "\t\t\tif (res\$code == VkConstants.$r) $returnStatement;\n";
                }
     } elsif ($jrtype ne "void") {
                print $f "\t\t\treturn res\$code;\n";
@@ -1304,7 +1328,7 @@ sub exportCreateFunction {
 
     # FIXME: see vkCreateGraphicsPipelines multiple 'success' types
     # This might cause leaks?
-    print $f "\t\t\tif (res\$code == VkResult.VK_SUCCESS)\n\t" if ($jrtype ne 'void');
+    print $f "\t\t\tif (res\$code == VkConstants.VK_SUCCESS)\n\t" if ($jrtype ne 'void');
 
     # initialise extension function tables if required
     if ($data{$instanceType}->{type} eq "VK_DEFINE_HANDLE") {
@@ -1315,8 +1339,13 @@ sub exportCreateFunction {
                } else {
                        print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0), instanceDispatch, deviceDispatch);\n";
                }
-    } else {
+    } elsif ($data{$instanceType}->{type} eq 'VK_DEFINE_NON_DISPATCHABLE_HANDLE') {
                print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0));\n";
+    } elsif ($instanceType eq 'uint32_t') {
+               print $f "\t\t\treturn instance\$h.get(Memory.INT, 0);\n";
+       } else {
+               print "Unhandled return type for 'constructor' method\n".Dumper($instanceType);
+               die;
     }
 
     print $f "\t\t} catch (Throwable t) { throw new RuntimeException(t); }\n";
@@ -1464,6 +1493,7 @@ sub formatLayout {
     if ($s->{category} eq 'struct') {
                my $sizeof = 0;
                my $index = 0;
+               my $minAlign = 8;
 
                $layout = 'MemoryLayout.structLayout(';
                for $m (@{$s->{members}}) {
@@ -1477,6 +1507,8 @@ sub formatLayout {
                                $sizeof += $pad;
                        }
 
+                       $minAlign = $align if $align > $minAlign;
+
                        #if ($type =~ m/\.LAYOUT$/) {
                        {
                                if ($m->{fullType} =~ m/\[(\d+)\]/) {
@@ -1494,7 +1526,7 @@ sub formatLayout {
                        $layout .= $type.".withName(\"$m->{name}\")";
                        $sizeof += $size;
                }
-               my $pad = $sizeof & 63;
+               my $pad = $sizeof & ($minAlign - 1);
                $layout .= ',' if ($pad);
                $layout .= "MemoryLayout.paddingLayout($pad)" if ($pad);
                $layout .= ')';
@@ -1608,7 +1640,6 @@ foreach $x (values %functionSets) {
 
 # look for all 'create' functions - last parameter is a writeable handle*
 # also work out which ones are static or not - first parameter is (dispatchable?) handle
-%functionCreate = ();
 foreach $x (values %functionSets) {
     foreach $y (@{$x}) {
                my $cmd = $commands{$y};
@@ -1702,7 +1733,7 @@ $baseDir = $targetDirectory.'/'.$baseDir;
 
 make_path($baseDir);
 
-# dump all structure (and enum?) types
+# dump all structure types
 foreach $x (sort keys %dump) {
     my $d = $dump{$x};
 
@@ -1746,22 +1777,30 @@ foreach $x (sort keys %dump) {
                print $f "\t$s->{name}(MemorySegment segment) {\n";
                print $f "\t\tthis.segment = segment;\n";
                if ($isTyped && $members[0]->{values}) {
-                       print $f "\t\tsegment.set(Memory.INT, 0, VkStructureType.$members[0]->{values});\n";
+                       print $f "\t\tsegment.set(Memory.INT, 0, VkConstants.$members[0]->{values});\n";
                }
                print $f "\t}\n";
 
                print $f "\tpublic final MemoryAddress address() { return segment.address(); }\n";
                print $f "\tpublic final ResourceScope scope() { return segment.scope(); }\n";
 
+               print $f "\tpublic String toString() { return String.format(\"$s->{name}\{ \$\%016x \}\", address().toRawLongValue()); }\n";
+
                # basic factory methods
                print $f "\tpublic static $s->{name} create(SegmentAllocator frame) {\n";
                print $f "\t\treturn new $s->{name}(frame.allocate(LAYOUT));\n";
                print $f "\t}\n\n";
 
                if (!$isTyped) {
-                       print $f "\tpublic static $s->{name} createArray(SegmentAllocator frame, long count) {\n";
+                       print $f "\tpublic static $s->{name} createArray(long count, SegmentAllocator frame) {\n";
                        print $f "\t\treturn new $s->{name}(frame.allocateArray(LAYOUT, count));\n";
                        print $f "\t}\n\n";
+               } else {
+                       print $f "\tpublic static $s->{name} createArray(long count, SegmentAllocator frame) {\n";
+                       print $f "\t\t$s->{name} self = new $s->{name}(frame.allocateArray(LAYOUT, count));\n";
+                       print $f "\t\tfor (long i=1;i<count;i++) self.segment.set(Memory.INT, i * LAYOUT.byteSize(), VkConstants.$members[0]->{values});\n";
+                       print $f "\t\treturn self;\n";
+                       print $f "\t}\n\n";
                }
 
                print $f "\tpublic static $s->{name} create(ResourceScope scope) {\n";
@@ -1769,7 +1808,7 @@ foreach $x (sort keys %dump) {
                print $f "\t}\n\n";
 
                if (!$isTyped) {
-                       print $f "\tpublic static $s->{name} createArray(ResourceScope scope, long count) {\n";
+                       print $f "\tpublic static $s->{name} createArray(long count, ResourceScope scope) {\n";
                        print $f "\t\treturn new $s->{name}(MemorySegment.allocateNative(LAYOUT.byteSize() * count, scope));\n";
                        print $f "\t}\n\n";
                }
@@ -1991,11 +2030,11 @@ foreach $x (sort keys %dump) {
                                                print $f "\t\t$src\$VH.set(seg, value != null ? value.length : 0);\n";
                                        }
                                } elsif (defined($data{$jtype})) {
-                                       print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+                                       print $f "\t\t$m->{name}\$VH.set(seg, Memory.address(value));\n";
                                } elsif ($jtype =~ m/Array$|^HandleArray/) {
-                                       print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+                                       print $f "\t\t$m->{name}\$VH.set(seg, Memory.address(value));\n";
                                } else {
-                                       print $f "\t\t$m->{name}\$VH.set(segment, value);\n";
+                                       print $f "\t\t$m->{name}\$VH.set(seg, value);\n";
                                }
                                print $f "\t}\n";
                        }
@@ -2077,6 +2116,9 @@ foreach $x (sort keys %dump) {
                        # embedded structs are handled as offsets in the accessors
                        if (functionDescriptorType($m) =~ m/\.LAYOUT$/) {
                                print $f "\t// static final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
+                       } elsif ($m->{fullType} =~ m/\[.+\]/) {
+                               print $f "\tstatic final MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement());\n";
+
                        } else {
                                print $f "\tstatic final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
                        }
@@ -2084,68 +2126,6 @@ foreach $x (sort keys %dump) {
 
                print $f "}\n";
                close($f);
-    } elsif ($s->{category} eq "enum" || $s->{category} eq 'enum:bitmask') {
-               my @members = @{$s->{members}};
-               my $type = calcSize($s) == 32 ? "int" : "long";
-               my $L = $type eq "long" ? "L" : "";
-               my %seen = ();
-
-               open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
-
-               print $f "package $targetPackage;\n";
-               print $f "import au.notzed.nativez.Memory;\n";
-               print $f "public class $s->{name} {\n";
-
-               my $tries = 0;
-               do {
-                       my @later = ();
-                       foreach $m (@members) {
-                               next if ($seen{$m->{name}});
-
-                               if (defined($m->{value})) {
-                                       print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
-                                       print $f "\tpublic static final $type $m->{name} = $m->{value}$L;\n";
-                                       $seen{$m->{name}} = $m;
-                               } elsif (defined($m->{alias})) {
-                                       if ($seen{$m->{alias}}) {
-                                               print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
-                                               print $f "\tpublic static final $type $m->{name} = $m->{alias};\n";
-                                               $seen{$m->{name}} = $m;
-                                       } else {
-                                               push @later, $m;
-                                       }
-                               } else {
-                                       print Dumper($s);
-                                       print Dumper($m);
-                                       die();
-                               }
-                       }
-                       @members = (@later);
-               } while ($#members >= 0 && $tries++ < 5);
-
-               if ($#members >= 0) {
-                       print Dumper($s);
-                       print "Left over\n";
-                       foreach $m (@members) {
-                               print Dumper($m);
-                       }
-                       print "seen\n";
-                       foreach $m (keys %seen) {
-                               print Dumper($seen{$m});
-                       }
-                       die("unable to resolve aliases");
-               }
-
-               # FIXME: this shouldn't be necessary, the other generators should just use the right primitive types
-               if ($type eq "int") {
-                       print $f "final static jdk.incubator.foreign.ValueLayout.OfInt LAYOUT = Memory.INT;\n";
-               } else {
-                       print $f "final static jdk.incubator.foreign.ValueLayout.OfLong LAYOUT = Memory.LONG;\n";
-               }
-
-               print $f "}\n";
-               close($f);
-
     } elsif ($s->{category} eq 'handle') {
                open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
 
@@ -2159,6 +2139,8 @@ foreach $x (sort keys %dump) {
 
                print $f "\tfinal MemoryAddress address;\n";
 
+               print $f "\tpublic String toString() { return String.format(\"$s->{name}\{ \$\%016x \}\", address().toRawLongValue()); }\n";
+
                # instances use a different constructor
                if ($s->{type} eq 'VK_DEFINE_HANDLE') {
                        print $f "\tfinal DispatchInstance instanceDispatch;\n";
@@ -2216,6 +2198,10 @@ foreach $x (sort keys %dump) {
                        print $f "\t\treturn HandleArray.createArray(count, frame, (a,s) -> $s->{name}.create(a));\n";
                        print $f "\t}\n\n";
 
+                       print $f "\tpublic static HandleArray<$s->{name}> create(SegmentAllocator frame, $s->{name} ... values) {\n";
+                       print $f "\t\treturn HandleArray.create(frame, (a,s) -> $s->{name}.create(a), values);\n";
+                       print $f "\t}\n\n";
+
                        #print $f "\tpublic static HandleArray<$s->{name}> createArray(ResourceScope scope, long count) {\n";
                        #print $f "\t\treturn new HandleArray<>($s->{name}::new, MemorySegment.allocateNative(Memory.POINTER.byteSize() * count, Memory.POINTER.byteAlignment(), scope));\n";
                        #print $f "\t}\n\n";
@@ -2321,6 +2307,93 @@ foreach $x (sort keys %dump) {
 
 }
 
+# dump all enums in one class otherwise it's too much of a headfuck to use
+open(my $f, ">", "$baseDir/VkConstants.java") || die("unable to open $baseDir/VkConstants.java");
+
+print $f "package $targetPackage;\n";
+print $f "import au.notzed.nativez.Memory;\n";
+print $f "public class VkConstants {\n";
+
+# first dump the api constants, these have limited types but need mapping to java format
+print $f "\t// API Constants\n";
+foreach $x (values %apiConstants) {
+       my $v = $x->{value};
+
+       $v =~ s/LL//;
+       $v =~ s/U//;
+
+       print $f "\tpublic static final $typeToJavaPrimitive{$x->{type}} $x->{name} = $v;\n";
+}
+
+foreach $x (sort keys %dump) {
+    my $d = $dump{$x};
+
+    next if $toDrop{$d->{name}};
+    next if ($d->{category} eq 'command');
+
+    my $name = $d->{name};
+    my $s = $data{$name};
+
+       if ($s->{category} eq "enum" || $s->{category} eq 'enum:bitmask') {
+               my @members = @{$s->{members}};
+               my $type = calcSize($s) == 32 ? "int" : "long";
+               my $L = $type eq "long" ? "L" : "";
+               my %seen = ();
+
+               print $f "\t/* $s->{name} */\n";
+
+               my $tries = 0;
+               do {
+                       my @later = ();
+                       foreach $m (@members) {
+                               next if ($seen{$m->{name}});
+
+                               if (defined($m->{value})) {
+                                       print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+                                       print $f "\tpublic static final $type $m->{name} = $m->{value}$L;\n";
+                                       $seen{$m->{name}} = $m;
+                               } elsif (defined($m->{alias})) {
+                                       if ($seen{$m->{alias}}) {
+                                               print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+                                               print $f "\tpublic static final $type $m->{name} = $m->{alias};\n";
+                                               $seen{$m->{name}} = $m;
+                                       } else {
+                                               push @later, $m;
+                                       }
+                               } else {
+                                       print Dumper($s);
+                                       print Dumper($m);
+                                       die();
+                               }
+                       }
+                       @members = (@later);
+               } while ($#members >= 0 && $tries++ < 5);
+
+               if ($#members >= 0) {
+                       print Dumper($s);
+                       print "Left over\n";
+                       foreach $m (@members) {
+                               print Dumper($m);
+                       }
+                       print "seen\n";
+                       foreach $m (keys %seen) {
+                               print Dumper($seen{$m});
+                       }
+                       die("unable to resolve aliases");
+               }
+
+               # FIXME: this shouldn't be necessary, the other generators should just use the right primitive types
+               #if ($type eq "int") {
+               #       print $f "final static jdk.incubator.foreign.ValueLayout.OfInt LAYOUT = Memory.INT;\n";
+               #} else {
+               #       print $f "final static jdk.incubator.foreign.ValueLayout.OfLong LAYOUT = Memory.LONG;\n";
+               #}
+       }
+}
+
+print $f "}\n";
+close($f);
+
 # ###################################################################### #
 # create extension handles
 my %byExtension = ();
index 9b120b3..e502e01 100644 (file)
@@ -1,8 +1,11 @@
 
 G=src/notzed.vkregistry/gen
 
-bin/status/notzed.vkregistry.classes: bin/status/notzed.vkregistry.export
-bin/status/notzed.vkregistry.classes: bin/modules/notzed.vkregistry/vulkan/test/mandelbrot.bin
+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: $(G)/export-vulkan $(G)/VkDevice-part.java $(G)/VkInstance-part.java
        $(G)/export-vulkan -t vulkan -d bin/gen/notzed.vkregistry/classes
@@ -12,3 +15,11 @@ bin/status/notzed.vkregistry.export: $(G)/export-vulkan $(G)/VkDevice-part.java
 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 $@ $<
diff --git a/src/notzed.xlib/classes/module-info.java b/src/notzed.xlib/classes/module-info.java
new file mode 100644 (file)
index 0000000..d424a8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.xlib {
+       requires notzed.nativez;
+
+       exports xlib;
+}
diff --git a/src/notzed.xlib/gen/gen.make b/src/notzed.xlib/gen/gen.make
new file mode 100644 (file)
index 0000000..922b827
--- /dev/null
@@ -0,0 +1,3 @@
+
+notzed.xlib_API = xlib
+notzed.xlib_APIFLAGS = -t xlib -Isrc/notzed.xlib/gen
diff --git a/src/notzed.xlib/gen/xlib.api b/src/notzed.xlib/gen/xlib.api
new file mode 100644 (file)
index 0000000..1cd19b5
--- /dev/null
@@ -0,0 +1,63 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+%include types.api;
+%include code.api;
+
+struct <default> default=all access=rw rename=s/^_// field:rename=studly-caps {
+       class rename=XClass;
+       visualid rename=VisualID;
+       xid rename=XID;
+}
+
+struct _XPrivDisplay {
+       screens array array-size=nscreens access=r;
+}
+
+struct Screen access=rw
+ template=code:class=struct-array
+{
+}
+
+#struct Visual {
+#       Class field:rename=XClass;
+#}
+
+#struct XVisualInfo {
+#       Class rename=XClass;
+#}
+
+library XLib {
+       define:xlib;
+       func:XCreateColormap;
+       func:XCreateWindow;
+       func:XFlush;
+       func:XGetVisualInfo;
+       func:XInitThreads;
+       func:XInternAtom;
+       func:XMapWindow;
+       func:XOpenDisplay;
+       func:XSelectInput;
+
+#      #define DefaultScreen(dpy)      (((_XPrivDisplay)(dpy))->default_screen)
+#      #define RootWindow(dpy, scr)    (ScreenOfDisplay(dpy,scr)->root)
+#   #define ScreenOfDisplay(dpy, scr)(&((_XPrivDisplay)(dpy))->screens[scr])
+
+       code:<inline> {{
+       public static int DefaultScreen(XDisplay dpy) {
+               XPrivDisplay pdpy = XPrivDisplay.create(dpy.address, dpy.scope);
+               return pdpy.getDefaultScreen();
+       }
+
+       public static long RootWindow(XDisplay dpy, int scr) {
+               XPrivDisplay pdpy = XPrivDisplay.create(dpy.address, dpy.scope);
+               Screen screen = pdpy.getScreens().getAtIndex(scr);
+               return screen.getRoot();
+       }
+
+       }}
+
+}
+
+define xlib xlib.h {
+       /Pixel$|^Input|^CW|Mask$|^Alloc/        include;
+}
diff --git a/src/notzed.xlib/gen/xlib.h b/src/notzed.xlib/gen/xlib.h
new file mode 100644 (file)
index 0000000..18f0d17
--- /dev/null
@@ -0,0 +1,2 @@
+
+#include <X11/Xutil.h>