Add some class from tutorial
This commit is contained in:
parent
c4bd9d2558
commit
a07c55cf0f
11 changed files with 687 additions and 5 deletions
|
@ -15,7 +15,7 @@
|
|||
devShells = {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
jdk23 # When available
|
||||
jdk22
|
||||
maven
|
||||
];
|
||||
};
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -15,6 +15,7 @@
|
|||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<lwjgl.version>3.3.4</lwjgl.version>
|
||||
<joml.version>1.10.8</joml.version>
|
||||
<tinylog.version>2.7.0</tinylog.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -38,6 +39,11 @@
|
|||
<artifactId>joml</artifactId>
|
||||
<version>${joml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tinylog</groupId>
|
||||
<artifactId>tinylog-impl</artifactId>
|
||||
<version>${tinylog.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Natives -->
|
||||
<dependency>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
101
src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java
Normal file
101
src/main/java/fr/mrdev023/vulkan_java/renderer/Device.java
Normal file
|
@ -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<String> 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<String> getDeviceExtensions() {
|
||||
Set<String> 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);
|
||||
}
|
||||
}
|
223
src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java
Normal file
223
src/main/java/fr/mrdev023/vulkan_java/renderer/Instance.java
Normal file
|
@ -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<String> 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<String> 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<String> getInstanceExtensions() {
|
||||
Set<String> 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<String> 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<String> 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<String> 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<String> 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;
|
||||
}
|
||||
}
|
|
@ -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<String> 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<PhysicalDevice> 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;
|
||||
}
|
||||
}
|
59
src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java
Normal file
59
src/main/java/fr/mrdev023/vulkan_java/renderer/Queue.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
34
src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java
Normal file
34
src/main/java/fr/mrdev023/vulkan_java/renderer/Surface.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
36
src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java
Normal file
36
src/main/java/fr/mrdev023/vulkan_java/renderer/Vulkan.java
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue