From ca067c1ecf6205282fe38813858c8c143450797d Mon Sep 17 00:00:00 2001 From: Not Zed Date: Sat, 26 Mar 2022 22:29:26 +1030 Subject: [PATCH] Added vulkan cube demo. A few bug fixes and improvements in both code generators. --- Makefile | 12 +- .../classes/au/notzed/nativez/Frame.java | 3 +- .../au/notzed/nativez/HandleArray.java | 7 + src/notzed.nativez/lib/code.api | 10 +- src/notzed.nativez/lib/method.pm | 4 +- src/notzed.nativez/lib/types.api | 12 +- src/notzed.nativez/native/export.cc | 39 +- src/notzed.vkheader/gen/vkheader.api | 5 +- .../classes/module-info.java | 1 + .../VkPhysicalDeviceGroupProperties.java | 2 +- .../classes/vulkan/test/Cube.java | 55 + .../classes/vulkan/test/GLMaths.java | 131 ++ .../classes/vulkan/test/TestCube.java | 1135 +++++++++++++++++ .../{TestVulkan.java => TestMandelbrot.java} | 29 +- src/notzed.vkregistry/gen/VkDevice-part.java | 8 +- .../gen/VkInstance-part.java | 12 +- src/notzed.vkregistry/gen/cube.frag | 8 + src/notzed.vkregistry/gen/cube.vert | 14 + src/notzed.vkregistry/gen/export-vulkan | 223 ++-- src/notzed.vkregistry/gen/gen.make | 15 +- src/notzed.xlib/classes/module-info.java | 6 + src/notzed.xlib/gen/gen.make | 3 + src/notzed.xlib/gen/xlib.api | 63 + src/notzed.xlib/gen/xlib.h | 2 + 24 files changed, 1672 insertions(+), 127 deletions(-) create mode 100644 src/notzed.vkregistry/classes/vulkan/test/Cube.java create mode 100644 src/notzed.vkregistry/classes/vulkan/test/GLMaths.java create mode 100644 src/notzed.vkregistry/classes/vulkan/test/TestCube.java rename src/notzed.vkregistry/classes/vulkan/test/{TestVulkan.java => TestMandelbrot.java} (94%) create mode 100644 src/notzed.vkregistry/gen/cube.frag create mode 100644 src/notzed.vkregistry/gen/cube.vert create mode 100644 src/notzed.xlib/classes/module-info.java create mode 100644 src/notzed.xlib/gen/gen.make create mode 100644 src/notzed.xlib/gen/xlib.api create mode 100644 src/notzed.xlib/gen/xlib.h diff --git a/Makefile b/Makefile index 8d2d841..fafe48f 100644 --- 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 diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Frame.java b/src/notzed.nativez/classes/au/notzed/nativez/Frame.java index e45032e..560ba5a 100644 --- a/src/notzed.nativez/classes/au/notzed/nativez/Frame.java +++ b/src/notzed.nativez/classes/au/notzed/nativez/Frame.java @@ -29,7 +29,8 @@ import static jdk.incubator.foreign.ValueLayout.OfAddress; * * Any memory allocated is freed when the frame is closed. *

