From 45de080c7680a2e8616492a13114136559b4e681 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Tue, 26 Apr 2022 22:52:52 +0930 Subject: [PATCH] Some work on the header version of vulkan binding. It's still incomplete and probably at a dead end. --- src/notzed.nativez/bin/generate-api | 12 +- .../classes/au/notzed/nativez/Memory.java | 50 +- .../classes/au/notzed/nativez/Pointer.java | 3 + src/notzed.nativez/lib/api.pm | 40 +- src/notzed.nativez/lib/config.pm | 35 +- .../classes/module-info.java | 1 + .../classes/vulkan/test/Cube.java | 55 + .../classes/vulkan/test/GLMaths.java | 131 ++ .../classes/vulkan/test/TestCube.java | 1145 +++++++++++++++++ src/notzed.vkheader/classes/module-info.java | 1 + src/notzed.vkheader/gen/vkheader.api | 17 +- src/notzed.vkheader/gen/vkheader.h | 2 + src/notzed.vkheader/gen/vkheader.pm | 82 +- src/notzed.vkheader/gen/vulkan.pm | 20 +- 14 files changed, 1558 insertions(+), 36 deletions(-) create mode 100644 src/notzed.vkheader.test/classes/vulkan/test/Cube.java create mode 100644 src/notzed.vkheader.test/classes/vulkan/test/GLMaths.java create mode 100644 src/notzed.vkheader.test/classes/vulkan/test/TestCube.java diff --git a/src/notzed.nativez/bin/generate-api b/src/notzed.nativez/bin/generate-api index 9f35fa4..bec646d 100755 --- a/src/notzed.nativez/bin/generate-api +++ b/src/notzed.nativez/bin/generate-api @@ -25,7 +25,6 @@ use method; $SIG{__DIE__} = sub { Carp::confess( @_ ) }; $SIG{'INT'} = sub { Carp::confess() }; - $Data::Dumper::Indent = 1; my $apidef = "api.api"; @@ -139,11 +138,6 @@ sub formatItems { $res->{seen}->{"$_->{type}:$_->{name}"}++ == 0 } $api->findMatches($inc, $res->{ctx}); - if ($obj->{name} eq 'VkInstance') { - print Dumper($obj, $inc, \@list); - die; - } - if ($inc->{type} eq 'func' && $inc->{literal}) { push @{$res->{func}}, $inc->{literal}; } elsif ($inc->{type} eq 'func') { @@ -217,11 +211,12 @@ sub formatStruct { my $accessors = join "\n", map { my ($m, $type, $match) = ($_->{field}, $_->{type}, $_->{match}); map { - my $accessor = $api->{index}->{$_}; + my $tname = $_; + my $accessor = $api->{index}->{$tname}; map { code::applyTemplate($_, $match) } grep { $_ } map { api::findItem($accessor, $_) } map { my @list = (); - if ($_ =~ m/r/o) { + if ($_ =~ m/r/o || $tname eq 'code:getbyvalue') { push @list, 'get'; push @list, 'geti' if $_ =~ m/i/o; } @@ -349,6 +344,7 @@ sub exportStructs { print "gen ".($#list+1)." $obj->{type} $obj->{name}\n" if ($api->{vars}->{verbose} > 0); foreach my $s (@list) { + next if !$s->{output}; next if $api->{output}->{"$s->{type}:$s->{name}"}; print " $s->{name}\n" if ($api->{vars}->{verbose} > 1); diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Memory.java b/src/notzed.nativez/classes/au/notzed/nativez/Memory.java index 1484708..d115205 100644 --- a/src/notzed.nativez/classes/au/notzed/nativez/Memory.java +++ b/src/notzed.nativez/classes/au/notzed/nativez/Memory.java @@ -106,18 +106,22 @@ public class Memory { } } - public static MemoryAddress address(Addressable v) { + public static Addressable address(Addressable v) { return v != null ? v.address() : MemoryAddress.NULL; } - public static MemoryAddress address(Pointer v) { + public static Addressable address(Pointer v) { return v != null ? v.address() : MemoryAddress.NULL; } - public static MemoryAddress address(FunctionPointer v) { + public static Addressable address(FunctionPointer v) { return v != null ? v.symbol().address() : MemoryAddress.NULL; } + //public static long length(Pointer v) { + // return v != null ? v.length() : 0; + //} + public static long length(List list) { return list != null ? list.size() : 0; } @@ -137,4 +141,44 @@ public class Memory { public static long size(MemorySegment s) { return s != null ? s.byteSize() : 0; } + + public static String[] copyStringArray(MemoryAddress list, long len) { + if (list != MemoryAddress.NULL) { + String[] array = new String[(int)len]; + for (int i = 0; i < array.length; i++) { + MemoryAddress fu = list.getAtIndex(Memory.POINTER, i); + array[i] = fu != MemoryAddress.NULL ? fu.getUtf8String(0) : null; + } + return array; + } else { + return null; + } + } + + public static MemorySegment copyStringArray(String[] array, SegmentAllocator alloc) { + if (array != null) { + MemorySegment list = alloc.allocateArray(Memory.POINTER, array.length); + for (int i = 0; i < array.length; i++) { + list.setAtIndex(Memory.POINTER, i, array[i] != null ? alloc.allocateUtf8String(array[i]) : MemoryAddress.NULL); + } + return list; + } else { + return Memory.NULL; + } + } + + public static String toString(MemorySegment s, GroupLayout layout) { + StringBuilder sb = new StringBuilder(); + sb.append(layout.name().get()); + sb.append(" "); + sb.append(s.address().toString()); + sb.append("\n"); + for (MemoryLayout m: layout.memberLayouts()) { + if (m instanceof jdk.incubator.foreign.ValueLayout) { + var vh = layout.varHandle(jdk.incubator.foreign.MemoryLayout.PathElement.groupElement(m.name().get())); + sb.append(" ").append(m.name().get()).append(": ").append(vh.get(s)).append("\n"); + } + } + return sb.toString(); + } } diff --git a/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java b/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java index cf93675..3fd6fda 100644 --- a/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java +++ b/src/notzed.nativez/classes/au/notzed/nativez/Pointer.java @@ -11,4 +11,7 @@ public interface Pointer { default ResourceScope scope() { return ResourceScope.globalScope(); } + //default long length() { + // return 1; + //} } diff --git a/src/notzed.nativez/lib/api.pm b/src/notzed.nativez/lib/api.pm index c758616..c3c520b 100644 --- a/src/notzed.nativez/lib/api.pm +++ b/src/notzed.nativez/lib/api.pm @@ -1,6 +1,7 @@ # TODO: define more & consistent stage processing hooks, currently implicit require init() and stage.postprocess # TODO: code: etc should probably instead be code: where is one of the template fields - methods, init, etc. +# TODO: calls should probably use dynamic invocation - downcall is not bound to address package api; @@ -125,6 +126,13 @@ sub new { } } + # create type indices + { + my $index = {}; + map { $index->{$_->{type}}->{$_->{name}} = $_ } values %{$self->{data}}; + $self->{databytype} = $index; + } + analyseAPI($self); preprocess($self); @@ -167,6 +175,19 @@ sub new { push @{$self->{types}}, initType($self, $copyIndex, $obj); } + # handle external references - rename and supress output + # Not sure of the best place to put this + foreach my $obj (grep { $_->{type} eq 'extern' } @{$self->{api}}) { + foreach my $inc (@{$obj->{items}}) { + my @list = findMatches($self, $inc, $obj); + + foreach my $s (@list) { + $s->{rename} = $obj->{name}.'.'.$inc->{"$s->{type}:rename"}->($s->{name}, $s); + $s->{output} = 0; + } + } + } + return $self; } @@ -307,16 +328,21 @@ sub findTemplate { } # find value of first option of the given name +# TBD: moved to config.pm sub optionValue { my $name = shift; my $or = shift; + #my $match = sub { $_ =~ m/^\Q$name\E=(.*)$/on ? $1 : undef }; + #print "optionValue $name\n"; foreach my $obj (@_) { foreach my $opt (@{$obj->{options}}) { #print "? $name $opt = ".($opt =~m/^\Q$name\E=(.*)$/)."\n"; #print " = $opt\n" if ($opt =~m/^\Q$name\E=(.*)$/); return $1 if ($opt =~m/^\Q$name\E=(.*)$/); + #my $x = $match->($opt); + #return $x if defined($x); } } $or; @@ -355,6 +381,7 @@ sub optionValuesAll { } # find first occurance of a flag +# TBD: moved to config.pm sub optionFlag { my $name = shift; @@ -467,17 +494,22 @@ sub findMatches { my $api = shift; my $inc = shift; my $ctx = shift; - my $data = $api->{data}; + my $type = $inc->{type}; + my $data = $api->{databytype}->{$type}; if ($inc->{matcher}) { - grep { $inc->{matcher}->($_, $ctx) } grep { $_->{type} eq $inc->{type} } values %$data; + #grep { $inc->{matcher}->($_, $ctx) } grep { $_->{type} eq $inc->{type} } values %$data; + grep { $inc->{matcher}->($_, $ctx) } values %$data; } else { - my $s = $data->{$inc->{match}}; + my $s = $api->{data}->{$inc->{match}}; if (defined($s)) { $s; } else { - map { $data->{$_} } grep { $_ =~ m/$inc->{regex}/ } keys %$data; + #map { $data->{$_} } grep { $_ =~ m/$inc->{regex}/ } keys %$data; + #map { $data->{$_} } grep { "$type:$_" =~ m/$inc->{regex}/ } keys %$data; + grep { "$_->{type}:$_->{name}" =~ m/$inc->{regex}/ } values %$data; + #grep { "$_->{type}:$_->{name}" =~ m/$inc->{regex}/ } values %{$api->{data}}; } } } diff --git a/src/notzed.nativez/lib/config.pm b/src/notzed.nativez/lib/config.pm index 6fb67a3..21a3973 100644 --- a/src/notzed.nativez/lib/config.pm +++ b/src/notzed.nativez/lib/config.pm @@ -40,6 +40,33 @@ sub new { return $self; } +# find first occurance of flag +# name, ...objects +sub optionFlag { + my $name = shift; + + foreach my $obj (@_) { + foreach my $opt (@{$obj->{options}}) { + return 1 if ($opt eq $name); + } + } + undef; +} + +# find value of first option of the given name +# name, ...objects +sub optionValue { + my $name = shift; + my $or = shift; + + foreach my $obj (@_) { + foreach my $opt (@{$obj->{options}}) { + return $1 if ($opt =~m/^\Q$name\E=(.*)$/); + } + } + $or; +} + sub findInclude { my $self = shift; my $file = shift; @@ -101,16 +128,18 @@ sub loadControlFile { die("$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}:$tokeniser->{colno}: expected match token"); } } elsif ($state == 2) { - # token token token* [ '{' | literal ] + # token token token* [ '{' | literal | ';' ] if ($t eq '{') { $state = 3; + } elsif ($t eq ';') { + $state = 0; } elsif ($tokeniser->{type} eq 'literal') { $target->{literal} = stripLiteral($t); - $state =0; + $state = 0; } elsif ($tokeniser->{type} eq 'token') { push @{$target->{options}}, $t; } else { - die("$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}:$tokeniser->{colno}: expecting { or {{literal}} or option: not '$t'"); + die("$tokeniser->{file}->{path}:$tokeniser->{file}->{lineno}:$tokeniser->{colno}: expecting {, ;, or {{literal}} or option: not '$t'"); } } elsif ($state == 3) { if ($t eq '}') { diff --git a/src/notzed.vkheader.test/classes/module-info.java b/src/notzed.vkheader.test/classes/module-info.java index 489e41b..47d607e 100644 --- a/src/notzed.vkheader.test/classes/module-info.java +++ b/src/notzed.vkheader.test/classes/module-info.java @@ -1,6 +1,7 @@ module notzed.vkheader.test { requires notzed.vkheader; + requires notzed.xlib; requires java.desktop; } diff --git a/src/notzed.vkheader.test/classes/vulkan/test/Cube.java b/src/notzed.vkheader.test/classes/vulkan/test/Cube.java new file mode 100644 index 0000000..e8f824e --- /dev/null +++ b/src/notzed.vkheader.test/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.vkheader.test/classes/vulkan/test/GLMaths.java b/src/notzed.vkheader.test/classes/vulkan/test/GLMaths.java new file mode 100644 index 0000000..3414740 --- /dev/null +++ b/src/notzed.vkheader.test/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.vkheader.test/classes/vulkan/test/TestCube.java b/src/notzed.vkheader.test/classes/vulkan/test/TestCube.java new file mode 100644 index 0000000..1433d08 --- /dev/null +++ b/src/notzed.vkheader.test/classes/vulkan/test/TestCube.java @@ -0,0 +1,1145 @@ + /* +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()) { + var cb = PFN_vkDebugUtilsMessengerCallbackEXT.upcall((severity, flags, data, dummy) -> { + 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, + null); + + logger = instance.vkCreateDebugUtilsMessengerEXT(info, null, scope); + } + + //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, scope); + 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, + window); + + surface = instance.vkCreateXlibSurfaceKHR(surfaceinfo, null, scope); + 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(frame, scope); + + // 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.getQueueFlagsAtIndex(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((SegmentAllocator)scope); + physicalDevice.vkGetPhysicalDeviceMemoryProperties(memory_properties); + device_features = VkPhysicalDeviceFeatures.create((SegmentAllocator)scope); + physicalDevice.vkGetPhysicalDeviceFeatures(device_features); + + FloatArray qpri = FloatArray.create(frame, 0.0f); + VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create( + frame, + 0, + graphics_queue, + qpri); + String [] extensions = { + "VK_KHR_swapchain" + }; + VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame); + features.setDepthClamp(1); + VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create( + frame, + 0, + qinfo, + null, + extensions, + features); + + device = physicalDevice.vkCreateDevice(devinfo, null, scope); + + 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.getFormatAtIndex(0) == VK_FORMAT_UNDEFINED) { + format = VK_FORMAT_B8G8R8A8_UNORM; + } else { + format = surfFormats.getFormatAtIndex(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. + null, + (surfCapabilities.getSupportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 + ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfCapabilities.getCurrentTransform(), + compositeAlpha, + VK_PRESENT_MODE_FIFO_KHR, + VK_TRUE, + null); + chaininfo.getImageExtent().setWidth(swapchainExtent.getWidth()); + chaininfo.getImageExtent().setHeight(swapchainExtent.getHeight()); + + chain = device.vkCreateSwapchainKHR(chaininfo, null, scope); + + 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, scope)); + } + + chainImageFormat = format; + } + } + + void init_device_queue() { + graphics_queue = device.vkGetDeviceQueue(graphics_queue_index, 0, scope); + if (graphics_queue_index == present_queue_index) { + present_queue = graphics_queue; + } else { + present_queue = device.vkGetDeviceQueue(present_queue_index, 0, scope); + } + } + + 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, scope); + + VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame, + cmd_pool, + VK_COMMAND_BUFFER_LEVEL_PRIMARY, + 1); + + cmd = VkCommandBuffer.createArray(instance, 1, (SegmentAllocator)scope, scope); + device.vkAllocateCommandBuffers(cmdinfo, cmd); + } + } + + // 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, + null, + VK_IMAGE_LAYOUT_UNDEFINED); + imageinfo.getExtent().setWidth(width); + imageinfo.getExtent().setHeight(height); + imageinfo.getExtent().setDepth(1); + + depthImage = device.vkCreateImage(imageinfo, null, scope); + + 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, scope); + + 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, scope); + + 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, + layout_binding); + + desc_layout = device.vkCreateDescriptorSetLayout(descriptor_layout, null, scope); + layout_table.setAtIndex(0, desc_layout); + + VkPipelineLayoutCreateInfo pipeline_info = VkPipelineLayoutCreateInfo.create(frame, + 0, + 1, + layout_table, + null); + + pipeline_layout = device.vkCreatePipelineLayout(pipeline_info, null, scope); + + VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 1); + + VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame, + 0, + 1, + type_count); + + desc_pool = device.vkCreateDescriptorPool(descriptor_pool, null, scope); + + 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.setFormatAtIndex(1, depthFormat); + attachments.setSamplesAtIndex(1, NUM_SAMPLES); + attachments.setLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_CLEAR); + attachments.setStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_STORE); + attachments.setStencilLoadOpAtIndex(1, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + attachments.setStencilStoreOpAtIndex(1, VK_ATTACHMENT_STORE_OP_DONT_CARE); + attachments.setInitialLayoutAtIndex(1, VK_IMAGE_LAYOUT_UNDEFINED); + attachments.setFinalLayoutAtIndex(1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + attachments.setFlagsAtIndex(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, + null, + color_reference, + null, + depth_reference, + null); + + VkRenderPassCreateInfo rp_info = VkRenderPassCreateInfo.create(frame, + 0, + //(int)attachments.length(), + attachments, + subpass, + null); + + render_pass = device.vkCreateRenderPass(rp_info, null, scope); + } + } + + 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, scope)); + 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.setBindingAtIndex(1, 0); + vi_attribs.setLocationAtIndex(1, 1); + vi_attribs.setFormatAtIndex(1, VK_FORMAT_R32G32B32A32_SFLOAT); + vi_attribs.setOffsetAtIndex(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); + + VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.create(frame, + 0, + vi_binding, + 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, + att_state, + new float[] { 1.0f, 1.0f, 1.0f, 1.0f }); + + VkPipelineViewportStateCreateInfo vp = VkPipelineViewportStateCreateInfo.create(frame, + 0, + //1, null, + //1, null); + null, + null); + // TODO: should just take counts + vp.setViewportCount(1); + vp.setScissorCount(1); + + + 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, scope)); + shader.setAtIndex(1, device.vkCreateShaderModule(fsInfo, null, scope)); + + VkPipelineShaderStageCreateInfo shaderStages = VkPipelineShaderStageCreateInfo.createArray(2, (SegmentAllocator)scope); + + shaderStages.setSType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); + shaderStages.setStage(VK_SHADER_STAGE_VERTEX_BIT); + shaderStages.setName("main"); + shaderStages.setModule(shader.get(0)); + + shaderStages.setSTypeAtIndex(1, VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO); + shaderStages.setStageAtIndex(1, VK_SHADER_STAGE_FRAGMENT_BIT); + shaderStages.setNameAtIndex(1, "main"); + shaderStages.setModuleAtIndex(1, shader.get(1)); + + VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(frame, + 0, + //2, shaderStages, + 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, scope); + + VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame, 0); + drawFence = device.vkCreateFence(fenceInfo, null, scope); + } + } + + + 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, scope)); + + // FIXME: why is this not autogenerating the length bits for all fields? + VkSubmitInfo submit_info = VkSubmitInfo.create(frame, + 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.getAtIndex(1).getDepthStencil(); + 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), + 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.createArray(1, frame);//, chainSemaphore, scope); + + semaphores.setAtIndex(0, chainSemaphore); + + VkSubmitInfo submit_info = VkSubmitInfo.create(frame, + semaphores, pipe_stage_flags, + 1, this.cmd, + 0, null); + + HandleArray fences = VkFence.createArray(1, frame); + + fences.setAtIndex(0, 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.createArray(1, frame); + chains.setAtIndex(0, chain); + VkPresentInfoKHR present = VkPresentInfoKHR.create(frame, + 0, null, + chains, + chainIndices, + null); + // HACK: auto-set length is busted + present.setSwapchainCount(chains.size()); + + 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, scope); + + 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, scope); + + 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 { %stage.postprocess vkheader::postprocess; +# extern package ... types +extern xlib { + struct:_XDisplay rename=XDisplay; +} + # field rename function # field:remname=func:[function] # [function] is a defined function that will take: @@ -128,7 +133,8 @@ struct VkInstance_T { } } }} - func:; +# func:; + func:vkCreateInstance; func:; # a few helpers @@ -146,7 +152,7 @@ struct VkInstance_T { } struct VkDevice_T { - func:; +# func:; func:; code: {{ @@ -165,7 +171,7 @@ struct VkDevice_T { } struct VkPhysicalDevice_T { - func:; +# func:; func:; code: {{ @@ -182,7 +188,7 @@ struct VkPhysicalDevice_T { } struct /Vk/ { - func:; +# func:; func:; } @@ -204,8 +210,9 @@ func vkCreateInstance { } library VkConstants { - enum://; + define:api-constants; # this is generated by vkheader.pm.init define:extensions; + enum://; } enum // { diff --git a/src/notzed.vkheader/gen/vkheader.h b/src/notzed.vkheader/gen/vkheader.h index 3c9b0dd..396aaa2 100644 --- a/src/notzed.vkheader/gen/vkheader.h +++ b/src/notzed.vkheader/gen/vkheader.h @@ -1,2 +1,4 @@ +#define VK_USE_PLATFORM_XLIB_KHR + #include diff --git a/src/notzed.vkheader/gen/vkheader.pm b/src/notzed.vkheader/gen/vkheader.pm index bee1f34..6febd49 100644 --- a/src/notzed.vkheader/gen/vkheader.pm +++ b/src/notzed.vkheader/gen/vkheader.pm @@ -1,6 +1,6 @@ # -# TODO: constructors initialise sType -# +# TODO: array constructors initialise sType for each item +# FIXME: if the same len is defined for multiple targes then don't bother hiding it/setting it implicitly# package vkheader; @@ -48,6 +48,7 @@ sub init { $r->{vkheader} = $s; $api->{data}->{"struct:$r->{name}_T"} = $s; + #$api->{databytype}->{'struct'}->{"$r->{name}_T"} = $s; } # link api to registry @@ -67,6 +68,48 @@ sub init { } map { delete $api->{data}->{$_} } @delete; + #map { m/^(.*):(.*)$/; delete $api->{databytype}->{$1}->{$2} } @delete; + + # create psuedo-define of api constants + my %typeMap = ( + uint32_t => 'u32', + uint64_t => 'u64', + float => 'f32' + ); + my $d = $registry->{data}->{'define:API Constants'}; + my $apiConstants = { + name => 'api-constants', + type => 'define', + items => [] + }; + foreach my $m (@{$d->{items}}) { + my $v = $m->{value}; + + $v =~ s/LL//; + $v =~ s/U|F|\(|\)//g; + + die "Unknown constant type: $m->{type}", if !defined $typeMap{$m->{type}}; + + push @{$apiConstants->{items}}, { + name => $m->{name}, + value => $v, + type => $typeMap{$m->{type}} + }; + } + $api->{data}->{'define:api-constants'} = $apiConstants; + #$api->{databytype}->{'define'}->{'api-constants'} = $apiConstants; + + # mark all functions for matchObjectFunction + foreach my $c (grep { $_->{type} eq 'func' } values %{$api->{data}}) { + my $items = $c->{items}; + + if ($items->[0]->{deref} =~ m/^u64:\$\{(.*)\}$/) { + my $type = $registry->{index}->{"type:$1"}; + if (defined ($type) && $type->{type} eq 'VK_DEFINE_HANDLE') { + $c->{vkobject} = $1; + } + } + } 1; } @@ -161,6 +204,8 @@ sub analyseInOut { $rw = $rw."i" if $arrays->{$r->{name}}; + print "edit: array-struct $s->{name}\n" if $arrays->{$r->{name}}; + $s->{vkaccess}= $rw; $s->{access} = $rw; $s->{"$s->{type}:template"} = 'code:class=struct-array' if $arrays->{$r->{name}}; @@ -219,6 +264,7 @@ sub analyseFunctions { $d->{'init:template'} = $d->{items}->[0]->{deref} eq 'u64:${VkInstance_T}' ? 'code:vulkan=invoke-dynamic-init-instance' : 'code:vulkan=invoke-dynamic-init'; $d->{'func:template'} = 'code:vulkan=invoke-dynamic'; + print "edit: extension: $c->{name}\n"; } # look for constructors and member functions @@ -249,7 +295,7 @@ sub analyseFunctions { $d->{scope} = $last->{scope} = 'explicit'; # blah, for now $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance'); - print "xxxa static $flast->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n"; + print "edit: static create: $c->{name}\n"; } elsif ($ffirst->{fullType} eq $ffirst->{baseType} && $rfirst->{type} eq 'VK_DEFINE_HANDLE') { # member functions @@ -257,6 +303,8 @@ sub analyseFunctions { $first->{instance} = 1; $d->{static} = 0; + print "edit: member function: $c->{name}\n"; + # member create function if ($iscreate) { $last->{output} = 0; @@ -265,7 +313,7 @@ sub analyseFunctions { $d->{scope} = $last->{scope} = 'explicit'; $last->{select}->{vkinstance} = 1 if ($flast->{baseType} ne 'VkInstance' && $rlast->{type} eq 'VK_DEFINE_HANDLE'); - print "xxxb $ffirst->{baseType}.$c->{name} $last->{deref} $rlast->{type} ".join("",split(/\n/,Dumper($flast)))."\n"; + print "edit: member create: $c->{name}\n"; } } @@ -273,6 +321,7 @@ sub analyseFunctions { #&& $rlast->{type} eq 'VK_DEFINE_HANDLE') ) { $last->{array} = 1; + print "edit: array $c->{name} ($flast->{name})\n"; } # Link in successcodes (failure codes?) @@ -281,6 +330,8 @@ sub analyseFunctions { $d->{success}->{success} = join ',', map { "VkConstants.$_" } split /,/,$c->{successcodes}; $d->{result}->{output} = 0; + print "edit: successcodes: $c->{name}: $c->{successcodes}\n"; + # output return value if there is more than 1 success code if ($c->{successcodes} =~ tr/,/,/ >= 1 && !defined($d->{return})) { $d->{result}->{output} = 1; @@ -435,15 +486,18 @@ sub matchStruct { sub matchObjectFunction { my $c = shift; my $s = shift; - my $items = $c->{items}; - my $r = $s->{vkregistry}; - die "vkregistry field missing ".Dumper($s) if !defined($r); + return $c->{vkobject} eq $s->{name}; - return $r->{type} eq 'VK_DEFINE_HANDLE' - #&& !$registry->{alias}->{"type:$s->{name}"} - && defined($items->[0]) - && $items->[0]->{deref} eq "u64:\${$s->{name}}"; +# my $items = $c->{items}; +# my $r = $s->{vkregistry}; + +# die "vkregistry field missing ".Dumper($s) if !defined($r); + +# return $r->{type} eq 'VK_DEFINE_HANDLE' +# #&& !$registry->{alias}->{"type:$s->{name}"} +# && defined($items->[0]) +# && $items->[0]->{deref} eq "u64:\${$s->{name}}"; } # a static create function @@ -453,6 +507,12 @@ sub matchCreateFunction { my $items = $c->{items}; my $r = $s->{vkregistry}; + if (($c->{vkiscreate}) + && ($c->{vkisstatic}) + && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}")) { + print "match-create: $c->{name}\n"; + } + return ($c->{vkiscreate}) && ($c->{vkisstatic}) && ($items->[$#$items]->{deref} eq "u64:u64:\${$s->{name}}"); diff --git a/src/notzed.vkheader/gen/vulkan.pm b/src/notzed.vkheader/gen/vulkan.pm index cc5ec98..fbe0f4a 100644 --- a/src/notzed.vkheader/gen/vulkan.pm +++ b/src/notzed.vkheader/gen/vulkan.pm @@ -82,8 +82,8 @@ sub new { my $info = loadMember($yn); my $s = $ya; - $s->{name} = $info->{name}, - $s->{type} => $info->{baseType} if defined $info->{baseType}; + $s->{name} = $info->{name}; + $s->{type} => $info->{baseType} if defined $info->{baseType}; $data->{"type:$s->{name}"} = $s; } else { @@ -101,6 +101,22 @@ sub new { if ($xa->{type} =~ m/enum|bitmask/o) { $data->{"type:$xa->{name}"} = { category => $xa->{type}, name => $xa->{name} }; } elsif ($xa->{name} eq 'API Constants') { + my $d = { category => "define", name => $xa->{name}, items =>[] }; + + $data->{"define:$xa->{name}"} = $d; + + while ($#{$xn} >= 0) { + my $yt = shift @{$xn}; + my $yn = shift @{$xn}; + + next if $yt ne 'enum'; + + my $ya = shift @{$yn}; + + next if $ya->{alias}; + + push @{$d->{items}}, $ya; + } } } elsif ($xt eq 'commands') { while ($#{$xn} >= 0) { -- 2.39.2