Yet another attempt at a vulkan binding generator.
authorNot Zed <notzed@gmail.com>
Tue, 26 Apr 2022 13:25:23 +0000 (22:55 +0930)
committerNot Zed <notzed@gmail.com>
Tue, 26 Apr 2022 13:32:01 +0000 (23:02 +0930)
This uses the registry exclusively to form the api and
parameterised templates to generate output.

16 files changed:
Makefile
README
src/notzed.vulkan.test/classes/module-info.java [new file with mode: 0644]
src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java [new file with mode: 0755]
src/notzed.vulkan.test/gen/cube.frag [new file with mode: 0644]
src/notzed.vulkan.test/gen/cube.vert [new file with mode: 0644]
src/notzed.vulkan.test/gen/gen.make [new file with mode: 0644]
src/notzed.vulkan.test/gen/mandelbrot.comp [new file with mode: 0644]
src/notzed.vulkan/classes/module-info.java [new file with mode: 0644]
src/notzed.vulkan/gen/command-types.api [new file with mode: 0644]
src/notzed.vulkan/gen/gen.make [new file with mode: 0644]
src/notzed.vulkan/gen/generate-vulkan [new file with mode: 0755]
src/notzed.vulkan/gen/struct-types.api [new file with mode: 0644]
src/notzed.vulkan/gen/vulkan.pm [new file with mode: 0644]
src/notzed.xcb/classes/module-info.java [new file with mode: 0644]
src/notzed.xcb/classes/xcb/Connection.java [new file with mode: 0644]

index 1bc01ce..1f9ce1d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,9 +9,10 @@ java_MODULES = notzed.nativez \
        notzed.apistatic notzed.apiobject \
        notzed.ffmpeg \
        notzed.clstatic \
-       notzed.xlib \
+       notzed.xlib notzed.xcb \
        notzed.vkregistry notzed.vkregistry.test \
-       notzed.vkheader notzed.vkheader.test
+       notzed.vkheader notzed.vkheader.test \
+       notzed.vulkan notzed.vulkan.test
 native_MODULES = notzed.api
 
 notzed.apistatic_JDEPMOD = notzed.nativez notzed.api
@@ -20,19 +21,23 @@ notzed.ffmpeg_JDEPMOD = notzed.nativez
 notzed.clstatic_JDEPMOD = notzed.nativez
 notzed.vkregistry_JDEPMOD = notzed.nativez
 notzed.vkregistry.test_JDEPMOD = notzed.vkregistry notzed.xlib
-notzed.vkheader_JDEPMOD = notzed.nativez
+notzed.vkheader_JDEPMOD = notzed.nativez notzed.xlib
 notzed.vkheader.test_JDEPMOD = notzed.vkheader
+notzed.vulkan_JDEPMOD = notzed.nativez notzed.xlib notzed.xcb
+notzed.vulkan.test_JDEPMOD = notzed.vulkan
 
 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.test_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube
-notzed.vkheader.test_JMAIN = vulkan.test.TestMandelbrot
+notzed.vkheader.test_JMAIN = vulkan.test.TestMandelbrot vulkan.test.TestCube
+notzed.vulkan.test_JMAIN = vulkan.test.TestMandelbrot
 
 $(foreach module,$(java_MODULES),$(eval $(module)_JMAINFLAGS=--enable-native-access=notzed.nativez,$(module)))
 
 notzed.vkregistry.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.vkregistry
-notzed.vkheader.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.vkheader
+notzed.vkheader.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.vkheader
+notzed.vulkan.test_JMAINFLAGS = --enable-native-access=notzed.nativez,notzed.xlib,notzed.xcb,notzed.vulkan
 
 include java.make
diff --git a/README b/README
index 2451943..ee4c1ab 100644 (file)
--- a/README
+++ b/README
@@ -113,13 +113,20 @@ The demo requires a file 'movie.api' in the root directory to run.
 notzed.vkheader uses the header-based generator on the vulkan
 installed headers.  This is still very incomplete work in progress.
 Much meta-data is lost such as which functions are extensions during
-the generation of the headers.
+the generation of the headers.  This is incomplete and dead-ended.
 
 notzed.vkregistry uses a completely different generator which directly
 parses the official xml registry specification for vulkan
 (/usr/share/vulkan/registry/vk.xml).  This is directly converted to
 about-as-java-friendly a vulkan api as one can hope for, particularly
-the constructors for all the config objects.
+the constructors for all the config objects.  This is incomplete and
+dead-ended.
+
+notzed.vulkan also uses a different generator which directly parses
+the official xml registry specification for vulkan
+(/usr/share/vulkan/registry/vk.xml).  This version uses templates to
+generate the various structures in more concise and relatively
+consistenet way.  Work in progress.
 
 Export process
 --------------
