+ * This is like the {@link ColoredRotatingQuadDemo}, but it adds a depth buffer to avoid false overdraw.
+ *
+ * @author Kai Burjack
+ */
+public class VulkanStarter {
+ private static boolean debug = System.getProperty("NDEBUG") == null;
+
+ private static ByteBuffer[] layers = {
+ memUTF8("VK_LAYER_LUNARG_standard_validation"),
+ };
+
+ /**
+ * This is just -1L, but it is nicer as a symbolic constant.
+ */
+ private static final long UINT64_MAX = 0xFFFFFFFFFFFFFFFFL;
+
+ /**
+ * Create a Vulkan instance using LWJGL 3.
+ *
+ * @return the VkInstance handle
+ */
+ private static VkInstance createInstance(PointerBuffer requiredExtensions) {
+ VkApplicationInfo appInfo = VkApplicationInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_APPLICATION_INFO)
+ .apiVersion(VK_API_VERSION_1_0);
+ PointerBuffer ppEnabledExtensionNames = memAllocPointer(requiredExtensions.remaining() + 1);
+ ppEnabledExtensionNames.put(requiredExtensions);
+ ByteBuffer VK_EXT_DEBUG_REPORT_EXTENSION = memUTF8(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
+ ppEnabledExtensionNames.put(VK_EXT_DEBUG_REPORT_EXTENSION);
+ ppEnabledExtensionNames.flip();
+ PointerBuffer ppEnabledLayerNames = memAllocPointer(layers.length);
+ for (int i = 0; debug && i < layers.length; i++)
+ ppEnabledLayerNames.put(layers[i]);
+ ppEnabledLayerNames.flip();
+ VkInstanceCreateInfo pCreateInfo = VkInstanceCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO)
+ .pApplicationInfo(appInfo)
+ .ppEnabledExtensionNames(ppEnabledExtensionNames)
+ .ppEnabledLayerNames(ppEnabledLayerNames);
+ PointerBuffer pInstance = memAllocPointer(1);
+ int err = vkCreateInstance(pCreateInfo, null, pInstance);
+ long instance = pInstance.get(0);
+ memFree(pInstance);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create VkInstance: " + VKUtils.translateVulkanResult(err));
+ }
+ VkInstance ret = new VkInstance(instance, pCreateInfo);
+ pCreateInfo.free();
+ memFree(ppEnabledLayerNames);
+ memFree(VK_EXT_DEBUG_REPORT_EXTENSION);
+ memFree(ppEnabledExtensionNames);
+ memFree(appInfo.pApplicationName());
+ memFree(appInfo.pEngineName());
+ appInfo.free();
+ return ret;
+ }
+
+ private static long setupDebugging(VkInstance instance, int flags, VkDebugReportCallbackEXT callback) {
+ VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = VkDebugReportCallbackCreateInfoEXT.calloc()
+ .sType(VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT)
+ .pfnCallback(callback)
+ .flags(flags);
+ LongBuffer pCallback = memAllocLong(1);
+ int err = vkCreateDebugReportCallbackEXT(instance, dbgCreateInfo, null, pCallback);
+ long callbackHandle = pCallback.get(0);
+ memFree(pCallback);
+ dbgCreateInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create VkInstance: " + VKUtils.translateVulkanResult(err));
+ }
+ return callbackHandle;
+ }
+
+ private static VkPhysicalDevice getFirstPhysicalDevice(VkInstance instance) {
+ IntBuffer pPhysicalDeviceCount = memAllocInt(1);
+ int err = vkEnumeratePhysicalDevices(instance, pPhysicalDeviceCount, null);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get number of physical devices: " + VKUtils.translateVulkanResult(err));
+ }
+ PointerBuffer pPhysicalDevices = memAllocPointer(pPhysicalDeviceCount.get(0));
+ err = vkEnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
+ long physicalDevice = pPhysicalDevices.get(0);
+ memFree(pPhysicalDeviceCount);
+ memFree(pPhysicalDevices);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get physical devices: " + VKUtils.translateVulkanResult(err));
+ }
+ return new VkPhysicalDevice(physicalDevice, instance);
+ }
+
+ private static class DeviceAndGraphicsQueueFamily {
+ VkDevice device;
+ int queueFamilyIndex;
+ VkPhysicalDeviceMemoryProperties memoryProperties;
+ }
+
+ private static DeviceAndGraphicsQueueFamily createDeviceAndGetGraphicsQueueFamily(VkPhysicalDevice physicalDevice) {
+ IntBuffer pQueueFamilyPropertyCount = memAllocInt(1);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, null);
+ int queueCount = pQueueFamilyPropertyCount.get(0);
+ VkQueueFamilyProperties.Buffer queueProps = VkQueueFamilyProperties.calloc(queueCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, queueProps);
+ memFree(pQueueFamilyPropertyCount);
+ int graphicsQueueFamilyIndex;
+ for (graphicsQueueFamilyIndex = 0; graphicsQueueFamilyIndex < queueCount; graphicsQueueFamilyIndex++) {
+ if ((queueProps.get(graphicsQueueFamilyIndex).queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0)
+ break;
+ }
+ queueProps.free();
+ FloatBuffer pQueuePriorities = memAllocFloat(1).put(0.0f);
+ pQueuePriorities.flip();
+ VkDeviceQueueCreateInfo.Buffer queueCreateInfo = VkDeviceQueueCreateInfo.calloc(1)
+ .sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO)
+ .queueFamilyIndex(graphicsQueueFamilyIndex)
+ .pQueuePriorities(pQueuePriorities);
+
+ PointerBuffer extensions = memAllocPointer(1);
+ ByteBuffer VK_KHR_SWAPCHAIN_EXTENSION = memUTF8(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ extensions.put(VK_KHR_SWAPCHAIN_EXTENSION);
+ extensions.flip();
+ PointerBuffer ppEnabledLayerNames = memAllocPointer(layers.length);
+ for (int i = 0; debug && i < layers.length; i++)
+ ppEnabledLayerNames.put(layers[i]);
+ ppEnabledLayerNames.flip();
+
+ VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO)
+ .pQueueCreateInfos(queueCreateInfo)
+ .ppEnabledExtensionNames(extensions)
+ .ppEnabledLayerNames(ppEnabledLayerNames);
+
+ PointerBuffer pDevice = memAllocPointer(1);
+ int err = vkCreateDevice(physicalDevice, deviceCreateInfo, null, pDevice);
+ long device = pDevice.get(0);
+ memFree(pDevice);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create device: " + VKUtils.translateVulkanResult(err));
+ }
+
+ VkPhysicalDeviceMemoryProperties memoryProperties = VkPhysicalDeviceMemoryProperties.calloc();
+ vkGetPhysicalDeviceMemoryProperties(physicalDevice, memoryProperties);
+
+ DeviceAndGraphicsQueueFamily ret = new DeviceAndGraphicsQueueFamily();
+ ret.device = new VkDevice(device, physicalDevice, deviceCreateInfo);
+ ret.queueFamilyIndex = graphicsQueueFamilyIndex;
+ ret.memoryProperties = memoryProperties;
+
+ deviceCreateInfo.free();
+ memFree(ppEnabledLayerNames);
+ memFree(VK_KHR_SWAPCHAIN_EXTENSION);
+ memFree(extensions);
+ memFree(pQueuePriorities);
+ return ret;
+ }
+
+ private static boolean getSupportedDepthFormat(VkPhysicalDevice physicalDevice, IntBuffer depthFormat) {
+ // Since all depth formats may be optional, we need to find a suitable depth format to use
+ // Start with the highest precision packed format
+ int[] depthFormats = {
+ VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_FORMAT_D32_SFLOAT,
+ VK_FORMAT_D24_UNORM_S8_UINT,
+ VK_FORMAT_D16_UNORM_S8_UINT,
+ VK_FORMAT_D16_UNORM
+ };
+
+ VkFormatProperties formatProps = VkFormatProperties.calloc();
+ for (int format : depthFormats) {
+ vkGetPhysicalDeviceFormatProperties(physicalDevice, format, formatProps);
+ // Format must support depth stencil attachment for optimal tiling
+ if ((formatProps.optimalTilingFeatures() & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
+ depthFormat.put(0, format);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class ColorAndDepthFormatAndSpace {
+ int colorFormat;
+ int colorSpace;
+ int depthFormat;
+ }
+
+ private static ColorAndDepthFormatAndSpace getColorFormatAndSpace(VkPhysicalDevice physicalDevice, long surface) {
+ IntBuffer pQueueFamilyPropertyCount = memAllocInt(1);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, null);
+ int queueCount = pQueueFamilyPropertyCount.get(0);
+ VkQueueFamilyProperties.Buffer queueProps = VkQueueFamilyProperties.calloc(queueCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, queueProps);
+ memFree(pQueueFamilyPropertyCount);
+
+ // Iterate over each queue to learn whether it supports presenting:
+ IntBuffer supportsPresent = memAllocInt(queueCount);
+ for (int i = 0; i < queueCount; i++) {
+ supportsPresent.position(i);
+ int err = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, supportsPresent);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to physical device surface support: " + VKUtils.translateVulkanResult(err));
+ }
+ }
+
+ // Search for a graphics and a present queue in the array of queue families, try to find one that supports both
+ int graphicsQueueNodeIndex = Integer.MAX_VALUE;
+ int presentQueueNodeIndex = Integer.MAX_VALUE;
+ for (int i = 0; i < queueCount; i++) {
+ if ((queueProps.get(i).queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0) {
+ if (graphicsQueueNodeIndex == Integer.MAX_VALUE) {
+ graphicsQueueNodeIndex = i;
+ }
+ if (supportsPresent.get(i) == VK_TRUE) {
+ graphicsQueueNodeIndex = i;
+ presentQueueNodeIndex = i;
+ break;
+ }
+ }
+ }
+ queueProps.free();
+ if (presentQueueNodeIndex == Integer.MAX_VALUE) {
+ // If there's no queue that supports both present and graphics try to find a separate present queue
+ for (int i = 0; i < queueCount; ++i) {
+ if (supportsPresent.get(i) == VK_TRUE) {
+ presentQueueNodeIndex = i;
+ break;
+ }
+ }
+ }
+ memFree(supportsPresent);
+
+ // Generate error if could not find both a graphics and a present queue
+ if (graphicsQueueNodeIndex == Integer.MAX_VALUE) {
+ throw new AssertionError("No graphics queue found");
+ }
+ if (presentQueueNodeIndex == Integer.MAX_VALUE) {
+ throw new AssertionError("No presentation queue found");
+ }
+ if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
+ throw new AssertionError("Presentation queue != graphics queue");
+ }
+
+ // Get list of supported formats
+ IntBuffer pFormatCount = memAllocInt(1);
+ int err = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pFormatCount, null);
+ int formatCount = pFormatCount.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to query number of physical device surface formats: " + VKUtils.translateVulkanResult(err));
+ }
+
+ VkSurfaceFormatKHR.Buffer surfFormats = VkSurfaceFormatKHR.calloc(formatCount);
+ err = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pFormatCount, surfFormats);
+ memFree(pFormatCount);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to query physical device surface formats: " + VKUtils.translateVulkanResult(err));
+ }
+
+ int colorFormat;
+ if (formatCount == 1 && surfFormats.get(0).format() == VK_FORMAT_UNDEFINED) {
+ colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ } else {
+ colorFormat = surfFormats.get(0).format();
+ }
+ int colorSpace = surfFormats.get(0).colorSpace();
+ surfFormats.free();
+
+ // Find suitable depth format
+ IntBuffer pDepthFormat = memAllocInt(1).put(0, -1);
+ getSupportedDepthFormat(physicalDevice, pDepthFormat);
+ int depthFormat = pDepthFormat.get(0);
+
+ ColorAndDepthFormatAndSpace ret = new ColorAndDepthFormatAndSpace();
+ ret.colorFormat = colorFormat;
+ ret.colorSpace = colorSpace;
+ ret.depthFormat = depthFormat;
+ return ret;
+ }
+
+ private static long createCommandPool(VkDevice device, int queueNodeIndex) {
+ VkCommandPoolCreateInfo cmdPoolInfo = VkCommandPoolCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO)
+ .queueFamilyIndex(queueNodeIndex)
+ .flags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
+ LongBuffer pCmdPool = memAllocLong(1);
+ int err = vkCreateCommandPool(device, cmdPoolInfo, null, pCmdPool);
+ long commandPool = pCmdPool.get(0);
+ cmdPoolInfo.free();
+ memFree(pCmdPool);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create command pool: " + VKUtils.translateVulkanResult(err));
+ }
+ return commandPool;
+ }
+
+ private static VkQueue createDeviceQueue(VkDevice device, int queueFamilyIndex) {
+ PointerBuffer pQueue = memAllocPointer(1);
+ vkGetDeviceQueue(device, queueFamilyIndex, 0, pQueue);
+ long queue = pQueue.get(0);
+ memFree(pQueue);
+ return new VkQueue(queue, device);
+ }
+
+ private static VkCommandBuffer createCommandBuffer(VkDevice device, long commandPool) {
+ VkCommandBufferAllocateInfo cmdBufAllocateInfo = VkCommandBufferAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO)
+ .commandPool(commandPool)
+ .level(VK_COMMAND_BUFFER_LEVEL_PRIMARY)
+ .commandBufferCount(1);
+ PointerBuffer pCommandBuffer = memAllocPointer(1);
+ int err = vkAllocateCommandBuffers(device, cmdBufAllocateInfo, pCommandBuffer);
+ cmdBufAllocateInfo.free();
+ long commandBuffer = pCommandBuffer.get(0);
+ memFree(pCommandBuffer);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to allocate command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ return new VkCommandBuffer(commandBuffer, device);
+ }
+
+ private static class Swapchain {
+ long swapchainHandle;
+ long[] images;
+ long[] imageViews;
+ }
+
+ private static Swapchain createSwapChain(VkDevice device, VkPhysicalDevice physicalDevice, long surface, long oldSwapChain, VkCommandBuffer commandBuffer, int newWidth,
+ int newHeight, int colorFormat, int colorSpace) {
+ int err;
+ // Get physical device surface properties and formats
+ VkSurfaceCapabilitiesKHR surfCaps = VkSurfaceCapabilitiesKHR.calloc();
+ err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, surfCaps);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get physical device surface capabilities: " + VKUtils.translateVulkanResult(err));
+ }
+
+ IntBuffer pPresentModeCount = memAllocInt(1);
+ err = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, null);
+ int presentModeCount = pPresentModeCount.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get number of physical device surface presentation modes: " + VKUtils.translateVulkanResult(err));
+ }
+
+ IntBuffer pPresentModes = memAllocInt(presentModeCount);
+ err = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes);
+ memFree(pPresentModeCount);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get physical device surface presentation modes: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Try to use mailbox mode. Low latency and non-tearing
+ int swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
+ for (int i = 0; i < presentModeCount; i++) {
+ if (pPresentModes.get(i) == VK_PRESENT_MODE_MAILBOX_KHR) {
+ swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+ break;
+ }
+ if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (pPresentModes.get(i) == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
+ swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ }
+ }
+ memFree(pPresentModes);
+
+ // Determine the number of images
+ int desiredNumberOfSwapchainImages = surfCaps.minImageCount() + 1;
+ if ((surfCaps.maxImageCount() > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount())) {
+ desiredNumberOfSwapchainImages = surfCaps.maxImageCount();
+ }
+
+ VkExtent2D currentExtent = surfCaps.currentExtent();
+ int currentWidth = currentExtent.width();
+ int currentHeight = currentExtent.height();
+ if (currentWidth != -1 && currentHeight != -1) {
+ width = currentWidth;
+ height = currentHeight;
+ } else {
+ width = newWidth;
+ height = newHeight;
+ }
+
+ int preTransform;
+ if ((surfCaps.supportedTransforms() & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0) {
+ preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ } else {
+ preTransform = surfCaps.currentTransform();
+ }
+ surfCaps.free();
+
+ VkSwapchainCreateInfoKHR swapchainCI = VkSwapchainCreateInfoKHR.calloc()
+ .sType(VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR)
+ .surface(surface)
+ .minImageCount(desiredNumberOfSwapchainImages)
+ .imageFormat(colorFormat)
+ .imageColorSpace(colorSpace)
+ .imageUsage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
+ .preTransform(preTransform)
+ .imageArrayLayers(1)
+ .imageSharingMode(VK_SHARING_MODE_EXCLUSIVE)
+ .presentMode(swapchainPresentMode)
+ .oldSwapchain(oldSwapChain)
+ .clipped(true)
+ .compositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
+ swapchainCI.imageExtent()
+ .width(width)
+ .height(height);
+ LongBuffer pSwapChain = memAllocLong(1);
+ err = vkCreateSwapchainKHR(device, swapchainCI, null, pSwapChain);
+ swapchainCI.free();
+ long swapChain = pSwapChain.get(0);
+ memFree(pSwapChain);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create swap chain: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // If we just re-created an existing swapchain, we should destroy the old swapchain at this point.
+ // Note: destroying the swapchain also cleans up all its associated presentable images once the platform is done with them.
+ if (oldSwapChain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(device, oldSwapChain, null);
+ }
+
+ IntBuffer pImageCount = memAllocInt(1);
+ err = vkGetSwapchainImagesKHR(device, swapChain, pImageCount, null);
+ int imageCount = pImageCount.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get number of swapchain images: " + VKUtils.translateVulkanResult(err));
+ }
+
+ LongBuffer pSwapchainImages = memAllocLong(imageCount);
+ err = vkGetSwapchainImagesKHR(device, swapChain, pImageCount, pSwapchainImages);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to get swapchain images: " + VKUtils.translateVulkanResult(err));
+ }
+ memFree(pImageCount);
+
+ long[] images = new long[imageCount];
+ long[] imageViews = new long[imageCount];
+ LongBuffer pBufferView = memAllocLong(1);
+ VkImageViewCreateInfo colorAttachmentView = VkImageViewCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO)
+ .format(colorFormat)
+ .viewType(VK_IMAGE_VIEW_TYPE_2D);
+ colorAttachmentView.subresourceRange()
+ .aspectMask(VK_IMAGE_ASPECT_COLOR_BIT)
+ .levelCount(1)
+ .layerCount(1);
+ for (int i = 0; i < imageCount; i++) {
+ images[i] = pSwapchainImages.get(i);
+ colorAttachmentView.image(images[i]);
+ err = vkCreateImageView(device, colorAttachmentView, null, pBufferView);
+ imageViews[i] = pBufferView.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create image view: " + VKUtils.translateVulkanResult(err));
+ }
+ }
+ colorAttachmentView.free();
+ memFree(pBufferView);
+ memFree(pSwapchainImages);
+
+ Swapchain ret = new Swapchain();
+ ret.images = images;
+ ret.imageViews = imageViews;
+ ret.swapchainHandle = swapChain;
+ return ret;
+ }
+
+ private static class DepthStencil {
+ long view;
+ }
+
+ private static DepthStencil createDepthStencil(VkDevice device, VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties, int depthFormat, VkCommandBuffer setupCmdBuffer) {
+ VkImageCreateInfo imageCreateInfo = VkImageCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO)
+ .imageType(VK_IMAGE_TYPE_2D)
+ .format(depthFormat)
+ .mipLevels(1)
+ .arrayLayers(1)
+ .samples(VK_SAMPLE_COUNT_1_BIT)
+ .tiling(VK_IMAGE_TILING_OPTIMAL)
+ .usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ imageCreateInfo.extent().width(width).height(height).depth(1);
+
+ VkMemoryAllocateInfo mem_alloc = VkMemoryAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
+
+ VkImageViewCreateInfo depthStencilViewCreateInfo = VkImageViewCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO)
+ .viewType(VK_IMAGE_VIEW_TYPE_2D)
+ .format(depthFormat);
+ depthStencilViewCreateInfo.subresourceRange()
+ .aspectMask(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)
+ .levelCount(1)
+ .layerCount(1);
+
+ VkMemoryRequirements memReqs = VkMemoryRequirements.calloc();
+ int err;
+
+ LongBuffer pDepthStencilImage = memAllocLong(1);
+ err = vkCreateImage(device, imageCreateInfo, null, pDepthStencilImage);
+ long depthStencilImage = pDepthStencilImage.get(0);
+ memFree(pDepthStencilImage);
+ imageCreateInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create depth-stencil image: " + VKUtils.translateVulkanResult(err));
+ }
+ vkGetImageMemoryRequirements(device, depthStencilImage, memReqs);
+ mem_alloc.allocationSize(memReqs.size());
+ IntBuffer pMemoryTypeIndex = memAllocInt(1);
+ getMemoryType(physicalDeviceMemoryProperties, memReqs.memoryTypeBits(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, pMemoryTypeIndex);
+ mem_alloc.memoryTypeIndex(pMemoryTypeIndex.get(0));
+ memFree(pMemoryTypeIndex);
+ LongBuffer pDepthStencilMem = memAllocLong(1);
+ err = vkAllocateMemory(device, mem_alloc, null, pDepthStencilMem);
+ long depthStencilMem = pDepthStencilMem.get(0);
+ memFree(pDepthStencilMem);
+ mem_alloc.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create depth-stencil memory: " + VKUtils.translateVulkanResult(err));
+ }
+
+ err = vkBindImageMemory(device, depthStencilImage, depthStencilMem, 0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to bind depth-stencil image to memory: " + VKUtils.translateVulkanResult(err));
+ }
+
+ depthStencilViewCreateInfo.image(depthStencilImage);
+ LongBuffer pDepthStencilView = memAllocLong(1);
+ err = vkCreateImageView(device, depthStencilViewCreateInfo, null, pDepthStencilView);
+ long depthStencilView = pDepthStencilView.get(0);
+ memFree(pDepthStencilView);
+ depthStencilViewCreateInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create depth-stencil image view: " + VKUtils.translateVulkanResult(err));
+ }
+
+ DepthStencil ret = new DepthStencil();
+ ret.view = depthStencilView;
+ return ret;
+ }
+
+ private static long createRenderPass(VkDevice device, int colorFormat, int depthFormat) {
+ VkAttachmentDescription.Buffer attachments = VkAttachmentDescription.calloc(2);
+ attachments.get(0) // <- color attachment
+ .format(colorFormat)
+ .samples(VK_SAMPLE_COUNT_1_BIT)
+ .loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR)
+ .storeOp(VK_ATTACHMENT_STORE_OP_STORE)
+ .stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE)
+ .stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE)
+ .initialLayout(VK_IMAGE_LAYOUT_UNDEFINED)
+ .finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+ attachments.get(1) // <- depth-stencil attachment
+ .format(depthFormat)
+ .samples(VK_SAMPLE_COUNT_1_BIT)
+ .loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR)
+ .storeOp(VK_ATTACHMENT_STORE_OP_STORE)
+ .stencilLoadOp(VK_ATTACHMENT_LOAD_OP_DONT_CARE)
+ .stencilStoreOp(VK_ATTACHMENT_STORE_OP_DONT_CARE)
+ .initialLayout(VK_IMAGE_LAYOUT_UNDEFINED)
+ .finalLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+
+ VkAttachmentReference.Buffer colorReference = VkAttachmentReference.calloc(1)
+ .attachment(0)
+ .layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ VkAttachmentReference depthReference = VkAttachmentReference.calloc()
+ .attachment(1)
+ .layout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+ VkSubpassDescription.Buffer subpass = VkSubpassDescription.calloc(1)
+ .pipelineBindPoint(VK_PIPELINE_BIND_POINT_GRAPHICS)
+ .colorAttachmentCount(colorReference.remaining())
+ .pColorAttachments(colorReference) // <- only color attachment
+ .pDepthStencilAttachment(depthReference) // <- and depth-stencil
+ ;
+
+ VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO)
+ .pAttachments(attachments)
+ .pSubpasses(subpass)
+ ;
+
+ LongBuffer pRenderPass = memAllocLong(1);
+ int err = vkCreateRenderPass(device, renderPassInfo, null, pRenderPass);
+ long renderPass = pRenderPass.get(0);
+ memFree(pRenderPass);
+ renderPassInfo.free();
+ depthReference.free();
+ colorReference.free();
+ subpass.free();
+ attachments.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create clear render pass: " + VKUtils.translateVulkanResult(err));
+ }
+ return renderPass;
+ }
+
+ private static long[] createFramebuffers(VkDevice device, Swapchain swapchain, long renderPass, int width, int height, DepthStencil depthStencil) {
+ LongBuffer attachments = memAllocLong(2);
+ attachments.put(1, depthStencil.view);
+ VkFramebufferCreateInfo fci = VkFramebufferCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO)
+ .pAttachments(attachments)
+ .height(height)
+ .width(width)
+ .layers(1)
+ .renderPass(renderPass);
+ // Create a framebuffer for each swapchain image
+ long[] framebuffers = new long[swapchain.images.length];
+ LongBuffer pFramebuffer = memAllocLong(1);
+ for (int i = 0; i < swapchain.images.length; i++) {
+ attachments.put(0, swapchain.imageViews[i]);
+ int err = vkCreateFramebuffer(device, fci, null, pFramebuffer);
+ long framebuffer = pFramebuffer.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create framebuffer: " + VKUtils.translateVulkanResult(err));
+ }
+ framebuffers[i] = framebuffer;
+ }
+ memFree(attachments);
+ memFree(pFramebuffer);
+ fci.free();
+ return framebuffers;
+ }
+
+ private static void submitCommandBuffer(VkQueue queue, VkCommandBuffer commandBuffer) {
+ if (commandBuffer == null || commandBuffer.address() == NULL)
+ return;
+ VkSubmitInfo submitInfo = VkSubmitInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
+ PointerBuffer pCommandBuffers = memAllocPointer(1)
+ .put(commandBuffer)
+ .flip();
+ submitInfo.pCommandBuffers(pCommandBuffers);
+ int err = vkQueueSubmit(queue, submitInfo, VK_NULL_HANDLE);
+ memFree(pCommandBuffers);
+ submitInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to submit command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ }
+
+ private static long loadShader(String classPath, VkDevice device, int stage) throws IOException {
+ ByteBuffer shaderCode = VKUtils.glslToSpirv(classPath, stage);
+ int err;
+ VkShaderModuleCreateInfo moduleCreateInfo = VkShaderModuleCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO)
+ .pCode(shaderCode);
+ LongBuffer pShaderModule = memAllocLong(1);
+ err = vkCreateShaderModule(device, moduleCreateInfo, null, pShaderModule);
+ long shaderModule = pShaderModule.get(0);
+ memFree(pShaderModule);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create shader module: " + VKUtils.translateVulkanResult(err));
+ }
+ return shaderModule;
+ }
+
+ private static VkPipelineShaderStageCreateInfo loadShader(VkDevice device, String classPath, int stage) throws IOException {
+ VkPipelineShaderStageCreateInfo shaderStage = VkPipelineShaderStageCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO)
+ .stage(stage)
+ .module(loadShader(classPath, device, stage))
+ .pName(memUTF8("main"));
+ return shaderStage;
+ }
+
+ private static boolean getMemoryType(VkPhysicalDeviceMemoryProperties deviceMemoryProperties, int typeBits, int properties, IntBuffer typeIndex) {
+ int bits = typeBits;
+ for (int i = 0; i < 32; i++) {
+ if ((bits & 1) == 1) {
+ if ((deviceMemoryProperties.memoryTypes(i).propertyFlags() & properties) == properties) {
+ typeIndex.put(0, i);
+ return true;
+ }
+ }
+ bits >>= 1;
+ }
+ return false;
+ }
+
+ private static class Vertices {
+ long verticesBuf;
+ VkPipelineVertexInputStateCreateInfo createInfo;
+ }
+
+ private static Vertices createVertices(VkPhysicalDeviceMemoryProperties deviceMemoryProperties, VkDevice device) {
+ ByteBuffer vertexBuffer = memAlloc(2 * 3 * (3 + 3) * 4);
+ FloatBuffer fb = vertexBuffer.asFloatBuffer();
+ // first triangle
+ fb.put(-0.5f).put(-0.5f).put(0.5f).put(1.0f).put(0.0f).put(0.0f);
+ fb.put( 0.5f).put(-0.5f).put(0.5f).put(0.0f).put(1.0f).put(0.0f);
+ fb.put( 0.0f).put( 0.5f).put(0.5f).put(0.0f).put(0.0f).put(1.0f);
+ // second triangle
+ fb.put( 0.5f).put(-0.5f).put(-0.5f).put(1.0f).put(1.0f).put(0.0f);
+ fb.put(-0.5f).put(-0.5f).put(-0.5f).put(0.0f).put(1.0f).put(1.0f);
+ fb.put( 0.0f).put( 0.5f).put(-0.5f).put(1.0f).put(0.0f).put(1.0f);
+
+ VkMemoryAllocateInfo memAlloc = VkMemoryAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
+ VkMemoryRequirements memReqs = VkMemoryRequirements.calloc();
+
+ int err;
+
+ // Generate vertex buffer
+ // Setup
+ VkBufferCreateInfo bufInfo = VkBufferCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO)
+ .size(vertexBuffer.remaining())
+ .usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
+ LongBuffer pBuffer = memAllocLong(1);
+ err = vkCreateBuffer(device, bufInfo, null, pBuffer);
+ long verticesBuf = pBuffer.get(0);
+ memFree(pBuffer);
+ bufInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create vertex buffer: " + VKUtils.translateVulkanResult(err));
+ }
+
+ vkGetBufferMemoryRequirements(device, verticesBuf, memReqs);
+ memAlloc.allocationSize(memReqs.size());
+ IntBuffer memoryTypeIndex = memAllocInt(1);
+ getMemoryType(deviceMemoryProperties, memReqs.memoryTypeBits(), VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, memoryTypeIndex);
+ memAlloc.memoryTypeIndex(memoryTypeIndex.get(0));
+ memFree(memoryTypeIndex);
+ memReqs.free();
+
+ LongBuffer pMemory = memAllocLong(1);
+ err = vkAllocateMemory(device, memAlloc, null, pMemory);
+ long verticesMem = pMemory.get(0);
+ memFree(pMemory);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to allocate vertex memory: " + VKUtils.translateVulkanResult(err));
+ }
+
+ PointerBuffer pData = memAllocPointer(1);
+ err = vkMapMemory(device, verticesMem, 0, vertexBuffer.remaining(), 0, pData);
+ memAlloc.free();
+ long data = pData.get(0);
+ memFree(pData);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to map vertex memory: " + VKUtils.translateVulkanResult(err));
+ }
+
+ memCopy(memAddress(vertexBuffer), data, vertexBuffer.remaining());
+ memFree(vertexBuffer);
+ vkUnmapMemory(device, verticesMem);
+ err = vkBindBufferMemory(device, verticesBuf, verticesMem, 0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to bind memory to vertex buffer: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Binding description
+ VkVertexInputBindingDescription.Buffer bindingDescriptor = VkVertexInputBindingDescription.calloc(1)
+ .binding(0) // <- we bind our vertex buffer to point 0
+ .stride((3 + 3) * 4)
+ .inputRate(VK_VERTEX_INPUT_RATE_VERTEX);
+
+ // Attribute descriptions
+ // Describes memory layout and shader attribute locations
+ VkVertexInputAttributeDescription.Buffer attributeDescriptions = VkVertexInputAttributeDescription.calloc(2);
+ // Location 0 : Position
+ attributeDescriptions.get(0)
+ .binding(0) // <- binding point used in the VkVertexInputBindingDescription
+ .location(0) // <- location in the shader's attribute layout (inside the shader source)
+ .format(VK_FORMAT_R32G32B32_SFLOAT)
+ .offset(0);
+ // Location 1 : Color
+ attributeDescriptions.get(1)
+ .binding(0) // <- binding point used in the VkVertexInputBindingDescription
+ .location(1) // <- location in the shader's attribute layout (inside the shader source)
+ .format(VK_FORMAT_R32G32B32_SFLOAT)
+ .offset(3 * 4);
+
+ // Assign to vertex buffer
+ VkPipelineVertexInputStateCreateInfo vi = VkPipelineVertexInputStateCreateInfo.calloc();
+ vi.sType(VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO);
+ vi.pVertexBindingDescriptions(bindingDescriptor);
+ vi.pVertexAttributeDescriptions(attributeDescriptions);
+
+ Vertices ret = new Vertices();
+ ret.createInfo = vi;
+ ret.verticesBuf = verticesBuf;
+ return ret;
+ }
+
+ private static long createDescriptorPool(VkDevice device) {
+ // We need to tell the API the number of max. requested descriptors per type
+ VkDescriptorPoolSize.Buffer typeCounts = VkDescriptorPoolSize.calloc(1)
+ // This example only uses one descriptor type (uniform buffer) and only
+ // requests one descriptor of this type
+ .type(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+ .descriptorCount(1);
+ // For additional types you need to add new entries in the type count list
+ // E.g. for two combined image samplers :
+ // typeCounts[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ // typeCounts[1].descriptorCount = 2;
+
+ // Create the global descriptor pool
+ // All descriptors used in this example are allocated from this pool
+ VkDescriptorPoolCreateInfo descriptorPoolInfo = VkDescriptorPoolCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO)
+ .pPoolSizes(typeCounts)
+ // Set the max. number of sets that can be requested
+ // Requesting descriptors beyond maxSets will result in an error
+ .maxSets(1);
+
+ LongBuffer pDescriptorPool = memAllocLong(1);
+ int err = vkCreateDescriptorPool(device, descriptorPoolInfo, null, pDescriptorPool);
+ long descriptorPool = pDescriptorPool.get(0);
+ memFree(pDescriptorPool);
+ descriptorPoolInfo.free();
+ typeCounts.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create descriptor pool: " + VKUtils.translateVulkanResult(err));
+ }
+ return descriptorPool;
+ }
+
+ private static class UboDescriptor {
+ long memory;
+ long buffer;
+ long offset;
+ long range;
+ }
+
+ private static UboDescriptor createUniformBuffer(VkPhysicalDeviceMemoryProperties deviceMemoryProperties, VkDevice device) {
+ int err;
+ // Create a new buffer
+ VkBufferCreateInfo bufferInfo = VkBufferCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO)
+ .size(16 * 4)
+ .usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
+ LongBuffer pUniformDataVSBuffer = memAllocLong(1);
+ err = vkCreateBuffer(device, bufferInfo, null, pUniformDataVSBuffer);
+ long uniformDataVSBuffer = pUniformDataVSBuffer.get(0);
+ memFree(pUniformDataVSBuffer);
+ bufferInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create UBO buffer: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Get memory requirements including size, alignment and memory type
+ VkMemoryRequirements memReqs = VkMemoryRequirements.calloc();
+ vkGetBufferMemoryRequirements(device, uniformDataVSBuffer, memReqs);
+ long memSize = memReqs.size();
+ int memoryTypeBits = memReqs.memoryTypeBits();
+ memReqs.free();
+ // Gets the appropriate memory type for this type of buffer allocation
+ // Only memory types that are visible to the host
+ IntBuffer pMemoryTypeIndex = memAllocInt(1);
+ getMemoryType(deviceMemoryProperties, memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, pMemoryTypeIndex);
+ int memoryTypeIndex = pMemoryTypeIndex.get(0);
+ memFree(pMemoryTypeIndex);
+ // Allocate memory for the uniform buffer
+ LongBuffer pUniformDataVSMemory = memAllocLong(1);
+ VkMemoryAllocateInfo allocInfo = VkMemoryAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO)
+ .allocationSize(memSize)
+ .memoryTypeIndex(memoryTypeIndex);
+ err = vkAllocateMemory(device, allocInfo, null, pUniformDataVSMemory);
+ long uniformDataVSMemory = pUniformDataVSMemory.get(0);
+ memFree(pUniformDataVSMemory);
+ allocInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to allocate UBO memory: " + VKUtils.translateVulkanResult(err));
+ }
+ // Bind memory to buffer
+ err = vkBindBufferMemory(device, uniformDataVSBuffer, uniformDataVSMemory, 0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to bind UBO memory: " + VKUtils.translateVulkanResult(err));
+ }
+
+ UboDescriptor ret = new UboDescriptor();
+ ret.memory = uniformDataVSMemory;
+ ret.buffer = uniformDataVSBuffer;
+ ret.offset = 0L;
+ ret.range = 16 * 4;
+
+ return ret;
+ }
+
+ private static long createDescriptorSet(VkDevice device, long descriptorPool, long descriptorSetLayout, UboDescriptor uniformDataVSDescriptor) {
+ LongBuffer pDescriptorSetLayout = memAllocLong(1);
+ pDescriptorSetLayout.put(0, descriptorSetLayout);
+ VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO)
+ .descriptorPool(descriptorPool)
+ .pSetLayouts(pDescriptorSetLayout);
+
+ LongBuffer pDescriptorSet = memAllocLong(1);
+ int err = vkAllocateDescriptorSets(device, allocInfo, pDescriptorSet);
+ long descriptorSet = pDescriptorSet.get(0);
+ memFree(pDescriptorSet);
+ allocInfo.free();
+ memFree(pDescriptorSetLayout);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create descriptor set: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Update descriptor sets determining the shader binding points
+ // For every binding point used in a shader there needs to be one
+ // descriptor set matching that binding point
+ VkDescriptorBufferInfo.Buffer descriptor = VkDescriptorBufferInfo.calloc(1)
+ .buffer(uniformDataVSDescriptor.buffer)
+ .range(uniformDataVSDescriptor.range)
+ .offset(uniformDataVSDescriptor.offset);
+ // Binding 0 : Uniform buffer
+ VkWriteDescriptorSet.Buffer writeDescriptorSet = VkWriteDescriptorSet.calloc(1)
+ .sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET)
+ .dstSet(descriptorSet)
+ .descriptorCount(1)
+ .descriptorType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+ .pBufferInfo(descriptor)
+ .dstBinding(0); // <- Binds this uniform buffer to binding point 0
+ vkUpdateDescriptorSets(device, writeDescriptorSet, null);
+ writeDescriptorSet.free();
+ descriptor.free();
+
+ return descriptorSet;
+ }
+
+ private static long createDescriptorSetLayout(VkDevice device) {
+ int err;
+ // One binding for a UBO used in a vertex shader
+ VkDescriptorSetLayoutBinding.Buffer layoutBinding = VkDescriptorSetLayoutBinding.calloc(1)
+ .binding(0) // <- Binding 0 : Uniform buffer (Vertex shader)
+ .descriptorType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+ .descriptorCount(1)
+ .stageFlags(VK_SHADER_STAGE_VERTEX_BIT);
+ // Build a create-info struct to create the descriptor set layout
+ VkDescriptorSetLayoutCreateInfo descriptorLayout = VkDescriptorSetLayoutCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO)
+ .pBindings(layoutBinding);
+
+ LongBuffer pDescriptorSetLayout = memAllocLong(1);
+ err = vkCreateDescriptorSetLayout(device, descriptorLayout, null, pDescriptorSetLayout);
+ long descriptorSetLayout = pDescriptorSetLayout.get(0);
+ memFree(pDescriptorSetLayout);
+ descriptorLayout.free();
+ layoutBinding.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create descriptor set layout: " + VKUtils.translateVulkanResult(err));
+ }
+ return descriptorSetLayout;
+ }
+
+ private static class Pipeline {
+ long pipeline;
+ long layout;
+ }
+
+ private static Pipeline createPipeline(VkDevice device, long renderPass, VkPipelineVertexInputStateCreateInfo vi, long descriptorSetLayout) throws IOException {
+ int err;
+ // Vertex input state
+ // Describes the topoloy used with this pipeline
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = VkPipelineInputAssemblyStateCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO)
+ .topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
+
+ // Rasterization state
+ VkPipelineRasterizationStateCreateInfo rasterizationState = VkPipelineRasterizationStateCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO)
+ .polygonMode(VK_POLYGON_MODE_FILL)
+ .cullMode(VK_CULL_MODE_NONE) // <- VK_CULL_MODE_BACK_BIT would work here, too!
+ .frontFace(VK_FRONT_FACE_COUNTER_CLOCKWISE)
+ .lineWidth(1.0f);
+
+ // Color blend state
+ // Describes blend modes and color masks
+ VkPipelineColorBlendAttachmentState.Buffer colorWriteMask = VkPipelineColorBlendAttachmentState.calloc(1)
+ .colorWriteMask(0xF); // <- RGBA
+ VkPipelineColorBlendStateCreateInfo colorBlendState = VkPipelineColorBlendStateCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO)
+ .pAttachments(colorWriteMask);
+
+ // Viewport state
+ VkPipelineViewportStateCreateInfo viewportState = VkPipelineViewportStateCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO)
+ .viewportCount(1) // <- one viewport
+ .scissorCount(1); // <- one scissor rectangle
+
+ // Enable dynamic states
+ // Describes the dynamic states to be used with this pipeline
+ // Dynamic states can be set even after the pipeline has been created
+ // So there is no need to create new pipelines just for changing
+ // a viewport's dimensions or a scissor box
+ IntBuffer pDynamicStates = memAllocInt(2);
+ pDynamicStates.put(VK_DYNAMIC_STATE_VIEWPORT).put(VK_DYNAMIC_STATE_SCISSOR).flip();
+ VkPipelineDynamicStateCreateInfo dynamicState = VkPipelineDynamicStateCreateInfo.calloc()
+ // The dynamic state properties themselves are stored in the command buffer
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO)
+ .pDynamicStates(pDynamicStates);
+
+ // Depth and stencil state
+ // Describes depth and stenctil test and compare ops
+ VkPipelineDepthStencilStateCreateInfo depthStencilState = VkPipelineDepthStencilStateCreateInfo.calloc()
+ // No depth test/write and no stencil used
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO)
+ .depthTestEnable(true)
+ .depthWriteEnable(true)
+ .depthCompareOp(VK_COMPARE_OP_LESS_OR_EQUAL);
+ depthStencilState.back()
+ .failOp(VK_STENCIL_OP_KEEP)
+ .passOp(VK_STENCIL_OP_KEEP)
+ .compareOp(VK_COMPARE_OP_ALWAYS);
+ depthStencilState.front(depthStencilState.back());
+
+ // Multi sampling state
+ // No multi sampling used in this example
+ VkPipelineMultisampleStateCreateInfo multisampleState = VkPipelineMultisampleStateCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO)
+ .rasterizationSamples(VK_SAMPLE_COUNT_1_BIT);
+
+ // Load shaders
+ VkPipelineShaderStageCreateInfo.Buffer shaderStages = VkPipelineShaderStageCreateInfo.calloc(2);
+ shaderStages.get(0).set(loadShader(device, "org/lwjgl/demo/vulkan/twoRotatingTriangles.vert", VK_SHADER_STAGE_VERTEX_BIT));
+ shaderStages.get(1).set(loadShader(device, "org/lwjgl/demo/vulkan/twoRotatingTriangles.frag", VK_SHADER_STAGE_FRAGMENT_BIT));
+
+ // Create the pipeline layout that is used to generate the rendering pipelines that
+ // are based on this descriptor set layout
+ LongBuffer pDescriptorSetLayout = memAllocLong(1).put(0, descriptorSetLayout);
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = VkPipelineLayoutCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO)
+ .pSetLayouts(pDescriptorSetLayout);
+
+ LongBuffer pPipelineLayout = memAllocLong(1);
+ err = vkCreatePipelineLayout(device, pipelineLayoutCreateInfo, null, pPipelineLayout);
+ long layout = pPipelineLayout.get(0);
+ memFree(pPipelineLayout);
+ pipelineLayoutCreateInfo.free();
+ memFree(pDescriptorSetLayout);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create pipeline layout: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Assign states
+ VkGraphicsPipelineCreateInfo.Buffer pipelineCreateInfo = VkGraphicsPipelineCreateInfo.calloc(1)
+ .sType(VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO)
+ .layout(layout) // <- the layout used for this pipeline (NEEDS TO BE SET! even though it is basically empty)
+ .renderPass(renderPass) // <- renderpass this pipeline is attached to
+ .pVertexInputState(vi)
+ .pInputAssemblyState(inputAssemblyState)
+ .pRasterizationState(rasterizationState)
+ .pColorBlendState(colorBlendState)
+ .pMultisampleState(multisampleState)
+ .pViewportState(viewportState)
+ .pDepthStencilState(depthStencilState)
+ .pStages(shaderStages)
+ .pDynamicState(dynamicState);
+
+ // Create rendering pipeline
+ LongBuffer pPipelines = memAllocLong(1);
+ err = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, pipelineCreateInfo, null, pPipelines);
+ long pipeline = pPipelines.get(0);
+ shaderStages.free();
+ multisampleState.free();
+ depthStencilState.free();
+ dynamicState.free();
+ memFree(pDynamicStates);
+ viewportState.free();
+ colorBlendState.free();
+ colorWriteMask.free();
+ rasterizationState.free();
+ inputAssemblyState.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create pipeline: " + VKUtils.translateVulkanResult(err));
+ }
+
+ Pipeline ret = new Pipeline();
+ ret.layout = layout;
+ ret.pipeline = pipeline;
+ return ret;
+ }
+
+ private static VkCommandBuffer[] createRenderCommandBuffers(VkDevice device, long commandPool, long[] framebuffers, long renderPass, int width, int height,
+ Pipeline pipeline, long descriptorSet, long verticesBuf) {
+ // Create the render command buffers (one command buffer per framebuffer image)
+ VkCommandBufferAllocateInfo cmdBufAllocateInfo = VkCommandBufferAllocateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO)
+ .commandPool(commandPool)
+ .level(VK_COMMAND_BUFFER_LEVEL_PRIMARY)
+ .commandBufferCount(framebuffers.length);
+ PointerBuffer pCommandBuffer = memAllocPointer(framebuffers.length);
+ int err = vkAllocateCommandBuffers(device, cmdBufAllocateInfo, pCommandBuffer);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to allocate render command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ VkCommandBuffer[] renderCommandBuffers = new VkCommandBuffer[framebuffers.length];
+ for (int i = 0; i < framebuffers.length; i++) {
+ renderCommandBuffers[i] = new VkCommandBuffer(pCommandBuffer.get(i), device);
+ }
+ memFree(pCommandBuffer);
+ cmdBufAllocateInfo.free();
+
+ // Create the command buffer begin structure
+ VkCommandBufferBeginInfo cmdBufInfo = VkCommandBufferBeginInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
+
+ // Specify clear color (cornflower blue)
+ VkClearValue.Buffer clearValues = VkClearValue.calloc(2);
+ clearValues.get(0).color()
+ .float32(0, 100/255.0f)
+ .float32(1, 149/255.0f)
+ .float32(2, 237/255.0f)
+ .float32(3, 1.0f);
+ // Specify clear depth-stencil
+ clearValues.get(1).depthStencil().depth(1.0f).stencil(0);
+
+ // Specify everything to begin a render pass
+ VkRenderPassBeginInfo renderPassBeginInfo = VkRenderPassBeginInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO)
+ .renderPass(renderPass)
+ .pClearValues(clearValues);
+ VkRect2D renderArea = renderPassBeginInfo.renderArea();
+ renderArea.offset().set(0, 0);
+ renderArea.extent().set(width, height);
+
+ for (int i = 0; i < renderCommandBuffers.length; ++i) {
+ // Set target frame buffer
+ renderPassBeginInfo.framebuffer(framebuffers[i]);
+
+ err = vkBeginCommandBuffer(renderCommandBuffers[i], cmdBufInfo);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to begin render command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+
+ vkCmdBeginRenderPass(renderCommandBuffers[i], renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ // Update dynamic viewport state
+ VkViewport.Buffer viewport = VkViewport.calloc(1)
+ .height(height)
+ .width(width)
+ .minDepth(0.0f)
+ .maxDepth(1.0f);
+ vkCmdSetViewport(renderCommandBuffers[i], 0, viewport);
+ viewport.free();
+
+ // Update dynamic scissor state
+ VkRect2D.Buffer scissor = VkRect2D.calloc(1);
+ scissor.extent().set(width, height);
+ scissor.offset().set(0, 0);
+ vkCmdSetScissor(renderCommandBuffers[i], 0, scissor);
+ scissor.free();
+
+ // Bind descriptor sets describing shader binding points
+ LongBuffer descriptorSets = memAllocLong(1).put(0, descriptorSet);
+ vkCmdBindDescriptorSets(renderCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, descriptorSets, null);
+ memFree(descriptorSets);
+
+ // Bind the rendering pipeline (including the shaders)
+ vkCmdBindPipeline(renderCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline);
+
+ // Bind triangle vertices
+ LongBuffer offsets = memAllocLong(1);
+ offsets.put(0, 0L);
+ LongBuffer pBuffers = memAllocLong(1);
+ pBuffers.put(0, verticesBuf);
+ vkCmdBindVertexBuffers(renderCommandBuffers[i], 0, pBuffers, offsets);
+ memFree(pBuffers);
+ memFree(offsets);
+
+ // Draw triangle
+ vkCmdDraw(renderCommandBuffers[i], 6, 1, 0, 0);
+
+ vkCmdEndRenderPass(renderCommandBuffers[i]);
+
+ err = vkEndCommandBuffer(renderCommandBuffers[i]);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to begin render command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ }
+ renderPassBeginInfo.free();
+ clearValues.free();
+ cmdBufInfo.free();
+ return renderCommandBuffers;
+ }
+
+ private static void updateUbo(VkDevice device, UboDescriptor ubo, float angle) {
+ Matrix4f m = new Matrix4f()
+ .scale(1, -1, 1) // <- correcting viewport transformation (what Direct3D does, too)
+ .perspective((float) Math.toRadians(45.0f), (float) width / height, 0.1f, 10.0f, true)
+ .lookAt(0, 1, 3,
+ 0, 0, 0,
+ 0, 1, 0)
+ .rotateY(angle);
+ PointerBuffer pData = memAllocPointer(1);
+ int err = vkMapMemory(device, ubo.memory, 0, 16 * 4, 0, pData);
+ long data = pData.get(0);
+ memFree(pData);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to map UBO memory: " + VKUtils.translateVulkanResult(err));
+ }
+ ByteBuffer matrixBuffer = memByteBuffer(data, 16 * 4);
+ m.get(matrixBuffer);
+ vkUnmapMemory(device, ubo.memory);
+ }
+
+ /*
+ * All resources that must be reallocated on window resize.
+ */
+ private static Swapchain swapchain;
+ private static long[] framebuffers;
+ private static VkCommandBuffer[] renderCommandBuffers;
+ private static int width, height;
+ private static DepthStencil depthStencil;
+
+ public static void main(String[] args) throws IOException {
+ if (!glfwInit()) {
+ throw new RuntimeException("Failed to initialize GLFW");
+ }
+ if (!glfwVulkanSupported()) {
+ throw new AssertionError("GLFW failed to find the Vulkan loader");
+ }
+
+ /* Look for instance extensions */
+ PointerBuffer requiredExtensions = glfwGetRequiredInstanceExtensions();
+ if (requiredExtensions == null) {
+ throw new AssertionError("Failed to find list of required Vulkan extensions");
+ }
+
+ // Create the Vulkan instance
+ final VkInstance instance = createInstance(requiredExtensions);
+ final VkDebugReportCallbackEXT debugCallback = new VkDebugReportCallbackEXT() {
+ public int invoke(int flags, int objectType, long object, long location, int messageCode, long pLayerPrefix, long pMessage, long pUserData) {
+ System.err.println("ERROR OCCURED: " + VkDebugReportCallbackEXT.getString(pMessage));
+ return 0;
+ }
+ };
+ final long debugCallbackHandle = setupDebugging(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, debugCallback);
+ final VkPhysicalDevice physicalDevice = getFirstPhysicalDevice(instance);
+ final DeviceAndGraphicsQueueFamily deviceAndGraphicsQueueFamily = createDeviceAndGetGraphicsQueueFamily(physicalDevice);
+ final VkDevice device = deviceAndGraphicsQueueFamily.device;
+ int queueFamilyIndex = deviceAndGraphicsQueueFamily.queueFamilyIndex;
+ final VkPhysicalDeviceMemoryProperties memoryProperties = deviceAndGraphicsQueueFamily.memoryProperties;
+
+ // Create GLFW window
+ glfwDefaultWindowHints();
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
+ long window = glfwCreateWindow(800, 600, "GLFW Vulkan Demo", NULL, NULL);
+ GLFWKeyCallback keyCallback;
+ glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
+ public void invoke(long window, int key, int scancode, int action, int mods) {
+ if (action != GLFW_RELEASE)
+ return;
+ if (key == GLFW_KEY_ESCAPE)
+ glfwSetWindowShouldClose(window, true);
+ }
+ });
+ LongBuffer pSurface = memAllocLong(1);
+ int err = glfwCreateWindowSurface(instance, window, null, pSurface);
+ final long surface = pSurface.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create surface: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Create static Vulkan resources
+ final ColorAndDepthFormatAndSpace colorAndDepthFormatAndSpace = getColorFormatAndSpace(physicalDevice, surface);
+ final long commandPool = createCommandPool(device, queueFamilyIndex);
+ final VkCommandBuffer setupCommandBuffer = createCommandBuffer(device, commandPool);
+ final VkQueue queue = createDeviceQueue(device, queueFamilyIndex);
+ final long renderPass = createRenderPass(device, colorAndDepthFormatAndSpace.colorFormat, colorAndDepthFormatAndSpace.depthFormat);
+ final long renderCommandPool = createCommandPool(device, queueFamilyIndex);
+ final Vertices vertices = createVertices(memoryProperties, device);
+ UboDescriptor uboDescriptor = createUniformBuffer(memoryProperties, device);
+ final long descriptorPool = createDescriptorPool(device);
+ final long descriptorSetLayout = createDescriptorSetLayout(device);
+ final long descriptorSet = createDescriptorSet(device, descriptorPool, descriptorSetLayout, uboDescriptor);
+ final Pipeline pipeline = createPipeline(device, renderPass, vertices.createInfo, descriptorSetLayout);
+
+ final class SwapchainRecreator {
+ boolean mustRecreate = true;
+ void recreate() {
+ // Begin the setup command buffer (the one we will use for swapchain/framebuffer creation)
+ VkCommandBufferBeginInfo cmdBufInfo = VkCommandBufferBeginInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
+ int err = vkBeginCommandBuffer(setupCommandBuffer, cmdBufInfo);
+ cmdBufInfo.free();
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to begin setup command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ long oldChain = swapchain != null ? swapchain.swapchainHandle : VK_NULL_HANDLE;
+ // Create the swapchain (this will also add a memory barrier to initialize the framebuffer images)
+ swapchain = createSwapChain(device, physicalDevice, surface, oldChain, setupCommandBuffer,
+ width, height, colorAndDepthFormatAndSpace.colorFormat, colorAndDepthFormatAndSpace.colorSpace);
+ // Create depth-stencil image
+ depthStencil = createDepthStencil(device, memoryProperties, colorAndDepthFormatAndSpace.depthFormat, setupCommandBuffer);
+ err = vkEndCommandBuffer(setupCommandBuffer);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to end setup command buffer: " + VKUtils.translateVulkanResult(err));
+ }
+ submitCommandBuffer(queue, setupCommandBuffer);
+ vkQueueWaitIdle(queue);
+
+ if (framebuffers != null) {
+ for (int i = 0; i < framebuffers.length; i++)
+ vkDestroyFramebuffer(device, framebuffers[i], null);
+ }
+ framebuffers = createFramebuffers(device, swapchain, renderPass, width, height, depthStencil);
+ // Create render command buffers
+ if (renderCommandBuffers != null) {
+ vkResetCommandPool(device, renderCommandPool, VKUtils.VK_FLAGS_NONE);
+ }
+ renderCommandBuffers = createRenderCommandBuffers(device, renderCommandPool, framebuffers, renderPass, width, height, pipeline, descriptorSet,
+ vertices.verticesBuf);
+
+ mustRecreate = false;
+ }
+ }
+ final SwapchainRecreator swapchainRecreator = new SwapchainRecreator();
+
+ // Handle canvas resize
+ GLFWFramebufferSizeCallback framebufferSizeCallback = new GLFWFramebufferSizeCallback() {
+ public void invoke(long window, int width, int height) {
+ if (width <= 0 || height <= 0)
+ return;
+ swapchainRecreator.mustRecreate = true;
+ VulkanStarter.width = width;
+ VulkanStarter.height = height;
+ }
+ };
+ glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
+ glfwShowWindow(window);
+
+ // Pre-allocate everything needed in the render loop
+
+ IntBuffer pImageIndex = memAllocInt(1);
+ int currentBuffer = 0;
+ PointerBuffer pCommandBuffers = memAllocPointer(1);
+ LongBuffer pSwapchains = memAllocLong(1);
+ LongBuffer pImageAcquiredSemaphore = memAllocLong(1);
+ LongBuffer pRenderCompleteSemaphore = memAllocLong(1);
+
+ // Info struct to create a semaphore
+ VkSemaphoreCreateInfo semaphoreCreateInfo = VkSemaphoreCreateInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO);
+
+ // Info struct to submit a command buffer which will wait on the semaphore
+ IntBuffer pWaitDstStageMask = memAllocInt(1);
+ pWaitDstStageMask.put(0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+ VkSubmitInfo submitInfo = VkSubmitInfo.calloc()
+ .sType(VK_STRUCTURE_TYPE_SUBMIT_INFO)
+ .waitSemaphoreCount(pImageAcquiredSemaphore.remaining())
+ .pWaitSemaphores(pImageAcquiredSemaphore)
+ .pWaitDstStageMask(pWaitDstStageMask)
+ .pCommandBuffers(pCommandBuffers)
+ .pSignalSemaphores(pRenderCompleteSemaphore);
+
+ // Info struct to present the current swapchain image to the display
+ VkPresentInfoKHR presentInfo = VkPresentInfoKHR.calloc()
+ .sType(VK_STRUCTURE_TYPE_PRESENT_INFO_KHR)
+ .pWaitSemaphores(pRenderCompleteSemaphore)
+ .swapchainCount(pSwapchains.remaining())
+ .pSwapchains(pSwapchains)
+ .pImageIndices(pImageIndex)
+ .pResults(null);
+
+ // The render loop
+ long lastTime = System.nanoTime();
+ float time = 0.0f;
+ while (!glfwWindowShouldClose(window)) {
+ // Handle window messages. Resize events happen exactly here.
+ // So it is safe to use the new swapchain images and framebuffers afterwards.
+ glfwPollEvents();
+ if (swapchainRecreator.mustRecreate)
+ swapchainRecreator.recreate();
+
+ // Create a semaphore to wait for the swapchain to acquire the next image
+ err = vkCreateSemaphore(device, semaphoreCreateInfo, null, pImageAcquiredSemaphore);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create image acquired semaphore: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Create a semaphore to wait for the render to complete, before presenting
+ err = vkCreateSemaphore(device, semaphoreCreateInfo, null, pRenderCompleteSemaphore);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to create render complete semaphore: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Get next image from the swap chain (back/front buffer).
+ // This will setup the imageAquiredSemaphore to be signalled when the operation is complete
+ err = vkAcquireNextImageKHR(device, swapchain.swapchainHandle, UINT64_MAX, pImageAcquiredSemaphore.get(0), VK_NULL_HANDLE, pImageIndex);
+ currentBuffer = pImageIndex.get(0);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to acquire next swapchain image: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Select the command buffer for the current framebuffer image/attachment
+ pCommandBuffers.put(0, renderCommandBuffers[currentBuffer]);
+
+ // Update UBO
+ long thisTime = System.nanoTime();
+ time += (thisTime - lastTime) / 1E9f;
+ lastTime = thisTime;
+ updateUbo(device, uboDescriptor, time);
+
+ // Submit to the graphics queue
+ err = vkQueueSubmit(queue, submitInfo, VK_NULL_HANDLE);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to submit render queue: " + VKUtils.translateVulkanResult(err));
+ }
+
+ // Present the current buffer to the swap chain
+ // This will display the image
+ pSwapchains.put(0, swapchain.swapchainHandle);
+ err = vkQueuePresentKHR(queue, presentInfo);
+ if (err != VK_SUCCESS) {
+ throw new AssertionError("Failed to present the swapchain image: " + VKUtils.translateVulkanResult(err));
+ }
+ // Create and submit post present barrier
+ vkQueueWaitIdle(queue);
+
+ // Destroy this semaphore (we will create a new one in the next frame)
+ vkDestroySemaphore(device, pImageAcquiredSemaphore.get(0), null);
+ vkDestroySemaphore(device, pRenderCompleteSemaphore.get(0), null);
+ }
+ presentInfo.free();
+ memFree(pWaitDstStageMask);
+ submitInfo.free();
+ memFree(pImageAcquiredSemaphore);
+ memFree(pRenderCompleteSemaphore);
+ semaphoreCreateInfo.free();
+ memFree(pSwapchains);
+ memFree(pCommandBuffers);
+
+ vkDestroyDebugReportCallbackEXT(instance, debugCallbackHandle, null);
+
+ framebufferSizeCallback.free();
+ keyCallback.free();
+ glfwDestroyWindow(window);
+ glfwTerminate();
+
+ // We don't bother disposing of all Vulkan resources.
+ // Let the OS process manager take care of it.
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKFactory.java b/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKFactory.java
new file mode 100644
index 0000000..cb38512
--- /dev/null
+++ b/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKFactory.java
@@ -0,0 +1,432 @@
+package com.github.hydos.ginger.engine.vulkan.utils;
+
+import java.nio.Buffer;
+
+import org.lwjgl.system.MemoryStack;
+import org.lwjgl.util.vma.VmaAllocationCreateInfo;
+import org.lwjgl.util.vma.VmaAllocationInfo;
+import org.lwjgl.util.vma.VmaAllocatorCreateInfo;
+import org.lwjgl.util.vma.VmaVulkanFunctions;
+import org.lwjgl.vulkan.*;
+import static org.lwjgl.vulkan.EXTDebugReport.*;
+import static org.lwjgl.vulkan.KHR8bitStorage.*;
+import static org.lwjgl.vulkan.KHRGetMemoryRequirements2.*;
+import static org.lwjgl.vulkan.KHRGetPhysicalDeviceProperties2.*;
+import static org.lwjgl.vulkan.KHRShaderFloat16Int8.*;
+import static org.lwjgl.vulkan.KHRSwapchain.*;
+import static org.lwjgl.vulkan.NVRayTracing.*;
+import static org.lwjgl.vulkan.VK10.*;
+
+public class VKFactory {
+ static VmaVulkanFunctions VmaVulkanFunctions(MemoryStack stack) {
+ return VmaVulkanFunctions.callocStack(stack);
+ }
+
+ static VmaAllocatorCreateInfo VmaAllocatorCreateInfo(MemoryStack stack) {
+ return VmaAllocatorCreateInfo.callocStack(stack);
+ }
+
+ static VkInstanceCreateInfo VkInstanceCreateInfo(MemoryStack stack) {
+ return VkInstanceCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
+ }
+
+ static VkApplicationInfo VkApplicationInfo(MemoryStack stack) {
+ return VkApplicationInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_APPLICATION_INFO);
+ }
+
+ static VkDebugReportCallbackCreateInfoEXT VkDebugReportCallbackCreateInfoEXT(MemoryStack stack) {
+ return VkDebugReportCallbackCreateInfoEXT.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT);
+ }
+
+ static VkDeviceCreateInfo VkDeviceCreateInfo(MemoryStack stack) {
+ return VkDeviceCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
+ }
+
+ static VkDeviceQueueCreateInfo.Buffer VkDeviceQueueCreateInfo(MemoryStack stack) {
+ return VkDeviceQueueCreateInfo.callocStack(1, stack).sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO);
+ }
+
+ static VkPhysicalDevice8BitStorageFeaturesKHR VkPhysicalDevice8BitStorageFeaturesKHR(MemoryStack stack) {
+ return VkPhysicalDevice8BitStorageFeaturesKHR.callocStack(stack).sType(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR);
+ }
+
+ static VkPhysicalDeviceFloat16Int8FeaturesKHR VkPhysicalDeviceFloat16Int8FeaturesKHR(MemoryStack stack) {
+ return VkPhysicalDeviceFloat16Int8FeaturesKHR.callocStack(stack).sType(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR);
+ }
+
+ static VkPhysicalDeviceProperties2 VkPhysicalDeviceProperties2(MemoryStack stack) {
+ return VkPhysicalDeviceProperties2.callocStack(stack).sType(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR);
+ }
+
+ static VkPhysicalDeviceRayTracingPropertiesNV VkPhysicalDeviceRayTracingPropertiesNV(MemoryStack stack) {
+ return VkPhysicalDeviceRayTracingPropertiesNV.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV);
+ }
+
+ static VkSwapchainCreateInfoKHR VkSwapchainCreateInfoKHR(MemoryStack stack) {
+ return VkSwapchainCreateInfoKHR.callocStack(stack).sType(VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
+ }
+
+ static VkImageViewCreateInfo VkImageViewCreateInfo(MemoryStack stack) {
+ return VkImageViewCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO);
+ }
+
+ static VkCommandPoolCreateInfo VkCommandPoolCreateInfo(MemoryStack stack) {
+ return VkCommandPoolCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO);
+ }
+
+ static VkMemoryRequirements VkMemoryRequirements(MemoryStack stack) {
+ return VkMemoryRequirements.callocStack(stack);
+ }
+
+ static VkImageCreateInfo VkImageCreateInfo(MemoryStack stack) {
+ return VkImageCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO);
+ }
+
+ static VkImageMemoryBarrier.Buffer VkImageMemoryBarrier(MemoryStack stack) {
+ return VkImageMemoryBarrier.callocStack(1, stack).sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER);
+ }
+
+ static VkFenceCreateInfo VkFenceCreateInfo(MemoryStack stack) {
+ return VkFenceCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO);
+ }
+
+ static VkSubmitInfo VkSubmitInfo(MemoryStack stack) {
+ return VkSubmitInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_SUBMIT_INFO);
+ }
+
+ static VkCommandBufferBeginInfo VkCommandBufferBeginInfo(MemoryStack stack) {
+ return VkCommandBufferBeginInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
+ }
+
+ static VkCommandBufferAllocateInfo VkCommandBufferAllocateInfo(MemoryStack stack) {
+ return VkCommandBufferAllocateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO);
+ }
+
+ static VkMemoryAllocateInfo VkMemoryAllocateInfo(MemoryStack stack) {
+ return VkMemoryAllocateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
+ }
+
+ static VkBufferCreateInfo VkBufferCreateInfo(MemoryStack stack) {
+ return VkBufferCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO);
+ }
+
+ static VkGeometryAABBNV VkGeometryAABBNV(VkGeometryAABBNV geometry) {
+ return geometry.sType(VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV);
+ }
+
+ static VkGeometryTrianglesNV VkGeometryTrianglesNV(VkGeometryTrianglesNV geometry) {
+ return geometry.sType(VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV);
+ }
+
+ static VkGeometryNV VkGeometryNV(MemoryStack stack) {
+ return VkGeometryNV.callocStack(stack).sType(VK_STRUCTURE_TYPE_GEOMETRY_NV);
+ }
+
+ static VkMemoryBarrier.Buffer VkMemoryBarrier(MemoryStack stack) {
+ return VkMemoryBarrier.callocStack(1, stack).sType(VK_STRUCTURE_TYPE_MEMORY_BARRIER);
+ }
+
+ static VkBindAccelerationStructureMemoryInfoNV.Buffer VkBindAccelerationStructureMemoryInfoNV(MemoryStack stack) {
+ return VkBindAccelerationStructureMemoryInfoNV.callocStack(1, stack)
+ .sType(VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV);
+ }
+
+ static VkAccelerationStructureInfoNV VkAccelerationStructureInfoNV(MemoryStack stack) {
+ return VkAccelerationStructureInfoNV.callocStack(stack).sType(VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV);
+ }
+
+ static VkMemoryRequirements2KHR VkMemoryRequirements2KHR(MemoryStack stack) {
+ return VkMemoryRequirements2KHR.callocStack(stack).sType(VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR);
+ }
+
+ static VkAccelerationStructureMemoryRequirementsInfoNV VkAccelerationStructureMemoryRequirementsInfoNV(
+ MemoryStack stack) {
+ return VkAccelerationStructureMemoryRequirementsInfoNV.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV);
+ }
+
+ static VkAccelerationStructureCreateInfoNV VkAccelerationStructureCreateInfoNV(MemoryStack stack) {
+ return VkAccelerationStructureCreateInfoNV.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV);
+ }
+
+ static VkPipelineShaderStageCreateInfo.Buffer VkPipelineShaderStageCreateInfo(MemoryStack stack, int count) {
+ VkPipelineShaderStageCreateInfo.Buffer ret = VkPipelineShaderStageCreateInfo.callocStack(count, stack);
+ ret.forEach(sci -> sci.sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO));
+ return ret;
+ }
+
+ static VkDescriptorSetLayoutBinding.Buffer VkDescriptorSetLayoutBinding(MemoryStack stack, int count) {
+ return VkDescriptorSetLayoutBinding.callocStack(count, stack);
+ }
+
+ static VkDescriptorSetLayoutBinding VkDescriptorSetLayoutBinding(MemoryStack stack) {
+ return VkDescriptorSetLayoutBinding.callocStack(stack);
+ }
+
+ static VkRayTracingPipelineCreateInfoNV.Buffer VkRayTracingPipelineCreateInfoNV(MemoryStack stack) {
+ return VkRayTracingPipelineCreateInfoNV.callocStack(1, stack)
+ .sType(VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV);
+ }
+
+ static VkRayTracingShaderGroupCreateInfoNV.Buffer VkRayTracingShaderGroupCreateInfoNV(int size, MemoryStack stack) {
+ VkRayTracingShaderGroupCreateInfoNV.Buffer buf = VkRayTracingShaderGroupCreateInfoNV.callocStack(size, stack);
+ buf.forEach(info -> info.sType(VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV)
+ .anyHitShader(VK_SHADER_UNUSED_NV)
+ .closestHitShader(VK_SHADER_UNUSED_NV)
+ .generalShader(VK_SHADER_UNUSED_NV)
+ .intersectionShader(VK_SHADER_UNUSED_NV));
+ return buf;
+ }
+
+ static VkPipelineLayoutCreateInfo VkPipelineLayoutCreateInfo(MemoryStack stack) {
+ return VkPipelineLayoutCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO);
+ }
+
+ static VkDescriptorSetLayoutCreateInfo VkDescriptorSetLayoutCreateInfo(MemoryStack stack) {
+ return VkDescriptorSetLayoutCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
+ }
+
+ static VkDescriptorBufferInfo.Buffer VkDescriptorBufferInfo(MemoryStack stack, int count) {
+ return VkDescriptorBufferInfo.callocStack(count, stack);
+ }
+
+ static VkDescriptorImageInfo.Buffer VkDescriptorImageInfo(MemoryStack stack, int count) {
+ return VkDescriptorImageInfo.callocStack(count, stack);
+ }
+
+ static VkDescriptorPoolSize.Buffer VkDescriptorPoolSize(MemoryStack stack, int count) {
+ return VkDescriptorPoolSize.callocStack(count, stack);
+ }
+
+ static VkWriteDescriptorSetAccelerationStructureNV VkWriteDescriptorSetAccelerationStructureNV(MemoryStack stack) {
+ return VkWriteDescriptorSetAccelerationStructureNV.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV);
+ }
+
+ static VkWriteDescriptorSet VkWriteDescriptorSet(MemoryStack stack) {
+ return VkWriteDescriptorSet.callocStack(stack).sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET);
+ }
+
+ static VkDescriptorSetAllocateInfo VkDescriptorSetAllocateInfo(MemoryStack stack) {
+ return VkDescriptorSetAllocateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
+ }
+
+ static VkDescriptorPoolCreateInfo VkDescriptorPoolCreateInfo(MemoryStack stack) {
+ return VkDescriptorPoolCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);
+ }
+
+ static VkPresentInfoKHR VkPresentInfoKHR(MemoryStack stack) {
+ return VkPresentInfoKHR.callocStack(stack).sType(VK_STRUCTURE_TYPE_PRESENT_INFO_KHR);
+ }
+
+ static VkSemaphoreCreateInfo VkSemaphoreCreateInfo(MemoryStack stack) {
+ return VkSemaphoreCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO);
+ }
+
+ static VkQueueFamilyProperties.Buffer VkQueueFamilyProperties(int count) {
+ return VkQueueFamilyProperties.callocStack(count);
+ }
+
+ static VkPhysicalDeviceFeatures VkPhysicalDeviceFeatures(MemoryStack stack) {
+ return VkPhysicalDeviceFeatures.callocStack(stack);
+ }
+
+ static VkPhysicalDeviceFeatures2 VkPhysicalDeviceFeatures2(MemoryStack stack) {
+ return VkPhysicalDeviceFeatures2.callocStack(stack).sType(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR);
+ }
+
+ static VkPhysicalDeviceProperties VkPhysicalDeviceProperties(MemoryStack stack) {
+ return VkPhysicalDeviceProperties.callocStack(stack);
+ }
+
+ static VkGeometryNV.Buffer VkGeometryNV(MemoryStack stack, int count) {
+ VkGeometryNV.Buffer buf = VkGeometryNV.callocStack(count, stack);
+ buf.forEach(info -> info.sType(VK_STRUCTURE_TYPE_GEOMETRY_NV));
+ return buf;
+ }
+
+ static VkPipelineShaderStageCreateInfo VkPipelineShaderStageCreateInfo(MemoryStack stack) {
+ return VkPipelineShaderStageCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO);
+ }
+
+ static VkShaderModuleCreateInfo VkShaderModuleCreateInfo(MemoryStack stack) {
+ return VkShaderModuleCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO);
+ }
+
+ static VkSurfaceCapabilitiesKHR VkSurfaceCapabilitiesKHR(MemoryStack stack) {
+ return VkSurfaceCapabilitiesKHR.callocStack(stack);
+ }
+
+ static VkSurfaceFormatKHR.Buffer VkSurfaceFormatKHR(MemoryStack stack, int count) {
+ return VkSurfaceFormatKHR.callocStack(count, stack);
+ }
+
+ static VmaAllocationCreateInfo VmaAllocationCreateInfo(MemoryStack stack) {
+ return VmaAllocationCreateInfo.callocStack(stack);
+ }
+
+ static VmaAllocationInfo VmaAllocationInfo(MemoryStack stack) {
+ return VmaAllocationInfo.callocStack(stack);
+ }
+
+ static VkBufferCopy.Buffer VkBufferCopy(MemoryStack stack, int count) {
+ return VkBufferCopy.callocStack(count, stack);
+ }
+
+ static VkSamplerCreateInfo VkSamplerCreateInfo(MemoryStack stack) {
+ return VkSamplerCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
+ }
+
+ static VkBufferImageCopy.Buffer VkBufferImageCopy(MemoryStack stack) {
+ return VkBufferImageCopy.callocStack(1, stack);
+ }
+
+ static VkImageSubresourceRange VkImageSubresourceRange(MemoryStack stack) {
+ return VkImageSubresourceRange.callocStack(stack);
+ }
+
+ static VkComponentMapping VkComponentMapping(MemoryStack stack) {
+ return VkComponentMapping.callocStack(stack);
+ }
+
+ static VkAttachmentReference VkAttachmentReference(MemoryStack stack) {
+ return VkAttachmentReference.callocStack(stack);
+ }
+
+ static VkAttachmentReference.Buffer VkAttachmentReference(MemoryStack stack, int count) {
+ return VkAttachmentReference.callocStack(count, stack);
+ }
+
+ static VkSubpassDescription.Buffer VkSubpassDescription(MemoryStack stack, int count) {
+ return VkSubpassDescription.callocStack(count, stack);
+ }
+
+ static VkAttachmentDescription.Buffer VkAttachmentDescription(MemoryStack stack, int count) {
+ return VkAttachmentDescription.callocStack(count, stack);
+ }
+
+ static VkRenderPassCreateInfo VkRenderPassCreateInfo(MemoryStack stack) {
+ return VkRenderPassCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO);
+ }
+
+ static VkOffset3D VkOffset3D(MemoryStack stack) {
+ return VkOffset3D.callocStack(stack);
+ }
+
+ static VkImageBlit.Buffer VkImageBlit(MemoryStack stack, int count) {
+ return VkImageBlit.callocStack(count, stack);
+ }
+
+ static VkSpecializationMapEntry.Buffer VkSpecializationMapEntry(MemoryStack stack, int count) {
+ return VkSpecializationMapEntry.callocStack(count, stack);
+ }
+
+ static VkSpecializationInfo VkSpecializationInfo(MemoryStack stack) {
+ return VkSpecializationInfo.callocStack(stack);
+ }
+
+ static VkQueryPoolCreateInfo VkQueryPoolCreateInfo(MemoryStack stack) {
+ return VkQueryPoolCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO);
+ }
+
+ static VkGeometryNV.Buffer VkGeometryNV(int count) {
+ return VkGeometryNV.calloc(count).sType(VK_STRUCTURE_TYPE_GEOMETRY_NV);
+ }
+
+ static VkFramebufferCreateInfo VkFramebufferCreateInfo(MemoryStack stack) {
+ return VkFramebufferCreateInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO);
+ }
+
+ static VkVertexInputBindingDescription.Buffer VkVertexInputBindingDescription(MemoryStack stack, int count) {
+ return VkVertexInputBindingDescription.callocStack(count, stack);
+ }
+
+ static VkVertexInputAttributeDescription.Buffer VkVertexInputAttributeDescription(MemoryStack stack, int count) {
+ return VkVertexInputAttributeDescription.callocStack(count, stack);
+ }
+
+ static VkPipelineVertexInputStateCreateInfo VkPipelineVertexInputStateCreateInfo(MemoryStack stack) {
+ return VkPipelineVertexInputStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineInputAssemblyStateCreateInfo VkPipelineInputAssemblyStateCreateInfo(MemoryStack stack) {
+ return VkPipelineInputAssemblyStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineRasterizationStateCreateInfo VkPipelineRasterizationStateCreateInfo(MemoryStack stack) {
+ return VkPipelineRasterizationStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineColorBlendAttachmentState.Buffer VkPipelineColorBlendAttachmentState(MemoryStack stack,
+ int count) {
+ return VkPipelineColorBlendAttachmentState.callocStack(count, stack);
+ }
+
+ static VkPipelineColorBlendStateCreateInfo VkPipelineColorBlendStateCreateInfo(MemoryStack stack) {
+ return VkPipelineColorBlendStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineViewportStateCreateInfo VkPipelineViewportStateCreateInfo(MemoryStack stack) {
+ return VkPipelineViewportStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineDynamicStateCreateInfo VkPipelineDynamicStateCreateInfo(MemoryStack stack) {
+ return VkPipelineDynamicStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineDepthStencilStateCreateInfo VkPipelineDepthStencilStateCreateInfo(MemoryStack stack) {
+ return VkPipelineDepthStencilStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO);
+ }
+
+ static VkPipelineMultisampleStateCreateInfo VkPipelineMultisampleStateCreateInfo(MemoryStack stack) {
+ return VkPipelineMultisampleStateCreateInfo.callocStack(stack)
+ .sType(VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO);
+ }
+
+ static VkGraphicsPipelineCreateInfo.Buffer VkGraphicsPipelineCreateInfo(MemoryStack stack, int count) {
+ VkGraphicsPipelineCreateInfo.Buffer ret = VkGraphicsPipelineCreateInfo.callocStack(count, stack);
+ ret.forEach(pci -> pci.sType(VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO));
+ return ret;
+ }
+
+ static VkClearValue.Buffer VkClearValue(MemoryStack stack, int count) {
+ return VkClearValue.callocStack(count, stack);
+ }
+
+ static VkRenderPassBeginInfo VkRenderPassBeginInfo(MemoryStack stack) {
+ return VkRenderPassBeginInfo.callocStack(stack).sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO);
+ }
+
+ static VkViewport.Buffer VkViewport(MemoryStack stack, int count) {
+ return VkViewport.callocStack(count, stack);
+ }
+
+ static VkRect2D.Buffer VkRect2D(MemoryStack stack, int count) {
+ return VkRect2D.callocStack(count, stack);
+ }
+
+ static VkFormatProperties VkFormatProperties(MemoryStack stack) {
+ return VkFormatProperties.callocStack(stack);
+ }
+
+ static VkSubpassDependency.Buffer VkSubpassDependency(MemoryStack stack, int count) {
+ return VkSubpassDependency.callocStack(count, stack);
+ }
+
+ static VkImageCopy.Buffer VkImageCopy(MemoryStack stack, int count) {
+ return VkImageCopy.callocStack(count, stack);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKUtils.java b/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKUtils.java
new file mode 100644
index 0000000..01e0a3d
--- /dev/null
+++ b/src/main/java/com/github/hydos/ginger/engine/vulkan/utils/VKUtils.java
@@ -0,0 +1,467 @@
+package com.github.hydos.ginger.engine.vulkan.utils;
+
+import org.lwjgl.assimp.*;
+import org.lwjgl.system.Callback;
+import org.lwjgl.system.MemoryStack;
+import org.lwjgl.util.shaderc.ShadercIncludeResolve;
+import org.lwjgl.util.shaderc.ShadercIncludeResult;
+import org.lwjgl.util.shaderc.ShadercIncludeResultRelease;
+import org.lwjgl.vulkan.*;
+
+import com.github.hydos.ginger.engine.opengl.render.tools.IOUtil;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.lwjgl.BufferUtils.*;
+import static org.lwjgl.assimp.Assimp.*;
+import static org.lwjgl.system.MemoryUtil.*;
+import static org.lwjgl.util.shaderc.Shaderc.*;
+import static org.lwjgl.vulkan.EXTDebugReport.*;
+import static org.lwjgl.vulkan.KHRDisplaySwapchain.*;
+import static org.lwjgl.vulkan.KHRSurface.*;
+import static org.lwjgl.vulkan.KHRSwapchain.*;
+import static org.lwjgl.vulkan.NVRayTracing.*;
+import static org.lwjgl.vulkan.VK12.*;
+
+/**
+ * Utility functions for Vulkan.
+ *
+ * @author Kai Burjack
+ */
+public class VKUtils {
+
+ public static final int VK_FLAGS_NONE = 0;
+
+ private static int vulkanStageToShadercKind(int stage) {
+ switch (stage) {
+ case VK_SHADER_STAGE_VERTEX_BIT:
+ return shaderc_vertex_shader;
+ case VK_SHADER_STAGE_FRAGMENT_BIT:
+ return shaderc_fragment_shader;
+ case VK_SHADER_STAGE_RAYGEN_BIT_NV:
+ return shaderc_raygen_shader;
+ case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
+ return shaderc_closesthit_shader;
+ case VK_SHADER_STAGE_MISS_BIT_NV:
+ return shaderc_miss_shader;
+ case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
+ return shaderc_anyhit_shader;
+ default:
+ throw new IllegalArgumentException("Stage: " + stage);
+ }
+ }
+
+ public static ByteBuffer glslToSpirv(String classPath, int vulkanStage) throws IOException {
+ ByteBuffer src = IOUtil.ioResourceToByteBuffer(classPath, 1024);
+ long compiler = shaderc_compiler_initialize();
+ long options = shaderc_compile_options_initialize();
+ ShadercIncludeResolve resolver;
+ ShadercIncludeResultRelease releaser;
+ shaderc_compile_options_set_optimization_level(options, shaderc_optimization_level_performance);
+ shaderc_compile_options_set_include_callbacks(options, resolver = new ShadercIncludeResolve() {
+ public long invoke(long user_data, long requested_source, int type, long requesting_source, long include_depth) {
+ ShadercIncludeResult res = ShadercIncludeResult.calloc();
+ try {
+ String src = classPath.substring(0, classPath.lastIndexOf('/')) + "/" + memUTF8(requested_source);
+ res.content(IOUtil.ioResourceToByteBuffer(src, 1024));
+ res.source_name(memUTF8(src));
+ return res.address();
+ } catch (IOException e) {
+ throw new AssertionError("Failed to resolve include: " + src);
+ }
+ }
+ }, releaser = new ShadercIncludeResultRelease() {
+ public void invoke(long user_data, long include_result) {
+ ShadercIncludeResult result = ShadercIncludeResult.create(include_result);
+ memFree(result.source_name());
+ result.free();
+ }
+ }, 0L);
+ long res;
+ try (MemoryStack stack = MemoryStack.stackPush()) {
+ res = shaderc_compile_into_spv(compiler, src, vulkanStageToShadercKind(vulkanStage),
+ stack.UTF8(classPath), stack.UTF8("main"), options);
+ if (res == 0L)
+ throw new AssertionError("Internal error during compilation!");
+ }
+ if (shaderc_result_get_compilation_status(res) != shaderc_compilation_status_success) {
+ throw new AssertionError("Shader compilation failed: " + shaderc_result_get_error_message(res));
+ }
+ int size = (int) shaderc_result_get_length(res);
+ ByteBuffer resultBytes = createByteBuffer(size);
+ resultBytes.put(shaderc_result_get_bytes(res));
+ resultBytes.flip();
+ shaderc_compiler_release(res);
+ shaderc_compiler_release(compiler);
+ releaser.free();
+ resolver.free();
+ return resultBytes;
+ }
+
+ public static void _CHECK_(int ret, String msg) {
+ if (ret != VK_SUCCESS)
+ throw new AssertionError(msg + ": " + translateVulkanResult(ret));
+ }
+
+ /**
+ * Translates a Vulkan {@code VkResult} value to a String describing the result.
+ *
+ * @param result
+ * the {@code VkResult} value
+ *
+ * @return the result description
+ */
+ public static String translateVulkanResult(int result) {
+ switch (result) {
+ // Success codes
+ case VK_SUCCESS:
+ return "Command successfully completed.";
+ case VK_NOT_READY:
+ return "A fence or query has not yet completed.";
+ case VK_TIMEOUT:
+ return "A wait operation has not completed in the specified time.";
+ case VK_EVENT_SET:
+ return "An event is signaled.";
+ case VK_EVENT_RESET:
+ return "An event is unsignaled.";
+ case VK_INCOMPLETE:
+ return "A return array was too small for the result.";
+ case VK_SUBOPTIMAL_KHR:
+ return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully.";
+
+ // Error codes
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ return "A host memory allocation has failed.";
+ case VK_ERROR_OUT_OF_DEVICE_MEMORY:
+ return "A device memory allocation has failed.";
+ case VK_ERROR_INITIALIZATION_FAILED:
+ return "Initialization of an object could not be completed for implementation-specific reasons.";
+ case VK_ERROR_DEVICE_LOST:
+ return "The logical or physical device has been lost.";
+ case VK_ERROR_MEMORY_MAP_FAILED:
+ return "Mapping of a memory object has failed.";
+ case VK_ERROR_LAYER_NOT_PRESENT:
+ return "A requested layer is not present or could not be loaded.";
+ case VK_ERROR_EXTENSION_NOT_PRESENT:
+ return "A requested extension is not supported.";
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ return "A requested feature is not supported.";
+ case VK_ERROR_INCOMPATIBLE_DRIVER:
+ return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons.";
+ case VK_ERROR_TOO_MANY_OBJECTS:
+ return "Too many objects of the type have already been created.";
+ case VK_ERROR_FORMAT_NOT_SUPPORTED:
+ return "A requested format is not supported on this device.";
+ case VK_ERROR_SURFACE_LOST_KHR:
+ return "A surface is no longer available.";
+ case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
+ return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API.";
+ case VK_ERROR_OUT_OF_DATE_KHR:
+ return "A surface has changed in such a way that it is no longer compatible with the swapchain, and further presentation requests using the "
+ + "swapchain will fail. Applications must query the new surface properties and recreate their swapchain if they wish to continue" + "presenting to the surface.";
+ case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
+ return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an" + " image.";
+ case VK_ERROR_VALIDATION_FAILED_EXT:
+ return "A validation layer found an error.";
+ default:
+ return String.format("%s [%d]", "Unknown", Integer.valueOf(result));
+ }
+ }
+
+ public static String format_to_string(int format) {
+ switch (format) {
+ case 0: return "UNDEFINED";
+ case 1: return "R4G4_UNORM_PACK8";
+ case 2: return "R4G4B4A4_UNORM_PACK16";
+ case 3: return "B4G4R4A4_UNORM_PACK16";
+ case 4: return "R5G6B5_UNORM_PACK16";
+ case 5: return "B5G6R5_UNORM_PACK16";
+ case 6: return "R5G5B5A1_UNORM_PACK16";
+ case 7: return "B5G5R5A1_UNORM_PACK16";
+ case 8: return "A1R5G5B5_UNORM_PACK16";
+ case 9: return "R8_UNORM";
+ case 10: return "R8_SNORM";
+ case 11: return "R8_USCALED";
+ case 12: return "R8_SSCALED";
+ case 13: return "R8_UINT";
+ case 14: return "R8_SINT";
+ case 15: return "R8_SRGB";
+ case 16: return "R8G8_UNORM";
+ case 17: return "R8G8_SNORM";
+ case 18: return "R8G8_USCALED";
+ case 19: return "R8G8_SSCALED";
+ case 20: return "R8G8_UINT";
+ case 21: return "R8G8_SINT";
+ case 22: return "R8G8_SRGB";
+ case 23: return "R8G8B8_UNORM";
+ case 24: return "R8G8B8_SNORM";
+ case 25: return "R8G8B8_USCALED";
+ case 26: return "R8G8B8_SSCALED";
+ case 27: return "R8G8B8_UINT";
+ case 28: return "R8G8B8_SINT";
+ case 29: return "R8G8B8_SRGB";
+ case 30: return "B8G8R8_UNORM";
+ case 31: return "B8G8R8_SNORM";
+ case 32: return "B8G8R8_USCALED";
+ case 33: return "B8G8R8_SSCALED";
+ case 34: return "B8G8R8_UINT";
+ case 35: return "B8G8R8_SINT";
+ case 36: return "B8G8R8_SRGB";
+ case 37: return "R8G8B8A8_UNORM";
+ case 38: return "R8G8B8A8_SNORM";
+ case 39: return "R8G8B8A8_USCALED";
+ case 40: return "R8G8B8A8_SSCALED";
+ case 41: return "R8G8B8A8_UINT";
+ case 42: return "R8G8B8A8_SINT";
+ case 43: return "R8G8B8A8_SRGB";
+ case 44: return "B8G8R8A8_UNORM";
+ case 45: return "B8G8R8A8_SNORM";
+ case 46: return "B8G8R8A8_USCALED";
+ case 47: return "B8G8R8A8_SSCALED";
+ case 48: return "B8G8R8A8_UINT";
+ case 49: return "B8G8R8A8_SINT";
+ case 50: return "B8G8R8A8_SRGB";
+ case 51: return "A8B8G8R8_UNORM_PACK32";
+ case 52: return "A8B8G8R8_SNORM_PACK32";
+ case 53: return "A8B8G8R8_USCALED_PACK32";
+ case 54: return "A8B8G8R8_SSCALED_PACK32";
+ case 55: return "A8B8G8R8_UINT_PACK32";
+ case 56: return "A8B8G8R8_SINT_PACK32";
+ case 57: return "A8B8G8R8_SRGB_PACK32";
+ case 58: return "A2R10G10B10_UNORM_PACK32";
+ case 59: return "A2R10G10B10_SNORM_PACK32";
+ case 60: return "A2R10G10B10_USCALED_PACK32";
+ case 61: return "A2R10G10B10_SSCALED_PACK32";
+ case 62: return "A2R10G10B10_UINT_PACK32";
+ case 63: return "A2R10G10B10_SINT_PACK32";
+ case 64: return "A2B10G10R10_UNORM_PACK32";
+ case 65: return "A2B10G10R10_SNORM_PACK32";
+ case 66: return "A2B10G10R10_USCALED_PACK32";
+ case 67: return "A2B10G10R10_SSCALED_PACK32";
+ case 68: return "A2B10G10R10_UINT_PACK32";
+ case 69: return "A2B10G10R10_SINT_PACK32";
+ case 70: return "R16_UNORM";
+ case 71: return "R16_SNORM";
+ case 72: return "R16_USCALED";
+ case 73: return "R16_SSCALED";
+ case 74: return "R16_UINT";
+ case 75: return "R16_SINT";
+ case 76: return "R16_SFLOAT";
+ case 77: return "R16G16_UNORM";
+ case 78: return "R16G16_SNORM";
+ case 79: return "R16G16_USCALED";
+ case 80: return "R16G16_SSCALED";
+ case 81: return "R16G16_UINT";
+ case 82: return "R16G16_SINT";
+ case 83: return "R16G16_SFLOAT";
+ case 84: return "R16G16B16_UNORM";
+ case 85: return "R16G16B16_SNORM";
+ case 86: return "R16G16B16_USCALED";
+ case 87: return "R16G16B16_SSCALED";
+ case 88: return "R16G16B16_UINT";
+ case 89: return "R16G16B16_SINT";
+ case 90: return "R16G16B16_SFLOAT";
+ case 91: return "R16G16B16A16_UNORM";
+ case 92: return "R16G16B16A16_SNORM";
+ case 93: return "R16G16B16A16_USCALED";
+ case 94: return "R16G16B16A16_SSCALED";
+ case 95: return "R16G16B16A16_UINT";
+ case 96: return "R16G16B16A16_SINT";
+ case 97: return "R16G16B16A16_SFLOAT";
+ case 98: return "R32_UINT";
+ case 99: return "R32_SINT";
+ case 100: return "R32_SFLOAT";
+ case 101: return "R32G32_UINT";
+ case 102: return "R32G32_SINT";
+ case 103: return "R32G32_SFLOAT";
+ case 104: return "R32G32B32_UINT";
+ case 105: return "R32G32B32_SINT";
+ case 106: return "R32G32B32_SFLOAT";
+ case 107: return "R32G32B32A32_UINT";
+ case 108: return "R32G32B32A32_SINT";
+ case 109: return "R32G32B32A32_SFLOAT";
+ case 110: return "R64_UINT";
+ case 111: return "R64_SINT";
+ case 112: return "R64_SFLOAT";
+ case 113: return "R64G64_UINT";
+ case 114: return "R64G64_SINT";
+ case 115: return "R64G64_SFLOAT";
+ case 116: return "R64G64B64_UINT";
+ case 117: return "R64G64B64_SINT";
+ case 118: return "R64G64B64_SFLOAT";
+ case 119: return "R64G64B64A64_UINT";
+ case 120: return "R64G64B64A64_SINT";
+ case 121: return "R64G64B64A64_SFLOAT";
+ case 122: return "B10G11R11_UFLOAT_PACK32";
+ case 123: return "E5B9G9R9_UFLOAT_PACK32";
+ case 124: return "D16_UNORM";
+ case 125: return "X8_D24_UNORM_PACK32";
+ case 126: return "D32_SFLOAT";
+ case 127: return "S8_UINT";
+ case 128: return "D16_UNORM_S8_UINT";
+ case 129: return "D24_UNORM_S8_UINT";
+ case 130: return "D32_SFLOAT_S8_UINT";
+ case 131: return "BC1_RGB_UNORM_BLOCK";
+ case 132: return "BC1_RGB_SRGB_BLOCK";
+ case 133: return "BC1_RGBA_UNORM_BLOCK";
+ case 134: return "BC1_RGBA_SRGB_BLOCK";
+ case 135: return "BC2_UNORM_BLOCK";
+ case 136: return "BC2_SRGB_BLOCK";
+ case 137: return "BC3_UNORM_BLOCK";
+ case 138: return "BC3_SRGB_BLOCK";
+ case 139: return "BC4_UNORM_BLOCK";
+ case 140: return "BC4_SNORM_BLOCK";
+ case 141: return "BC5_UNORM_BLOCK";
+ case 142: return "BC5_SNORM_BLOCK";
+ case 143: return "BC6H_UFLOAT_BLOCK";
+ case 144: return "BC6H_SFLOAT_BLOCK";
+ case 145: return "BC7_UNORM_BLOCK";
+ case 146: return "BC7_SRGB_BLOCK";
+ case 147: return "ETC2_R8G8B8_UNORM_BLOCK";
+ case 148: return "ETC2_R8G8B8_SRGB_BLOCK";
+ case 149: return "ETC2_R8G8B8A1_UNORM_BLOCK";
+ case 150: return "ETC2_R8G8B8A1_SRGB_BLOCK";
+ case 151: return "ETC2_R8G8B8A8_UNORM_BLOCK";
+ case 152: return "ETC2_R8G8B8A8_SRGB_BLOCK";
+ case 153: return "EAC_R11_UNORM_BLOCK";
+ case 154: return "EAC_R11_SNORM_BLOCK";
+ case 155: return "EAC_R11G11_UNORM_BLOCK";
+ case 156: return "EAC_R11G11_SNORM_BLOCK";
+ case 157: return "ASTC_4x4_UNORM_BLOCK";
+ case 158: return "ASTC_4x4_SRGB_BLOCK";
+ case 159: return "ASTC_5x4_UNORM_BLOCK";
+ case 160: return "ASTC_5x4_SRGB_BLOCK";
+ case 161: return "ASTC_5x5_UNORM_BLOCK";
+ case 162: return "ASTC_5x5_SRGB_BLOCK";
+ case 163: return "ASTC_6x5_UNORM_BLOCK";
+ case 164: return "ASTC_6x5_SRGB_BLOCK";
+ case 165: return "ASTC_6x6_UNORM_BLOCK";
+ case 166: return "ASTC_6x6_SRGB_BLOCK";
+ case 167: return "ASTC_8x5_UNORM_BLOCK";
+ case 168: return "ASTC_8x5_SRGB_BLOCK";
+ case 169: return "ASTC_8x6_UNORM_BLOCK";
+ case 170: return "ASTC_8x6_SRGB_BLOCK";
+ case 171: return "ASTC_8x8_UNORM_BLOCK";
+ case 172: return "ASTC_8x8_SRGB_BLOCK";
+ case 173: return "ASTC_10x5_UNORM_BLOCK";
+ case 174: return "ASTC_10x5_SRGB_BLOCK";
+ case 175: return "ASTC_10x6_UNORM_BLOCK";
+ case 176: return "ASTC_10x6_SRGB_BLOCK";
+ case 177: return "ASTC_10x8_UNORM_BLOCK";
+ case 178: return "ASTC_10x8_SRGB_BLOCK";
+ case 179: return "ASTC_10x10_UNORM_BLOCK";
+ case 180: return "ASTC_10x10_SRGB_BLOCK";
+ case 181: return "ASTC_12x10_UNORM_BLOCK";
+ case 182: return "ASTC_12x10_SRGB_BLOCK";
+ case 183: return "ASTC_12x12_UNORM_BLOCK";
+ case 184: return "ASTC_12x12_SRGB_BLOCK";
+ case 1000156000: return "G8B8G8R8_422_UNORM";
+ case 1000156001: return "B8G8R8G8_422_UNORM";
+ case 1000156002: return "G8_B8_R8_3PLANE_420_UNORM";
+ case 1000156003: return "G8_B8R8_2PLANE_420_UNORM";
+ case 1000156004: return "G8_B8_R8_3PLANE_422_UNORM";
+ case 1000156005: return "G8_B8R8_2PLANE_422_UNORM";
+ case 1000156006: return "G8_B8_R8_3PLANE_444_UNORM";
+ case 1000156007: return "R10X6_UNORM_PACK16";
+ case 1000156008: return "R10X6G10X6_UNORM_2PACK16";
+ case 1000156009: return "R10X6G10X6B10X6A10X6_UNORM_4PACK16";
+ case 1000156010: return "G10X6B10X6G10X6R10X6_422_UNORM_4PACK16";
+ case 1000156011: return "B10X6G10X6R10X6G10X6_422_UNORM_4PACK16";
+ case 1000156012: return "G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16";
+ case 1000156013: return "G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16";
+ case 1000156014: return "G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16";
+ case 1000156015: return "G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16";
+ case 1000156016: return "G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16";
+ case 1000156017: return "R12X4_UNORM_PACK16";
+ case 1000156018: return "R12X4G12X4_UNORM_2PACK16";
+ case 1000156019: return "R12X4G12X4B12X4A12X4_UNORM_4PACK16";
+ case 1000156020: return "G12X4B12X4G12X4R12X4_422_UNORM_4PACK16";
+ case 1000156021: return "B12X4G12X4R12X4G12X4_422_UNORM_4PACK16";
+ case 1000156022: return "G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16";
+ case 1000156023: return "G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16";
+ case 1000156024: return "G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16";
+ case 1000156025: return "G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16";
+ case 1000156026: return "G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16";
+ case 1000156027: return "G16B16G16R16_422_UNORM";
+ case 1000156028: return "B16G16R16G16_422_UNORM";
+ case 1000156029: return "G16_B16_R16_3PLANE_420_UNORM";
+ case 1000156030: return "G16_B16R16_2PLANE_420_UNORM";
+ case 1000156031: return "G16_B16_R16_3PLANE_422_UNORM";
+ case 1000156032: return "G16_B16R16_2PLANE_422_UNORM";
+ case 1000156033: return "G16_B16_R16_3PLANE_444_UNORM";
+ case 1000054000: return "PVRTC1_2BPP_UNORM_BLOCK_IMG";
+ case 1000054001: return "PVRTC1_4BPP_UNORM_BLOCK_IMG";
+ case 1000054002: return "PVRTC2_2BPP_UNORM_BLOCK_IMG";
+ case 1000054003: return "PVRTC2_4BPP_UNORM_BLOCK_IMG";
+ case 1000054004: return "PVRTC1_2BPP_SRGB_BLOCK_IMG";
+ case 1000054005: return "PVRTC1_4BPP_SRGB_BLOCK_IMG";
+ case 1000054006: return "PVRTC2_2BPP_SRGB_BLOCK_IMG";
+ case 1000054007: return "PVRTC2_4BPP_SRGB_BLOCK_IMG";
+ default: return "";
+ }
+ }
+
+ static AIScene loadModel(String classpathResource) {
+ AIFileIO fileIo = AIFileIO.create();
+ List