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
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
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
--------------
--- /dev/null
+
+module notzed.vulkan.test {
+ requires notzed.vulkan;
+ requires notzed.xlib;
+
+ requires java.desktop;
+}
--- /dev/null
+ /*
+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();
+ }
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+
+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 $@ $<
--- /dev/null
+#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;
+}
--- /dev/null
+
+module notzed.vulkan {
+ requires transitive notzed.nativez;
+ requires notzed.xlib;
+ requires notzed.xcb;
+
+ exports vulkan;
+}
--- /dev/null
+# -*- 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;
+}
--- /dev/null
+
+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 $@
--- /dev/null
+#!/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);
+}
--- /dev/null
+# -*- 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;
+}
--- /dev/null
+
+# 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;
--- /dev/null
+
+module notzed.xcb {
+ requires transitive notzed.nativez;
+
+ exports xcb;
+}
--- /dev/null
+
+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();
+ }
+
+}