From a07c55cf0fd65462921b4ad1ad36bcbd3b361d1e Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 13 Oct 2024 20:36:13 +0200 Subject: [PATCH] Add some class from tutorial --- flake.nix | 2 +- pom.xml | 6 + .../java/fr/mrdev023/vulkan_java/App.java | 9 + .../mrdev023/vulkan_java/renderer/Device.java | 101 ++++++++ .../vulkan_java/renderer/Instance.java | 223 ++++++++++++++++++ .../vulkan_java/renderer/PhysicalDevice.java | 178 ++++++++++++++ .../mrdev023/vulkan_java/renderer/Queue.java | 59 +++++ .../vulkan_java/renderer/Surface.java | 34 +++ .../mrdev023/vulkan_java/renderer/Vulkan.java | 36 +++ .../vulkan_java/renderer/VulkanUtils.java | 36 +++ .../mrdev023/vulkan_java/window/Display.java | 8 +- 11 files changed, 687 insertions(+), 5 deletions(-) create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/PhysicalDevice.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/renderer/VulkanUtils.java diff --git a/flake.nix b/flake.nix index e960d40..fc74497 100644 --- a/flake.nix +++ b/flake.nix @@ -15,7 +15,7 @@ devShells = { default = pkgs.mkShell { packages = with pkgs; [ - jdk23 # When available + jdk22 maven ]; }; diff --git a/pom.xml b/pom.xml index 7cc7d5e..5ed906e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ 21 3.3.4 1.10.8 + 2.7.0 @@ -38,6 +39,11 @@ joml ${joml.version} + + org.tinylog + tinylog-impl + ${tinylog.version} + diff --git a/src/main/java/fr/mrdev023/vulkan_java/App.java b/src/main/java/fr/mrdev023/vulkan_java/App.java index d677ae2..0b76f45 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/App.java +++ b/src/main/java/fr/mrdev023/vulkan_java/App.java @@ -1,16 +1,25 @@ package fr.mrdev023.vulkan_java; +import fr.mrdev023.vulkan_java.renderer.Vulkan; import fr.mrdev023.vulkan_java.window.Display; +import fr.mrdev023.vulkan_java.window.Input; public class App { public static void main(String[] args) { Display.create("My first application", 800, 600); + Display.printMonitorsInfo(); + Input.init(); + Vulkan.init(); while (!Display.isCloseRequested()) { Display.updateEvent(); + Input.update(); + Display.updateFrame(); } + Vulkan.destroy(); + Input.destroy(); Display.destroy(); } } diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java new file mode 100644 index 0000000..8e9e3e3 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java @@ -0,0 +1,101 @@ +package fr.mrdev023.vulkan_java.renderer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; +import org.tinylog.Logger; + +import java.nio.*; +import java.util.*; + +import static fr.mrdev023.vulkan_java.renderer.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.KHRPortabilitySubset.VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME; +import static org.lwjgl.vulkan.VK11.*; + +public class Device { + + private final PhysicalDevice physicalDevice; + private final VkDevice vkDevice; + + public Device(PhysicalDevice physicalDevice) { + Logger.debug("Creating device"); + + this.physicalDevice = physicalDevice; + try (MemoryStack stack = MemoryStack.stackPush()) { + + // Define required extensions + Set deviceExtensions = getDeviceExtensions(); + boolean usePortability = deviceExtensions.contains(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) && VulkanUtils.getOS() == VulkanUtils.OSType.MACOS; + int numExtensions = usePortability ? 2 : 1; + PointerBuffer requiredExtensions = stack.mallocPointer(numExtensions); + requiredExtensions.put(stack.ASCII(KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME)); + if (usePortability) { + requiredExtensions.put(stack.ASCII(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)); + } + requiredExtensions.flip(); + + // Set up required features + VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.calloc(stack); + + // Enable all the queue families + VkQueueFamilyProperties.Buffer queuePropsBuff = physicalDevice.getVkQueueFamilyProps(); + int numQueuesFamilies = queuePropsBuff.capacity(); + VkDeviceQueueCreateInfo.Buffer queueCreationInfoBuf = VkDeviceQueueCreateInfo.calloc(numQueuesFamilies, stack); + for (int i = 0; i < numQueuesFamilies; i++) { + FloatBuffer priorities = stack.callocFloat(queuePropsBuff.get(i).queueCount()); + queueCreationInfoBuf.get(i) + .sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO) + .queueFamilyIndex(i) + .pQueuePriorities(priorities); + } + + VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO) + .ppEnabledExtensionNames(requiredExtensions) + .pEnabledFeatures(features) + .pQueueCreateInfos(queueCreationInfoBuf); + + PointerBuffer pp = stack.mallocPointer(1); + vkCheck(vkCreateDevice(physicalDevice.getVkPhysicalDevice(), deviceCreateInfo, null, pp), + "Failed to create device"); + vkDevice = new VkDevice(pp.get(0), physicalDevice.getVkPhysicalDevice(), deviceCreateInfo); + } + } + + public void destroy() { + Logger.debug("Destroying Vulkan device"); + vkDestroyDevice(vkDevice, null); + } + + private Set getDeviceExtensions() { + Set deviceExtensions = new HashSet<>(); + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer numExtensionsBuf = stack.callocInt(1); + vkEnumerateDeviceExtensionProperties(physicalDevice.getVkPhysicalDevice(), (String) null, numExtensionsBuf, null); + int numExtensions = numExtensionsBuf.get(0); + Logger.debug("Device supports [{}] extensions", numExtensions); + + VkExtensionProperties.Buffer propsBuff = VkExtensionProperties.calloc(numExtensions, stack); + vkEnumerateDeviceExtensionProperties(physicalDevice.getVkPhysicalDevice(), (String) null, numExtensionsBuf, propsBuff); + for (int i = 0; i < numExtensions; i++) { + VkExtensionProperties props = propsBuff.get(i); + String extensionName = props.extensionNameString(); + deviceExtensions.add(extensionName); + Logger.debug("Supported device extension [{}]", extensionName); + } + } + return deviceExtensions; + } + + public PhysicalDevice getPhysicalDevice() { + return physicalDevice; + } + + public VkDevice getVkDevice() { + return vkDevice; + } + + public void waitIdle() { + vkDeviceWaitIdle(vkDevice); + } +} \ No newline at end of file diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java new file mode 100644 index 0000000..6bf1879 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java @@ -0,0 +1,223 @@ +package fr.mrdev023.vulkan_java.renderer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFWVulkan; +import org.lwjgl.system.*; +import org.lwjgl.vulkan.*; +import org.tinylog.Logger; + +import java.nio.*; +import java.util.*; + +import static fr.mrdev023.vulkan_java.renderer.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.EXTDebugUtils.*; +import static org.lwjgl.vulkan.KHRPortabilityEnumeration.VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +import static org.lwjgl.vulkan.VK11.*; + +public class Instance { + + public static final int MESSAGE_SEVERITY_BITMASK = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + public static final int MESSAGE_TYPE_BITMASK = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + private static final String PORTABILITY_EXTENSION = "VK_KHR_portability_enumeration"; + + private final VkInstance vkInstance; + + private VkDebugUtilsMessengerCreateInfoEXT debugUtils; + private long vkDebugHandle; + + public Instance(boolean validate) { + Logger.debug("Creating Vulkan instance"); + try (MemoryStack stack = MemoryStack.stackPush()) { + // Create application information + ByteBuffer appShortName = stack.UTF8("VulkanBook"); + VkApplicationInfo appInfo = VkApplicationInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_APPLICATION_INFO) + .pApplicationName(appShortName) + .applicationVersion(1) + .pEngineName(appShortName) + .engineVersion(0) + .apiVersion(VK_API_VERSION_1_1); + + // Validation layers + List validationLayers = getSupportedValidationLayers(); + int numValidationLayers = validationLayers.size(); + boolean supportsValidation = validate; + if (validate && numValidationLayers == 0) { + supportsValidation = false; + Logger.warn("Request validation but no supported validation layers found. Falling back to no validation"); + } + Logger.debug("Validation: {}", supportsValidation); + + // Set required layers + PointerBuffer requiredLayers = null; + if (supportsValidation) { + requiredLayers = stack.mallocPointer(numValidationLayers); + for (int i = 0; i < numValidationLayers; i++) { + Logger.debug("Using validation layer [{}]", validationLayers.get(i)); + requiredLayers.put(i, stack.ASCII(validationLayers.get(i))); + } + } + + Set instanceExtensions = getInstanceExtensions(); + + // GLFW Extension + PointerBuffer glfwExtensions = GLFWVulkan.glfwGetRequiredInstanceExtensions(); + if (glfwExtensions == null) { + throw new RuntimeException("Failed to find the GLFW platform surface extensions"); + } + + PointerBuffer requiredExtensions; + + boolean usePortability = instanceExtensions.contains(PORTABILITY_EXTENSION) && + VulkanUtils.getOS() == VulkanUtils.OSType.MACOS; + if (supportsValidation) { + ByteBuffer vkDebugUtilsExtension = stack.UTF8(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + int numExtensions = usePortability ? glfwExtensions.remaining() + 2 : glfwExtensions.remaining() + 1; + requiredExtensions = stack.mallocPointer(numExtensions); + requiredExtensions.put(glfwExtensions).put(vkDebugUtilsExtension); + if (usePortability) { + requiredExtensions.put(stack.UTF8(PORTABILITY_EXTENSION)); + } + } else { + int numExtensions = usePortability ? glfwExtensions.remaining() + 1 : glfwExtensions.remaining(); + requiredExtensions = stack.mallocPointer(numExtensions); + requiredExtensions.put(glfwExtensions); + if (usePortability) { + requiredExtensions.put(stack.UTF8(KHRPortabilitySubset.VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)); + } + } + requiredExtensions.flip(); + + long extension = MemoryUtil.NULL; + if (supportsValidation) { + debugUtils = createDebugCallBack(); + extension = debugUtils.address(); + } + + // Create instance info + VkInstanceCreateInfo instanceInfo = VkInstanceCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) + .pNext(extension) + .pApplicationInfo(appInfo) + .ppEnabledLayerNames(requiredLayers) + .ppEnabledExtensionNames(requiredExtensions); + if (usePortability) { + instanceInfo.flags(VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR); + } + + PointerBuffer pInstance = stack.mallocPointer(1); + vkCheck(vkCreateInstance(instanceInfo, null, pInstance), "Error creating instance"); + vkInstance = new VkInstance(pInstance.get(0), instanceInfo); + + vkDebugHandle = VK_NULL_HANDLE; + if (supportsValidation) { + LongBuffer longBuff = stack.mallocLong(1); + vkCheck(vkCreateDebugUtilsMessengerEXT(vkInstance, debugUtils, null, longBuff), "Error creating debug utils"); + vkDebugHandle = longBuff.get(0); + } + } + } + + private static VkDebugUtilsMessengerCreateInfoEXT createDebugCallBack() { + return VkDebugUtilsMessengerCreateInfoEXT + .calloc() + .sType(VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) + .messageSeverity(MESSAGE_SEVERITY_BITMASK) + .messageType(MESSAGE_TYPE_BITMASK) + .pfnUserCallback((messageSeverity, messageTypes, pCallbackData, pUserData) -> { + VkDebugUtilsMessengerCallbackDataEXT callbackData = VkDebugUtilsMessengerCallbackDataEXT.create(pCallbackData); + if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) != 0) { + Logger.info("VkDebugUtilsCallback, {}", callbackData.pMessageString()); + } else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0) { + Logger.warn("VkDebugUtilsCallback, {}", callbackData.pMessageString()); + } else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0) { + Logger.error("VkDebugUtilsCallback, {}", callbackData.pMessageString()); + } else { + Logger.debug("VkDebugUtilsCallback, {}", callbackData.pMessageString()); + } + return VK_FALSE; + }); + } + + public void destroy() { + Logger.debug("Destroying Vulkan instance"); + if (vkDebugHandle != VK_NULL_HANDLE) { + vkDestroyDebugUtilsMessengerEXT(vkInstance, vkDebugHandle, null); + } + vkDestroyInstance(vkInstance, null); + if (debugUtils != null) { + debugUtils.pfnUserCallback().free(); + debugUtils.free(); + } + } + + private Set getInstanceExtensions() { + Set instanceExtensions = new HashSet<>(); + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer numExtensionsBuf = stack.callocInt(1); + vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, null); + int numExtensions = numExtensionsBuf.get(0); + Logger.debug("Instance supports [{}] extensions", numExtensions); + + VkExtensionProperties.Buffer instanceExtensionsProps = VkExtensionProperties.calloc(numExtensions, stack); + vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, instanceExtensionsProps); + for (int i = 0; i < numExtensions; i++) { + VkExtensionProperties props = instanceExtensionsProps.get(i); + String extensionName = props.extensionNameString(); + instanceExtensions.add(extensionName); + Logger.debug("Supported instance extension [{}]", extensionName); + } + } + return instanceExtensions; + } + + private List getSupportedValidationLayers() { + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer numLayersArr = stack.callocInt(1); + vkEnumerateInstanceLayerProperties(numLayersArr, null); + int numLayers = numLayersArr.get(0); + Logger.debug("Instance supports [{}] layers", numLayers); + + VkLayerProperties.Buffer propsBuf = VkLayerProperties.calloc(numLayers, stack); + vkEnumerateInstanceLayerProperties(numLayersArr, propsBuf); + List supportedLayers = new ArrayList<>(); + for (int i = 0; i < numLayers; i++) { + VkLayerProperties props = propsBuf.get(i); + String layerName = props.layerNameString(); + supportedLayers.add(layerName); + Logger.debug("Supported layer [{}]", layerName); + } + + List layersToUse = new ArrayList<>(); + + // Main validation layer + if (supportedLayers.contains("VK_LAYER_KHRONOS_validation")) { + layersToUse.add("VK_LAYER_KHRONOS_validation"); + return layersToUse; + } + + // Fallback 1 + if (supportedLayers.contains("VK_LAYER_LUNARG_standard_validation")) { + layersToUse.add("VK_LAYER_LUNARG_standard_validation"); + return layersToUse; + } + + // Fallback 2 (set) + List requestedLayers = new ArrayList<>(); + requestedLayers.add("VK_LAYER_GOOGLE_threading"); + requestedLayers.add("VK_LAYER_LUNARG_parameter_validation"); + requestedLayers.add("VK_LAYER_LUNARG_object_tracker"); + requestedLayers.add("VK_LAYER_LUNARG_core_validation"); + requestedLayers.add("VK_LAYER_GOOGLE_unique_objects"); + + return requestedLayers.stream().filter(supportedLayers::contains).toList(); + } + } + + public VkInstance getVkInstance() { + return vkInstance; + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/PhysicalDevice.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/PhysicalDevice.java new file mode 100644 index 0000000..1f3a169 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/PhysicalDevice.java @@ -0,0 +1,178 @@ +package fr.mrdev023.vulkan_java.renderer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; +import org.tinylog.Logger; + +import java.nio.IntBuffer; +import java.util.*; + +import static fr.mrdev023.vulkan_java.renderer.VulkanUtils.vkCheck; +import static org.lwjgl.vulkan.VK11.*; + +public class PhysicalDevice { + + private final VkExtensionProperties.Buffer vkDeviceExtensions; + private final VkPhysicalDeviceMemoryProperties vkMemoryProperties; + private final VkPhysicalDevice vkPhysicalDevice; + private final VkPhysicalDeviceFeatures vkPhysicalDeviceFeatures; + private final VkPhysicalDeviceProperties vkPhysicalDeviceProperties; + private final VkQueueFamilyProperties.Buffer vkQueueFamilyProps; + + private PhysicalDevice(VkPhysicalDevice vkPhysicalDevice) { + try (MemoryStack stack = MemoryStack.stackPush()) { + this.vkPhysicalDevice = vkPhysicalDevice; + + IntBuffer intBuffer = stack.mallocInt(1); + + // Get device properties + vkPhysicalDeviceProperties = VkPhysicalDeviceProperties.calloc(); + vkGetPhysicalDeviceProperties(vkPhysicalDevice, vkPhysicalDeviceProperties); + + // Get device extensions + vkCheck(vkEnumerateDeviceExtensionProperties(vkPhysicalDevice, (String) null, intBuffer, null), + "Failed to get number of device extension properties"); + vkDeviceExtensions = VkExtensionProperties.calloc(intBuffer.get(0)); + vkCheck(vkEnumerateDeviceExtensionProperties(vkPhysicalDevice, (String) null, intBuffer, vkDeviceExtensions), + "Failed to get extension properties"); + + // Get Queue family properties + vkGetPhysicalDeviceQueueFamilyProperties(vkPhysicalDevice, intBuffer, null); + vkQueueFamilyProps = VkQueueFamilyProperties.calloc(intBuffer.get(0)); + vkGetPhysicalDeviceQueueFamilyProperties(vkPhysicalDevice, intBuffer, vkQueueFamilyProps); + + vkPhysicalDeviceFeatures = VkPhysicalDeviceFeatures.calloc(); + vkGetPhysicalDeviceFeatures(vkPhysicalDevice, vkPhysicalDeviceFeatures); + + // Get Memory information and properties + vkMemoryProperties = VkPhysicalDeviceMemoryProperties.calloc(); + vkGetPhysicalDeviceMemoryProperties(vkPhysicalDevice, vkMemoryProperties); + } + } + + public static PhysicalDevice createPhysicalDevice(Instance instance, Optional preferredDeviceName) { + Logger.debug("Selecting physical devices"); + PhysicalDevice selectedPhysicalDevice = null; + try (MemoryStack stack = MemoryStack.stackPush()) { + // Get available devices + PointerBuffer pPhysicalDevices = getPhysicalDevices(instance, stack); + int numDevices = pPhysicalDevices.capacity(); + if (numDevices <= 0) { + throw new RuntimeException("No physical devices found"); + } + + // Populate available devices + List devices = new ArrayList<>(); + for (int i = 0; i < numDevices; i++) { + VkPhysicalDevice vkPhysicalDevice = new VkPhysicalDevice(pPhysicalDevices.get(i), instance.getVkInstance()); + PhysicalDevice physicalDevice = new PhysicalDevice(vkPhysicalDevice); + + String deviceName = physicalDevice.getDeviceName(); + if (physicalDevice.hasGraphicsQueueFamily() && physicalDevice.hasKHRSwapChainExtension()) { + Logger.debug("Device [{}] supports required extensions", deviceName); + if (preferredDeviceName.isPresent() && preferredDeviceName.get().equals(deviceName)) { + selectedPhysicalDevice = physicalDevice; + break; + } + devices.add(physicalDevice); + } else { + Logger.debug("Device [{}] does not support required extensions", deviceName); + physicalDevice.destroy(); + } + } + + // No preferred device or it does not meet requirements, just pick the first one + selectedPhysicalDevice = selectedPhysicalDevice == null && !devices.isEmpty() ? devices.remove(0) : selectedPhysicalDevice; + + // Clean up non-selected devices + for (PhysicalDevice physicalDevice : devices) { + physicalDevice.destroy(); + } + + if (selectedPhysicalDevice == null) { + throw new RuntimeException("No suitable physical devices found"); + } + Logger.debug("Selected device: [{}]", selectedPhysicalDevice.getDeviceName()); + } + + return selectedPhysicalDevice; + } + + protected static PointerBuffer getPhysicalDevices(Instance instance, MemoryStack stack) { + PointerBuffer pPhysicalDevices; + // Get number of physical devices + IntBuffer intBuffer = stack.mallocInt(1); + vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, null), + "Failed to get number of physical devices"); + int numDevices = intBuffer.get(0); + Logger.debug("Detected {} physical device(s)", numDevices); + + // Populate physical devices list pointer + pPhysicalDevices = stack.mallocPointer(numDevices); + vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, pPhysicalDevices), + "Failed to get physical devices"); + return pPhysicalDevices; + } + + public void destroy() { + if (Logger.isDebugEnabled()) { + Logger.debug("Destroying physical device [{}]", vkPhysicalDeviceProperties.deviceNameString()); + } + vkMemoryProperties.free(); + vkPhysicalDeviceFeatures.free(); + vkQueueFamilyProps.free(); + vkDeviceExtensions.free(); + vkPhysicalDeviceProperties.free(); + } + + public String getDeviceName() { + return vkPhysicalDeviceProperties.deviceNameString(); + } + + public VkPhysicalDeviceMemoryProperties getVkMemoryProperties() { + return vkMemoryProperties; + } + + public VkPhysicalDevice getVkPhysicalDevice() { + return vkPhysicalDevice; + } + + public VkPhysicalDeviceFeatures getVkPhysicalDeviceFeatures() { + return vkPhysicalDeviceFeatures; + } + + public VkPhysicalDeviceProperties getVkPhysicalDeviceProperties() { + return vkPhysicalDeviceProperties; + } + + public VkQueueFamilyProperties.Buffer getVkQueueFamilyProps() { + return vkQueueFamilyProps; + } + + private boolean hasGraphicsQueueFamily() { + boolean result = false; + int numQueueFamilies = vkQueueFamilyProps != null ? vkQueueFamilyProps.capacity() : 0; + for (int i = 0; i < numQueueFamilies; i++) { + VkQueueFamilyProperties familyProps = vkQueueFamilyProps.get(i); + if ((familyProps.queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0) { + result = true; + break; + } + } + return result; + } + + private boolean hasKHRSwapChainExtension() { + boolean result = false; + int numExtensions = vkDeviceExtensions != null ? vkDeviceExtensions.capacity() : 0; + for (int i = 0; i < numExtensions; i++) { + String extensionName = vkDeviceExtensions.get(i).extensionNameString(); + if (KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME.equals(extensionName)) { + result = true; + break; + } + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java new file mode 100644 index 0000000..0b10561 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java @@ -0,0 +1,59 @@ +package fr.mrdev023.vulkan_java.renderer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; +import org.tinylog.Logger; + +import static org.lwjgl.vulkan.VK11.*; + +public class Queue { + + private final VkQueue vkQueue; + + public Queue(Device device, int queueFamilyIndex, int queueIndex) { + Logger.debug("Creating queue"); + + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pQueue = stack.mallocPointer(1); + vkGetDeviceQueue(device.getVkDevice(), queueFamilyIndex, queueIndex, pQueue); + long queue = pQueue.get(0); + vkQueue = new VkQueue(queue, device.getVkDevice()); + } + } + + public VkQueue getVkQueue() { + return vkQueue; + } + + public void waitIdle() { + vkQueueWaitIdle(vkQueue); + } + + public static class GraphicsQueue extends Queue { + + public GraphicsQueue(Device device, int queueIndex) { + super(device, getGraphicsQueueFamilyIndex(device), queueIndex); + } + + private static int getGraphicsQueueFamilyIndex(Device device) { + int index = -1; + PhysicalDevice physicalDevice = device.getPhysicalDevice(); + VkQueueFamilyProperties.Buffer queuePropsBuff = physicalDevice.getVkQueueFamilyProps(); + int numQueuesFamilies = queuePropsBuff.capacity(); + for (int i = 0; i < numQueuesFamilies; i++) { + VkQueueFamilyProperties props = queuePropsBuff.get(i); + boolean graphicsQueue = (props.queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0; + if (graphicsQueue) { + index = i; + break; + } + } + + if (index < 0) { + throw new RuntimeException("Failed to get graphics Queue family index"); + } + return index; + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java new file mode 100644 index 0000000..45e2585 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java @@ -0,0 +1,34 @@ +package fr.mrdev023.vulkan_java.renderer; + +import org.lwjgl.glfw.GLFWVulkan; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.KHRSurface; +import org.tinylog.Logger; + +import java.nio.LongBuffer; + +public class Surface { + + private final PhysicalDevice physicalDevice; + private final long vkSurface; + + public Surface(PhysicalDevice physicalDevice, long windowHandle) { + Logger.debug("Creating Vulkan surface"); + this.physicalDevice = physicalDevice; + try (MemoryStack stack = MemoryStack.stackPush()) { + LongBuffer pSurface = stack.mallocLong(1); + GLFWVulkan.glfwCreateWindowSurface(this.physicalDevice.getVkPhysicalDevice().getInstance(), windowHandle, + null, pSurface); + vkSurface = pSurface.get(0); + } + } + + public void destroy() { + Logger.debug("Destroying Vulkan surface"); + KHRSurface.vkDestroySurfaceKHR(physicalDevice.getVkPhysicalDevice().getInstance(), vkSurface, null); + } + + public long getVkSurface() { + return vkSurface; + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java new file mode 100644 index 0000000..20170b1 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java @@ -0,0 +1,36 @@ +package fr.mrdev023.vulkan_java.renderer; + +import fr.mrdev023.vulkan_java.window.Display; + +import java.util.Optional; + +import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported; + +public class Vulkan { + + private static Instance instance; + private static PhysicalDevice physicalDevice; + private static Device device; + private static Surface surface; + private static Queue.GraphicsQueue graphicsQueue; + + public static void init() { + if (!glfwVulkanSupported()) { + throw new IllegalStateException("Cannot find a compatible Vulkan installable client driver (ICD)"); + } + + instance = new Instance(true); + physicalDevice = PhysicalDevice.createPhysicalDevice(instance, Optional.empty()); + device = new Device(physicalDevice); + surface = new Surface(physicalDevice, Display.getWindow()); + graphicsQueue = new Queue.GraphicsQueue(device, 0); + } + + public static void destroy() { + surface.destroy(); + device.destroy(); + physicalDevice.destroy(); + instance.destroy(); + } + +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/renderer/VulkanUtils.java b/src/main/java/fr/mrdev023/vulkan_java/renderer/VulkanUtils.java new file mode 100644 index 0000000..9845121 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/renderer/VulkanUtils.java @@ -0,0 +1,36 @@ +package fr.mrdev023.vulkan_java.renderer; + +import java.util.Locale; + +import static org.lwjgl.vulkan.VK11.VK_SUCCESS; + +public class VulkanUtils { + + private VulkanUtils() { + // Utility class + } + + public static OSType getOS() { + OSType result; + String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); + if (os.contains("mac") || os.contains("darwin")) { + result = OSType.MACOS; + } else if (os.contains("win")) { + result = OSType.WINDOWS; + } else if (os.contains("nux")) { + result = OSType.LINUX; + } else { + result = OSType.OTHER; + } + + return result; + } + + public static void vkCheck(int err, String errMsg) { + if (err != VK_SUCCESS) { + throw new RuntimeException(errMsg + ": " + err); + } + } + + public enum OSType {WINDOWS, MACOS, LINUX, OTHER} +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/window/Display.java b/src/main/java/fr/mrdev023/vulkan_java/window/Display.java index 3c85ea0..d4faec1 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/window/Display.java +++ b/src/main/java/fr/mrdev023/vulkan_java/window/Display.java @@ -1,5 +1,6 @@ package fr.mrdev023.vulkan_java.window; +import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.system.MemoryUtil.*; @@ -22,6 +23,7 @@ public class Display { TITLE = title; displayMode = new DisplayMode(width, height); window = glfwCreateWindow(displayMode.getWidth(), displayMode.getHeight(), TITLE, NULL, NULL); + glfwMakeContextCurrent(window); } public static void setMouseGrabbed(boolean a) { @@ -80,6 +82,8 @@ public class Display { } for (int i = 0; i < monitors.capacity(); i++) { m = glfwGetVideoMode(monitors.get(i)); + if (m == null) continue; + System.out.println(glfwGetMonitorName(monitors.get(i)) + "(" + i + ") : " + m.width() + "x" + m.height() + ":" + m.refreshRate() + "Hz"); } @@ -89,10 +93,6 @@ public class Display { return glfwWindowShouldClose(window); } - public static void createContext() { - glfwMakeContextCurrent(window); - } - public static void updateEvent() { glfwPollEvents(); }