--- /dev/null
+
+package vulkan.test;
+
+//struct Vertex {
+// float posX, posY, posZ, posW; // Position data
+// float r, g, b, a; // Color
+//};
+
+class Cube {
+ static final int dataStride = 8 * 4;
+ static final float[] data = new float[] {
+ // red face
+ -1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ 1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ 1, -1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ -1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ 1, 1, 1, 1.f, 1.f, 0.f, 0.f, 1.f,
+ // green face
+ -1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ 1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ -1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ 1, -1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ 1, 1, -1, 1.f, 0.f, 1.f, 0.f, 1.f,
+ // blue face
+ -1, 1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ -1, 1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ -1, -1, 1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ -1, -1, -1, 1.f, 0.f, 0.f, 1.f, 1.f,
+ // yellow face
+ 1, 1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ 1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ 1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ 1, -1, 1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ 1, 1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ 1, -1, -1, 1.f, 1.f, 1.f, 0.f, 1.f,
+ // magenta face
+ 1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ 1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ 1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ -1, 1, 1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ -1, 1, -1, 1.f, 1.f, 0.f, 1.f, 1.f,
+ // cyan face
+ 1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ 1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ -1, -1, 1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ 1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ -1, -1, -1, 1.f, 0.f, 1.f, 1.f, 1.f,
+ };
+}
--- /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.util.List;
+import java.util.Collections;
+
+import java.lang.invoke.*;
+import jdk.incubator.foreign.*;
+import jdk.incubator.foreign.MemoryLayout.PathElement;
+import au.notzed.nativez.*;
+
+import vulkan.*;
+import static vulkan.VkConstants.*;
+
+import xlib.*;
+import static xlib.XLib.*;
+import static vulkan.test.GLMaths.*;
+
+public class TestCube {
+ static final boolean debug = true;
+
+ final static int NUM_SAMPLES = VK_SAMPLE_COUNT_1_BIT;
+ final static int NUM_DESCRIPTOR_SETS = 1;
+
+ ResourceScope scope = ResourceScope.newSharedScope();
+
+ int width = 800;
+ int height = 800;
+ float projection[] = new float[16];
+ float view[] = new float[16];
+ float model[] = new float[16];
+ float clip[] = new float[] {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 1.0f
+ };
+ float mvp[] = new float[16];
+
+ VkInstance instance;
+ VkPhysicalDevice physicalDevice;
+ VkPhysicalDeviceMemoryProperties memory_properties;
+ VkPhysicalDeviceFeatures device_features;
+
+ int present_queue_index;
+ int graphics_queue_index;
+
+ VkDevice device;
+ VkSwapchainKHR chain;
+
+ VkQueue graphics_queue;
+ VkQueue present_queue;
+
+ int chainImageFormat;
+ HandleArray<VkImage> chainImage;
+ HandleArray<VkImageView> chainImageView;
+
+ int depthFormat;
+ VkImage depthImage;
+ VkImageView depthView;
+ VkDeviceMemory depthMemory;
+
+ VkCommandPool cmd_pool;
+ HandleArray<VkCommandBuffer> cmd;
+
+ BufferMemory uniform;
+ VkPipelineLayout pipeline_layout;
+
+ VkDescriptorSetLayout desc_layout;
+ VkDescriptorPool desc_pool;
+ HandleArray<VkDescriptorSet> desc_set = VkDescriptorSet.createArray(1, (SegmentAllocator)scope);
+
+
+ VkRenderPass render_pass;
+ HandleArray<VkFramebuffer> framebuffers;
+
+ BufferMemory vertex;
+ HandleArray<VkBuffer> vertexBuffer = VkBuffer.createArray(1, (SegmentAllocator)scope);
+ VkVertexInputBindingDescription vi_binding = VkVertexInputBindingDescription.createArray(1, (SegmentAllocator)scope);
+ VkVertexInputAttributeDescription vi_attribs = VkVertexInputAttributeDescription.createArray(2, (SegmentAllocator)scope);
+
+ IntArray cube_vs;
+ IntArray cube_fs;
+ HandleArray<VkShaderModule> shader = VkShaderModule.createArray(2, (SegmentAllocator)scope);
+
+ HandleArray<VkPipeline> pipeline = VkPipeline.createArray(1, (SegmentAllocator)scope);
+
+ VkSemaphore chainSemaphore;
+ VkFence drawFence;
+
+ record BufferMemory (VkBuffer buffer, VkDeviceMemory memory, long size) {
+ public void free(VkDevice device) {
+ device.vkFreeMemory(memory, null);
+ device.vkDestroyBuffer(buffer, null);
+ }
+ }
+
+ VkDebugUtilsMessengerEXT logger;
+
+ void init_debug() throws Exception {
+ if (!debug)
+ return;
+ try (Frame frame = Frame.frame()) {
+ NativeSymbol cb = PFN_vkDebugUtilsMessengerCallbackEXT.of((severity, flags, data) -> {
+ System.out.printf("Debug: %d: %s\n", severity, data.getMessage());
+ return 0;
+ }, scope);
+ VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.create(frame,
+ 0,
+ VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
+ | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
+ | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
+ VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
+ cb.address(),
+ null);
+
+ logger = instance.vkCreateDebugUtilsMessengerEXT(info, null);
+ }
+
+ //typedef VkBool32 (*PFN_vkDebugUtilsMessengerCallbackEXT)(VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT *, void *);
+
+ }
+
+ void init_instance() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ VkInstanceCreateInfo info = VkInstanceCreateInfo.create(frame,
+ 0,
+ VkApplicationInfo.create(frame, "cube", 1, "cube-engine", 2, VK_MAKE_API_VERSION(0, 1, 0, 0)),
+ new String[] { "VK_LAYER_KHRONOS_validation" },
+ new String[] { "VK_KHR_surface", "VK_KHR_xlib_surface", "VK_EXT_debug_utils" }
+ );
+
+ instance = VkInstance.vkCreateInstance(info, null);
+ System.out.printf("instance = %s\n", instance);
+ }
+ }
+
+ XDisplay display;
+ long window;
+ long wm_delete_window;
+ VkSurfaceKHR surface;
+
+ void init_surface() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ XInitThreads();
+ display = XOpenDisplay(null);
+ long visualMask = VisualScreenMask;
+ IntArray numberOfVisuals = IntArray.createArray(1, frame);
+ XVisualInfo vInfoTemplate = XVisualInfo.create(frame);
+
+ vInfoTemplate.setScreen(DefaultScreen(display));
+
+ XVisualInfo visualInfo = XGetVisualInfo(display, visualMask, vInfoTemplate, numberOfVisuals);
+ long colormap = XCreateColormap(display, RootWindow(display, vInfoTemplate.getScreen()), visualInfo.getVisual(), AllocNone);
+
+ XSetWindowAttributes windowAttributes = XSetWindowAttributes.create(frame);
+
+ windowAttributes.setColormap(colormap);
+ windowAttributes.setBackgroundPixel(0xffffffff);
+ windowAttributes.setBorderPixel(0);
+ windowAttributes.setEventMask(KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask);
+
+ window = XCreateWindow(display, RootWindow(display, vInfoTemplate.getScreen()),
+ 0, 0, width, height,
+ 0, visualInfo.getDepth(), InputOutput, visualInfo.getVisual(),
+ CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, windowAttributes);
+
+ XSelectInput(display, window, ExposureMask | KeyPressMask);
+ XMapWindow(display, window);
+ XFlush(display);
+ wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+
+ VkXlibSurfaceCreateInfoKHR surfaceinfo = VkXlibSurfaceCreateInfoKHR.create(frame,
+ 0,
+ display.address(),
+ window);
+
+ surface = instance.vkCreateXlibSurfaceKHR(surfaceinfo, null);
+ System.out.printf("surface: %s\n", surface);
+ }
+ }
+
+ void init_device() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ IntArray count$h = IntArray.create(frame, 1);
+ IntArray present$h = IntArray.create(frame, 1);
+ HandleArray<VkPhysicalDevice> devs;
+ int count;
+ int res;
+
+ devs = instance.vkEnumeratePhysicalDevices();
+
+ // Search for device and queue indices
+ int devid = -1;
+ int present_queue = -1;
+ int graphics_queue = -1;
+ for (int i = 0; i < devs.length(); i++) {
+ VkPhysicalDevice dev = devs.getAtIndex(i);
+ VkQueueFamilyProperties famprops;
+
+ // TODO: change to return the allocated array directly
+ dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, null);
+ famprops = VkQueueFamilyProperties.createArray(count$h.getAtIndex(0), frame);
+ dev.vkGetPhysicalDeviceQueueFamilyProperties(count$h, famprops);
+
+ for (int j = 0; j < famprops.length(); j++) {
+ boolean present;
+
+ dev.vkGetPhysicalDeviceSurfaceSupportKHR(j, surface, present$h);
+ present = present$h.get(0) != 0;
+
+ if (present && present_queue == -1)
+ present_queue = j;
+ if ((famprops.getQueueFlags(j) & VK_QUEUE_GRAPHICS_BIT) != 0) {
+ graphics_queue = j;
+ if (present) {
+ present_queue = j;
+ break;
+ }
+ }
+ }
+ if (present_queue != -1 && graphics_queue != -1) {
+ devid = i;
+ break;
+ }
+ }
+
+ if (devid == -1)
+ throw new Exception("Cannot find a suitable device");
+
+ physicalDevice = devs.getAtIndex(devid);
+ present_queue_index = present_queue;
+ graphics_queue_index = graphics_queue;
+
+ // NOTE: app scope
+ memory_properties = VkPhysicalDeviceMemoryProperties.create(scope);
+ physicalDevice.vkGetPhysicalDeviceMemoryProperties(memory_properties);
+ device_features = VkPhysicalDeviceFeatures.create(scope);
+ physicalDevice.vkGetPhysicalDeviceFeatures(device_features);
+
+ FloatArray qpri = FloatArray.create(frame, 0.0f);
+ VkDeviceQueueCreateInfo qinfo = VkDeviceQueueCreateInfo.create(
+ frame,
+ 0,
+ graphics_queue,
+ 1,
+ qpri);
+ String [] extensions = {
+ "VK_KHR_swapchain"
+ };
+ VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.create(frame);
+ features.setDepthClamp(1);
+ VkDeviceCreateInfo devinfo = VkDeviceCreateInfo.create(
+ frame,
+ 0,
+ 1,
+ qinfo,
+ null,
+ extensions,
+ features);
+
+ device = physicalDevice.vkCreateDevice(devinfo, null);
+
+ System.out.printf("device = %s\n", device);
+
+ /* ************************************************************** */
+ int format;
+ int formatCount;
+ physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, count$h, null);
+ formatCount = count$h.getAtIndex(0);
+ VkSurfaceFormatKHR surfFormats = VkSurfaceFormatKHR.createArray(formatCount, frame);
+ physicalDevice.vkGetPhysicalDeviceSurfaceFormatsKHR(surface, count$h, surfFormats);
+ // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
+ // the surface has no preferred format. Otherwise, at least one
+ // supported format will be returned.
+ if (formatCount == 1 && surfFormats.getFormat(0) == VK_FORMAT_UNDEFINED) {
+ format = VK_FORMAT_B8G8R8A8_UNORM;
+ } else {
+ format = surfFormats.getFormat(0);
+ }
+
+ VkSurfaceCapabilitiesKHR surfCapabilities = VkSurfaceCapabilitiesKHR.create(frame);
+
+ physicalDevice.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, surfCapabilities);
+
+ physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, count$h, null);
+ IntArray presentModes = IntArray.createArray(count$h.get(0), frame);
+ physicalDevice.vkGetPhysicalDeviceSurfacePresentModesKHR(surface, count$h, presentModes);
+
+ VkExtent2D swapchainExtent;
+ // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF.
+ if (surfCapabilities.getCurrentExtent().getWidth() == 0xFFFFFFFF) {
+ // If the surface size is undefined, the size is set to
+ // the size of the images requested.
+ swapchainExtent = VkExtent2D.create(frame,
+ clampi(width, surfCapabilities.getMinImageExtent().getWidth(), surfCapabilities.getMaxImageExtent().getWidth()),
+ clampi(height, surfCapabilities.getMinImageExtent().getHeight(), surfCapabilities.getMaxImageExtent().getHeight()));
+ } else {
+ // If the surface size is defined, the swap chain size must match
+ swapchainExtent = surfCapabilities.getCurrentExtent();
+ }
+ int compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ int compositeAlphaFlags[] = {
+ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+ VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
+ };
+ for (int flag: compositeAlphaFlags) {
+ if ((surfCapabilities.getSupportedCompositeAlpha() & flag) != 0) {
+ compositeAlpha = flag;
+ break;
+ }
+ }
+
+ VkSwapchainCreateInfoKHR chaininfo = VkSwapchainCreateInfoKHR.create(frame,
+ 0,
+ surface,
+ surfCapabilities.getMinImageCount(),
+ format,
+ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+ 1, //.imageArrayLayers = 1,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_SHARING_MODE_EXCLUSIVE,
+ // assumes queues are same.
+ 0,
+ null,
+ (surfCapabilities.getSupportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0
+ ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : surfCapabilities.getCurrentTransform(),
+ compositeAlpha,
+ VK_PRESENT_MODE_FIFO_KHR,
+ 1,
+ null);
+ chaininfo.getImageExtent().setWidth(swapchainExtent.getWidth());
+ chaininfo.getImageExtent().setHeight(swapchainExtent.getHeight());
+
+ chain = device.vkCreateSwapchainKHR(chaininfo, null);
+
+ int chainImageCount;
+ device.vkGetSwapchainImagesKHR(chain, count$h, null);
+ chainImageCount = count$h.get(0);
+ chainImage = VkImage.createArray(chainImageCount, (SegmentAllocator)scope);
+ chainImageView = VkImageView.createArray(chainImageCount, (SegmentAllocator)scope);
+
+ device.vkGetSwapchainImagesKHR(chain, count$h, chainImage);
+
+ VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(frame,
+ 0,
+ null,
+ VK_IMAGE_VIEW_TYPE_2D,
+ format);
+ VkComponentMapping components = viewinfo.getComponents();
+ components.setR(VK_COMPONENT_SWIZZLE_R);
+ components.setG(VK_COMPONENT_SWIZZLE_G);
+ components.setB(VK_COMPONENT_SWIZZLE_B);
+ components.setA(VK_COMPONENT_SWIZZLE_A);
+ VkImageSubresourceRange subresourceRange = viewinfo.getSubresourceRange();
+ subresourceRange.setAspectMask(VK_IMAGE_ASPECT_COLOR_BIT);
+ subresourceRange.setLevelCount(1);
+ subresourceRange.setLayerCount(1);
+
+ for (int i = 0; i < chainImageCount; i++) {
+ viewinfo.setImage(chainImage.get(i));
+
+ chainImageView.setAtIndex(i, device.vkCreateImageView(viewinfo, null));
+ }
+
+ chainImageFormat = format;
+ }
+ }
+
+ void init_device_queue() {
+ graphics_queue = device.vkGetDeviceQueue(graphics_queue_index, 0);
+ if (graphics_queue_index == present_queue_index) {
+ present_queue = graphics_queue;
+ } else {
+ present_queue = device.vkGetDeviceQueue(present_queue_index, 0);
+ }
+ }
+
+ void init_command() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ VkCommandPoolCreateInfo poolinfo = VkCommandPoolCreateInfo.create(frame,
+ 0,
+ graphics_queue_index);
+
+ cmd_pool = device.vkCreateCommandPool(poolinfo, null);
+
+ VkCommandBufferAllocateInfo cmdinfo = VkCommandBufferAllocateInfo.create(frame,
+ cmd_pool,
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ 1);
+
+ cmd = device.vkAllocateCommandBuffers(cmdinfo);
+ }
+ }
+
+ // parameterise as init_image?
+ void init_depth() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ int format = VK_FORMAT_D16_UNORM;
+ VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+ VkImageCreateInfo imageinfo = VkImageCreateInfo.create(frame, 0,
+ VK_IMAGE_TYPE_2D,
+ format,
+ 1,
+ 1,
+ NUM_SAMPLES,
+ 0,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0, null,
+ VK_IMAGE_LAYOUT_UNDEFINED);
+ imageinfo.getExtent().setWidth(width);
+ imageinfo.getExtent().setHeight(height);
+ imageinfo.getExtent().setDepth(1);
+
+ depthImage = device.vkCreateImage(imageinfo, null);
+
+ device.vkGetImageMemoryRequirements(depthImage, req);
+ VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+ req.getSize(),
+ find_memory_type(memory_properties, req.getMemoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
+
+ depthMemory = device.vkAllocateMemory(alloc, null);
+
+ device.vkBindImageMemory(depthImage, depthMemory, 0);
+
+ VkImageViewCreateInfo viewinfo = VkImageViewCreateInfo.create(frame, 0,
+ depthImage,
+ VK_IMAGE_VIEW_TYPE_2D,
+ VK_FORMAT_D16_UNORM);
+
+ VkComponentMapping components = viewinfo.getComponents();
+ components.setR(VK_COMPONENT_SWIZZLE_R);
+ components.setG(VK_COMPONENT_SWIZZLE_G);
+ components.setB(VK_COMPONENT_SWIZZLE_B);
+ components.setA(VK_COMPONENT_SWIZZLE_A);
+ VkImageSubresourceRange subresourceRange = viewinfo.getSubresourceRange();
+ subresourceRange.setAspectMask(VK_IMAGE_ASPECT_DEPTH_BIT);
+ subresourceRange.setLevelCount(1);
+ subresourceRange.setLayerCount(1);
+
+ depthView = device.vkCreateImageView(viewinfo, null);
+
+ depthFormat = format;
+ }
+ }
+
+ void init_uniform() throws Exception {
+ uniform = init_buffer(mvp.length * 4, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(mvp));
+ }
+
+ void init_descriptor() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ HandleArray<VkDescriptorSetLayout> layout_table = VkDescriptorSetLayout.createArray(1, frame);
+ VkDescriptorSetLayoutBinding layout_binding = VkDescriptorSetLayoutBinding.create(frame,
+ 0,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ 1,
+ VK_SHADER_STAGE_VERTEX_BIT,
+ null);
+ VkDescriptorSetLayoutCreateInfo descriptor_layout = VkDescriptorSetLayoutCreateInfo.create(frame,
+ 0,
+ 1,
+ layout_binding);
+
+ desc_layout = device.vkCreateDescriptorSetLayout(descriptor_layout, null);
+ layout_table.setAtIndex(0, desc_layout);
+
+ VkPipelineLayoutCreateInfo pipeline_info = VkPipelineLayoutCreateInfo.create(frame,
+ 0,
+ 1,
+ layout_table,
+ 0,
+ null);
+
+ pipeline_layout = device.vkCreatePipelineLayout(pipeline_info, null);
+
+ VkDescriptorPoolSize type_count = VkDescriptorPoolSize.create(frame,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ 1);
+
+ VkDescriptorPoolCreateInfo descriptor_pool = VkDescriptorPoolCreateInfo.create(frame,
+ 0,
+ 1,
+ 1,
+ type_count);
+
+ desc_pool = device.vkCreateDescriptorPool(descriptor_pool, null);
+
+ VkDescriptorSetAllocateInfo alloc_info = VkDescriptorSetAllocateInfo.create(frame,
+ desc_pool,
+ 1,
+ layout_table);
+
+ device.vkAllocateDescriptorSets(alloc_info, desc_set);
+
+ VkDescriptorBufferInfo uniformInfo = VkDescriptorBufferInfo.create(frame, uniform.buffer, 0, uniform.size);
+ VkWriteDescriptorSet writes = VkWriteDescriptorSet.create(frame,
+ desc_set.getAtIndex(0),
+ 0,
+ 0,
+ 1,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ null,
+ uniformInfo,
+ null);
+
+ device.vkUpdateDescriptorSets(1, writes, 0, null);
+ }
+ }
+
+ void init_render() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ VkAttachmentDescription attachments = VkAttachmentDescription.createArray(2, frame);
+
+ attachments.setFormat(chainImageFormat);
+ attachments.setSamples(NUM_SAMPLES);
+ attachments.setLoadOp(VK_ATTACHMENT_LOAD_OP_CLEAR);
+ attachments.setStoreOp(VK_ATTACHMENT_STORE_OP_STORE);
+ attachments.setStencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+ attachments.setStencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE);
+ attachments.setInitialLayout(VK_IMAGE_LAYOUT_UNDEFINED);
+ attachments.setFinalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+ attachments.setFlags(0);
+
+ attachments.setFormat(1, depthFormat);
+ attachments.setSamples(1, NUM_SAMPLES);
+ attachments.setLoadOp(1, VK_ATTACHMENT_LOAD_OP_CLEAR);
+ attachments.setStoreOp(1, VK_ATTACHMENT_STORE_OP_STORE);
+ attachments.setStencilLoadOp(1, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+ attachments.setStencilStoreOp(1, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+ attachments.setInitialLayout(1, VK_IMAGE_LAYOUT_UNDEFINED);
+ attachments.setFinalLayout(1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+ attachments.setFlags(1, 0);
+
+ VkAttachmentReference color_reference = VkAttachmentReference.create(frame,
+ 0,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ VkAttachmentReference depth_reference = VkAttachmentReference.create(frame,
+ 1,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+ VkSubpassDescription subpass = VkSubpassDescription.create(frame,
+ 0,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ 0, null,
+ 1, color_reference,
+ null,
+ depth_reference,
+ 0, null);
+
+ VkRenderPassCreateInfo rp_info = VkRenderPassCreateInfo.create(frame,
+ 0,
+ (int)attachments.length(), attachments,
+ 1, subpass,
+ 0, null);
+
+ render_pass = device.vkCreateRenderPass(rp_info, null);
+ }
+ }
+
+ void init_framebuffer() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ HandleArray<VkImageView> attachments = VkImageView.createArray(2, frame);
+
+ attachments.setAtIndex(1, depthView);
+
+ VkFramebufferCreateInfo fb_info = VkFramebufferCreateInfo.create(frame,
+ 0,
+ render_pass,
+ 2,
+ attachments,
+ width,
+ height,
+ 1);
+
+ framebuffers = VkFramebuffer.createArray(chainImage.length(), (SegmentAllocator)scope);
+ for (int i = 0; i < chainImage.size(); i++) {
+ attachments.setAtIndex(0, chainImageView.get(i));
+ framebuffers.setAtIndex(i, device.vkCreateFramebuffer(fb_info, null));
+ System.out.printf("framebuffer[%d] = %s\n", i, framebuffers.getAtIndex(i));
+ }
+ }
+ }
+
+ void init_vertexbuffer() throws Exception {
+ try (Frame frame = Frame.frame()) {
+ vertex = init_buffer(Cube.data.length * 4, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, MemorySegment.ofArray(Cube.data));
+
+ vertexBuffer.setAtIndex(0, vertex.buffer);
+
+ /* ***************************************** */
+ vi_binding.setBinding(0);
+ vi_binding.setInputRate(VK_VERTEX_INPUT_RATE_VERTEX);
+ vi_binding.setStride(Cube.dataStride);
+
+ vi_attribs.setBinding(0);
+ vi_attribs.setLocation(0);
+ vi_attribs.setFormat(VK_FORMAT_R32G32B32A32_SFLOAT);
+ vi_attribs.setOffset(0);
+ vi_attribs.setBinding(1, 0);
+ vi_attribs.setLocation(1, 1);
+ vi_attribs.setFormat(1, VK_FORMAT_R32G32B32A32_SFLOAT);
+ vi_attribs.setOffset(1, 16);
+ }
+ }
+
+ void init_pipeline() throws Exception {
+ int res;
+ try (Frame frame = Frame.frame()) {
+ IntArray dynamicStateEnables = IntArray.create(frame,
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR);
+
+ VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo.create(frame,
+ 0, dynamicStateEnables.size(), dynamicStateEnables);
+
+ VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.create(frame,
+ 0,
+ 1, vi_binding,
+ 2, vi_attribs);
+
+ VkPipelineInputAssemblyStateCreateInfo ia = VkPipelineInputAssemblyStateCreateInfo.create(frame,
+ 0,
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ 0);
+
+ VkPipelineRasterizationStateCreateInfo rs = VkPipelineRasterizationStateCreateInfo.create(frame,
+ 0,
+ VK_TRUE,
+ VK_FALSE,
+ VK_POLYGON_MODE_FILL,
+ VK_CULL_MODE_BACK_BIT,
+ VK_FRONT_FACE_CLOCKWISE,
+ VK_FALSE,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f);
+
+ VkPipelineColorBlendAttachmentState att_state = VkPipelineColorBlendAttachmentState.create(frame,
+ VK_FALSE,
+ VK_BLEND_FACTOR_ZERO,
+ VK_BLEND_FACTOR_ZERO,
+ VK_BLEND_OP_ADD,
+ VK_BLEND_FACTOR_ZERO,
+ VK_BLEND_FACTOR_ZERO,
+ VK_BLEND_OP_ADD,
+ 0xf);
+
+ VkPipelineColorBlendStateCreateInfo cb = VkPipelineColorBlendStateCreateInfo.create(frame,
+ 0,
+ VK_FALSE,
+ VK_LOGIC_OP_NO_OP,
+ 1, att_state,
+ 1.0f, 1.0f, 1.0f, 1.0f);
+
+ VkPipelineViewportStateCreateInfo vp = VkPipelineViewportStateCreateInfo.create(frame,
+ 0,
+ 1, null,
+ 1, null);
+
+ VkPipelineDepthStencilStateCreateInfo ds = VkPipelineDepthStencilStateCreateInfo.create(frame,
+ 0,
+ VK_TRUE,
+ VK_TRUE,
+ VK_COMPARE_OP_LESS_OR_EQUAL,
+ VK_FALSE,
+ VK_FALSE,
+ 0.0f,
+ 0.0f);
+ VkStencilOpState back = ds.getBack();
+
+ back.setFailOp(VK_STENCIL_OP_KEEP);
+ back.setPassOp(VK_STENCIL_OP_KEEP);
+ back.setCompareOp(VK_COMPARE_OP_ALWAYS);
+ back.setCompareMask(0);
+ back.setReference(0);
+ back.setDepthFailOp(VK_STENCIL_OP_KEEP);
+ back.setWriteMask(0);
+
+ VkStencilOpState front = ds.getFront();
+
+ front.setFailOp(VK_STENCIL_OP_KEEP);
+ front.setPassOp(VK_STENCIL_OP_KEEP);
+ front.setCompareOp(VK_COMPARE_OP_ALWAYS);
+ front.setCompareMask(0);
+ front.setReference(0);
+ front.setDepthFailOp(VK_STENCIL_OP_KEEP);
+ front.setWriteMask(0);
+
+ VkPipelineMultisampleStateCreateInfo ms = VkPipelineMultisampleStateCreateInfo.create(frame,
+ 0,
+ NUM_SAMPLES,
+ 0, //.sampleShadingEnable = VK_FALSE,
+ 0.0f,
+ null,
+ 0, //.alphaToCoverageEnable = VK_FALSE,
+ 0 //.alphaToOneEnable = VK_FALSE,
+ );
+
+ VkShaderModuleCreateInfo vsInfo = VkShaderModuleCreateInfo.create(frame,
+ 0,
+ cube_vs.length() * 4,
+ cube_vs);
+ VkShaderModuleCreateInfo fsInfo = VkShaderModuleCreateInfo.create(frame,
+ 0,
+ cube_fs.length() * 4,
+ cube_fs);
+
+ shader.setAtIndex(0, device.vkCreateShaderModule(vsInfo, null));
+ shader.setAtIndex(1, device.vkCreateShaderModule(fsInfo, null));
+
+ VkPipelineShaderStageCreateInfo shaderStages = VkPipelineShaderStageCreateInfo.createArray(2, (SegmentAllocator)scope);
+
+ shaderStages.setStage(VK_SHADER_STAGE_VERTEX_BIT);
+ shaderStages.setName(frame, "main");
+ shaderStages.setModule(shader.get(0));
+
+ shaderStages.setStage(1, VK_SHADER_STAGE_FRAGMENT_BIT);
+ shaderStages.setName(frame, 1, "main");
+ shaderStages.setModule(1, shader.get(1));
+
+ VkGraphicsPipelineCreateInfo pipeline = VkGraphicsPipelineCreateInfo.create(frame,
+ 0,
+ 2, shaderStages,
+ vi,
+ ia,
+ null,
+ vp,
+ rs,
+ ms,
+ ds,
+ cb,
+ dynamicState,
+ pipeline_layout,
+ render_pass,
+ 0,
+ null,
+ 0);
+
+ res = device.vkCreateGraphicsPipelines(null, 1, pipeline, null, this.pipeline);
+
+ VkSemaphoreCreateInfo seminfo = VkSemaphoreCreateInfo.create(frame, 0);
+ chainSemaphore = device.vkCreateSemaphore(seminfo, null);
+
+ VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame, 0);
+ drawFence = device.vkCreateFence(fenceInfo, null);
+ }
+ }
+
+
+ void execute_begin_command_buffer() throws Exception {
+ /* DEPENDS on init_command() */
+ try (Frame frame = Frame.frame()) {
+ VkCommandBufferBeginInfo cmd_buf_info = VkCommandBufferBeginInfo.create(frame,
+ 0,
+ null);
+
+ cmd.getAtIndex(0).vkBeginCommandBuffer(cmd_buf_info);
+ }
+ }
+
+ void execute_end_command_buffer() throws Exception {
+ cmd.getAtIndex(0).vkEndCommandBuffer();
+ }
+
+ final static long FENCE_TIMEOUT = 100000000;
+
+ void execute_queue_command_buffer() throws Exception {
+ int res;
+
+ /* Queue the command buffer for execution */
+ try (Frame frame = Frame.frame()) {
+ IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+ VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.create(frame, 0);
+ HandleArray<VkFence> fences = VkFence.createArray(1, frame);
+
+ fences.setAtIndex(0, device.vkCreateFence(fenceInfo, null));
+
+ VkSubmitInfo submit_info = VkSubmitInfo.create(frame,
+ 0, null,
+ pipe_stage_flags,
+ 1, cmd,
+ 0, null);
+
+ graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+ do {
+ res = device.vkWaitForFences( 1, fences, 1, FENCE_TIMEOUT);
+ } while (res == VK_TIMEOUT);
+
+ device.vkDestroyFence(fences.getAtIndex(0), null);
+ }
+ }
+
+ void cmd_viewport() {
+ try (Frame frame = Frame.frame()) {
+ VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+ VkViewport viewport = VkViewport.create(frame,
+ 0, 0, width, height, 0.0f, 1.0f);
+ cmd.vkCmdSetViewport(0, 1, viewport);
+ }
+ }
+
+ void cmd_scissors() {
+ try (Frame frame = Frame.frame()) {
+ VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+ VkRect2D scissor = VkRect2D.create(frame);
+ VkExtent2D extent = scissor.getExtent();
+
+ extent.setWidth(width);
+ extent.setHeight(height);
+
+ cmd.vkCmdSetScissor(0, 1, scissor);
+ }
+ }
+
+ void cmd_paint() throws Exception {
+ int res;
+ try (Frame frame = Frame.frame()) {
+ int chainIndex;
+ IntArray chainIndices = IntArray.createArray(1, frame);
+ VkCommandBuffer cmd = this.cmd.getAtIndex(0);
+
+ device.vkAcquireNextImageKHR(chain, ~0L, chainSemaphore, null, chainIndices);
+ chainIndex = chainIndices.getAtIndex(0);
+ LongArray offsets = LongArray.createArray(1, frame);
+
+ VkClearValue clear_values = VkClearValue.createArray(2, frame);
+ FloatArray col = clear_values.getColor().getFloat32();
+ col.setAtIndex(0, 0.2f);
+ col.setAtIndex(1, 0.2f);
+ col.setAtIndex(2, 0.2f);
+ col.setAtIndex(3, 0.2f);
+ VkClearDepthStencilValue depthStencil = clear_values.getDepthStencil(1);
+ depthStencil.setDepth(1.0f);
+ depthStencil.setStencil(0);
+
+ System.out.printf("render framebuffer[%d] = %s\n", chainIndex, framebuffers.getAtIndex(chainIndex));
+
+ VkRenderPassBeginInfo rp_begin = VkRenderPassBeginInfo.create(frame,
+ render_pass,
+ framebuffers.getAtIndex(chainIndex),
+ 2,
+ clear_values);
+ VkExtent2D extent = rp_begin.getRenderArea().getExtent();
+ extent.setWidth(width);
+ extent.setHeight(height);
+
+ cmd.vkCmdBeginRenderPass(rp_begin, VK_SUBPASS_CONTENTS_INLINE);
+
+ cmd.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.getAtIndex(0));
+ cmd.vkCmdBindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, NUM_DESCRIPTOR_SETS, desc_set, 0, null);
+ cmd.vkCmdBindVertexBuffers(0, 1, vertexBuffer, offsets);
+
+ cmd_viewport();
+ cmd_scissors();
+
+ cmd.vkCmdDraw(12 * 3, 1, 0, 0);
+ cmd.vkCmdEndRenderPass();
+
+ cmd.vkEndCommandBuffer();
+
+ IntArray pipe_stage_flags = IntArray.create(frame, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+ HandleArray<VkSemaphore> semaphores = VkSemaphore.create(frame, chainSemaphore);
+
+ VkSubmitInfo submit_info = VkSubmitInfo.create(frame,
+ 1, semaphores,
+ pipe_stage_flags,
+ 1, this.cmd,
+ 0, null);
+
+ HandleArray<VkFence> fences = VkFence.create(frame, drawFence);
+
+ // Queue the command buffer for execution
+ device.vkResetFences(1, fences);
+
+ graphics_queue.vkQueueSubmit(1, submit_info, drawFence);
+
+ // Make sure command buffer is finished before presenting
+ do {
+ res = device.vkWaitForFences(1, fences, VK_TRUE, FENCE_TIMEOUT);
+ } while (res == VK_TIMEOUT);
+
+ // Now present the image in the window
+ HandleArray<VkSwapchainKHR> chains = VkSwapchainKHR.create(frame, chain);
+ VkPresentInfoKHR present = VkPresentInfoKHR.create(frame,
+ 0, null,
+ 1, chains,
+ chainIndices,
+ null);
+
+ present_queue.vkQueuePresentKHR(present);
+ }
+ }
+
+ /**
+ * Buffers are created in three steps:
+ * 1) create buffer, specifying usage and size
+ * 2) allocate memory based on memory requirements
+ * 3) bind memory
+ *
+ */
+ BufferMemory init_buffer(long dataSize, int usage, int properties, MemorySegment init) throws Exception {
+ try (Frame frame = Frame.frame()) {
+ VkMemoryRequirements req = VkMemoryRequirements.create(frame);
+ VkBufferCreateInfo buf_info = VkBufferCreateInfo.create(frame,
+ 0,
+ dataSize,
+ usage,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0,
+ null);
+
+ VkBuffer buffer = device.vkCreateBuffer(buf_info, null);
+
+ device.vkGetBufferMemoryRequirements(buffer, req);
+
+ VkMemoryAllocateInfo alloc = VkMemoryAllocateInfo.create(frame,
+ req.getSize(),
+ find_memory_type(memory_properties, req.getMemoryTypeBits(), properties));
+
+ VkDeviceMemory memory = device.vkAllocateMemory(alloc, null);
+
+ if (init != null) {
+ MemorySegment mem = device.vkMapMemory(memory, 0, dataSize, 0, scope);
+ mem.copyFrom(init);
+ device.vkUnmapMemory(memory);
+ }
+
+ device.vkBindBufferMemory(buffer, memory, 0);
+
+ return new BufferMemory(buffer, memory, dataSize);
+ }
+ }
+
+ void shutdown() {
+ device.vkDestroyFence(drawFence, null);
+ device.vkDestroySemaphore(chainSemaphore, null);
+
+ device.vkDestroyPipeline(pipeline.getAtIndex(0), null);
+ for (int i=0;i<shader.size();i++)
+ device.vkDestroyShaderModule(shader.getAtIndex(i), null);
+
+ vertex.free(device);
+ uniform.free(device);
+
+ for (int i=0;i<framebuffers.size();i++)
+ device.vkDestroyFramebuffer(framebuffers.getAtIndex(i), null);
+
+ device.vkDestroyRenderPass(render_pass, null);
+
+ device.vkDestroyDescriptorPool(desc_pool, null);
+ device.vkDestroyPipelineLayout(pipeline_layout, null);
+ device.vkDestroyDescriptorSetLayout(desc_layout, null);
+
+ device.vkDestroyImageView(depthView, null);
+ device.vkFreeMemory(depthMemory, null);
+ device.vkDestroyImage(depthImage, null);
+
+ for (int i = 0; i < chainImageView.size(); i++)
+ device.vkDestroyImageView(chainImageView.getAtIndex(i), null);
+
+ device.vkDestroySwapchainKHR(chain, null);
+
+ device.vkDestroyCommandPool(cmd_pool, null);
+ device.vkDestroyDevice(null);
+
+ instance.vkDestroySurfaceKHR(surface, null);
+
+ if (logger != null)
+ instance.vkDestroyDebugUtilsMessengerEXT(logger, null);
+ instance.vkDestroyInstance(null);
+ }
+
+ IntArray loadSPIRV0(String name) throws IOException {
+ // hmm any way to just load this directly?
+ try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(8192).order(ByteOrder.nativeOrder());
+ int length = Channels.newChannel(is).read(bb);
+
+ bb.position(0);
+ bb.limit(length);
+
+ return IntArray.create(MemorySegment.ofByteBuffer(bb));
+ }
+ }
+
+ IntArray loadSPIRV(String name) throws IOException {
+ try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+ MemorySegment seg = ((SegmentAllocator)scope).allocateArray(Memory.INT, 2048);
+ int length = Channels.newChannel(is).read(seg.asByteBuffer());
+
+ return IntArray.create(seg.asSlice(0, length));
+ }
+ }
+ IntArray loadSPIRV(String name, SegmentAllocator alloc) throws IOException {
+ try (InputStream is = TestCube.class.getResourceAsStream(name)) {
+ MemorySegment seg = alloc.allocateArray(Memory.INT, 2048);
+ int length = Channels.newChannel(is).read(seg.asByteBuffer());
+
+ return IntArray.create(seg.asSlice(0, length));
+ }
+ }
+
+ /**
+ * This finds the memory type index for the memory on a specific device.
+ */
+ static int find_memory_type(VkPhysicalDeviceMemoryProperties memory, int typeMask, int query) {
+ VkMemoryType mtypes = memory.getMemoryTypes();
+
+ for (int i = 0; i < memory.getMemoryTypeCount(); i++) {
+ if (((1 << i) & typeMask) != 0 && ((mtypes.getPropertyFlags(i) & query) == query))
+ return i;
+ }
+ return -1;
+ }
+
+ public static int VK_MAKE_API_VERSION(int variant, int major, int minor, int patch) {
+ return (variant << 29) | (major << 22) | (minor << 12) | patch;
+ }
+
+ static int clampi(int v, int min, int max) {
+ return v < min ? min : v < max ? v : max;
+ }
+
+ void init_matrices() {
+ float eye[] = new float[] {-5, 3, -10};
+ float centre[] = new float[] {0, 0, 0};
+ float up[] = new float[] {0, -1, 0};
+ float t0[] = new float[16], t1[] = new float[16];
+
+ perspective(projection, (float)(Math.PI * 0.25), 1.0f, 0.1f, 100.0f);
+ lookAt(view, eye, centre, up);
+ identity4f(model);
+ mult4x4f(t0, clip, projection);
+ mult4x4f(t1, t0, view);
+ mult4x4f(mvp, t1, model);
+ }
+
+ void demo() throws Exception {
+ cube_vs = loadSPIRV("cube_vs.bin");
+ cube_fs = loadSPIRV("cube_fs.bin");
+
+ init_matrices();
+
+ init_instance();
+ init_debug();
+
+ init_surface();
+ init_device();
+ init_device_queue();
+ init_command();
+ init_depth();
+ init_uniform();
+
+ init_descriptor();
+ init_render();
+ init_framebuffer();
+ init_vertexbuffer();
+ init_pipeline();
+
+ execute_begin_command_buffer();
+
+ cmd_paint();
+
+ System.out.println("behold the prize!");
+ Thread.sleep(2000);
+
+ shutdown();
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.loadLibrary("vulkan");
+ System.loadLibrary("X11");
+
+ new TestCube().demo();
+ }
+}
# TODO: things that take a Memory.*Array probably don't need to also take a count, if one is defined in the registry
# TODO: the api constants, where to?
# TODO: vkDestroyDevice and vkDestroyInstance should close the ResourceScope they have
+# TODO: inlined array accessor should use $SH method to get segment
use Data::Dumper;
use File::Path qw(make_path);
'VkCommandBufferAllocateInfo' => 3
);
+# functions which return last argument via a *
+# most are auto-detected in the setup phase
+my %functionCreate = (
+ # don't really help much
+ #vkAcquireNextImageKHR => { create => 1, static => 0 },
+ #vkAcquireNextImage2KHR => { create => 1, static => 0 }
+ );
+
# unused but this documents all INSTANCE creation functions
%dynamicInit = (
}
}
+# ##################################################################### #
+
+# convert the shitty xml loader format into something easier to use
+# ... types
+
foreach $x (grep { $_->isa('types') } @{$registry->{Kids}}) {
foreach $t (grep { $_->isa('type') } @{$x->{Kids}}) {
if (!defined($t->{alias})) {
}
}
+# ... enums
foreach $x (grep { $_->isa('enums') } @{$registry->{Kids}}) {
if ($x->{type} eq "enum") {
my @members = ();
}
}
+# ... commands
foreach $x (grep { $_->isa('commands') } @{$registry->{Kids}}) {
foreach $y (grep { $_->isa('command') } @{$x->{Kids}}) {
if (!defined($y->{alias})) {
}
}
+# parse feature descriptions
+
foreach $x (grep { $_->isa('feature') } @{$registry->{Kids}}) {
my %feature = (
api => $x->{api},
push @features, \%feature;
}
+# and extensions
foreach $x (grep { $_->isa('extensions') } @{$registry->{Kids}}) {
foreach $y (grep { $_->isa('extension') } @{$x->{Kids}}) {
my %extension = (
}
}
+# ##################################################################### #
# TODO: unused
sub findType {
if (defined($type->{name})) {
return $type->{name};
} else {
- return "MemorySegment";
+ return "Addressable";
}
} else {
# FIXME: This loops to handle a double-alias for some flag types
# TODO: don't include return type if it's success
#$desc = ($cmd->{successcodes} eq 'VK_SUCCESS') ? $desc = 'void' : $jrtype;
$desc = $params[$#params]->{baseType};
+
+ # HACK: for integer returns
+ $desc = 'int' if $desc eq 'uint32_t';
+
$desc .= " $proto->{name}(";
# TODO: static or not
if ($cmd->{successcodes}) {
my $returnStatement = ($cmd->{successcodes} eq 'VK_SUCCESS') ? 'return' : 'return res$code';
foreach $r (split /,/, $cmd->{successcodes}) {
- print $f "\t\t\tif (res\$code == VkResult.$r) $returnStatement;\n";
+ print $f "\t\t\tif (res\$code == VkConstants.$r) $returnStatement;\n";
}
} elsif ($jrtype ne "void") {
print $f "\t\t\treturn res\$code;\n";
# FIXME: see vkCreateGraphicsPipelines multiple 'success' types
# This might cause leaks?
- print $f "\t\t\tif (res\$code == VkResult.VK_SUCCESS)\n\t" if ($jrtype ne 'void');
+ print $f "\t\t\tif (res\$code == VkConstants.VK_SUCCESS)\n\t" if ($jrtype ne 'void');
# initialise extension function tables if required
if ($data{$instanceType}->{type} eq "VK_DEFINE_HANDLE") {
} else {
print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0), instanceDispatch, deviceDispatch);\n";
}
- } else {
+ } elsif ($data{$instanceType}->{type} eq 'VK_DEFINE_NON_DISPATCHABLE_HANDLE') {
print $f "\t\t\treturn $instanceType.create(instance\$h.get(Memory.POINTER, 0));\n";
+ } elsif ($instanceType eq 'uint32_t') {
+ print $f "\t\t\treturn instance\$h.get(Memory.INT, 0);\n";
+ } else {
+ print "Unhandled return type for 'constructor' method\n".Dumper($instanceType);
+ die;
}
print $f "\t\t} catch (Throwable t) { throw new RuntimeException(t); }\n";
if ($s->{category} eq 'struct') {
my $sizeof = 0;
my $index = 0;
+ my $minAlign = 8;
$layout = 'MemoryLayout.structLayout(';
for $m (@{$s->{members}}) {
$sizeof += $pad;
}
+ $minAlign = $align if $align > $minAlign;
+
#if ($type =~ m/\.LAYOUT$/) {
{
if ($m->{fullType} =~ m/\[(\d+)\]/) {
$layout .= $type.".withName(\"$m->{name}\")";
$sizeof += $size;
}
- my $pad = $sizeof & 63;
+ my $pad = $sizeof & ($minAlign - 1);
$layout .= ',' if ($pad);
$layout .= "MemoryLayout.paddingLayout($pad)" if ($pad);
$layout .= ')';
# look for all 'create' functions - last parameter is a writeable handle*
# also work out which ones are static or not - first parameter is (dispatchable?) handle
-%functionCreate = ();
foreach $x (values %functionSets) {
foreach $y (@{$x}) {
my $cmd = $commands{$y};
make_path($baseDir);
-# dump all structure (and enum?) types
+# dump all structure types
foreach $x (sort keys %dump) {
my $d = $dump{$x};
print $f "\t$s->{name}(MemorySegment segment) {\n";
print $f "\t\tthis.segment = segment;\n";
if ($isTyped && $members[0]->{values}) {
- print $f "\t\tsegment.set(Memory.INT, 0, VkStructureType.$members[0]->{values});\n";
+ print $f "\t\tsegment.set(Memory.INT, 0, VkConstants.$members[0]->{values});\n";
}
print $f "\t}\n";
print $f "\tpublic final MemoryAddress address() { return segment.address(); }\n";
print $f "\tpublic final ResourceScope scope() { return segment.scope(); }\n";
+ print $f "\tpublic String toString() { return String.format(\"$s->{name}\{ \$\%016x \}\", address().toRawLongValue()); }\n";
+
# basic factory methods
print $f "\tpublic static $s->{name} create(SegmentAllocator frame) {\n";
print $f "\t\treturn new $s->{name}(frame.allocate(LAYOUT));\n";
print $f "\t}\n\n";
if (!$isTyped) {
- print $f "\tpublic static $s->{name} createArray(SegmentAllocator frame, long count) {\n";
+ print $f "\tpublic static $s->{name} createArray(long count, SegmentAllocator frame) {\n";
print $f "\t\treturn new $s->{name}(frame.allocateArray(LAYOUT, count));\n";
print $f "\t}\n\n";
+ } else {
+ print $f "\tpublic static $s->{name} createArray(long count, SegmentAllocator frame) {\n";
+ print $f "\t\t$s->{name} self = new $s->{name}(frame.allocateArray(LAYOUT, count));\n";
+ print $f "\t\tfor (long i=1;i<count;i++) self.segment.set(Memory.INT, i * LAYOUT.byteSize(), VkConstants.$members[0]->{values});\n";
+ print $f "\t\treturn self;\n";
+ print $f "\t}\n\n";
}
print $f "\tpublic static $s->{name} create(ResourceScope scope) {\n";
print $f "\t}\n\n";
if (!$isTyped) {
- print $f "\tpublic static $s->{name} createArray(ResourceScope scope, long count) {\n";
+ print $f "\tpublic static $s->{name} createArray(long count, ResourceScope scope) {\n";
print $f "\t\treturn new $s->{name}(MemorySegment.allocateNative(LAYOUT.byteSize() * count, scope));\n";
print $f "\t}\n\n";
}
print $f "\t\t$src\$VH.set(seg, value != null ? value.length : 0);\n";
}
} elsif (defined($data{$jtype})) {
- print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+ print $f "\t\t$m->{name}\$VH.set(seg, Memory.address(value));\n";
} elsif ($jtype =~ m/Array$|^HandleArray/) {
- print $f "\t\t$m->{name}\$VH.set(segment, Memory.address(value));\n";
+ print $f "\t\t$m->{name}\$VH.set(seg, Memory.address(value));\n";
} else {
- print $f "\t\t$m->{name}\$VH.set(segment, value);\n";
+ print $f "\t\t$m->{name}\$VH.set(seg, value);\n";
}
print $f "\t}\n";
}
# embedded structs are handled as offsets in the accessors
if (functionDescriptorType($m) =~ m/\.LAYOUT$/) {
print $f "\t// static final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
+ } elsif ($m->{fullType} =~ m/\[.+\]/) {
+ print $f "\tstatic final MethodHandle $m->{name}\$SH = LAYOUT.sliceHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"), MemoryLayout.PathElement.sequenceElement());\n";
+
} else {
print $f "\tstatic final VarHandle $m->{name}\$VH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement(\"$m->{name}\"));\n";
}
print $f "}\n";
close($f);
- } elsif ($s->{category} eq "enum" || $s->{category} eq 'enum:bitmask') {
- my @members = @{$s->{members}};
- my $type = calcSize($s) == 32 ? "int" : "long";
- my $L = $type eq "long" ? "L" : "";
- my %seen = ();
-
- open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
-
- print $f "package $targetPackage;\n";
- print $f "import au.notzed.nativez.Memory;\n";
- print $f "public class $s->{name} {\n";
-
- my $tries = 0;
- do {
- my @later = ();
- foreach $m (@members) {
- next if ($seen{$m->{name}});
-
- if (defined($m->{value})) {
- print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
- print $f "\tpublic static final $type $m->{name} = $m->{value}$L;\n";
- $seen{$m->{name}} = $m;
- } elsif (defined($m->{alias})) {
- if ($seen{$m->{alias}}) {
- print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
- print $f "\tpublic static final $type $m->{name} = $m->{alias};\n";
- $seen{$m->{name}} = $m;
- } else {
- push @later, $m;
- }
- } else {
- print Dumper($s);
- print Dumper($m);
- die();
- }
- }
- @members = (@later);
- } while ($#members >= 0 && $tries++ < 5);
-
- if ($#members >= 0) {
- print Dumper($s);
- print "Left over\n";
- foreach $m (@members) {
- print Dumper($m);
- }
- print "seen\n";
- foreach $m (keys %seen) {
- print Dumper($seen{$m});
- }
- die("unable to resolve aliases");
- }
-
- # FIXME: this shouldn't be necessary, the other generators should just use the right primitive types
- if ($type eq "int") {
- print $f "final static jdk.incubator.foreign.ValueLayout.OfInt LAYOUT = Memory.INT;\n";
- } else {
- print $f "final static jdk.incubator.foreign.ValueLayout.OfLong LAYOUT = Memory.LONG;\n";
- }
-
- print $f "}\n";
- close($f);
-
} elsif ($s->{category} eq 'handle') {
open(my $f, ">", "$baseDir/$s->{name}.java") || die("unable to open $baseDir/$s->{name}.java");
print $f "\tfinal MemoryAddress address;\n";
+ print $f "\tpublic String toString() { return String.format(\"$s->{name}\{ \$\%016x \}\", address().toRawLongValue()); }\n";
+
# instances use a different constructor
if ($s->{type} eq 'VK_DEFINE_HANDLE') {
print $f "\tfinal DispatchInstance instanceDispatch;\n";
print $f "\t\treturn HandleArray.createArray(count, frame, (a,s) -> $s->{name}.create(a));\n";
print $f "\t}\n\n";
+ print $f "\tpublic static HandleArray<$s->{name}> create(SegmentAllocator frame, $s->{name} ... values) {\n";
+ print $f "\t\treturn HandleArray.create(frame, (a,s) -> $s->{name}.create(a), values);\n";
+ print $f "\t}\n\n";
+
#print $f "\tpublic static HandleArray<$s->{name}> createArray(ResourceScope scope, long count) {\n";
#print $f "\t\treturn new HandleArray<>($s->{name}::new, MemorySegment.allocateNative(Memory.POINTER.byteSize() * count, Memory.POINTER.byteAlignment(), scope));\n";
#print $f "\t}\n\n";
}
+# dump all enums in one class otherwise it's too much of a headfuck to use
+open(my $f, ">", "$baseDir/VkConstants.java") || die("unable to open $baseDir/VkConstants.java");
+
+print $f "package $targetPackage;\n";
+print $f "import au.notzed.nativez.Memory;\n";
+print $f "public class VkConstants {\n";
+
+# first dump the api constants, these have limited types but need mapping to java format
+print $f "\t// API Constants\n";
+foreach $x (values %apiConstants) {
+ my $v = $x->{value};
+
+ $v =~ s/LL//;
+ $v =~ s/U//;
+
+ print $f "\tpublic static final $typeToJavaPrimitive{$x->{type}} $x->{name} = $v;\n";
+}
+
+foreach $x (sort keys %dump) {
+ my $d = $dump{$x};
+
+ next if $toDrop{$d->{name}};
+ next if ($d->{category} eq 'command');
+
+ my $name = $d->{name};
+ my $s = $data{$name};
+
+ if ($s->{category} eq "enum" || $s->{category} eq 'enum:bitmask') {
+ my @members = @{$s->{members}};
+ my $type = calcSize($s) == 32 ? "int" : "long";
+ my $L = $type eq "long" ? "L" : "";
+ my %seen = ();
+
+ print $f "\t/* $s->{name} */\n";
+
+ my $tries = 0;
+ do {
+ my @later = ();
+ foreach $m (@members) {
+ next if ($seen{$m->{name}});
+
+ if (defined($m->{value})) {
+ print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+ print $f "\tpublic static final $type $m->{name} = $m->{value}$L;\n";
+ $seen{$m->{name}} = $m;
+ } elsif (defined($m->{alias})) {
+ if ($seen{$m->{alias}}) {
+ print $f "\t/**\n\t * $m->{comment}\n\t*/\n" if ($m->{comment});
+ print $f "\tpublic static final $type $m->{name} = $m->{alias};\n";
+ $seen{$m->{name}} = $m;
+ } else {
+ push @later, $m;
+ }
+ } else {
+ print Dumper($s);
+ print Dumper($m);
+ die();
+ }
+ }
+ @members = (@later);
+ } while ($#members >= 0 && $tries++ < 5);
+
+ if ($#members >= 0) {
+ print Dumper($s);
+ print "Left over\n";
+ foreach $m (@members) {
+ print Dumper($m);
+ }
+ print "seen\n";
+ foreach $m (keys %seen) {
+ print Dumper($seen{$m});
+ }
+ die("unable to resolve aliases");
+ }
+
+ # FIXME: this shouldn't be necessary, the other generators should just use the right primitive types
+ #if ($type eq "int") {
+ # print $f "final static jdk.incubator.foreign.ValueLayout.OfInt LAYOUT = Memory.INT;\n";
+ #} else {
+ # print $f "final static jdk.incubator.foreign.ValueLayout.OfLong LAYOUT = Memory.LONG;\n";
+ #}
+ }
+}
+
+print $f "}\n";
+close($f);
+
# ###################################################################### #
# create extension handles
my %byExtension = ();