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();
}