- * 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 { diff --git a/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java b/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java index 3f7d299..f7be38d 100644 --- a/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java +++ b/src/notzed.nativez/classes/au/notzed/nativez/HandleArray.java @@ -61,6 +61,13 @@ public class HandleArray extends AbstractList implements P return create(MemorySegment.ofAddress(address, size * Memory.POINTER.byteSize(), scope), create); } + public static HandleArray create(SegmentAllocator alloc, BiFunction create, T... values) { + HandleArray array = create(alloc.allocateArray(Memory.POINTER, values.length), create); + for (int i=0;i { // 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(); diff --git a/src/notzed.nativez/lib/method.pm b/src/notzed.nativez/lib/method.pm index 9cf6f24..2476803 100644 --- a/src/notzed.nativez/lib/method.pm +++ b/src/notzed.nativez/lib/method.pm @@ -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]); diff --git a/src/notzed.nativez/lib/types.api b/src/notzed.nativez/lib/types.api index 633d554..671a7bd 100644 --- a/src/notzed.nativez/lib/types.api +++ b/src/notzed.nativez/lib/types.api @@ -195,7 +195,7 @@ type /^u64:(?[ui]8)$/ select=segment copy= { # *i8 with no flag = String type /^u64:(?i8)$/ copy= { 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:(?[uif]\d+)$/ copy= { tojavai {{ "({typei}){value}" }} } +# *type struct? handle? with size +# maybe should be handled by getbyvalue +type /^u64:\$\{(\w+)\}$/ select=array-size copy= { + 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= { type {{ "$data->{$m->{type}}->{rename}" }} @@ -222,7 +230,7 @@ type /^u64:\$\{(\w+)\}$/ copy= { # *void with size type /^u64:v$/ select=array-size copy= { type {{ 'MemorySegment' }} - tojava {{ "MemorySegment.ofAddress({value}, $m->{'array-size'}->{name}, {scope})" }} + tojava {{ "MemorySegment.ofAddress({value}, $m->{'array-size'}->{name} * 8, {scope})" }} } # *void diff --git a/src/notzed.nativez/native/export.cc b/src/notzed.nativez/native/export.cc index ac87eb3..d8b183b 100644 --- a/src/notzed.nativez/native/export.cc +++ b/src/notzed.nativez/native/export.cc @@ -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) diff --git a/src/notzed.vkheader/gen/vkheader.api b/src/notzed.vkheader/gen/vkheader.api index 2c67a19..4c93262 100644 --- a/src/notzed.vkheader/gen/vkheader.api +++ b/src/notzed.vkheader/gen/vkheader.api @@ -21,10 +21,13 @@ struct VkInstance_T { #}} } -struct // { +struct /Vk/ { func: instance:0 vkheader::matchObjectFunction; } +call /PFN_vk/ access=rw { +} + enum // { } diff --git a/src/notzed.vkregistry/classes/module-info.java b/src/notzed.vkregistry/classes/module-info.java index ccc933f..b8ea86d 100644 --- a/src/notzed.vkregistry/classes/module-info.java +++ b/src/notzed.vkregistry/classes/module-info.java @@ -1,6 +1,7 @@ module notzed.vkregistry { requires transitive notzed.nativez; + requires transitive notzed.xlib; requires java.desktop; diff --git a/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java b/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java index f3f596e..93b9838 100644 --- a/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java +++ b/src/notzed.vkregistry/classes/vulkan/VkPhysicalDeviceGroupProperties.java @@ -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 index 0000000..e8f824e --- /dev/null +++ b/src/notzed.vkregistry/classes/vulkan/test/Cube.java @@ -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 index 0000000..3414740 --- /dev/null +++ b/src/notzed.vkregistry/classes/vulkan/test/GLMaths.java @@ -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 index 0000000..6e7ec57 --- /dev/null +++ b/src/notzed.vkregistry/classes/vulkan/test/TestCube.java @@ -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 chainImage; + HandleArray chainImageView; + + int depthFormat; + VkImage depthImage; + VkImageView depthView; + VkDeviceMemory depthMemory; + + VkCommandPool cmd_pool; + HandleArray cmd; + + BufferMemory uniform; + VkPipelineLayout pipeline_layout; + + VkDescriptorSetLayout desc_layout; + VkDescriptorPool desc_pool; + HandleArray desc_set = VkDescriptorSet.createArray(1, (SegmentAllocator)scope); + + + VkRenderPass render_pass; + HandleArray framebuffers; + + BufferMemory vertex; + HandleArray 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 shader = VkShaderModule.createArray(2, (SegmentAllocator)scope); + + HandleArray 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 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 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 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 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 semaphores = VkSemaphore.create(frame, chainSemaphore); + + VkSubmitInfo submit_info = VkSubmitInfo.create(frame, + 1, semaphores, + pipe_stage_flags, + 1, this.cmd, + 0, null); + + HandleArray 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 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 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(); } } diff --git a/src/notzed.vkregistry/gen/VkDevice-part.java b/src/notzed.vkregistry/gen/VkDevice-part.java index 7b6d5e0..a9ac4f1 100644 --- a/src/notzed.vkregistry/gen/VkDevice-part.java +++ b/src/notzed.vkregistry/gen/VkDevice-part.java @@ -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)); } diff --git a/src/notzed.vkregistry/gen/VkInstance-part.java b/src/notzed.vkregistry/gen/VkInstance-part.java index b2de68a..0894d69 100644 --- a/src/notzed.vkregistry/gen/VkInstance-part.java +++ b/src/notzed.vkregistry/gen/VkInstance-part.java @@ -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 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 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 index 0000000..de24544 --- /dev/null +++ b/src/notzed.vkregistry/gen/cube.frag @@ -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 index 0000000..5d21e1e --- /dev/null +++ b/src/notzed.vkregistry/gen/cube.vert @@ -0,0 +1,14 @@ +#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout (std140, binding = 0) uniform bufferVals { + mat4 mvp; +} data; +layout (location = 0) in vec4 pos; +layout (location = 1) in vec4 inColor; +layout (location = 0) out vec4 outColor; + +void main() { + outColor = inColor; + gl_Position = data.mvp * pos; +} diff --git a/src/notzed.vkregistry/gen/export-vulkan b/src/notzed.vkregistry/gen/export-vulkan index 375dff2..35c6128 100755 --- a/src/notzed.vkregistry/gen/export-vulkan +++ b/src/notzed.vkregistry/gen/export-vulkan @@ -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{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 = (); diff --git a/src/notzed.vkregistry/gen/gen.make b/src/notzed.vkregistry/gen/gen.make index 9b120b3..e502e01 100644 --- a/src/notzed.vkregistry/gen/gen.make +++ b/src/notzed.vkregistry/gen/gen.make @@ -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 index 0000000..d424a8d --- /dev/null +++ b/src/notzed.xlib/classes/module-info.java @@ -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 index 0000000..922b827 --- /dev/null +++ b/src/notzed.xlib/gen/gen.make @@ -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 index 0000000..1cd19b5 --- /dev/null +++ b/src/notzed.xlib/gen/xlib.api @@ -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=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: {{ + 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 index 0000000..18f0d17 --- /dev/null +++ b/src/notzed.xlib/gen/xlib.h @@ -0,0 +1,2 @@ + +#include -- 2.39.2