diff --git a/.classpath b/.classpath index c051951..4c9282d 100644 --- a/.classpath +++ b/.classpath @@ -15,5 +15,18 @@ + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index a261f1f..885b676 100644 --- a/pom.xml +++ b/pom.xml @@ -91,13 +91,28 @@ + jitpack.io https://jitpack.io + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots + false + true + + + org.lwjgl + lwjgl-vma + + + org.lwjgl + lwjgl-shaderc + org.joml joml diff --git a/src/main/java/com/github/hydos/ginger/VulkanStarter.java b/src/main/java/com/github/hydos/ginger/VulkanStarter.java new file mode 100644 index 0000000..a40b45b --- /dev/null +++ b/src/main/java/com/github/hydos/ginger/VulkanStarter.java @@ -0,0 +1,1461 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package com.github.hydos.ginger; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFWVulkan.*; +import static org.lwjgl.system.MemoryUtil.*; +import static org.lwjgl.vulkan.EXTDebugReport.*; +import static org.lwjgl.vulkan.KHRSurface.*; +import static org.lwjgl.vulkan.KHRSwapchain.*; +import static org.lwjgl.vulkan.VK10.*; + +import java.io.IOException; +import java.nio.*; + +import org.joml.Matrix4f; +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.*; +import org.lwjgl.vulkan.*; + +import com.github.hydos.ginger.engine.vulkan.utils.VKUtils; + +/** + * Renders two rotating triangles on a cornflower blue background on a GLFW window with Vulkan. + *

+ * 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 callbacks = new ArrayList<>(); + AIFileOpenProc fileOpenProc = new AIFileOpenProc() { + public long invoke(long pFileIO, long fileName, long openMode) { + AIFile aiFile = AIFile.create(); + final ByteBuffer data; + String fileNameUtf8 = memUTF8(fileName); + try { + data = IOUtil.ioResourceToByteBuffer(fileNameUtf8, 8192); + } catch (IOException e) { + throw new RuntimeException("Could not open file: " + fileNameUtf8); + } + AIFileReadProc fileReadProc = new AIFileReadProc() { + public long invoke(long pFile, long pBuffer, long size, long count) { + long max = Math.min(data.remaining(), size * count); + memCopy(memAddress(data) + data.position(), pBuffer, max); + return max; + } + }; + callbacks.add(fileReadProc); + AIFileSeek fileSeekProc = new AIFileSeek() { + public int invoke(long pFile, long offset, int origin) { + if (origin == Assimp.aiOrigin_CUR) { + data.position(data.position() + (int) offset); + } else if (origin == Assimp.aiOrigin_SET) { + data.position((int) offset); + } else if (origin == Assimp.aiOrigin_END) { + data.position(data.limit() + (int) offset); + } + return 0; + } + }; + callbacks.add(fileSeekProc); + AIFileTellProc fileTellProc = new AIFileTellProc() { + public long invoke(long pFile) { + return data.limit(); + } + }; + callbacks.add(fileTellProc); + aiFile.ReadProc(fileReadProc); + aiFile.SeekProc(fileSeekProc); + aiFile.FileSizeProc(fileTellProc); + return aiFile.address(); + } + }; + AIFileCloseProc fileCloseProc = new AIFileCloseProc() { + public void invoke(long pFileIO, long pFile) { + /* Nothing to do */ + } + }; + callbacks.add(fileOpenProc); + callbacks.add(fileCloseProc); + fileIo.set(fileOpenProc, fileCloseProc, NULL); + AIScene scene = aiImportFileEx(classpathResource, + aiProcess_JoinIdenticalVertices | aiProcess_Triangulate, fileIo); + callbacks.forEach(Callback::free); + return scene; + } +} \ No newline at end of file