diff --git a/src/notzed.vulkan.test/classes/module-info.java b/src/notzed.vulkan.test/classes/module-info.java
new file mode 100644 (file)
index 0000000..452fec9
--- /dev/null
@@ -0,0 +1,7 @@
+
+module notzed.vulkan.test {
+       requires notzed.vulkan;
+       requires notzed.xlib;
+
+       requires java.desktop;
+}
diff --git a/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java b/src/notzed.vulkan.test/classes/vulkan/test/TestMandelbrot.java
new file mode 100755 (executable)
index 0000000..4ebb002
--- /dev/null
@@ -0,0 +1,600 @@
+ /*
+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.lang.invoke.*;
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.PathElement;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+
+import static vulkan.VkConstants.*;
+
+public class TestMandelbrot {
+       static final boolean debug = true;
+       ResourceScope scope = ResourceScope.newSharedScope();
+
+       int WIDTH = 1920*1;
+       int HEIGHT = 1080*1;
+
+       VkInstance instance;
+       VkPhysicalDevice physicalDevice;
+
+       VkDevice device;
+       VkQueue computeQueue;
+
+       long dstBufferSize = WIDTH * HEIGHT * 4;
+       //VkBuffer dstBuffer;
+       //VkDeviceMemory dstMemory;
+       BufferMemory dst;
+
+       VkDescriptorSetLayout descriptorSetLayout;
+       VkDescriptorPool descriptorPool;
+       HandleArray<VkDescriptorSet> descriptorSets;
+
+       int computeQueueIndex;
+       VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
+
+       String mandelbrot_entry = "main";
+       IntArray mandelbrot_cs;
+
+       VkShaderModule mandelbrotShader;
+       VkPipelineLayout pipelineLayout;
+       HandleArray<VkPipeline> computePipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+       VkCommandPool commandPool;
+       HandleArray<VkCommandBuffer> commandBuffers;
+
+       record BufferMemory ( VkBuffer buffer, VkDeviceMemory memory ) {};
+
+       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, "test", 1, "test-engine", 2, VK_MAKE_API_VERSION(0, 1, 0, 0)),
+                               new String[] { "VK_LAYER_KHRONOS_validation" },
+                               debug ? new String[] { "VK_EXT_debug_utils" } : null
+                               );
+
+                       instance = VkInstance.vkCreateInstance(info, null, scope);
+               }
+       }
+
+       void init_device() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       HandleArray<VkPhysicalDevice> devs;
+                       int count;
+                       int res;
+
+                       devs = instance.vkEnumeratePhysicalDevices(frame, scope);
+
+                       int best = 0;
+                       int devid = -1;
+                       int queueid = -1;
+
+                       for (int i=0;i<devs.length();i++) {
+                               VkPhysicalDevice dev = devs.getAtIndex(i);
+                               VkQueueFamilyProperties famprops = dev.vkGetPhysicalDeviceQueueFamilyProperties(frame);
+                               int family_count = (int)famprops.length();
+
+                               for (int j=0;j<family_count;j++) {
+                                       var flags = famprops.getAtIndex(j).getQueueFlags();
+                                       int score = 0;
+
+                                       if ((flags & VK_QUEUE_COMPUTE_BIT) != 0)
+                                               score += 1;
+                                       if ((flags & VK_QUEUE_GRAPHICS_BIT) == 0)
+                                               score += 1;
+
+                                       if (score > best) {
+                                               score = best;
+                                               devid = i;
+                                               queueid = j;
+                                       }
+                               }
+                       }
+
+                       if (devid == -1)
+                               throw new Exception("Cannot find a suitable device");
+
+                       computeQueueIndex = queueid;
+                       physicalDevice = devs.getAtIndex(devid);
+
+                       FloatArray qpri = FloatArray.create(frame, 0.0f);
+                       VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+                               frame,
+                               0,
+                               queueid,
+                               qpri);
+                       VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+                               frame,
+                               0,
+                               qinfo,
+                               null,
+                               null,
+                               null);
+
+                       device = physicalDevice.vkCreateDevice(devinfo, null, scope);
+
+                       System.out.printf("device = %s\n", device.address());
+
+                       // NOTE: app scope
+                       deviceMemoryProperties = VkPhysicalDeviceMemoryProperties.create((SegmentAllocator)scope);
+                       physicalDevice.vkGetPhysicalDeviceMemoryProperties(deviceMemoryProperties);
+
+                       computeQueue = device.vkGetDeviceQueue(queueid, 0, scope);
+               }
+       }
+
+       /**
+        * 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) 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,
+                               null);
+
+                       VkBuffer buffer = device.vkCreateBuffer(buf_info, null, scope);
+
+                       device.vkGetBufferMemoryRequirements(buffer, req);
+
+                       VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+                               req.getSize(),
+                               find_memory_type(deviceMemoryProperties, req.getMemoryTypeBits(), properties));
+
+                       VkDeviceMemory memory = device.vkAllocateMemory(alloc, null, scope);
+
+                       device.vkBindBufferMemory(buffer, memory, 0);
+
+                       return new BufferMemory(buffer, memory);
+               }
+       }
+
+       /**
+        * Descriptors are used to bind and describe memory blocks
+        * to shaders.
+        *
+        * *Pool is used to allocate descriptors, it is per-device.
+        * *Layout is used to group descriptors for a given pipeline,
+        * The descriptors describe individually-addressable blocks.
+        */
+       void init_descriptor() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       /* Create descriptorset layout */
+                       VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(frame,
+                               0,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1,
+                               VK_SHADER_STAGE_COMPUTE_BIT,
+                               null);
+
+                       VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(frame,
+                               0,
+                               layout_binding);
+
+                       descriptorSetLayout = device.vkCreateDescriptorSetLayout(descriptor_layout, null, scope);
+
+                       /* Create descriptor pool */
+                       VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               1);
+
+                       VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame,
+                               0,
+                               1,
+                               type_count);
+
+                       descriptorPool = device.vkCreateDescriptorPool(descriptor_pool, null, scope);
+
+                       /* Allocate from pool */
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(frame,
+                               descriptorPool,
+                               layout_table);
+
+                       descriptorSets = device.vkAllocateDescriptorSets(alloc_info, (SegmentAllocator)scope);
+
+                       /* Bind a buffer to the descriptor */
+                       VkDescriptorBufferInfo bufferInfo = VkDescriptorBufferInfo.create(frame,
+                               dst.buffer,
+                               0,
+                               dstBufferSize);
+
+                       VkWriteDescriptorSet writeSet = VkWriteDescriptorSet.create(frame,
+                               descriptorSets.getAtIndex(0),
+                               0,
+                               0,
+                               1,
+                               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+                               null,
+                               bufferInfo,
+                               null);
+
+                       System.out.println(writeSet);
+
+                       device.vkUpdateDescriptorSets(writeSet, null);
+               }
+       }
+
+       /**
+        * Create the compute pipeline.  This is the shader and data layouts for it.
+        */
+       void init_pipeline() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       /* Set shader code */
+                       VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(frame,
+                               0,
+                               mandelbrot_cs.length() * 4,
+                               mandelbrot_cs);
+
+                       mandelbrotShader = device.vkCreateShaderModule(vsInfo, null, scope);
+
+                       /* Link shader to layout */
+                       HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+
+                       layout_table.setAtIndex(0, descriptorSetLayout);
+
+                       VkPipelineLayoutCreateInfo pipelineinfo = VkPipelineLayoutCreateInfo.create(frame,
+                               0,
+                               layout_table,
+                               null);
+
+                       pipelineLayout = device.vkCreatePipelineLayout(pipelineinfo, null, scope);
+
+                       /* Create pipeline */
+                       VkComputePipelineCreateInfo pipeline = VkComputePipelineCreateInfo.create(frame,
+                               0,
+                               pipelineLayout,
+                               null,
+                               0);
+
+                       VkPipelineShaderStageCreateInfo stage = pipeline.getStage();
+
+                       stage.setStage(VK_SHADER_STAGE_COMPUTE_BIT);
+                       stage.setModule(mandelbrotShader);
+                       stage.setName(mandelbrot_entry, frame);
+
+                       device.vkCreateComputePipelines(null, pipeline, null, computePipeline);
+               }
+       }
+
+       /**
+        * Create a command buffer, this is somewhat like a display list.
+        */
+       void init_command_buffer() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(frame,
+                               0,
+                               computeQueueIndex);
+
+                       commandPool = device.vkCreateCommandPool(poolinfo, null, scope);
+
+                       VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
+                               commandPool,
+                               VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+                               1);
+
+                       // should it take a scope?
+                       commandBuffers = device.vkAllocateCommandBuffers(cmdinfo, (SegmentAllocator)scope, scope);
+
+                       /* Fill command buffer with commands for later operation */
+                       VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.create(frame,
+                               VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+                               null);
+
+                       commandBuffers.get(0).vkBeginCommandBuffer(beginInfo);
+
+                       /* Bind the compute operation and data */
+                       commandBuffers.get(0).vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, computePipeline.get(0));
+                       commandBuffers.get(0).vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, descriptorSets, null);
+
+                       /* Run it */
+                       commandBuffers.get(0).vkCmdDispatch(WIDTH, HEIGHT, 1);
+
+                       commandBuffers.get(0).vkEndCommandBuffer();
+               }
+       }
+
+       /**
+        * Execute the pre-created command buffer.
+        *
+        * A fence is used to wait for completion.
+        */
+       void execute() throws Exception {
+               try (Frame frame = Frame.frame()) {
+                       VkSubmitInfo submitInfo = VkSubmitInfo.create(frame);
+
+                       submitInfo.setCommandBufferCount(1);
+                       submitInfo.setCommandBuffers(commandBuffers);
+
+                       /* Create fence to mark the task completion */
+                       VkFence fence;
+                       HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+                       VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame);
+
+                       // maybe this should take a HandleArray<Fence> rather than being a constructor
+                       // FIXME: some local scope
+                       fence = device.vkCreateFence(fenceInfo, null, scope);
+                       fences.set(0, fence);
+
+                       /* Await completion */
+                       computeQueue.vkQueueSubmit(submitInfo, fence);
+
+                       int VK_TRUE = 1;
+                       int res;
+                       do {
+                               res = device.vkWaitForFences(fences, VK_TRUE, 1000000);
+                       } while (res == VK_TIMEOUT);
+
+                       device.vkDestroyFence(fence, null);
+               }
+       }
+
+       void shutdown() {
+               device.vkDestroyCommandPool(commandPool, null);
+               device.vkDestroyPipeline(computePipeline.getAtIndex(0), null);
+               device.vkDestroyPipelineLayout(pipelineLayout, null);
+               device.vkDestroyShaderModule(mandelbrotShader, null);
+
+               device.vkDestroyDescriptorPool(descriptorPool, null);
+               device.vkDestroyDescriptorSetLayout(descriptorSetLayout, null);
+
+               device.vkFreeMemory(dst.memory(), null);
+               device.vkDestroyBuffer(dst.buffer(), null);
+
+               device.vkDestroyDevice(null);
+               if (logger != null)
+                       instance.vkDestroyDebugUtilsMessengerEXT(logger, null);
+               instance.vkDestroyInstance(null);
+       }
+
+       /**
+        * Accesses the gpu buffer, converts it to RGB byte, and saves it as a pam file.
+        */
+       void save_result() throws Exception {
+               try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+                       MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
+                       byte[] pixels = new byte[WIDTH * HEIGHT * 3];
+
+                       System.out.printf("map %d bytes\n", dstBufferSize);
+
+                       for (int i = 0; i < WIDTH * HEIGHT; i++) {
+                               pixels[i * 3 + 0] = mem.get(Memory.BYTE, i * 4 + 0);
+                               pixels[i * 3 + 1] = mem.get(Memory.BYTE, i * 4 + 1);
+                               pixels[i * 3 + 2] = mem.get(Memory.BYTE, i * 4 + 2);
+                       }
+
+                       device.vkUnmapMemory(dst.memory());
+
+                       pam_save("mandelbrot.pam", WIDTH, HEIGHT, 3, pixels);
+               }
+       }
+
+       void show_result() throws Exception {
+               try (ResourceScope scope = ResourceScope.newConfinedScope()) {
+                       MemorySegment mem = device.vkMapMemory(dst.memory(), 0, dstBufferSize, 0, scope);
+                       int[] pixels = new int[WIDTH * HEIGHT];
+
+                       System.out.printf("map %d bytes\n", dstBufferSize);
+
+                       MemorySegment.ofArray(pixels).copyFrom(mem);
+
+                       device.vkUnmapMemory(dst.memory());
+
+                       swing_show(WIDTH, HEIGHT, pixels);
+               }
+       }
+
+       /**
+        * Trivial pnm format image output.
+        */
+       void pam_save(String name, int width, int height, int depth, byte[] pixels) throws IOException {
+               try (FileOutputStream fos = new FileOutputStream(name)) {
+                       fos.write(String.format("P6\n%d\n%d\n255\n", width, height).getBytes());
+                       fos.write(pixels);
+                       System.out.printf("wrote: %s\n", name);
+               }
+       }
+
+       static class DataImage extends JPanel {
+
+               final int w, h, stride;
+               final MemoryImageSource source;
+               final Image image;
+               final int[] pixels;
+
+               public DataImage(int w, int h, int[] pixels) {
+                       this.w = w;
+                       this.h = h;
+                       this.stride = w;
+                       this.pixels = pixels;
+                       this.source = new MemoryImageSource(w, h, pixels, 0, w);
+                       this.source.setAnimated(true);
+                       this.source.setFullBufferUpdates(true);
+                       this.image = Toolkit.getDefaultToolkit().createImage(source);
+               }
+
+               @Override
+               protected void paintComponent(Graphics g) {
+                       super.paintComponent(g);
+                       g.drawImage(image, 0, 0, this);
+               }
+       }
+
+       void swing_show(int w, int h, int[] pixels) {
+               JFrame window;
+               DataImage image = new DataImage(w, h, pixels);
+
+               window = new JFrame("mandelbrot");
+               window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+               window.setContentPane(image);
+               window.setSize(w, h);
+               window.setVisible(true);
+       }
+
+       IntArray loadSPIRV0(String name) throws IOException {
+               // hmm any way to just load this directly?
+               try (InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
+                       ByteBuffer bb = ByteBuffer.allocateDirect(8192).order(ByteOrder.nativeOrder());
+                       int length = Channels.newChannel(is).read(bb);
+
+                       bb.position(0);
+                       bb.limit(length);
+
+                       return IntArray.create(MemorySegment.ofByteBuffer(bb));
+               }
+       }
+
+       IntArray loadSPIRV(String name) throws IOException {
+               try (InputStream is = TestMandelbrot.class.getResourceAsStream(name)) {
+                       MemorySegment seg = ((SegmentAllocator)scope).allocateArray(Memory.INT, 2048);
+                       int length = Channels.newChannel(is).read(seg.asByteBuffer());
+
+                       return IntArray.create(seg.asSlice(0, length));
+               }
+       }
+
+       /**
+        * This finds the memory type index for the memory on a specific device.
+        */
+       static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+               VkMemoryType mtypes = memory.getMemoryTypes();
+
+               for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+                       if (((1 << i) & typeMask) != 0 && ((mtypes.getAtIndex(i).getPropertyFlags() & query) == query))
+                               return i;
+               }
+               return -1;
+       }
+
+       public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) {
+               return (variant << 29) | (major << 22) | (minor << 12) | patch;
+       }
+
+       void demo() throws Exception {
+               mandelbrot_cs = loadSPIRV("mandelbrot.bin");
+
+               init_instance();
+               init_debug();
+
+               init_device();
+
+               dst = init_buffer(dstBufferSize,
+                       VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                       VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+
+               init_descriptor();
+
+               init_pipeline();
+               init_command_buffer();
+
+               System.out.printf("Calculating %dx%d\n", WIDTH, HEIGHT);
+               execute();
+               //System.out.println("Saving ...");
+               //save_result();
+               System.out.println("Showing ...");
+               show_result();
+               System.out.println("Done.");
+
+               shutdown();
+       }
+
+
+       public static void main(String[] args) throws Throwable {
+               System.loadLibrary("vulkan");
+
+               new TestMandelbrot().demo();
+       }
+}
diff --git a/src/notzed.vulkan.test/gen/cube.frag b/src/notzed.vulkan.test/gen/cube.frag
new file mode 100644 (file)
index 0000000..de24544
--- /dev/null
@@ -0,0 +1,8 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (location = 0) in vec4 color;
+layout (location = 0) out vec4 outColor;
+void main() {
+   outColor = color;
+}
diff --git a/src/notzed.vulkan.test/gen/cube.vert b/src/notzed.vulkan.test/gen/cube.vert
new file mode 100644 (file)
index 0000000..5d21e1e
--- /dev/null
@@ -0,0 +1,14 @@
+#version 400
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+layout (std140, binding = 0) uniform bufferVals {
+    mat4 mvp;
+} data;
+layout (location = 0) in vec4 pos;
+layout (location = 1) in vec4 inColor;
+layout (location = 0) out vec4 outColor;
+
+void main() {
+   outColor = inColor;
+   gl_Position = data.mvp * pos;
+}
diff --git a/src/notzed.vulkan.test/gen/gen.make b/src/notzed.vulkan.test/gen/gen.make
new file mode 100644 (file)
index 0000000..1abdde1
--- /dev/null
@@ -0,0 +1,17 @@
+
+bin/status/notzed.vulkan.test.classes: \
+       bin/modules/notzed.vulkan.test/vulkan/test/mandelbrot.bin \
+       bin/modules/notzed.vulkan.test/vulkan/test/cube_vs.bin \
+       bin/modules/notzed.vulkan.test/vulkan/test/cube_fs.bin
+
+bin/modules/notzed.vulkan.test/vulkan/test/mandelbrot.bin: src/notzed.vulkan.test/gen/mandelbrot.comp
+       mkdir -p $(@D)
+       glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vulkan.test/vulkan/test/cube_vs.bin: src/notzed.vulkan.test/gen/cube.vert
+       mkdir -p $(@D)
+       glslangValidator --target-env vulkan1.0 -V -o $@ $<
+
+bin/modules/notzed.vulkan.test/vulkan/test/cube_fs.bin: src/notzed.vulkan.test/gen/cube.frag
+       mkdir -p $(@D)
+       glslangValidator --target-env vulkan1.0 -V -o $@ $<
diff --git a/src/notzed.vulkan.test/gen/mandelbrot.comp b/src/notzed.vulkan.test/gen/mandelbrot.comp
new file mode 100644 (file)
index 0000000..6a45590
--- /dev/null
@@ -0,0 +1,60 @@
+#version 450
+
+#define WIDTH (1920*1)
+#define HEIGHT (1080*1)
+#define LWS_X 8
+#define LWS_Y 8
+#define LIMIT 10000
+
+layout (local_size_x = LWS_X, local_size_y = LWS_Y, local_size_z = 1 ) in;
+
+layout(std430, binding = 0) buffer buf {
+       uint imageData[];
+};
+
+void main() {
+
+       /*
+         In order to fit the work into workgroups, some unnecessary threads are launched.
+         We terminate those threads here.
+       */
+       if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+               return;
+
+       float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+       float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+       /*
+         What follows is code for rendering the mandelbrot set.
+       */
+       vec2 uv = vec2(x, (y - 0.5) * (12.0 / 19.0) + 0.5);
+       float n = 0.0;
+       vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(4.0);
+       vec2 z = vec2(0.0);
+       const int M = LIMIT;
+
+       for (int i = 0; i<M; i++) {
+               z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+
+               if (dot(z, z) > 4)
+                       break;
+               n++;
+       }
+
+       // we use a simple cosine palette to determine color:
+       // http://iquilezles.org/www/articles/palettes/palettes.htm
+       float t = float(n) * 500.0 / float(M);
+       vec3 d = vec3(0.5, 0.5, 0.5);
+       vec3 e = vec3(0.5, 0.5, 0.5);
+       vec3 f = vec3(1.0, 1.0, 1.0);
+       vec3 g = vec3(0.00, 0.33, 0.67);
+
+       vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+
+       if (n == M)
+               color = vec4(0, 0, 0, 1);
+
+       // store the rendered mandelbrot set into a storage buffer:
+       imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x] = packUnorm4x8(color);
+       //imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color;
+}
diff --git a/src/notzed.vulkan/classes/module-info.java b/src/notzed.vulkan/classes/module-info.java
new file mode 100644 (file)
index 0000000..3e7cdec
--- /dev/null
@@ -0,0 +1,8 @@
+
+module notzed.vulkan {
+       requires transitive notzed.nativez;
+       requires notzed.xlib;
+       requires notzed.xcb;
+
+       exports vulkan;
+}
diff --git a/src/notzed.vulkan/gen/command-types.api b/src/notzed.vulkan/gen/command-types.api
new file mode 100644 (file)
index 0000000..0bf03ce
--- /dev/null
@@ -0,0 +1,562 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# things to check; vkCmdSetFragmentShadingRateKHR
+
+# types for function calls
+
+code method {
+  invoke {{
+       /* method:invoke */
+       static final MethodHandle {name}$FH = Memory.downcall("{name}",
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arguments}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$FH.invokeExact({invoke-arguments});
+                       {result-test}{
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-query {
+  invoke {{
+       /* method-query:invoke */
+       static final MethodHandle {name}$FH = Memory.downcall("{name}",
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arguments}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$FH.invokeExact({query-arguments});
+                       {result-test}{
+                               {query-init}
+                               {native-result-assign}{name}$FH.invokeExact({invoke-arguments});
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-extension {
+  dispatch {{
+       final NativeSymbol {name}$NS;
+  }}
+  invoke {{
+       /* method-extension:invoke */
+       final static MethodHandle {name}$DH = Memory.downcall(
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arguments}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arguments});
+                       {result-test}{
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+code method-extension-query {
+  dispatch {{
+       final NativeSymbol {name}$NS;
+  }}
+  invoke {{
+       /* method-extension:invoke */
+       final static MethodHandle {name}$DH = Memory.downcall(
+               {function-descriptor});
+       /**
+       * success: {successcodes}
+       * errors: {errorcodes}
+       */
+       public {static}{java-result} {rename}(
+               {java-arguments}) {
+               {native-result-define}
+               try {create-frame}{
+                       {native-init}
+                       {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {query-arguments});
+                       {result-test}{
+                               {query-init}
+                               {native-result-assign}{name}$DH.invokeExact(dispatch.{name}$NS, {invoke-arguments});
+                               {java-result-assign}
+                               {java-result-return}
+                       }
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+               {result-throw}
+       }
+  }}
+}
+
+type value {
+       java-arg        {{ {type} {name} }}
+       native-arg      {{ {carrier} {name}$ }}
+
+       invoke-arg      {{ {name} }}
+}
+
+type value-array {
+       java-arg        {{ {type} {name} }}
+       native-arg      {{ {carrier} {name}$ }}
+
+       invoke-arg      {{ {name} }}
+}
+
+type value-array2d {
+       java-arg        {{ {type} {name} }}
+       native-arg      {{ {carrier} {name}$ }}
+
+       invoke-arg      {{ {name} }}
+}
+
+type uint8_t,char value {
+       type    {{ byte }}
+       layout  {{ Memory.BYTE }}
+}
+
+type uint16_t value {
+       type    {{ short }}
+       layout  {{ Memory.SHORT }}
+}
+
+type uint32_t,int,int32_t value {
+       type    {{ int }}
+       layout  {{ Memory.INT }}
+}
+
+type uint64_t,int64_t,size_t value {
+       type    {{ long }}
+       layout  {{ Memory.LONG }}
+}
+
+type float value {
+       type    {{ float }}
+       layout  {{ Memory.FLOAT }}
+}
+
+type double value {
+       type    {{ double }}
+       layout  {{ Memory.DOUBLE }}
+}
+
+# ###################################################################### #
+# implied length types
+type uint64_t-length,size_t-length uint64_t {
+       java-arg        {{ }}
+       invoke-arg      {{ Memory.length({lengthfor}) }}
+}
+
+type uint32_t-length uint32_t {
+       java-arg        {{ }}
+       invoke-arg      {{ (int)Memory.length({lengthfor}) }}
+}
+
+# ###################################################################### #
+
+# type uint8_t[],char[] value-array {
+#      type    {{ ByteArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.BYTE) }}
+#      typei   {{ byte }}
+# }
+
+#type uint32_t[],int32_t[] value-array {
+#      type    {{ IntArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.INT) }}
+#      typei   {{ int }}
+#}
+
+# type uint64_t[] value-array {
+#      type    {{ LongArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.LONG) }}
+#      typei   {{ long }}
+# }
+
+# calls float[] =-> pointer
+#type float[] value-array {
+#      # or should it be float[] ?
+#      type    {{ FloatArray }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.FLOAT) }}
+#      typei   {{ float }}
+#}
+
+# type struct[] inline-array {
+#      type    {{ {baseType} }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, {baseType}.LAYOUT) }}
+# }
+
+# type float[][] value-array2d {
+#      type    {{ FloatArray }}
+#      typei   {{ float }}
+#      layout  {{ MemoryLayout.sequenceLayout({len1}, MemoryLayout.sequenceLayout({len2}, Memory.FLOAT)) }}
+# }
+
+# select=len?  or what?
+
+type pointer value {
+       layout  {{ Memory.POINTER }}
+       type    {{ MemoryAddress }}
+
+       invoke-arg      {{ Memory.address({name}) }}
+}
+
+type void* pointer;
+
+type funcpointer pointer {
+       type            {{ NativeSymbol }}
+}
+
+# FIXME: clenaup, value-pointer does nothing
+type value-pointer pointer {
+}
+
+type uint8_t* value-pointer {
+       type    ByteArray;
+}
+
+type uint32_t*,int32_t*,int* value-pointer {
+       type    IntArray;
+}
+
+type uint64_t* value-pointer {
+       type    LongArray;
+}
+
+type float* value-pointer {
+       type    FloatArray;
+}
+
+type size_t* value-pointer {
+       type    LongArray;
+}
+
+type pointer-length pointer {
+}
+
+type void*-length pointer-length {
+       type            MemorySegment;
+       java-get        {{ MemorySegment.ofAddress({native-get}, {length}, this.segment.scope()) }}
+}
+
+type uint8_t*-length pointer-length {
+       type    ByteArray;
+}
+
+type uint32_t*-length,int32_t*-length pointer-length {
+       type    IntArray;
+}
+
+type uint32_t[] pointer-length {
+       type    IntArray;
+       length  {{ {len1} }}
+}
+
+type uint64_t*-length pointer-length {
+       type    LongArray;
+}
+
+type float*-length pointer-length {
+       type    FloatArray;
+}
+
+type float[] pointer-length {
+       type    FloatArray;
+       length  {{ {len1} }}
+}
+
+# special handling for strings, will fail if it isn't
+type char* pointer need-frame {
+       type            {{ String }}
+
+       invoke-arg      {{ (Addressable)frame$.allocateUtf8String({name}) }}
+
+       # this just verifies it's a string type
+       length eval     {{
+               if ($v->{len} =~ m/null-terminated/) {
+                       1;
+               } else {
+                       die Dumper($v, $s);
+               }
+       }}
+
+}
+
+# type XXchar**-length pointer-length accessor=value-alloc {
+#      type    {{ String[] }}
+
+#      java-set        {{ {name}$VH.set(segment, Memory.copyStringArray({name}, alloc$)); {set-length} }}
+#      java-get        {{ Memory.copyStringArray((MemoryAddress){name}$VH.get(segment), {length}) }}
+
+#      set-length eval {{
+#              if ($v->{len} =~ m/(.*),null-terminated/) {
+#                      'set'.ucfirst($1).'({name}.length)';
+#              } else {
+#                      die Dumper($v, $s);
+#              }
+#      }}
+# }
+
+# FIXME: wrong
+type Xuint32_t** pointer {
+       type    {{ HandleArray<IntArray> }}
+       typei   {{ IntArray }}
+}
+
+type uint32_t**-length pointer {
+       type    {{ HandleArray<IntArray> }}
+       typei   {{ IntArray }}
+}
+
+type void** pointer {
+       type    PointerArray;
+}
+
+type void**-length pointer-length {
+       type    PointerArray;
+}
+
+type handle pointer {
+       type            {{ {baseType} }}
+}
+
+type handle* pointer {
+       type            {{ HandleArray<{typei}> }}
+       typei           {{ {baseType} }}
+
+       invoke-arg      {{ Memory.address({name}) }}
+}
+
+type handle*-length pointer-length {
+       type            {{ HandleArray<{baseType}> }}
+       typei           {{ {baseType} }}
+}
+
+type struct inline {
+       type    {{ {baseType} }}
+       layout  {{ {baseType}.LAYOUT }}
+}
+
+type struct* pointer {
+       type            {{ {baseType} }}
+}
+
+type struct*-length pointer-length {
+       type    {{ {baseType} }}
+       typei   {{ {baseType} }}
+}
+
+type struct**-length pointer-length {
+       type    {{ HandleArray<{baseType}> }}
+       typei   {{ {baseType} }}
+}
+
+type struct** pointer {
+}
+
+# xlib
+type XID,Window,VisualID uint64_t;
+
+type Display* handle {
+       type    xlib.XDisplay;
+}
+
+# xcb
+type xcb_window_t uint32_t;
+type xcb_visualid_t uint32_t;
+type xcb_connection_t* handle {
+       type    xcb.Connection;
+}
+
+# special types for call overrides
+type instance handle is-instance {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable)self }}
+       native-arg      {{ }}
+}
+
+type funcpointer-return funcpointer need-scope {
+       native-result-define    {{ MemoryAddress result$; }}
+       native-result-assign    {{ result$ = (MemoryAddress) }}
+       java-result-return              {{ return NativeSymbol.ofAddress(pName, result$, scope$); }}
+}
+
+type uint32_t-return uint32_t {
+       native-result-define    {{ int result$; }}
+       native-result-assign    {{ result$ = (int) }}
+       java-result-return              {{ return result$; /* uint32_t */ }}
+}
+
+type uint64_t-return,size_t-return uint64_t {
+       native-result-define    {{ long result$; }}
+       native-result-assign    {{ result$ = (long) }}
+       java-result-return              {{ return result$; /* uint64_t */ }}
+}
+
+# for handle constructors
+type handle*-output handle* need-frame need-scope {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+
+       java-result                     {{ {baseType} }}
+       native-init                     {{ MemorySegment {name} = frame$.allocate(Memory.POINTER); }}
+       java-result-assign      {{ {baseType} result$$ = {baseType}.create({name}.get(Memory.POINTER, 0), scope$); }}
+       java-result-return      {{ return result$$; }}
+}
+
+# for handle constructors of dispatchable types
+type dispatch*-output handle*-output {
+       java-result-assign      {{ {baseType} result$$ = {baseType}.create({name}.get(Memory.POINTER, 0), dispatch, scope$); }}
+}
+
+# for query and return types
+type handle*-length-query handle*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type dispatch*-length-query handle*-length-query need-scope {
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$, dispatch, scope$); }}
+}
+
+type struct*-length-query struct*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}.address() }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = {typei}.createArray({length}, alloc$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type void*-length-query void*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = alloc$.allocate({length}); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type uint32_t*-length-query uint32_t*-length need-alloc {
+       java-arg        {{ }}
+       invoke-arg      {{ {name} }}
+       query-arg       {{ (Addressable)MemoryAddress.NULL }}
+
+       java-result                     {{ {type} }}
+       query-init                      {{ {type} {name} = IntArray.createArray({length}, alloc$); }}
+       java-result-return      {{ return {name}; }}
+}
+
+type uint32_t*-querylen uint32_t* need-frame {
+       type            {{ MemorySegment }}
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}$ }}
+
+       native-init     {{ {type} {name}$ = alloc$.allocate(Memory.INT, 0); }}
+       query-init      {{ long {name} = {name}$.get(Memory.INT, 0); }}
+}
+
+type size_t*-querylen uint32_t* need-frame {
+       type            {{ MemorySegment }}
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name}$ }}
+
+       native-init     {{ {type} {name}$ = alloc$.allocate(Memory.LONG, 0); }}
+       query-init      {{ long {name} = {name}$.get(Memory.LONG, 0); }}
+}
+
+# Or should it just use an insert function?
+
+# there's only two like this
+type handle*-alloc handle* {
+       java-arg        {{ }}
+       java-result                     {{ {type} }}
+       java-result-return      {{ return {name}; }}
+}
+
+type VkCommandBuffer-alloc handle*-alloc need-scope need-alloc {
+       native-init     {{
+               {type} {name} = {typei}.createArray(
+                       (int)VkCommandBufferAllocateInfo.commandBufferCount$VH.get(pAllocateInfo.segment),
+                       alloc$,
+                       dispatch,
+                       scope$);
+       }}
+}
+
+type VkDescriptorSet-alloc handle*-alloc need-alloc {
+       native-init     {{
+               {type} {name} = {typei}.createArray(
+                       (int)VkCommandBufferAllocateInfo.commandBufferCount$VH.get(pAllocateInfo.segment),
+                       alloc$);
+       }}
+}
+
+type void**-output void** need-frame need-scope {
+       type            {{ MemorySegment }}
+
+       java-arg        {{ }}
+       invoke-arg      {{ (Addressable){name} }}
+
+       native-init     {{ MemorySegment {name} = frame$.allocate(Memory.POINTER); }}
+
+       java-result                     {{ {type} }}
+       java-result-assign      {{ {type} result$$ = MemorySegment.ofAddress({name}.get(Memory.POINTER, 0), {length}, scope$); }}
+       java-result-return      {{ return result$$; }}
+}
+
+type vkMapMemory-output void**-output {
+       length  {{ size }}
+}
+
+# some tweaks that the auto-discovery code misses/isn't worth adding to
+override commands {
+       vkGetPhysicalDeviceQueueFamilyProperties template=method-query
+               physicalDevice=type:instance
+               pQueueFamilyPropertyCount=type:uint32_t*-querylen
+               pQueueFamilyProperties=type:struct*-length-query;
+
+       vkAllocateCommandBuffers device=type:instance pCommandBuffers=type:VkCommandBuffer-alloc;
+       vkAllocateDescriptorSets device=type:instance pDescriptorSets=type:VkDescriptorSet-alloc;
+
+       vkMapMemory device=type:instance ppData=type:vkMapMemory-output;
+}
diff --git a/src/notzed.vulkan/gen/gen.make b/src/notzed.vulkan/gen/gen.make
new file mode 100644 (file)
index 0000000..3e34cad
--- /dev/null
@@ -0,0 +1,14 @@
+
+bin/status/notzed.vulkan.classes: \
+       bin/status/notzed.vulkan.export
+
+bin/status/notzed.vulkan.export: \
+       src/notzed.vulkan/gen/generate-vulkan \
+       src/notzed.vulkan/gen/vulkan.pm \
+       src/notzed.vulkan/gen/struct-types.api \
+       src/notzed.vulkan/gen/command-types.api
+
+bin/status/notzed.vulkan.export:
+       src/notzed.vulkan/gen/generate-vulkan -t vulkan -d bin/gen/notzed.vulkan/classes
+       mkdir -p $(@D)
+       touch $@
diff --git a/src/notzed.vulkan/gen/generate-vulkan b/src/notzed.vulkan/gen/generate-vulkan
new file mode 100755 (executable)
index 0000000..cb50afd
--- /dev/null
@@ -0,0 +1,1252 @@
+#!/usr/bin/perl
+
+use Data::Dumper;
+
+use File::Path qw(make_path);
+use File::Basename;
+
+use strict;
+
+use Carp 'verbose';
+use FindBin;
+use lib "$FindBin::Bin", 'bin/linux-amd64/lib';
+
+use vulkan;
+use config;
+use code;
+
+$SIG{__DIE__} = sub { Carp::confess( @_ ) };
+$SIG{'INT'} = sub { Carp::confess() };
+$Data::Dumper::Indent = 1;
+
+# ###################################################################### #
+
+my $enumInfo = {
+       'uint32_t' => {
+               type => 'int'
+       },
+               'uint64_t' => {
+                       type => 'long',
+                       suffix => 'L'
+       },
+               'float' => {
+                       type => 'float',
+                       suffix => 'f'
+       },
+       'const char *' => {
+               type => 'String'
+       },
+};
+
+# all seen types for dev only
+my $all = {};
+
+# ###################################################################### #
+my $vk = new vulkan();
+
+my $api = $vk->buildFeatures(
+       [ 'VK_VERSION_1_0', 'VK_VERSION_1_1', 'VK_VERSION_1_2' ],
+       [ 'xlib',
+         #'wayland',
+         'xcb' ]);
+
+my $sys = {};
+
+$sys->{output} = 'bin/gen/notzed.vulkan/classes';
+$sys->{package} = 'vulkan';
+$sys->{verbose} = 1;
+
+my $structTypes = loadTypes($api, 'struct-types.api');
+my $commandTypes = loadTypes($api, 'command-types.api');
+
+analyseTypes($vk, $api);
+
+# for structs
+my %defaultTemplate;
+
+foreach my $s (values %{$api->{types}}) {
+       my $overrides = $structTypes->{overrides};
+       my $types = $structTypes->{types};
+
+       next if (defined($overrides->{$s->{name}}));
+
+       my %lengths;
+       map { $lengths{$_->{lengthfrom}}++ if $_->{lengthfrom} } @{$s->{items}};
+
+       foreach my $m (@{$s->{items}}) {
+               my $nstar = $m->{deref} =~ tr/*/*/;
+
+               # FIXME: this belongs in analyse
+               if ($m->{lengthfrom} && $lengths{$m->{lengthfrom}} != 1) {
+                       $m->{'set-length'} = '';
+               }
+
+               if ($m->{lengthfor} && $nstar == 0 && $lengths{$m->{name}} == 1) {
+                       $overrides->{$s->{name}}->{$m->{name}}->{type} = $m->{deref}.'-implied';
+               }
+               if ($m->{deref} eq 'struct*-length') {
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               } elsif ($m->{deref} eq 'struct[]') {
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               }
+       }
+
+       if ($s->{returnedonly} eq 'true') {
+               $defaultTemplate{$s->{name}}->{name} = 'struct-readonly';
+       }
+}
+
+# build default overrides for commands
+foreach my $s (values %{$api->{commands}}) {
+       my $overrides = $commandTypes->{overrides};
+       my $types = $commandTypes->{types};
+
+       # check type updates anyway
+       foreach my $m (@{$s->{items}}) {
+               if ($m->{deref} eq 'struct*-length') {
+                       $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n);
+                       $defaultTemplate{$m->{baseType}}->{array} = 1;
+               } elsif ($m->{deref} eq 'struct*') {
+                       $defaultTemplate{$m->{baseType}}->{name} = 'struct-readwrite' if !($m->{fullType} =~ m/const/n);
+               }
+       }
+
+       next if (defined($overrides->{$s->{name}}));
+
+       my $first = $s->{items}->[0];
+       my $last = $s->{items}->[$#{$s->{items}}];
+       my $result = $s->{proto};
+       my $index = $s->{index};
+
+       #map { $index->{$_->{name}} = $_ } @{$s->{items}};
+
+       my $override = {};
+
+       # force handles to be instance types
+       if ($first->{deref} eq 'handle') {
+               $override->{$first->{name}}->{type} = 'instance';
+       }
+
+       # extension functions
+       if (defined($s->{extensions})) {
+               $override->{template} = $commandTypes->{templates}->{'method-extension'};
+       }
+
+       if ($last->{deref} eq 'handle*') {
+               if (!$last->{len}) {
+                       my $t = $api->{handles}->{$last->{baseType}};
+                       print "constructor: $s->{name}\n";# if $sys->{verbose};
+                       $override->{$last->{name}}->{type} =
+                               $t->{type} eq 'VK_DEFINE_HANDLE' && $t->{name} ne 'VkInstance' ?
+                               'dispatch*-output' : 'handle*-output';
+               } elsif ($index->{$last->{len}}) {
+                       print "constructor?: $s->{name}\n";# if $sys->{verbose};
+                       die;
+               } else {
+                       print "allocate-constructor?: $s->{name} $last->{len}\n";# if $sys->{verbose};
+               }
+       }
+
+       # ones we care about with output
+       #       handle*-length
+       #       struct*-length
+       #       uint32_t*-length
+       #       void*-length
+
+       if ($s->{successcodes} =~ m/VK_INCOMPLETE/ && $last->{deref} =~ m/-length$/) {
+               my $protoa = "$result->{fullType} $s->{name}("
+                       .join(', ', map { "$_->{fullType}" } @{$s->{items}})
+                       .")";
+               my $protob = "$result->{deref} $s->{name}("
+                       .join(', ', map { $_->{len} ? "$_->{deref} \[$_->{len}\]" : $_->{deref} } @{$s->{items}})
+                       .")";
+
+               print "array-constructor: $protoa\n";
+               print "array-constructor: $protob\n";
+               $override->{template} =
+                       defined($s->{extensions}) ?
+                       $commandTypes->{templates}->{'method-extension-query'} :
+                       $commandTypes->{templates}->{'method-query'};
+               foreach my $m (@{$s->{items}}) {
+                       if ($m->{deref} =~ m/-length$/ && (my $len = $index->{$m->{len}})) {
+                               my $type;
+
+                               if ($m->{deref} eq 'handle*-length' && $api->{handles}->{$last->{baseType}}->{type} eq 'VK_DEFINE_HANDLE') {
+                                       $type = 'dispatch*-length-query';
+                               } else {
+                                       $type = $m->{deref}.'-query';
+                               }
+
+                               die "no template $m->{deref}-query" if !defined($commandTypes->{types}->{$type});
+                               $override->{$m->{name}}->{type} = $type;
+
+                               die "no template $len->{deref}-querysize" if !defined($commandTypes->{types}->{$len->{deref}.'-querylen'});
+                               $override->{$len->{name}}->{type} = $len->{deref}.'-querylen';
+                       }
+               }
+       }
+
+       # implied lengths
+       foreach my $m (@{$s->{items}}) {
+               if ($m->{lengthfor}) {
+                       my $nstar = $m->{deref} =~ tr/*/*/;
+                       if ($nstar == 0) {
+                               die "No '$m->{deref}-length' type ".Dumper($s) if !defined $types->{$m->{deref}.'-length'};
+                               $override->{$m->{name}}->{type} = $m->{deref}.'-length';
+                       } else {
+                               if (defined($s->{extensions})) {
+                                       #$overrides->{$s->{name}}->{template} = $commandTypes->{templates}->{'method-extension'};
+                                       print "length-extension: $m->{deref} $s->{name} $m->{name} $m->{lengthfor}\n" if $sys->{verbose};
+                               } else {
+                                       #die "No '$m->{deref}-output' type ".Dumper($s) if !defined $types->{$m->{deref}.'-otuput'};
+
+                                       #$overrides->{$s->{name}}->{template} = $commandTypes->{templates}->{'method-query'};
+                                       #$overrides->{$s->{name}}->{$m->{name}}->{type} = $types->{$m->{deref}.'-output'};
+
+                                       print "length: $m->{deref} $s->{name} $m->{name} $m->{lengthfor}\n" if $sys->{verbose};
+                               }
+
+                       }
+                       # TODO: implied return things
+               }
+       }
+
+       $overrides->{$s->{name}} = $override if %{$override};
+}
+
+{
+       my $overrides = $structTypes->{overrides};
+       my $templates = $structTypes->{templates};
+
+       print Dumper(\%defaultTemplate);
+
+       foreach my $k (keys %defaultTemplate) {
+               print "what?: ".Dumper($overrides->{$k}->{template}) if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
+               next if defined($overrides->{$k}) && defined($overrides->{$k}->{template});
+
+               my $t = $defaultTemplate{$k};
+               my $name = $t->{name} || "struct-writeonly";
+
+               $name .= "-array" if $t->{array};
+
+               print "$name: $k\n";
+               die "No override $k $name" if !$templates->{$name};
+               $overrides->{$k}->{template} = $templates->{$name};
+       }
+}
+
+
+#$overrides->{$s->{name}}->{template} = $structTypes->{templates}->{'struct-readonly'};
+
+#print Dumper({ types=>$types, templates=>$templates });
+#print Dumper($vk);
+
+open(my $f, '>', 'types.pm');
+print $f Dumper($commandTypes, $structTypes);
+close $f;
+
+if (1) {
+
+       if (0) {
+               open(my $f, '>', 'api.pm');
+               print $f Dumper($api);
+               close $f;
+               die;
+       }
+
+       if (1) {
+               open(my $f, '>', 'data.pm');
+               print $f Dumper({
+                       'handles' => $api->{handles},
+                               'types' => $api->{types},
+                               'commands' => $api->{commands}
+                                               });
+               close $f;
+       }
+
+       if (0) {
+               my $f = openOutput($sys, "API");
+
+               print $f "package vulkan;\n";
+               print $f "import jdk.incubator.foreign.*;\n";
+               print $f "import java.lang.invoke.*;\n";
+               print $f "import au.notzed.nativez.*;\n";
+               print $f "public class API {\n";
+               print $f "MemoryAddress self;\n";
+
+               foreach my $c (values %{$api->{commands}}) {
+                       die if ($c->{alias});
+                       #print "$c->{name}\n";
+                       print $f formatFunction($api, $commandTypes, $c)."\n";
+                       #print formatFunctionDescriptor($commandTypes, $c)."\n";
+               }
+               print $f "}\n";
+               closeOutput($sys, "API", $f);
+       }
+
+       exportEnums($vk, $api, 'VkConstants');
+
+       # dump out the extension function tables
+       {
+               my $f = openOutput($sys, 'DispatchInstance');
+               my $template = $structTypes->{templates}->{dispatch};
+               my @init = ();
+               my @fieldInit = ();
+
+               foreach my $k (sort keys %{$api->{commands}}) {
+                       my $c = $api->{commands}->{$k};
+
+                       next if !defined($c->{extensions});
+
+                       push @fieldInit, code::formatTemplate($template->{'field-init'}, $c);
+                       push @init, code::formatTemplate($template->{'init'}, $c);
+
+               }
+
+               my $vars = {
+                       package => 'vulkan',
+                       Name => 'DispatchInstance',
+                       init => join("\n\t\t", @init),
+                       'field-init' => join("\n\t", @fieldInit),
+               };
+               print $f code::formatTemplateStream($template->{class}, $vars);
+
+               closeOutput($sys, 'DispatchInstance', $f);
+       }
+
+
+
+       foreach my $k (sort keys %{$api->{types}}) {
+               my $s = $api->{data}->{$k};
+
+               die if !defined $s;
+               next if $s->{alias};
+
+               my $f = openOutput($sys, $s->{Name});
+               print $f formatStruct($vk, $api, $s);
+               closeOutput($sys, $s->{Name}, $f);
+       }
+
+       foreach my $k (sort keys %{$api->{handles}}) {
+               my $s = $api->{data}->{$k};
+
+               die if !defined $s;
+               next if $s->{alias};
+
+               my $f = openOutput($sys, $s->{name});
+               print $f formatHandle($vk, $api, $s);
+               closeOutput($sys, $s->{name}, $f);
+       }
+
+       foreach my $k (sort keys %{$api->{funcpointers}}) {
+               my $s = $api->{data}->{$k};
+
+               die if !defined $s;
+               next if $s->{alias};
+
+               my $f = openOutput($sys, $s->{name});
+               print $f formatFunctionPointer($vk, $api, $s);
+               closeOutput($sys, $s->{name}, $f);
+       }
+
+
+       exit 0;
+
+       #print Dumper (grep { !defined $_->{items} } values %{$api->{enums}});
+
+       #print Dumper($api->{data});
+
+       print "Unique Types:\n";
+       foreach my $k (sort keys %$all) {
+               print "$k\n";
+       }
+
+       exit 0;
+
+       foreach my $k (sort keys %{$api->{commands}}) {
+               my $c = $api->{commands}->{$k};
+
+               #print Dumper ($c);
+
+               foreach my $m ($c->{proto}, @{$c->{items}}) {
+                       my $t = $vk->{data}->{$m->{baseType}};
+
+                       die if !defined $m->{baseType};
+                       print "? $m->{baseType} ($k $m->{name}\n" if !defined($t);
+
+                       while ($t->{alias}) {
+                               print "Alias: $t->{name} -> $t->{alias}\n";
+                               $t = $vk->{data}->{$t->{alias}};
+                       }
+               }
+       }
+
+} else {
+
+foreach my $k (sort keys %{$vk->{index}}) {
+       print "$k\n";
+}
+
+my $masks = {};
+my $enums = {};
+
+foreach my $k (sort keys %{$vk->{types}}) {
+       my $s = $vk->{types}->{$k};
+       #print Dumper($s);
+       foreach my $m (@{$s->{items}}) {
+               my $t = $vk->{data}->{$m->{baseType}};
+
+               die if !defined $m->{baseType};
+               print "? $m->{baseType} ($k $m->{name}\n" if !defined($t);
+
+               while ($t->{alias}) {
+                       print "Alias: $t->{name} -> $t->{alias}\n";
+                       $t = $vk->{data}->{$t->{alias}};
+               }
+
+               $masks->{$t->{name}} = $t if ($t->{category} eq 'bitmask');
+               $enums->{$t->{name}} = $t if ($t->{category} eq 'enum');
+       }
+}
+
+print Dumper($masks);
+foreach my $k (sort keys %{$masks}) {
+       my $s = $masks->{$k};
+       my $t;
+
+       if ($s->{requires}) {
+               $t = $vk->{data}->{$s->{requires}};
+       } elsif ($s->{name} =~ m/(.*)Flags([0-9A-Z]*)/o && defined $vk->{data}->{"$1FlagBits$2"}) {
+               print "> $s->{name} $1FlagBits$2\n";
+               $t = $vk->{data}->{"$1FlagBits$2"};
+       } else {
+               print "? $s->{name}\n";
+               $t = $s;
+       }
+       print "! $s->{name} r=$s->{requires}\n" if !defined($t);
+       #print Dumper($t);
+
+}
+}
+
+sub loadTypes {
+       my $api = shift;
+       my $file = shift;
+       my $config = new config({ includes=>[ $FindBin::Bin ] }, "$FindBin::Bin/$file");
+
+       my $types = {};
+       my $templates = {};
+       my $overrides = {};
+
+       foreach my $t (@{$config->{objects}}) {
+               if ($t->{type} eq 'type') {
+                       my $nopts = $#{$t->{options}};
+                       my $type;
+
+                       if ($nopts >= 0 && defined($types->{$t->{options}->[0]})) {
+                               $type = { %{$types->{$t->{options}->[0]}} };
+                       } elsif ($#{$t->{items}} >= 0) {
+                               $type = {};
+                       } else {
+                               die ("No prototype provided/found for empty type ".Dumper($t));
+                       }
+
+                       if ($#{$t->{items}} >= 0) {
+                               foreach my $s (@{$t->{items}}) {
+                                       my $x = {
+                                               code => defined($s->{literal}) ? $s->{literal} : $s->{options}->[$#{$s->{options}}]
+                                       };
+
+                                       $x->{eval} = 1 if config::optionFlag('eval', $s);
+
+                                       $type->{$s->{match}} = $x;
+                               }
+                       }
+
+                       # check other options
+                       foreach my $o (@{$t->{options}}) {
+                               if ($o =~ m/^accessor=(.*)$/o) {
+                                       die "No template $1" if !defined($templates->{$1});
+                                       $type->{accessor} = $templates->{$1};
+                               } elsif ($o eq 'need-frame') {
+                                       $type->{'need-frame'} = 1;
+                               } elsif ($o eq 'need-scope') {
+                                       $type->{'need-scope'} = 1;
+                               } elsif ($o eq 'need-alloc') {
+                                       $type->{'need-alloc'} = 1;
+                               } elsif ($o eq 'is-instance') {
+                                       $type->{'is-instance'} = 1;
+                               } else {
+                                       # doesn't ignore implied parant type
+                                       #die "Unknown option '$o' in ".Dumper($t);
+                               }
+                       }
+
+                       foreach my $k (split /,/,$t->{name}) {
+                               $types->{$k} = $type;
+                       }
+               } elsif ($t->{type} eq 'code') {
+                       my $code = {
+                               insert => {},
+                       };
+
+                       foreach my $s (@{$t->{items}}) {
+                               $code->{$s->{match}} = $s->{literal};
+                               $code->{$s->{match}} =~ s/^\t//gm;
+                       }
+                       foreach my $o (@{$t->{options}}) {
+                               if ($o =~ m/insert=(.*)/) {
+                                       foreach my $t (split /,/,$1) {
+                                               if ($t =~ m/(.*):(.*)/) {
+                                                       die if !defined $templates->{$1}->{$2};
+                                                       $code->{insert}->{$2} = $templates->{$1}->{$2};
+                                               }
+                                       }
+                               }
+                       }
+
+                       $templates->{$t->{name}} = $code;
+               } elsif ($t->{type} eq 'override') {
+                       foreach my $s (@{$t->{items}}) {
+                               my $c = { };
+                               foreach my $o (@{$s->{options}}) {
+                                       if ($o =~ m/^(.*)=type:(.*)/) {
+                                               die "No such type $s->{match} $2\n" if !defined $types->{$2};
+                                               $c->{$1}->{type} = $2;
+                                       } elsif ($o =~ m/^(.*)=accessor:(.*)/) {
+                                               die "No accessor template $o" if !defined($templates->{$2});
+                                               $c->{$1}->{accessor} = $templates->{$2};
+                                       #} elsif ($o =~ m/^(.*)=method:(.*)/) {
+                                       #       die "No method template $o" if !defined($templates->{$2});
+                                       #       $c->{$1}->{method} = $templates->{$2};
+                                       } elsif ($o =~ m/^template=(.*)/) {
+                                               die "No template $o" if !defined($templates->{$1});
+                                               $c->{template} = $templates->{$1};
+                                       }
+                               }
+                               $overrides->{$s->{match}} = $c;
+                       }
+               }
+       }
+
+       # templates should probably just go in one
+       {
+               types => $types,
+               templates => $templates,
+               overrides => $overrides,
+       };
+}
+
+sub uniq {
+  my %seen;
+  return grep { !$seen{$_}++ } @_;
+}
+
+sub exportEnums {
+       my $vk = shift;
+       my $api = shift;
+       my $name = shift;
+       my $seen = {};
+
+       my $f = openOutput($sys, $name);
+
+       print $f "package vulkan;\npublic interface VkConstants {\n";
+
+       # special case for api constants
+       # special case for api constants
+       {
+               my $s = $api->{data}->{'API Constants'};
+
+               print $f "\n\t// API Constants\n";
+
+               foreach my $m (@{$s->{items}}) {
+                       next if defined($m->{alias});
+                       next if $seen->{$m->{name}}++;
+
+                       my $i = $enumInfo->{$m->{type}};
+                       my $v = $m->{value};
+
+                       # convert to java
+                       $v =~ s/[()ULF]+//gon if (!($v =~ m/^"/));
+
+                       print $f "\tpublic final static $i->{type} $m->{name} = $v$i->{suffix};\n";
+               }
+       }
+
+       foreach my $k (sort keys %{$api->{enums}}) {
+               my $s = $api->{data}->{$k};
+               my $type = $s->{fullType} ? $s->{fullType} : 'VkFlags';
+               my $i = $enumInfo->{$vk->{data}->{$type}->{type}};
+
+               next if $s->{alias};
+
+               print $f "\n\t// $s->{name} $type\n";
+
+               foreach my $m (@{$s->{items}}) {
+                       next if defined($m->{alias});
+                       next if $seen->{$m->{name}}++;
+
+                       my $v = $m->{value};
+
+                       $v = 1<<$m->{bitpos} if !defined($v) && defined($m->{bitpos});
+
+                       die Dumper("Ca't work out value", $m, $s) if !defined($v);
+                       print $f "\tpublic final static $i->{type} $m->{name} = $v$i->{suffix};\n";
+               }
+       }
+       print $f "}\n";
+
+       closeOutput($sys, $name, $f);
+}
+
+# ###################################################################### #
+# class.name to class/name.java
+sub classToPath {
+       my $sys = shift;
+       my $name = shift;
+
+       $name = $sys->{package}.'.'.$name;
+       $name =~ s@\.@/@g;
+       $name = $sys->{output}.'/'.$name.'.java';
+       $name;
+}
+
+sub closeOutput {
+       my $sys = shift;
+       my $name = shift;
+       my $f = shift;
+       my $path = classToPath($sys, $name);
+
+       close($f) || die;
+       rename($path.'~', $path) || die ("rename failed: $!");
+}
+
+sub openOutput {
+       my $sys = shift;
+       my $name = shift;
+
+       my $path = classToPath($sys, $name);
+       my $dir = dirname($path);
+
+       make_path($dir) if (!-d $dir);
+
+       open(my $f, ">", $path.'~') || die ("Cannot open '$path' for writing");
+       print "writing '$path'\n" if $sys->{verbose} > 0;
+       $f;
+}
+
+
+# Calculate canonical derefernce types for each field and parameter
+sub analyseFields {
+       my $vk = shift;
+       my $api = shift;
+       my $seen = shift;
+       my $s = shift;
+
+       # what about const?
+       # FIXME: bitfields
+
+       my $index = {};
+
+       map { $index->{$_->{name}} = $_ } @_;
+
+       $s->{index} = $index;
+
+       foreach my $m (@_) {
+               my $t = $api->{data}->{$m->{baseType}};
+               my $nstar = $m->{fullType} =~ tr/*/*/;
+               my $type;
+               my $array = '';
+
+               # Check array sizes
+               if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/o) {
+                       $array = '[][]';
+                       $m->{len1} = $1;
+                       $m->{len2} = $2;
+               } elsif ($m->{fullType} =~ m/\[(.*)\]$/o) {
+                       my $isize = $1;
+                       my $size;
+                       if ($isize =~ m/^\d+$/n) {
+                               $size = $isize;
+                       } else {
+                               $size = $vk->{data}->{'API Constants'}->{index}->{$isize}->{value};
+                       }
+                       $array = '[]';
+                       $m->{len1} = $size;
+               } elsif ($m->{fullType} =~ m/[\[\]]/on) {
+                       die Dumper($m)
+               }
+
+               if (!defined($t)) {
+                       # will be primitive or external
+                       $type = $m->{baseType} if ($nstar == 0);
+                       $type = "$m->{baseType}*" if ($nstar == 1);
+                       $type = "$m->{baseType}**" if ($nstar == 2);
+                       die if $nstar > 2;
+               } else {
+                       # unmap aliases
+                       while ($t->{alias}) {
+                               print "alias $t->{name} -> $t->{alias}\n";
+                               $t = $api->{data}->{$t->{alias}};
+                               die if !defined $t;
+                               $m->{baseType} = $t->{name};
+                       }
+
+                       if ($t->{category} =~ m/enum|bitmask/on) {
+                               $t = $vk->{data}->{$t->{fullType}};
+                               $type = $t->{type} if ($nstar == 0);
+                               $type = "$t->{type}*" if ($nstar == 1);
+                               die if $nstar > 1;
+                       } elsif ($t->{category} =~ m/struct|union/on) {
+                               $m->{type} = $t->{name};
+                               $type = 'struct' if ($nstar == 0);
+                               $type = 'struct*' if ($nstar == 1);
+                               $type = 'struct**' if ($nstar == 2);
+                               die if $nstar > 2;
+                       } elsif ($t->{category} eq 'handle') {
+                               $m->{type} = $t->{name};
+                               #if ($t->{type} eq 'VK_DEFINE_HANDLE') {
+                               #       $type = 'dhandle' if ($nstar == 0);
+                               #       $type = 'dhandle*' if ($nstar == 1);
+                               #} else {
+                                       $type = 'handle' if ($nstar == 0);
+                                       $type = 'handle*' if ($nstar == 1);
+                               #}
+                               die if $nstar > 1;
+                       } elsif ($t->{category} eq 'basetype') {
+                               # ??
+                               $type = $t->{type} if ($nstar == 0);
+                               $type = "$t->{type}*" if ($nstar == 1);
+                               die Dumper($m, $t) if $nstar > 1;
+                       } elsif ($t->{category} eq 'funcpointer') {
+                               $m->{type} = $t->{name};
+                               $type = "funcpointer" if ($nstar == 0);
+                               die if $nstar > 0;
+                       } else {
+                               die Dumper($m, $t);
+                       }
+               }
+
+               # an array type with a length
+               if ($nstar > 0 && $m->{len}) {
+                       if ($s->{category} =~ m/struct|union/on) {
+                               if ($m->{altlen}) {
+                                       if ($m->{altlen} =~ m/^(.*)(VK_UUID_SIZE)(.*)$/) {
+                                               $m->{length} = $1.'VkConstants.'.$2.$3;
+                                       } elsif ($m->{altlen} =~ m/^([^a-zA-Z_]*)([a-zA-Z_]+)(.*)$/) {
+                                               my $len = $index->{$2};
+                                               if (defined $len) {
+                                                       $m->{length} = $1.'get'.ucfirst($2).'()'.$3;
+                                                       #$index->{$2}->{lengthfor} = $m->{name} if $index->{$2};
+                                                       $m->{lengthfrom} = $len->{name};
+                                               }
+                                       } else {
+                                               die "Unhandled len/altlen: ".Dumper($m);
+                                       }
+                               } elsif ($m->{len} =~ m/(.*),null-terminated/) {
+                                       my $len = $index->{$1};
+                                       if (defined $len) {
+                                               $m->{length} = "get$len->{Name}()";
+                                               $m->{lengthfrom} = $len->{name};
+                                               $len->{lengthfor} = $m->{name};
+                                               $m->{'set-length'} = "set$len->{Name}((int)Memory.length($m->{name}))";
+                                       }
+                               } elsif ($m->{len} eq 'null-terminated') {
+                                       # ignore
+                               } elsif ($m->{len} =~ m/^(.*),(\d+)$/) {
+                                       # ignore?
+                               } else {
+                                       my $len = $index->{$m->{len}};
+                                       if (defined $len) {
+                                               my $cast = ($len->{fullType} eq 'uint32_t') ? '(int)' : '';
+
+                                               die "Not simple type" if ($len->{fullType} ne $len->{baseType});
+                                               $m->{length} = "get$len->{Name}()";
+                                               $m->{lengthfrom} = $len->{name};
+                                               $len->{lengthfor} = $m->{name};
+                                               $m->{'set-length'} = "set$len->{Name}($cast"."Memory.length($m->{name}))";
+                                       } else {
+                                               die "what?".Dumper($m);
+                                       }
+                               }
+                       } elsif ($s->{category} eq 'command') {
+                               if ($m->{altlen}) {
+                                       die;
+                               } else {
+                                       $m->{length} = $m->{len} if $index->{$m->{len}};
+                                       $index->{$m->{len}}->{lengthfor} = $m->{name} if $index->{$m->{len}};
+                               }
+                       } else {
+                               die Dumper($s);
+                       }
+                       $type = $type.'-length' if $m->{length};
+               }
+
+               $seen->{$m->{fullType}} = $type.$array;
+               $m->{deref} = $type.$array;
+
+               my $name = $m->{name};
+               #if ($s->{type} =~ m/struct|union/on)
+               {
+                       # Strip leading 'p' for pointers
+                       if ($name eq 'ppGeometries') { # && $s->{name} eq 'VkAccelerationStructureBuildGeometryInfoKHR') {
+                               $name = 'PGeometries';
+                       } elsif ($nstar > 0 && $name =~ m/^p{$nstar}/) {
+                               my $strip = $nstar;
+
+                               if ($t->{category} eq 'handle' && $type ne 'handle*-length') {
+                                       $strip -= 1;
+                               }
+
+                               $name = substr $name, $strip;
+                       }
+
+                       $name =~ s/^pfn//o if $type eq 'funcpointer';
+                       # CamelCase
+                       $name =~ s/(?:^|_)(.)/\U$1/og;
+               }
+               $m->{Name} = $name;
+       }
+}
+
+sub analyseTypes {
+       my $vk = shift;
+       my $api = shift;
+       my $seen = {};
+
+       foreach my $s (grep { $_->{items} } values %{$api->{types}}) {
+               analyseFields($vk, $api, $seen, $s, @{$s->{items}});
+
+               my $name = $s->{name};
+               $name =~ s/(?:^|_)(.)/\U$1/og;
+               $s->{Name} = $name;
+
+               my $first = $s->{items}->[0];
+               my $second = $s->{items}->[1];
+
+               if ($first->{name} eq 'sType' && $second && $second->{name} eq 'pNext') {
+                       $first->{'no-setall'} = 1;
+                       $second->{'no-setall'} = 1;
+                       print "typed: $s->{name}\n";
+               } else {
+                       print "untyped: $s->{name}\n";
+               }
+       }
+
+       foreach my $c (values %{$api->{funcpointers}}) {
+               analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
+       }
+
+       foreach my $c (values %{$api->{commands}}) {
+               $c->{proto}->{name} = 'result$';
+               $c->{proto}->{Name} = 'result$';
+               analyseFields($vk, $api, $seen, $c, $c->{proto}, @{$c->{items}});
+
+               # collect all member functions on handles
+               my $first = $c->{items}->[0];
+               my $last = $c->{items}->[$#{$c->{items}}];
+               if ($first->{deref} eq 'handle') {
+                       my $t = $api->{handles}->{$first->{baseType}};
+                       while ($t->{alias}) {
+                               $t = $api->{handles}->{$t->{alias}};
+                       }
+                       die "No handle found ".Dumper($c) if !defined $t;
+                       push @{$t->{commands}}, $c->{name};
+               } elsif ($c->{name} =~ m/vkEnumerateInstance|vkCreateInstance/) {
+                       push @{$api->{handles}->{'VkInstance'}->{commands}}, $c->{name};
+               } else {
+                       die "No owner for call ".Dumper($c);
+               }
+       }
+
+       print "Unique Types:\n";
+       my $base = {};
+       map { $base->{$_} = 1 } values %$seen;
+
+       foreach my $k (sort keys %$base) {
+               print "$k\n";
+       }
+}
+
+# what else?
+# vk? api?
+# this way-over-evaluates, probably only call once on every field and member instead
+sub buildVars {
+       my $s = shift;
+       my $m = shift;
+       my $type = shift;
+       my $v = { %{$m} };
+
+       foreach my $k (keys %$type) {
+               my $t = $type->{$k};
+
+               if (ref($t) eq '') {
+                       $v->{$k} = $t;
+               } elsif ($t->{eval}) {
+                       $v->{$k} = eval $t->{code};
+
+                       die "Eval failed: $! $@: ".Dumper($m, $type) if !defined($v->{$k});
+               } elsif (defined($t->{code})) {
+                       $v->{$k} = $t->{code};
+               }
+       }
+
+       $v;
+}
+
+sub formatStructLayout {
+       my $types = shift;
+       my $s = shift;
+       my $offset = 0;
+       my @fields = ();
+
+       # This doens't need to worry about overrides
+
+       foreach my $m (@{$s->{items}}) {
+               my $type = $types->{$m->{deref}};
+               my $diff = $m->{bitOffset} - $offset;
+
+               push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+               if ($type) {
+                       my $v = buildVars($s, $m, $type);
+
+                       push @fields, code::formatTemplate($v->{layout}, $v).".withName(\"$m->{name}\") /* $m->{deref} $m->{fullType} */";
+                       $offset = $m->{bitOffset} + $m->{bitSize};
+               } else {
+                       push @fields, "/* Missing: $m->{deref} $m->{name} */";
+               }
+       }
+       my $diff = $s->{bitSize} - $offset;
+       push @fields, "MemoryLayout.paddingLayout($diff)" if $diff;
+
+       return "MemoryLayout.".$s->{category}."Layout(\n\t\t".join(",\n\t\t", @fields).").withName(\"$s->{name}\")";
+}
+
+sub formatStruct {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $templates = $structTypes->{templates};
+       my $types = $structTypes->{types};
+       my $overrides = $structTypes->{overrides};
+
+       my $override = $overrides->{$s->{name}};
+       my $template = $override->{template} ? $override->{template} : $templates->{'struct-writeonly'};
+
+       my $info = {
+               get => [],
+               set => [],
+               getorset => [],
+               varhandle => [],
+               init => [],
+               create => {},
+       };
+
+       if ($s->{category} eq 'struct') {
+               $info->{create}->{create} = {
+                       setallArgs => [ 'SegmentAllocator alloc$' ],
+                       setall => [],
+               };
+       } elsif ($s->{category} eq 'union') {
+               foreach my $m (@{$s->{items}}) {
+                       $info->{create}->{"create$m->{Name}"} = {
+                               setallArgs => [ 'SegmentAllocator alloc$' ],
+                               setall => [],
+                       };
+               }
+       } else {
+               die;
+       }
+
+       #map { $_->{typeInfo} = buildVars($s, $_, $types->{$_->{deref}}) } @{$s->{items}};
+
+       # FIXME: unions need multiple constructors!
+
+       foreach my $m (@{$s->{items}}) {
+               my $nstar = $m->{deref} =~ tr/*/*/;
+               my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
+               my $type = $types->{$deref};
+               my $v = buildVars($s, $m, $type);
+
+               die "No type $deref ".Dumper($m, $s) if !$type;
+
+               #push @{$info->{getset}}, map { "// $_" } split(/\n/, Dumper($m->{deref}, $type, $override->{$m->{name}}));
+               push @{$info->{varhandle}}, "/* ? Accessor: $m->{fullType} [$m->{deref}] $m->{name} len=$m->{len} lenfor=$m->{lengthfor} override=$deref */";
+
+               if ($type->{accessor}) {
+                       push @{$info->{getorset}}, code::formatTemplate($type->{accessor}->{getorset}, $v) if $type->{accessor}->{getorset};
+
+                       if ($m->{values}) {
+                               push @{$info->{init}}, code::formatTemplate($type->{accessor}->{init}, $v) if $type->{accessor}->{init};
+                               push @{$info->{get}}, code::formatTemplate($type->{accessor}->{get}, $v) if $type->{accessor}->{get};
+                       } else {
+                               push @{$info->{get}}, code::formatTemplate($type->{accessor}->{get}, $v) if $type->{accessor}->{get};
+                               push @{$info->{set}}, code::formatTemplate($type->{accessor}->{set}, $v) if $type->{accessor}->{set};
+
+                               # FIXME: something here is adding length parameters which are already handled by the setXX() calls
+                               if (!$m->{'no-setall'}) {
+                                       my $create = $s->{category} eq 'struct' ? $info->{create}->{create} : $info->{create}->{"create$m->{Name}"};
+                                       push @{$create->{setallArgs}}, code::formatTemplate($type->{accessor}->{'setall-arg'}, $v) if $type->{accessor}->{'setall-arg'};
+                                       push @{$create->{setall}}, code::formatTemplate($type->{accessor}->{setall}, $v) if $type->{accessor}->{setall};
+                               }
+                       }
+               }
+               push @{$info->{varhandle}}, code::formatTemplate($v->{handle}, $v) if $v->{handle};
+       }
+
+       # create constructors
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+               layout => formatStructLayout($types, $s),
+               init => join ("\n", @{$info->{init}}),
+               get => join ("\n", @{$info->{get}}),
+               set => join ("\n", @{$info->{set}}),
+               getorset => join ("\n", @{$info->{getorset}}),
+               #'java-setall-arguments' => join (",", @{$info->{setallArgs}}),
+               #'java-setall' => join ("\n", @{$info->{setall}}),
+               varhandle => join ("\n", @{$info->{varhandle}}),
+       };
+
+
+       # build sub-components using the full $v
+       my @createAll = ();
+       foreach my $k (keys %{$template->{insert}}) {
+               my $t = $template->{insert}->{$k};
+
+               if ($k eq 'create-all') {
+                       foreach my $kk (keys %{$info->{create}}) {
+                               my $create = $info->{create}->{$kk};
+
+                               if ($#{$create->{setallArgs}} > 0) {
+                                       my $v = {
+                                               create => $kk,
+                                               Name => $s->{Name},
+                                               'java-setall-arguments' => join (', ', @{$create->{setallArgs}}),
+                                               'java-setall' => join ("\n\t\t", @{$create->{setall}}),
+                                       };
+                                       push @createAll, code::formatTemplateStream($t, $v);
+                               }
+                       }
+               } else {
+                       $v->{$k} = code::formatTemplate($t, $v);
+               }
+       }
+       $v->{'create-all'} = join("\n", @createAll);
+
+       join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
+               code::formatTemplateStream($template->{class}, $v);
+}
+
+# TODO: the template here could be mapped from types.api perhaps?
+# also same for the various fields, init/getset/etc.
+sub formatHandle {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $templates = $structTypes->{templates};
+       my $overrides = $structTypes->{overrides};
+       my $override = $overrides->{$s->{name}};
+       my $template = $override->{template} ? $override->{template} : $templates->{handle};
+
+       my $info = {
+               init => [],
+               commands => [],
+       };
+
+       if (defined $s->{commands}) {
+               foreach my $k (sort @{$s->{commands}}) {
+                       my $c = $api->{commands}->{$k};
+                       push @{$info->{commands}}, formatFunction($api, $commandTypes, $c);
+               }
+       }
+
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+               init => join ("\n", @{$info->{init}}),
+               commands => join ("\n", @{$info->{commands}}),
+       };
+
+       code::formatTemplateStream($template->{class}, $v);
+}
+
+sub formatFunctionPointer {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $templates = $structTypes->{templates};
+       my $template = $templates->{funcpointer}->{class};
+
+       my $info = {
+               init => [],
+       };
+
+       my $vcall = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+       };
+
+       my $v = {
+               package => 'vulkan',
+               name => $s->{name},
+               Name => $s->{Name},
+               init => join ("\n", @{$info->{init}}),
+               upcall => code::formatTemplateStream($templates->{funcpointer}->{upcall}, $vcall),
+               downcall => code::formatTemplateStream($templates->{funcpointer}->{downcall}, $vcall),
+       };
+
+       code::formatTemplateStream($template, $v);
+}
+
+sub formatFunctionDescriptor {
+       my $ct = shift;
+       my $s = shift;
+
+       my $templates = $ct->{templates};
+       my $types = $ct->{types};
+       my $overrides = $ct->{overrides};
+
+       my @fields = ();
+       my $void = $s->{proto}->{fullType} eq 'void';
+       my $override = $overrides->{$s->{name}};
+
+       foreach my $m ($void ? () : $s->{proto}, @{$s->{items}}) {
+               my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
+               my $type = $types->{$deref};
+
+               die "No type found ".Dumper($m, $s, $override) if !$type;
+
+               my $v = buildVars($s, $m, $type);
+
+               push @fields, code::formatTemplate($v->{layout}, $v)." /* $m->{deref} $m->{name} */";
+       }
+
+       return ($void ? 'FunctionDescriptor.ofVoid(' : 'FunctionDescriptor.of(')
+               .join(",\n\t\t", @fields).')';
+}
+
+sub formatFunction {
+       my $api = shift;
+       my $ct = shift;
+       my $s = shift;
+       my $templates = $ct->{templates};
+       my $types = $ct->{types};
+       my $overrides = $ct->{overrides};
+       my $void = $s->{proto}->{fullType} eq 'void';
+       my $override = $overrides->{$s->{name}};
+       my $template = $override->{template} ? $override->{template} : $templates->{method};
+
+       my @javaArgs = ();
+       my @invokeArgs = ();
+       my @nativeInit = ();
+       my @queryInit = ();
+       my @queryArgs = ();
+
+       my $info = {
+               rename => $s->{name},
+               name => $s->{name},
+               'function-descriptor' => formatFunctionDescriptor($ct, $s),
+               'native-result-define' => '',
+               'native-result-assign' => '',
+               'result-test' => '',
+               'create-frame' => '',
+               'java-result' => 'void',
+               'java-result-assign' => '',
+               'java-result-return' => 'return;',
+               'result-throw' => '',
+       };
+
+       my $hasInstance = 0;
+       my $needFrame = 0;
+       my $needAlloc = 0;
+       my $needScope = 0;
+
+       #return if !defined($override->{template});
+       #return if ($s->{name} ne 'vkCmdUpdateBuffer');
+
+       foreach my $m (@{$s->{items}}) {
+               my $deref = defined($override->{$m->{name}}) && defined($override->{$m->{name}}->{type}) ? $override->{$m->{name}}->{type} : $m->{deref};
+               my $type = $types->{$deref};
+
+               die "No type found ".Dumper($m, $s, $override) if !$type;
+
+               my $v = buildVars($s, $m, $type);
+
+               #push @javaArgs, "/* $m->{name} $m->{deref} */ " if !$v->{'java-argument'};
+
+               push @javaArgs, "/* $m->{name} $m->{deref} */ ".code::formatTemplate($v->{'java-arg'}, $v) if ($v->{'java-arg'});
+               push @invokeArgs, code::formatTemplate($v->{'invoke-arg'}, $v) if ($v->{'invoke-arg'});
+
+               push @nativeInit, code::formatTemplate($v->{'native-init'}, $v) if ($v->{'native-init'});
+               push @queryInit, code::formatTemplate($v->{'query-init'}, $v) if ($v->{'query-init'});
+
+               if ($v->{'query-arg'}) {
+                       push @queryArgs, code::formatTemplate($v->{'query-arg'}, $v);
+               } elsif ($v->{'invoke-arg'}) {
+                       push @queryArgs, code::formatTemplate($v->{'invoke-arg'}, $v);
+               }
+
+               $info->{'java-result'} = code::formatTemplate($v->{'java-result'}, $v) if ($v->{'java-result'});
+               $info->{'java-result-return'} = code::formatTemplate($v->{'java-result-return'}, $v) if ($v->{'java-result-return'});
+               $info->{'java-result-assign'} = code::formatTemplate($v->{'java-result-assign'}, $v) if ($v->{'java-result-assign'});
+
+               $needScope = 1 if $type->{'need-scope'};
+               $needFrame = 1 if $type->{'need-frame'};
+               $needAlloc = 1 if $type->{'need-alloc'};
+               $hasInstance = 1 if $type->{'is-instance'};
+       }
+
+       $info->{'static'} = $hasInstance ? '' : 'static ';
+
+       if ($s->{successcodes}) {
+               my @codes = split(/,/,$s->{successcodes});
+
+               $info->{'native-result-define'} = 'int result$;';
+               $info->{'native-result-assign'} = 'result$ = (int)';
+               $info->{'result-test'} = 'if ('.join("||", map { '(result$ == VkConstants.'.$_.')' } @codes).')';
+               $info->{'result-throw'} = 'throw new RuntimeException("error " + result$);';
+
+               if ($#codes > 0 && $info->{'java-result'} eq 'void') {
+                       $info->{'java-result'} = 'int';
+                       $info->{'java-result-return'} = 'return result$;';
+               }
+
+       } elsif ($s->{proto}->{fullType} ne 'void') {
+               my $m = $s->{proto};
+               my $type = defined($override->{$m->{name}}) ? $override->{$m->{name}}->{type} : $types->{$m->{deref}.'-return'};
+
+               die Dumper($m, $s) if !defined($type);
+               die Dumper($m, $s) if !defined($type->{'java-result-return'});
+
+               my $v = buildVars($s, $m, $type);
+
+               $info->{'native-result-define'} = code::formatTemplate($v->{'native-result-define'}, $v);
+               $info->{'native-result-assign'} = code::formatTemplate($v->{'native-result-assign'}, $v);
+               $info->{'native-result-define'}.= join("", map { "// $_\n" } split(/\n/, Dumper($m)));
+               $info->{'java-result'} = code::formatTemplate($v->{type}, $v);
+               $info->{'java-result-return'} = code::formatTemplate($v->{'java-result-return'}, $v);
+
+               $needScope = 1 if $type->{'need-scope'};
+       }
+
+       $info->{'create-frame'} = '(Frame frame$ = Frame.frame())' if $needFrame;
+       push @javaArgs, 'SegmentAllocator alloc$' if $needAlloc;
+       push @javaArgs, 'ResourceScope scope$' if $needScope;
+
+       $info->{'java-arguments'} = join ",\n\t", @javaArgs;
+       $info->{'native-init'} = join "\n\t", @nativeInit;
+       $info->{'invoke-arguments'} = join ", ", @invokeArgs;
+       $info->{'query-init'} = join "\n\t\t\t", @queryInit;
+       $info->{'query-arguments'} = join ", ", @queryArgs;
+
+       $info->{successcodes} = $s->{successcodes} ? $s->{successcodes} : '';
+       $info->{errorcodes} = $s->{errorcodes} ? $s->{errorcodes}: '';
+
+       #join("\n", map { '// '.$_ } split(/\n/,Dumper($s)))."\n".
+       code::formatTemplate($template->{invoke}, $info);
+}
diff --git a/src/notzed.vulkan/gen/struct-types.api b/src/notzed.vulkan/gen/struct-types.api
new file mode 100644 (file)
index 0000000..1625945
--- /dev/null
@@ -0,0 +1,821 @@
+# -*- Mode:text; tab-width:4; electric-indent-mode: nil; indent-line-function:insert-tab; -*-
+
+# types for structs and unions
+
+#accessor=code template
+# {type} - java type
+# {java-get}
+# {java-set}
+# {init}*
+
+code value {
+  get {{
+       /* {deref} */
+       public {type} get{Name}() {
+               return {java-get};
+       }
+  }}
+  set {{
+       /* {deref} */
+       public void set{Name}({type} {name}) {
+               {java-set};
+       }
+  }}
+
+  # FIXME: only handles single element arrays
+  init {{ {name}$VH.set(this.segment, VkConstants.{values}); }}
+
+  # for complex constructors?
+  setall-arg {{ {type} {name} /* value */ }}
+  setall {{ self$.set{Name}({name}); }}
+}
+
+code value-array {
+  getorset {{
+       /* value-array {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+       public {typei} get{Name}Element(int i$) {
+               return {java-geti};
+       }
+       public void set{Name}Element(int i$, {typei} {name}) {
+               {java-seti};
+       }
+  }}
+}
+
+code value-array2d {
+  getorset {{
+       /* value-array2d {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+       public {typei} get{Name}Element(int i$, int j$) {
+               return {java-geti};
+       }
+       public void set{Name}Element(int i$, int j$, {typei} {name}) {
+               {java-seti};
+       }
+  }}
+}
+
+code inline {
+  getorset {{
+       /* inline {deref} */
+       public {type} get{Name}() {
+               try {
+                       return {java-get};
+               } catch (Throwable t) {
+                       throw new RuntimeException(t);
+               }
+       }
+  }}
+}
+
+# value with a SegmentAllocator passed to set()
+code value-alloc {
+  get {{
+       /* {deref} */
+       public {type} get{Name}() {
+               return {java-get};
+       }
+  }}
+  set {{
+       /* {deref} */
+       public void set{Name}({type} {name}, SegmentAllocator alloc$) {
+               {java-set};
+       }
+  }}
+  setall-arg {{ {type} {name} }}
+  setall {{ self$.set{Name}({name}, alloc$); }}
+}
+
+# implied accessors are ignored in constructors
+code value-implied {
+  get {{
+       /* {deref} */
+       public {type} get{Name}() {
+               return {java-get};
+       }
+  }}
+  set {{
+       /* {deref} */
+       public void set{Name}({type} {name}) {
+               {java-set};
+       }
+  }}
+
+  # supposed to be handled in set of the target, but ...?
+  setall {{ self$.set{Name}(({type})Memory.length({lengthfor})); }}
+}
+
+# ###################################################################### #
+
+code dispatch {
+  class {{
+       // template: dispatch:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+       class {Name} {
+
+               {field-init}
+
+               {Name}(VkInstance instance$, ResourceScope scope$) {
+                       {init}
+               }
+       }
+  }}
+  field-init {{        final NativeSymbol {name}$NS; }}
+  init {{ {name}$NS = instance$.vkGetInstanceProcAddr("{name}", scope$); }}
+
+}
+
+# non-dispatchable handle
+code handle {
+  class {{
+       // template: handle:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+
+               private {name}(MemoryAddress address, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, ResourceScope scope) {
+                       return new {name}(address, scope);
+               }
+
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc) {
+                       return HandleArray.createArray(1, alloc, {name}::create);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+       }
+  }}
+}
+
+# VkInstance
+code handle-instance {
+  class {{
+       // template: handle-instance:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+               final DispatchInstance dispatch;
+
+               private {name}(MemoryAddress address, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       this.dispatch = new DispatchInstance(this, scope);
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, ResourceScope scope) {
+                       return new {name}(address, scope);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+
+               {commands}
+       }
+  }}
+}
+
+# dispatchable handle
+code handle-dispatch {
+  class {{
+       // template: handle-dispatch:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {name} implements Pointer {
+               final NativeSymbol self;
+               final DispatchInstance dispatch;
+
+               private {name}(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
+                       this.self = NativeSymbol.ofAddress("{name}", address, scope);
+                       this.dispatch = dispatch;
+                       {init}
+               }
+
+               public static {name} create(MemoryAddress address, DispatchInstance dispatch, ResourceScope scope) {
+                       return new {name}(address, dispatch, scope);
+               }
+
+               // TODO: evaluate how scope fits here
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, DispatchInstance dispatch, ResourceScope scope) {
+                       return HandleArray.createArray(1, alloc, (a, s) -> create(a, dispatch, s), scope);
+               }
+
+               public static HandleArray<{name}> createArray(long length, SegmentAllocator alloc, VkInstance instance, ResourceScope scope) {
+                       return HandleArray.createArray(1, alloc, (a, s) -> create(a, instance.dispatch, s), scope);
+               }
+
+               public MemoryAddress address() {
+                       return self.address();
+               }
+
+               public ResourceScope scope() {
+                       return self.scope();
+               }
+
+               {commands}
+       }
+  }}
+}
+
+# FIXME: unimplemented
+code funcpointer {
+  class {{
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public interface {name} {
+
+               void call();
+
+               {upcall}
+               {downcall}
+       }
+  }}
+  upcall {{
+       public static FunctionPointer<{Name}> upcall({Name} target$, ResourceScope scope$) {
+               throw new UnsupportedOperationException();
+       }
+  }}
+  downcall {{
+       public static FunctionPointer<{Name}> downcall(MemoryAddress target$, ResourceScope scope$) {
+               throw new UnsupportedOperationException();
+       }
+  }}
+}
+
+# shared struct components
+code struct {
+  header {{
+               public MemorySegment segment;
+
+               public static final GroupLayout LAYOUT = {layout};
+
+               private {Name}(MemorySegment segment) {
+                       this.segment = segment;
+                       {init}
+               }
+
+               public static {Name} create(MemorySegment segment) {
+                       return new {Name}(segment);
+               }
+
+               public static {Name} create(MemoryAddress address, ResourceScope scope) {
+                       return new {Name}(MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope));
+               }
+
+               public static {Name} create(SegmentAllocator alloc) {
+                       return new {Name}(alloc.allocate(LAYOUT));
+               }
+
+               // Pointer
+               @Override
+               public MemoryAddress address() {
+                       return segment.address();
+               }
+
+               // Pointer
+               @Override
+               public ResourceScope scope() {
+                       return segment.scope();
+               }
+
+               @Override
+               public String toString() {
+                       return Memory.toString(segment, LAYOUT);
+               }
+  }}
+  create-all {{
+               public static {Name} {create}({java-setall-arguments}) {
+                       {Name} self$ = create(alloc$.allocate(LAYOUT));
+
+                       {java-setall}
+
+                       return self$;
+               }
+  }}
+  set-all {{
+               public void init({java-setall-arguments}) {
+                       {Name} self$ = this;
+
+                       {java-setall}
+               }
+  }}
+  array {{
+               static {Name} createArray(MemoryAddress addr, long length, ResourceScope scope) {
+                       return new {Name}(MemorySegment.ofAddress(addr, length * LAYOUT.byteSize(), scope));
+               }
+
+               public static {Name} createArray(long length, SegmentAllocator alloc) {
+                       return new {Name}(alloc.allocateArray(LAYOUT, length));
+               }
+
+               public final static MethodHandle LAYOUT$SH = MemoryLayout.sequenceLayout(LAYOUT).sliceHandle(MemoryLayout.PathElement.sequenceElement());
+
+               // Array
+               @Override
+               public long length() {
+                       return segment.byteSize() / LAYOUT.byteSize();
+               }
+
+               // Array
+               #Override
+               public {Name} getAtIndex(long index$) {
+                       try {
+                               return create((MemorySegment)LAYOUT$SH.invokeExact(this.segment, index$));
+                       } catch (Throwable t) {
+                               throw new RuntimeException(t);
+                       }
+               }
+  }}
+}
+
+# default - writeonly struct
+code struct-writeonly insert=struct:header,struct:create-all {
+  class {{
+       // template: struct-writeonly:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {create-all}
+
+               {set}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+code struct-writeonly-array insert=struct:header,struct:create-all,struct:array {
+  class {{
+       // template: struct-writeonly-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer, Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {set}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+code struct-readonly insert=struct:header {
+  class {{
+       // template: struct-readonly:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {get}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+code struct-readonly-array insert=struct:header,struct:array {
+  class {{
+       // template: struct-readonly-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer, Array<{Name}> {
+               {header}
+               {array}
+
+               {get}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+code struct-readwrite insert=struct:header,struct:create-all {
+  class {{
+       // template: struct-readwrite:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer {
+               {header}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+code struct-readwrite-array insert=struct:header,struct:create-all,struct:array {
+  class {{
+       // template: struct-readwrite-array:class
+       package {package};
+       import jdk.incubator.foreign.*;
+       import java.lang.invoke.*;
+       import au.notzed.nativez.*;
+
+       public class {Name} implements Pointer,Array<{Name}> {
+               {header}
+               {array}
+
+               {create-all}
+
+               {get}
+               {set}
+               {getorset}
+
+               {varhandle}
+       }
+  }}
+}
+
+# how to arrays?  in code?
+# how to lengths?
+
+type value accessor=value {
+       java-get        {{ ({type}){name}$VH.get(this.segment) }}
+       java-set        {{ {name}$VH.set(this.segment, {name}) }}
+       handle          {{ final static VarHandle {name}$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}")); }}
+}
+
+type value-array accessor=value-array {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       java-geti       {{ ({typei}){name}$EH.get(i$) }}
+       java-seti       {{ {name}$EH.set(i$, {name}) }}
+       handle          {{
+               final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));
+               final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement());
+       }}
+}
+
+type value-array2d accessor=value-array2d {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       java-geti       {{ ({typei}){name}$EH.get(i$, j$) }}
+       java-seti       {{ {name}$EH.set(i$, j$, {name}) }}
+       handle          {{
+               final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));
+               final static VarHandle {name}$EH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("{name}"), MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
+       }}
+}
+
+type inline accessor=inline {
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+       java-get        {{ {type}.create({native-get}) }}
+       handle          {{ final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}")); }}
+}
+
+type inline-array accessor=inline {
+#      type            {{ MemorySegment }}
+       native-get      {{ (MemorySegment){name}$SH.invokeExact(this.segment) }}
+#      java-get        {{ {native-get} }}
+       java-get        {{ {type}.create({native-get}) }}
+       handle          {{
+               final static MethodHandle {name}$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement("{name}"));
+       }}
+}
+
+type uint8_t,char value {
+       type    {{ byte }}
+       layout  {{ Memory.BYTE }}
+}
+
+type uint16_t value {
+       type    {{ short }}
+       layout  {{ Memory.SHORT }}
+}
+
+type uint32_t,int,int32_t value {
+       type    {{ int }}
+       layout  {{ Memory.INT }}
+}
+
+type uint64_t,int64_t,size_t value {
+       type    {{ long }}
+       layout  {{ Memory.LONG }}
+}
+
+type float value {
+       type    {{ float }}
+       layout  {{ Memory.FLOAT }}
+}
+
+type double value {
+       type    {{ double }}
+       layout  {{ Memory.DOUBLE }}
+}
+
+# ###################################################################### #
+# implied length types
+type uint64_t-implied,size_t-implied uint64_t accessor=value-implied {
+
+}
+
+type uint32_t-implied uint32_t accessor=value-implied {
+}
+
+# ###################################################################### #
+
+type uint8_t[],char[] value-array {
+       type    {{ ByteArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.BYTE) }}
+       typei   {{ byte }}
+}
+
+type uint32_t[],int32_t[] value-array {
+       type    {{ IntArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.INT) }}
+       typei   {{ int }}
+}
+
+type uint64_t[] value-array {
+       type    {{ LongArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.LONG) }}
+       typei   {{ long }}
+}
+
+type float[] value-array {
+       type    {{ FloatArray }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.FLOAT) }}
+       typei   {{ float }}
+}
+
+type struct[] inline-array {
+       type    {{ {baseType} }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, {baseType}.LAYOUT) }}
+}
+
+type float[][] value-array2d {
+       type    {{ FloatArray }}
+       typei   {{ float }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, MemoryLayout.sequenceLayout({len2}, Memory.FLOAT)) }}
+}
+
+# select=len?  or what?
+
+type pointer value {
+       layout  {{ Memory.POINTER }}
+       type    {{ MemoryAddress }}
+
+       native-get      {{ (MemoryAddress){name}$VH.get(this.segment) }}
+
+       java-get        {{ {native-get} }}
+       java-set        {{ {name}$VH.set(this.segment, Memory.address({name})) }}
+}
+
+type void* pointer;
+
+type value-pointer pointer {
+       java-get        {{ {type}.create((MemoryAddress){name}$VH.get(this.segment), segment.scope()) }}
+}
+
+type uint8_t* value-pointer {
+       type    ByteArray;
+}
+
+type uint32_t*,int32_t*,int* value-pointer {
+       type    IntArray;
+}
+
+type uint64_t* value-pointer {
+       type    LongArray;
+}
+
+type float* value-pointer {
+       type    FloatArray;
+}
+
+type size_t* value-pointer {
+       type    LongArray;
+}
+
+type pointer-length pointer {
+       java-get        {{ {type}.createArray({native-get}, {length}, this.segment.scope()) }}
+}
+
+type void*-length pointer-length {
+       type            MemorySegment;
+       java-get        {{ MemorySegment.ofAddress({native-get}, {length}, this.segment.scope()) }}
+}
+
+type uint8_t*-length pointer-length {
+       type    ByteArray;
+}
+
+type uint32_t*-length,int32_t*-length pointer-length {
+       type    IntArray;
+}
+
+type uint64_t*-length pointer-length {
+       type    LongArray;
+}
+
+type float*-length pointer-length {
+       type    FloatArray;
+}
+
+# special handling for strings, will fail if it isn't
+type char* pointer accessor=value-alloc {
+       type            {{ String }}
+
+       java-get        {{ ({native-get}).getUtf8String(0) }}
+       java-set        {{ {name}$VH.set(this.segment, alloc$.allocateUtf8String({name}).address()) }}
+
+       # this just verifies it's a string type
+       length eval     {{
+               if ($v->{len} =~ m/null-terminated/) {
+                       1;
+               } else {
+                       die Dumper($v, $s);
+               }
+       }}
+
+}
+
+type char**-length pointer-length accessor=value-alloc {
+       type    {{ String[] }}
+
+       java-set        {{ {name}$VH.set(this.segment, Memory.copyStringArray({name}, alloc$).address()); {set-length} }}
+       java-get        {{ Memory.copyStringArray((MemoryAddress){name}$VH.get(this.segment), {length}) }}
+
+#      set-length eval {{
+#              if ($v->{len} =~ m/(.*),null-terminated/) {
+#                      'set'.ucfirst($1).'({name}.length)';
+#              } else {
+#                      die Dumper($v, $s);
+#              }
+#      }}
+}
+
+# FIXME: wrong
+type uint32_t** pointer {
+       type    {{ HandleArray<IntArray> }}
+}
+
+type funcpointer pointer {
+       type            {{ FunctionPointer<{baseType}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ {baseType}.downcall({native-get}, this.segment.scope()) }}
+}
+
+type void** pointer {
+}
+
+type void**-length pointer-length {
+       type    PointerArray;
+}
+
+type handle pointer {
+       type            {{ {baseType} }}
+       java-get        {{ {type}.create({native-get}, this.segment.scope()) }}
+}
+
+# FIXME: wrong
+type handle[] {
+       type    {{ HandleArray<{typei}> }}
+       layout  {{ MemoryLayout.sequenceLayout({len1}, Memory.POINTER) }}
+       #java-get       {{ HandleArray.create({get}, {typei}::create) }}
+       java-get        {{ error }}
+       typei   {{ {baseType} }}
+}
+
+type handle* pointer {
+       type            {{ HandleArray<{typei}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ HandleArray.create({native-get}, {typei}::create) }}
+}
+
+type handle*-length pointer-length {
+       type            {{ HandleArray<{baseType}> }}
+       typei           {{ {baseType} }}
+       java-get        {{ HandleArray.createArray({native-get}, {length}, {typei}::create, this.segment.scope()) }}
+       java-set        {{ {name}$VH.set(this.segment, Memory.address({name})); {set-length} }}
+}
+
+type struct inline {
+       type    {{ {baseType} }}
+       layout  {{ {baseType}.LAYOUT }}
+}
+
+# how?  length?
+type struct* pointer {
+       type            {{ {baseType} }}
+       java-get        {{ {baseType}.create({native-get}, this.segment.scope()) }}
+}
+
+type struct*-length pointer-length {
+       type    {{ {baseType} }}
+       typei   {{ {baseType} }}
+}
+
+type struct** pointer {
+}
+
+# xlib
+type XID,Window,VisualID uint64_t;
+
+type Display* handle {
+       type    xlib.XDisplay;
+}
+
+# xcb
+type xcb_window_t uint32_t;
+type xcb_visualid_t uint32_t;
+type xcb_connection_t* handle {
+       type    xcb.Connection;
+}
+
+override structs {
+       VkInstance                      template=handle-instance;
+       VkPhysicalDevice        template=handle-dispatch;
+       VkDevice                        template=handle-dispatch;
+       VkCommandBuffer         template=handle-dispatch;
+       VkQueue                         template=handle-dispatch;
+
+       #VkQueueFamilyProperties        template=struct-readwrite;
+
+       # TODO: fill these in, there's a good number of them
+       #VkPhysicalDeviceMemoryProperties template=struct-readonly;
+       #VkMemoryRequirements template=struct-readonly;
+       #VkPipelineExecutableInternalRepresentationKHR template=struct-readonly;
+
+       # override default array-length behaviours, some of these are independent of the array pointer
+       VkAccelerationStructureBuildGeometryInfoKHR geometryCount=type:uint32_t;
+       VkDescriptorSetLayoutBinding pImmutableSamplers=type:handle* descriptorCount=type:uint32_t;
+       VkPipelineViewportStateCreateInfo scissorCount=type:uint32_t viewportCount=type:uint32_t;
+
+# can't really work out what this is it's a void ** but it stays it's a pointer to uint8_t * in the spec
+       VkCuLaunchInfoNVX       pParams=type:pointer pExtras=type:pointer;
+}
diff --git a/src/notzed.vulkan/gen/vulkan.pm b/src/notzed.vulkan/gen/vulkan.pm
new file mode 100644 (file)
index 0000000..4cc1e26
--- /dev/null
@@ -0,0 +1,830 @@
+
+# Routines for working with vulkan registry
+
+package vulkan;
+
+use strict;
+
+use Data::Dumper;
+use XML::Parser;
+use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
+
+sub new {
+       my $class = shift;
+       my $self = {
+               data => {},
+               extensions => [],
+               handles => {},
+               types => {},
+               commands => {},
+               features => [],
+               funcpointers => {},
+       };
+
+       bless $self, $class;
+
+       my $now = clock_gettime(CLOCK_REALTIME);
+
+       loadRegistry($self);
+
+       $now = clock_gettime(CLOCK_REALTIME) - $now;
+       print "$now load registry\n";
+
+       # build various indices
+       my $data = $self->{data};
+       my $handles = $self->{handles};
+       my $types = $self->{types};
+       my $commands = $self->{commands};
+       my $extensions = $self->{extensions};
+       my $funcpointers = $self->{funcpointers};
+
+       foreach my $t (keys %{$data}) {
+               my $v = $data->{$t};
+               $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
+               $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union|platform/;
+               $commands->{$v->{name}} = $v if $v->{category} eq 'command';
+               $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
+       }
+
+       # mark extension functions?
+       foreach my $e (@{$extensions}) {
+               foreach my $name (map { @{$_->{commands}} } @{$e->{require}}) {
+                       my $r = $data->{$name};
+
+                       die if !defined($r);
+
+                       push @{$r->{extensions}}, $e;
+               }
+       }
+
+       #print Dumper($self->{types});
+
+       # FIXME: link extensions up
+       # my $r = findData($data, $alias, "type:$ma->{name}");
+       # die "cann't find $ma->{name}" if !defined $r;
+       # push @{$r->{extensions}}, $ext;
+       # push @{$r->{commands}}, $ma->{name};
+
+       $self;
+}
+
+sub buildRequirement {
+       my $vk = shift;
+       my $data = shift;
+       my $req = shift;
+       my $ext = shift;
+       my $outconst = $data->{'API Constants'};
+       my $allconst = $vk->{data}->{'API Constants'};
+
+       # add a couple of constants that the api's dont reference
+       push @{$outconst->{items}}, grep { $_->{name} =~ m/VK_UUID_SIZE/ } @{$allconst->{items}};
+
+       #print " $req->{comment}\n";
+       #print Dumper($req->{types});
+       foreach my $c (@{$req->{commands}}, @{$req->{types}}) {
+               my $d = $vk->{data}->{$c};
+
+               #print Dumper({ d=> $d, c => $c });
+
+               # what about aliases?
+               if (defined $d) {
+                       if ($d->{category} eq 'enum' && !defined($d->{alias})) {
+                               $d = { %$d };
+                               $d->{items} = [ @{$d->{items}} ] if defined($d->{items});
+                       }
+                       $data->{$c} = $d;
+
+                       #print "Alias: $d->{alias}\n" if defined($d->{alias});
+               } else {
+                       print "Ignored: ".Dumper($c);
+               }
+       }
+       foreach my $c (@{$req->{enums}}) {
+               if ($c->{extends}) {
+                       my $d = $data->{$c->{extends}};
+
+                       if (defined($c->{value})) {
+                       } elsif (defined($c->{bitpos})) {
+                               $c->{value} =  "".(1<<$c->{bitpos});
+                       } elsif (defined($c->{extnumber})) {
+                               $c->{value} = "".(1000000000
+                                                                 + 1000 * ($c->{extnumber} - 1)
+                                                                 + $c->{offset});
+                       } elsif (defined($c->{offset})) {
+                               $c->{value} = $c->{dir}."".(1000000000
+                                                                                       + 1000 * ($ext->{number} - 1)
+                                                                                       + $c->{offset});
+                       } elsif (defined($c->{alias})) {
+                       } else {
+                               print Dumper($c);
+                               die;
+                       }
+
+                       push @{$d->{items}}, $c;
+               } elsif ($c->{value}) {
+                       if ($c->{value} =~ m/^"/) {
+                               push @{$outconst->{items}}, { %$c, type=>'const char *' };
+                       } else {
+                               push @{$outconst->{items}}, { %$c, type=>'uint32_t' };
+                       }
+               } elsif (!$c->{alias}) {
+                       my @list = grep { $_->{name} eq $c->{name} } @{$allconst->{items}};
+                       die "Can't find constant '$c->{name}'".Dumper($c) if ($#list < 0);
+                       push @{$outconst->{items}}, @list;
+               }
+       }
+}
+
+sub buildFeatures {
+       my $vk = shift;
+       my $vers = shift;
+       my $plat = shift;
+       my $data = {};
+       my $versions = {};
+       my $platform = {};
+
+       map { $versions->{$_} = 1 } @$vers;
+       map { $platform->{$_} = 1 } @$plat;
+
+       #print Dumper($vk->{features});
+
+       $data->{'API Constants'} = {
+               category => 'define',
+               items => [],
+       };
+
+       foreach my $feature (grep { $versions->{$_->{name}} } @{$vk->{features}}) {
+               print "Feature $feature->{name}\n";
+               foreach my $req (@{$feature->{require}}) {
+                       buildRequirement($vk, $data, $req);
+               }
+       }
+
+       foreach my $extension (grep { $_->{supported} eq 'vulkan' && (!defined($_->{platform}) || $platform->{$_->{platform}})
+                                                  } @{$vk->{extensions}}) {
+               foreach my $req (grep { (!defined($_->{feature})) || $versions->{$_->{feature}} }
+                                                @{$extension->{require}}) {
+                       print "Extension $extension->{name} $req->{feature}\n";
+                       buildRequirement($vk, $data, $req, $extension);
+               }
+       }
+
+       #print "rest\n";
+       #print Dumper($data);
+
+       # TODO: need to remove aliases here?
+       my $handles = {};
+       my $types = {};
+       my $commands = {};
+       my $enums = {};
+       my $bitmasks = {};
+       my $funcpointers = {};
+
+       foreach my $t (keys %{$data}) {
+               my $v = $data->{$t};
+               $handles->{$v->{name}} = $v if $v->{category} eq 'handle';
+               $types->{$v->{name}} = $v if $v->{category} =~ m/struct|union/on;
+               $commands->{$v->{name}} = $v if $v->{category} eq 'command';
+               $enums->{$v->{name}} = $v if $v->{category} eq 'enum';
+               $bitmasks->{$v->{name}} = $v if $v->{category} eq 'bitmask';
+               $funcpointers->{$v->{name}} = $v if $v->{category} eq 'funcpointer';
+       }
+
+       # link enums to their type(s)
+       foreach my $s (values %$bitmasks) {
+               my $t;
+
+               if ($s->{requires}) {
+                       $t = $data->{$s->{requires}};
+                       while ($t && $t->{alias}) {
+                               $t = $data->{$t->{alias}};
+                       }
+                       die if !defined($t);
+                       $t->{uses} = $s;
+                       $t->{fullType} = $s->{baseType};
+               } elsif ($s->{name} =~ m/(.*)Flags([0-9A-Z]*)/o && defined $data->{"$1FlagBits$2"}) {
+                       print "> $s->{name} $1FlagBits$2\n";
+                       $t = $data->{"$1FlagBits$2"};
+                       while ($t && $t->{alias}) {
+                               $t = $data->{$t->{alias}};
+                       }
+                       die if !defined($t);
+                       $t->{uses} = $s;
+                       $t->{fullType} = $s->{baseType};
+               } else {
+                       $t->{fullType} = 'VkFlags';
+               }
+       }
+       foreach my $s (values %$enums) {
+               $s->{fullType} = 'VkFlags' if !defined $s->{fullType};
+       }
+
+       if (0) {
+       # Have to actually map the types too
+       {
+               my $del = {};
+               my $add = {};
+               foreach my $c (values %{$types}) {
+                       if ($c->{alias}) {
+                               print "T";
+                               while ($c->{alias}) {
+                                       print " $c->{name}";
+                                       $c = $vk->{data}->{$c->{alias}};
+                               }
+                               print " -> $c->{name}\n";
+                       }
+               }
+               map {delete $types->{$_} } (keys %$del);
+               map {$data->{$_->{name}} = $_; $enums->{$_->{name}} = $_ } (values %$add);
+       }
+
+       # check type sare included?
+       {
+               my $del = {};
+               my $add = {};
+               foreach my $e (values %{$enums}) {
+                       if ($e->{alias}) {
+                               print "D";
+                               while ($e->{alias}) {
+                                       print " $e->{name}";
+                                       $del->{$e->{name}} = $e;
+                                       $e = $vk->{data}->{$e->{alias}};
+                               }
+                               die if !defined($e);
+                               print " -> $e->{name}\n";
+                               $add->{$e->{name}} = $e;
+                       }
+               }
+               map {delete $enums->{$_} } (keys %$del);
+               map {$data->{$_->{name}} = $_; $enums->{$_->{name}} = $_ } (values %$add);
+       }
+       }
+
+       my $api = {
+               data => $data,
+               handles => $handles,
+               types => $types,
+               commands => $commands,
+               funcpointers => $funcpointers,
+               enums => $enums,
+               bitmasks => $bitmasks,
+       };
+
+       # create sizes for every struct of interest
+       foreach my $s (values %$types) {
+               next if $s->{alias};
+
+               if ($s->{category} eq 'struct') {
+                       structSize($vk, $api, $s);
+               } elsif ($s->{category} eq 'union') {
+                       unionSize($vk, $api, $s);
+               } else {
+                       die;
+               }
+       }
+
+       return $api;
+}
+
+my $typeInfo = {
+       'void *' => { bitSize => 64, bitAlign => 64 },
+       'int' => { bitSize => 32, bitAlign => 32 },
+       'char' => { bitSize => 8, bitAlign => 8 },
+       'uint8_t' => { bitSize => 8, bitAlign => 8 },
+       'uint16_t' => { bitSize => 16, bitAlign => 16 },
+       'int32_t' => { bitSize => 32, bitAlign => 32 },
+       'uint32_t' => { bitSize => 32, bitAlign => 32 },
+       'int64_t' => { bitSize => 64, bitAlign => 64 },
+       'uint64_t' => { bitSize => 64, bitAlign => 64 },
+       'size_t' => { bitSize => 64, bitAlign => 64 },
+       'float' => { bitSize => 32, bitAlign => 32 },
+       'double' => { bitSize => 64, bitAlign => 64 },
+       'size_t' => { bitSize => 64, bitAlign => 64 },
+       'Window' => { bitSize => 64, bitAlign => 64 },
+       'Display' => { bitSize => 64, bitAlign => 64 },
+       'xcb_window_t' => { bitSize => 32, bitAlign => 32 },
+       'xcb_connection_t' => { bitSize => 64, bitAlign => 64 },
+#      'VkFlags' =>  { bitSize => 32, bitAlign => 32 },
+#      'VkFlags64' =>  { bitSize => 64, bitAlign => 64 },
+};
+
+# fuck how can i parameterise this shit?
+# ?create a 'deref' thing that all these things can work from?
+# this should probably do it too
+sub memberSize {
+       my $vk = shift;
+       my $api = shift;
+       my $m = shift;
+       my $t = $api->{data}->{$m->{baseType}};
+       my $nstar = $m->{fullType} =~ tr/*/*/;
+       my ($nbits) = $m->{fullType} =~ m/:(\d+)$/o;
+       my $array = 1;
+       my $info = $typeInfo->{'void *'};
+
+       # arrays and bitfields
+       if ($m->{fullType} =~ m/\[(.*)\]\[(.*)\]$/) {
+               $array = $1 * $2;
+       } elsif ($m->{fullType} =~ m/\[(\d+)\]$/o) {
+               $array = $1;
+       } elsif ($m->{fullType} =~ m/\[(.+)\]$/o) {
+               $array = $vk->{data}->{'API Constants'}->{index}->{$1}->{value};
+       }
+
+       if (!defined($t)) {
+               if ($nbits) {
+                       die Dumper($m) if $nstar > 0;
+                       $info = { bitSize => $nbits, bitAlign => 1 };
+               } else {
+                       $info = $typeInfo->{$m->{baseType}} if ($nstar == 0);
+               }
+       } else {
+               while ($t->{alias}) {
+                       $t = $api->{data}->{$t->{alias}};
+               }
+
+               die Dumper($m) if !defined $t;
+
+               if ($t->{category} =~ m/enum|bitmask/on) {
+                       if ($nbits) {
+                               die Dumper($m) if $nstar > 0;
+                               $info = { bitSize => $nbits, bitAlign => 1 };
+                       } else {
+                               $t = $vk->{data}->{$t->{fullType}};
+                               $info = $typeInfo->{$t->{type}} if ($nstar == 0);
+                       }
+               } elsif ($t->{category} eq 'struct') {
+                       $info = structSize($vk, $api, $t) if ($nstar == 0);
+               } elsif ($t->{category} eq 'union') {
+                       $info = unionSize($vk, $api, $t) if ($nstar == 0);
+               } elsif ($t->{category} eq 'handle') {
+                       # already set
+               } elsif ($t->{category} eq 'basetype') {
+                       $info = $typeInfo->{$t->{type}} if ($nstar == 0);
+               } elsif ($t->{category} eq 'funcpointer') {
+                       # already set
+               } else {
+                       die Dumper($m, $t);
+               }
+       }
+
+       die Dumper($m, $t) if !defined($info);
+
+       #print Dumper($m, $t, $info);
+       #print "size $m->{name} $m->{fullType} = $info->{bitSize}\n";
+
+
+       return { bitSize => $info->{bitSize} * $array, bitAlign => $info->{bitAlign} };
+}
+
+sub align {
+       my $v = shift;
+       my $a = shift;
+
+       return ($v + $a - 1) & ~($a - 1);
+}
+
+sub structSize {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $bitSize = 0;
+       my $bitAlign = 8;
+
+       if (!defined($s->{bitSize})) {
+               foreach my $m (@{$s->{items}}) {
+                       use integer;
+                       my $info = memberSize($vk, $api, $m);
+
+                       $bitSize = align($bitSize, $info->{bitAlign});
+
+                       $m->{bitOffset} = $bitSize;
+                       $m->{bitSize} = $info->{bitSize};
+
+                       $bitSize = $bitSize + $info->{bitSize};
+                       $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
+               }
+
+               $bitSize = align($bitSize, $bitAlign);
+
+               $s->{bitSize} = $bitSize;
+               $s->{bitAlign} = $bitAlign;
+       } else {
+               $bitSize = $s->{bitSize};
+               $bitAlign = $s->{bitAlign};
+       }
+
+       return { bitSize => $bitSize, bitAlign => $bitAlign };
+}
+
+sub unionSize {
+       my $vk = shift;
+       my $api = shift;
+       my $s = shift;
+       my $bitSize = 0;
+       my $bitAlign = 8;
+
+       if (!defined($s->{bitSize})) {
+               foreach my $m (@{$s->{items}}) {
+                       use integer;
+                       my $info = memberSize($vk, $api, $m);
+
+                       $m->{bitOffset} = 0;
+                       $m->{bitSize} = $info->{bitSize};
+
+                       $bitSize = $info->{bitSize} if $info->{bitSize} > $bitSize;
+                       $bitAlign = $info->{bitAlign} if $info->{bitAlign} > $bitAlign;
+               }
+
+               $bitSize = align($bitSize, $bitAlign);
+
+               $s->{bitSize} = $bitSize;
+               $s->{bitAlign} = $bitAlign;
+       } else {
+               $bitSize = $s->{bitSize};
+               $bitAlign = $s->{bitAlign};
+       }
+
+       return { bitSize => $bitSize, bitAlign => $bitAlign };
+}
+
+sub loadRegistry {
+       my $vk = shift;
+
+       my $xml = XML::Parser->new(Style => 'Tree');
+       my $doc = $xml->parsefile('/usr/share/vulkan/registry/vk.xml') || die "unable to parse vulkan registry";
+
+       #print Dumper($doc);
+
+       my $root = $doc->[1];
+       my $roota = shift @{$root};
+
+       my $data = $vk->{data};
+       my $alias = $vk->{alias};
+       my $extensions = $vk->{extensions};
+       my $features = $vk->{features};
+
+       # This destructively consumes the whole tree so must be one pass
+       while ($#{$root} >= 0) {
+               my $xt = shift @{$root};
+               my $xn = shift @{$root};
+
+               next if $xt eq '0';
+
+               my $xa = shift @{$xn};
+
+               if ($xt eq 'types') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'type';
+
+                               my $ya = $yn->[0];
+
+                               if ($ya->{category} =~ m/struct|union/) {
+                                       if (!defined($ya->{alias})) {
+                                               my $s = $ya;
+
+                                               $s->{items} = [];
+
+                                               shift @{$yn};
+                                               while ($#{$yn} >= 0) {
+                                                       my $mt = shift @{$yn};
+                                                       my $mm = shift @{$yn};
+
+                                                       push @{$s->{items}}, loadMember($mm) if $mt eq 'member';
+                                               }
+
+                                               $data->{$s->{name}} = $s;
+                                       } else {
+                                               $alias->{$ya->{name}} = $ya->{alias};
+                                               $data->{$ya->{name}} = $ya;
+                                       }
+                               } elsif ($ya->{category} =~ m/^(handle|basetype|funcpointer|bitmask)$/n) {
+                                       if (!defined($ya->{alias})) {
+                                               my $info = loadMember($yn);
+                                               my $s = $ya;
+
+                                               $s->{name} = $info->{name};
+                                               $s->{type} = $info->{baseType} if defined $info->{baseType};
+
+                                               analyseFunctionPointer($s) if ($s->{category} eq 'funcpointer');
+
+                                               $data->{$s->{name}} = $s;
+                                       } else {
+                                               $alias->{$ya->{name}} = $ya->{alias};
+                                               $data->{$ya->{name}} = $ya;
+                                       }
+                               } elsif ($ya->{category} eq 'enum') {
+                                       $data->{$ya->{name}} = $ya;
+                               } elsif ($ya->{requires} || $ya->{name} eq 'int') {
+                                       # ?? wtf to do with this
+                                       $ya->{category} = 'platform';
+                                       $data->{$ya->{name}} = $ya;
+                               }
+                       }
+               } elsif ($xt eq 'enums') {
+                       if ($xa->{type} =~ m/enum|bitmask/o) {
+                               # these are forward referenced from <types> block so re-use, or just overwrite?
+                               my $e = $data->{$xa->{name}};
+
+                               $e = { category => "enum", name => $xa->{name} } if (!defined($e));
+
+                               $e->{items} = [];
+
+                               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 @{$e->{items}}, $ya;
+                               }
+
+                               $data->{$xa->{name}} = $e;
+                       } elsif ($xa->{name} eq 'API Constants') {
+                               my $d = { category => "define", name => $xa->{name}, items =>[], index=>{} };
+
+                               $data->{$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;
+                                       $d->{index}->{$ya->{name}} = $ya;
+                               }
+                       }
+               } elsif ($xt eq 'commands') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'command';
+
+                               my $ya = shift @{$yn};
+
+                               if (!defined($ya->{alias})) {
+                                       my $cmd = $ya;
+
+                                       $cmd->{category} = 'command';
+                                       $cmd->{items} = [];
+                                       $cmd->{proto} = {};
+
+                                       while ($#{$yn} >= 0) {
+                                               my $zt = shift @{$yn};
+                                               my $zn = shift @{$yn};
+
+                                               if ($zt eq 'proto') {
+                                                       $cmd->{proto} = loadMember($zn);
+                                               } elsif ($zt eq 'param') {
+                                                       push @{$cmd->{items}}, loadMember($zn);
+                                               }
+                                       }
+
+                                       my $name = $cmd->{proto}->{name};
+
+                                       # check we parsed it properly
+                                       if ($cmd->{proto}->{fullType} eq "") {
+                                               print Dumper([$ya, $yn]);
+                                               die();
+                                       }
+                                       $cmd->{name} = $name;
+
+                                       $data->{$name} = $cmd;
+                               } else {
+                                       # want forward ref or not?
+                                       $alias->{$ya->{name}} = $ya->{alias};
+                                       $data->{$ya->{name}} = $ya;
+                               }
+                       }
+               } elsif ($xt eq 'feature') {
+                       my $feature = $xa;
+
+                       $feature->{require} = [];
+
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'require';
+
+                               push @{$feature->{require}}, loadRequire($data, $alias, $yn);
+                       }
+
+                       push @{$features}, $feature;
+               } elsif ($xt eq 'extensions') {
+                       while ($#{$xn} >= 0) {
+                               my $yt = shift @{$xn};
+                               my $yn = shift @{$xn};
+
+                               next if $yt ne 'extension';
+
+                               my $ext = shift @{$yn};
+
+                               $ext->{require} = [];
+
+                               while ($#{$yn} >= 0) {
+                                       my $zt = shift @{$yn};
+                                       my $zn = shift @{$yn};
+
+                                       next if $zt ne 'require';
+
+                                       push @{$ext->{require}}, loadRequire($data, $alias, $zn);
+                               }
+
+                               push @{$extensions}, $ext;
+                       }
+               } else {
+                       print "vulkan.pm: Ignore node: $xt\n";
+               }
+       }
+}
+
+# find an object including via alias
+sub findData {
+       my $data = shift;
+       my $alias = shift;
+       my $name = shift;
+
+       do {
+               my $s = $data->{$name};
+               return $s if defined $s;
+               #print "alias $name => $alias->{$name}\n";
+               $name = $alias->{$name};
+       } while ($name);
+
+       die "No match for type '$name'";
+}
+
+sub makeParameter {
+       my $name = shift;
+       my $fullType = shift;
+       my $type = $fullType;
+
+       $type =~ s/const|\*|\s//gon;
+
+       $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
+
+       # canonicalise spaces in c type
+       #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
+       $fullType =~ s/(?<! )\*/ */go;   # insert a space before * if there isn't one
+       $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
+
+       # fix brackets and trailing spaces
+       #$fullType =~ s/\( /(/go;
+       #$fullType =~ s/ \)/)/go;
+       #$fullType =~ s/ \[/[/go;
+       $fullType =~ s/^\s+|\s+$//go;
+
+       return {
+               name => $name,
+               Name => ucfirst($name),
+               fullType => $fullType,
+               baseType => $type,
+               type => $type,
+       };
+}
+
+# Convert function typedef into function info
+sub analyseFunctionPointer {
+       my $s = shift;
+
+       if ($s->{fullType} =~ m/^(.+)\s+\(VKAPI_PTR \*\)\((.*)\)$/o) {
+               my $rt = $1;
+               my @args = split /,/,$2;
+
+               $s->{proto} = makeParameter('result$', $rt);
+               $s->{items} = [];
+
+               foreach my $a (@args) {
+                       my ($fullType, $name) = $a =~ m/^(.*)\s+(\S+)$/o;
+
+                       push @{$s->{items}}, makeParameter($name, $fullType);
+               }
+       } else {
+               die Dumper($s);
+       }
+       $s->{Name} = $s->{name};
+       delete $s->{type};
+       delete $s->{baseType};
+       delete $s->{fullType};
+}
+
+sub loadMember {
+       my $nn = shift;
+       #my $x = (join '',split('\n',Dumper($nn)));     $x =~ s/ +/ /g; print "load: $x\n";
+       my $m = shift @{$nn};
+       my $baseType = "";
+       my $fullType = "";
+       my $name = "";
+
+       while ($#{$nn} >= 0) {
+               my $pt = shift @{$nn};
+               my $pn = shift @{$nn};
+
+               if ($pt eq '0') {
+                       $fullType .= $pn;
+               } elsif ($pt eq 'type') {
+                       die if $pn->[1] != 0;
+                       $baseType = $pn->[2];
+                       $fullType .= $baseType;
+               } elsif ($pt eq 'name') {
+                       die if $pn->[1] != 0;
+                       $name = $pn->[2];
+               } elsif ($pt eq 'enum') {
+                       die if $pn->[1] != 0;
+                       $fullType .= $pn->[2];
+               }
+       }
+
+       $fullType =~ s/^typedef (.*);$/\1/os; # strip out 'typedef' part
+       $fullType =~ s/\s{2,}/ /go; # collapse all whitespace to ' '
+
+       # canonicalise spaces in c type
+       #$fullType =~ s/(?<!const)\s+//go; # strip all spaces except those following const
+       $fullType =~ s/(?<! )\*/ */go;   # insert a space before * if there isn't one
+       $fullType =~ s/(?<=\*)(\S)/ \1/go;# insert a space after * if there isn't one
+
+       # fix brackets and trailing spaces
+       $fullType =~ s/\( /(/go;
+       $fullType =~ s/ \)/)/go;
+       $fullType =~ s/ \[/[/go;
+    $fullType =~ s/^\s+|\s+$//go;
+    $fullType =~ s/ :/:/go;
+
+       $m->{name} = $name;
+       $m->{baseType} = $baseType;
+       $m->{fullType} = $fullType;
+
+    $m;
+}
+
+sub loadRequire {
+       my $data = shift;
+       my $alias = shift;
+       my $nn = shift;
+       my $r = shift @{$nn};
+
+       $r->{enums} = [];
+       $r->{types} = [];
+       $r->{commands} = [];
+
+       while ($#{$nn} >= 0) {
+               my $mt = shift @{$nn};
+               my $mn = shift @{$nn};
+
+               if ($mt eq 'type') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{types}}, $ma->{name};
+               } elsif ($mt eq 'command') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{commands}}, $ma->{name};
+               } elsif ($mt eq 'enum') {
+                       my $ma = shift @{$mn};
+                       push @{$r->{enums}}, $ma;
+               }
+       }
+
+       $r;
+}
+
+sub findElements {
+       my $n = shift;
+       my $name = shift;
+       my @list;
+
+       while ($#{$n} >= 0) {
+               my $tag = shift @{$n};
+               my $con = shift @{$n};
+
+               if ($tag eq $name) {
+                       push @list, [$tag, $con];
+               }
+       }
+       @list;
+}
+
+sub scanElements {
+       my $n = shift;
+
+       while ($#{$n} >= 0) {
+               my $tag = shift @{$n};
+               my $con = shift @{$n};
+
+               print "$#{$n} ";
+               print "tag $tag\n";
+       }
+}
+
+1;
diff --git a/src/notzed.xcb/classes/module-info.java b/src/notzed.xcb/classes/module-info.java
new file mode 100644 (file)
index 0000000..4b1e747
--- /dev/null
@@ -0,0 +1,6 @@
+
+module notzed.xcb {
+       requires transitive notzed.nativez;
+
+       exports xcb;
+}
diff --git a/src/notzed.xcb/classes/xcb/Connection.java b/src/notzed.xcb/classes/xcb/Connection.java
new file mode 100644 (file)
index 0000000..27ff8fc
--- /dev/null
@@ -0,0 +1,28 @@
+
+package xcb;
+
+import jdk.incubator.foreign.*;
+import au.notzed.nativez.Pointer;
+
+/* small hack to get it to compile, unimplemented functions so far */
+
+public class Connection implements Pointer {
+       NativeSymbol symbol;
+
+       public Connection(MemoryAddress addr, ResourceScope scope) {
+               symbol = NativeSymbol.ofAddress("Connection", addr, scope);
+       }
+
+       public static Connection create(MemoryAddress addr, ResourceScope scope) {
+               return new Connection(addr, scope);
+       }
+
+       public MemoryAddress address() {
+               return symbol.address();
+       }
+
+       public ResourceScope scope() {
+               return symbol.scope();
+       }
+
+}