Working high fps vulkan example

liteCraft
hayden v 2020-03-05 14:40:57 +10:00
parent 14db9b4a22
commit a9d0fdd90c
18 changed files with 1002781 additions and 563 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +0,0 @@
package com.github.hydos.ginger;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.system.Configuration;
import com.github.hydos.ginger.engine.common.info.RenderAPI;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.vulkan.api.GingerVK;
/** @author hydos06
* the non ARR vulkan test example */
public class VulkanStarter
{
private static class BasicGinger2VulkanExample {
private static final int WIDTH = 600;
private static final int HEIGHT = 800;
// ======= FIELDS ======= //
private long window;
// ======= METHODS ======= //
public void run() {
Window.create(WIDTH, HEIGHT, "V u l k a n", 60, RenderAPI.Vulkan);
initVulkan();
mainLoop();
cleanup();
}
private void initVulkan() {
new GingerVK().start("Vulkan demo");
}
private void mainLoop() {
while(!Window.closed()) {
if(Window.shouldRender()) {
Window.update();
}
}
}
private void cleanup() {
GLFW.glfwDestroyWindow(window);
GLFW.glfwTerminate();
}
}
public static void main(String[] args) {
Configuration.DEBUG.set(true);
BasicGinger2VulkanExample app = new BasicGinger2VulkanExample();
app.run();
}
}

View File

@ -1,16 +0,0 @@
package com.github.hydos.ginger.engine.vulkan;
import org.lwjgl.vulkan.*;
public class VKConstants
{
public static final long UINT64_MAX = -1L;
public static VkInstance vulkanInstance;
public static VkPhysicalDevice physicalDevice;
public static VkDevice device;
public static VkQueue graphicsQueue;
public static long windowSurface;
public static VkQueue presentQueue;
}

View File

@ -1,19 +0,0 @@
package com.github.hydos.ginger.engine.vulkan.api;
import com.github.hydos.ginger.engine.vulkan.io.VKWindow;
import com.github.hydos.ginger.engine.vulkan.render.Swapchain;
import com.github.hydos.ginger.engine.vulkan.utils.VKUtils;
public class GingerVK
{
public void start(String gameName)
{
System.out.println("Game " + gameName + " successfuly started in Vulkan mode.");
VKUtils.createInstance();
VKWindow.createSurface();
VKUtils.createPhysicalDevice();
VKUtils.createLogicalDevice();
Swapchain.createSwapChain();
}
}

View File

@ -1,34 +0,0 @@
package com.github.hydos.ginger.engine.vulkan.io;
import java.nio.LongBuffer;
import org.lwjgl.glfw.GLFWVulkan;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VK12;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.vulkan.VKConstants;
import com.github.hydos.ginger.engine.vulkan.utils.*;
/**
* used for window related vulkan only things
* @author hydos
*/
public class VKWindow
{
public static void createSurface()
{
try(MemoryStack stack = MemoryStack.stackPush())
{
LongBuffer pSurface = stack.longs(VK12.VK_NULL_HANDLE);
int status = GLFWVulkan.glfwCreateWindowSurface(VKConstants.vulkanInstance, Window.getWindow(), null, pSurface);
if(status != VK12.VK_SUCCESS)
{
throw new VulkanException("Failed to create vulkan surface for window reason: " + VKUtils.translateVulkanResult(status));
}
VKConstants.windowSurface = pSurface.get(0);
}
}
}

View File

@ -1,15 +0,0 @@
package com.github.hydos.ginger.engine.vulkan.io;
public class VulkanException extends RuntimeException
{
public VulkanException(String string)
{
super(string);
}
/**
* the exception type thrown when a vulkan error is thrown
*/
private static final long serialVersionUID = -6985060773180054456L;
}

View File

@ -0,0 +1,61 @@
package com.github.hydos.ginger.engine.vulkan.misc;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import java.io.*;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
/**
* An utility class for dealing with alignments in Uniform Buffer Objects
*
* Vulkan expects the data in your structure to be aligned in memory in a specific way, for example:
*
* Scalars have to be aligned by N (= 4 bytes given 32 bit floats).
* A vec2 must be aligned by 2N (= 8 bytes)
* A vec3 or vec4 must be aligned by 4N (= 16 bytes)
* A nested structure must be aligned by the base alignment of its members rounded up to a multiple of 16.
* A mat4 matrix must have the same alignment as a vec4.
*
* */
public final class AlignmentUtils {
private AlignmentUtils() {}
public static int sizeof(Object obj) {
return obj == null ? 0 : SIZEOF_CACHE.getOrDefault(obj.getClass(), 0);
}
public static int alignof(Object obj) {
return obj == null ? 0 : SIZEOF_CACHE.getOrDefault(obj.getClass(), Integer.BYTES);
}
public static int alignas(int offset, int alignment) {
return offset % alignment == 0 ? offset : ((offset - 1) | (alignment - 1)) + 1;
}
private static final Map<Class<?>, Integer> SIZEOF_CACHE = new HashMap<>();
static {
SIZEOF_CACHE.put(Byte.class, Byte.BYTES);
SIZEOF_CACHE.put(Character.class, Character.BYTES);
SIZEOF_CACHE.put(Short.class, Short.BYTES);
SIZEOF_CACHE.put(Integer.class, Integer.BYTES);
SIZEOF_CACHE.put(Float.class, Float.BYTES);
SIZEOF_CACHE.put(Long.class, Long.BYTES);
SIZEOF_CACHE.put(Double.class, Double.BYTES);
SIZEOF_CACHE.put(Vector2f.class, 2 * Float.BYTES);
SIZEOF_CACHE.put(Vector3f.class, 3 * Float.BYTES);
SIZEOF_CACHE.put(Vector4f.class, 4 * Float.BYTES);
SIZEOF_CACHE.put(Matrix4f.class, SIZEOF_CACHE.get(Vector4f.class));
}
}

View File

@ -0,0 +1,47 @@
package com.github.hydos.ginger.engine.vulkan.misc;
import java.nio.LongBuffer;
import static org.lwjgl.system.MemoryStack.stackGet;
/**
* Wraps the needed sync objects for an in flight frame
*
* This frame's sync objects must be deleted manually
* */
public class Frame {
private final long imageAvailableSemaphore;
private final long renderFinishedSemaphore;
private final long fence;
public Frame(long imageAvailableSemaphore, long renderFinishedSemaphore, long fence) {
this.imageAvailableSemaphore = imageAvailableSemaphore;
this.renderFinishedSemaphore = renderFinishedSemaphore;
this.fence = fence;
}
public long imageAvailableSemaphore() {
return imageAvailableSemaphore;
}
public LongBuffer pImageAvailableSemaphore() {
return stackGet().longs(imageAvailableSemaphore);
}
public long renderFinishedSemaphore() {
return renderFinishedSemaphore;
}
public LongBuffer pRenderFinishedSemaphore() {
return stackGet().longs(renderFinishedSemaphore);
}
public long fence() {
return fence;
}
public LongBuffer pFence() {
return stackGet().longs(fence);
}
}

View File

@ -0,0 +1,133 @@
package com.github.hydos.ginger.engine.vulkan.misc;
import org.joml.Vector2f;
import org.joml.Vector2fc;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import org.lwjgl.PointerBuffer;
import org.lwjgl.assimp.*;
import java.io.File;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import static java.util.Objects.requireNonNull;
import static org.lwjgl.assimp.Assimp.*;
public class ModelLoader {
public static Model loadModel(File file, int flags) {
try(AIScene scene = aiImportFile(file.getAbsolutePath(), flags)) {
Logger logger = Logger.getLogger(ModelLoader.class.getSimpleName());
logger.info("Loading model " + file.getPath() + "...");
if(scene == null || scene.mRootNode() == null) {
throw new RuntimeException("Could not load model: " + aiGetErrorString());
}
Model model = new Model();
long startTime = System.nanoTime();
processNode(scene.mRootNode(), scene, model);
logger.info("Model loaded in " + ((System.nanoTime() - startTime) / 1e6) + "ms");
return model;
}
}
private static void processNode(AINode node, AIScene scene, Model model) {
if(node.mMeshes() != null) {
processNodeMeshes(scene, node, model);
}
if(node.mChildren() != null) {
PointerBuffer children = node.mChildren();
for(int i = 0;i < node.mNumChildren();i++) {
processNode(AINode.create(children.get(i)), scene, model);
}
}
}
private static void processNodeMeshes(AIScene scene, AINode node, Model model) {
PointerBuffer pMeshes = scene.mMeshes();
IntBuffer meshIndices = node.mMeshes();
for(int i = 0;i < meshIndices.capacity();i++) {
AIMesh mesh = AIMesh.create(pMeshes.get(meshIndices.get(i)));
processMesh(scene, mesh, model);
}
}
private static void processMesh(AIScene scene, AIMesh mesh, Model model) {
processPositions(mesh, model.positions);
processTexCoords(mesh, model.texCoords);
processIndices(mesh, model.indices);
}
private static void processPositions(AIMesh mesh, List<Vector3fc> positions) {
AIVector3D.Buffer vertices = requireNonNull(mesh.mVertices());
for(int i = 0;i < vertices.capacity();i++) {
AIVector3D position = vertices.get(i);
positions.add(new Vector3f(position.x(), position.y(), position.z()));
}
}
private static void processTexCoords(AIMesh mesh, List<Vector2fc> texCoords) {
AIVector3D.Buffer aiTexCoords = requireNonNull(mesh.mTextureCoords(0));
for(int i = 0;i < aiTexCoords.capacity();i++) {
final AIVector3D coords = aiTexCoords.get(i);
texCoords.add(new Vector2f(coords.x(), coords.y()));
}
}
private static void processIndices(AIMesh mesh, List<Integer> indices) {
AIFace.Buffer aiFaces = mesh.mFaces();
for(int i = 0;i < mesh.mNumFaces();i++) {
AIFace face = aiFaces.get(i);
IntBuffer pIndices = face.mIndices();
for(int j = 0;j < face.mNumIndices();j++) {
indices.add(pIndices.get(j));
}
}
}
public static class Model {
public final List<Vector3fc> positions;
public final List<Vector2fc> texCoords;
public final List<Integer> indices;
public Model() {
this.positions = new ArrayList<>();
this.texCoords = new ArrayList<>();
this.indices = new ArrayList<>();
}
}
}

View File

@ -0,0 +1,89 @@
package com.github.hydos.ginger.engine.vulkan.misc;
import org.lwjgl.system.NativeResource;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import static java.lang.ClassLoader.getSystemClassLoader;
import static org.lwjgl.system.MemoryUtil.NULL;
import static org.lwjgl.util.shaderc.Shaderc.*;
public class ShaderSPIRVUtils {
public static SPIRV compileShaderFile(String shaderFile, ShaderKind shaderKind) {
return compileShaderAbsoluteFile(getSystemClassLoader().getResource(shaderFile).toExternalForm(), shaderKind);
}
public static SPIRV compileShaderAbsoluteFile(String shaderFile, ShaderKind shaderKind) {
try {
String source = new String(Files.readAllBytes(Paths.get(new URI(shaderFile))));
return compileShader(shaderFile, source, shaderKind);
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
return null;
}
public static SPIRV compileShader(String filename, String source, ShaderKind shaderKind) {
long compiler = shaderc_compiler_initialize();
if(compiler == NULL) {
throw new RuntimeException("Failed to create shader compiler");
}
long result = shaderc_compile_into_spv(compiler, source, shaderKind.kind, filename, "main", NULL);
if(result == NULL) {
throw new RuntimeException("Failed to compile shader " + filename + " into SPIR-V");
}
if(shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
throw new RuntimeException("Failed to compile shader " + filename + "into SPIR-V:\n " + shaderc_result_get_error_message(result));
}
shaderc_compiler_release(compiler);
return new SPIRV(result, shaderc_result_get_bytes(result));
}
public enum ShaderKind {
VERTEX_SHADER(shaderc_glsl_vertex_shader),
GEOMETRY_SHADER(shaderc_glsl_geometry_shader),
FRAGMENT_SHADER(shaderc_glsl_fragment_shader);
private final int kind;
ShaderKind(int kind) {
this.kind = kind;
}
}
public static final class SPIRV implements NativeResource {
private final long handle;
private ByteBuffer bytecode;
public SPIRV(long handle, ByteBuffer bytecode) {
this.handle = handle;
this.bytecode = bytecode;
}
public ByteBuffer bytecode() {
return bytecode;
}
@Override
public void free() {
shaderc_result_release(handle);
bytecode = null; // Help the GC
}
}
}

View File

@ -0,0 +1,67 @@
package com.github.hydos.ginger.engine.vulkan.misc;
import org.joml.Vector2fc;
import org.joml.Vector3fc;
import org.lwjgl.vulkan.VK12;
import org.lwjgl.vulkan.VkVertexInputAttributeDescription;
import org.lwjgl.vulkan.VkVertexInputBindingDescription;
public class VKVertex {
private static final int SIZEOF = (3 + 3 + 2) * Float.BYTES;
private static final int OFFSETOF_POS = 0;
private static final int OFFSETOF_COLOR = 3 * Float.BYTES;
private static final int OFFSETOF_TEXTCOORDS = (3 + 3) * Float.BYTES;
private Vector3fc pos;
private Vector3fc color;
private Vector2fc texCoords;
public VKVertex(Vector3fc pos, Vector3fc color, Vector2fc texCoords) {
this.pos = pos;
this.color = color;
this.texCoords = texCoords;
}
private static VkVertexInputBindingDescription.Buffer getBindingDescription() {
VkVertexInputBindingDescription.Buffer bindingDescription =
VkVertexInputBindingDescription.callocStack(1);
bindingDescription.binding(0);
bindingDescription.stride(VKVertex.SIZEOF);
bindingDescription.inputRate(VK12.VK_VERTEX_INPUT_RATE_VERTEX);
return bindingDescription;
}
private static VkVertexInputAttributeDescription.Buffer getAttributeDescriptions() {
VkVertexInputAttributeDescription.Buffer attributeDescriptions =
VkVertexInputAttributeDescription.callocStack(3);
// Position
VkVertexInputAttributeDescription posDescription = attributeDescriptions.get(0);
posDescription.binding(0);
posDescription.location(0);
posDescription.format(VK12.VK_FORMAT_R32G32B32_SFLOAT);
posDescription.offset(OFFSETOF_POS);
// Color
VkVertexInputAttributeDescription colorDescription = attributeDescriptions.get(1);
colorDescription.binding(0);
colorDescription.location(1);
colorDescription.format(VK12.VK_FORMAT_R32G32B32_SFLOAT);
colorDescription.offset(OFFSETOF_COLOR);
// Texture coordinates
VkVertexInputAttributeDescription texCoordsDescription = attributeDescriptions.get(2);
texCoordsDescription.binding(0);
texCoordsDescription.location(2);
texCoordsDescription.format(VK12.VK_FORMAT_R32G32_SFLOAT);
texCoordsDescription.offset(OFFSETOF_TEXTCOORDS);
return attributeDescriptions.rewind();
}
}

View File

@ -1,173 +0,0 @@
package com.github.hydos.ginger.engine.vulkan.render;
import java.nio.*;
import java.util.*;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.vulkan.*;
import com.github.hydos.ginger.engine.common.io.Window;
import com.github.hydos.ginger.engine.vulkan.VKConstants;
import com.github.hydos.ginger.engine.vulkan.utils.VKUtils;
import com.github.hydos.ginger.engine.vulkan.utils.VKUtils.QueueFamilyIndices;
/**
* used for Vulkan FBO management as vulkan does not have a default FBO
* @author hydos
*
*/
public class Swapchain
{
public static long swapChain;
public static List<Long> swapChainImages;
public static int swapChainImageFormat;
public static VkExtent2D swapChainExtent;
public static class SwapChainSupportDetails
{
public VkSurfaceCapabilitiesKHR capabilities;
public VkSurfaceFormatKHR.Buffer formats;
public IntBuffer presentModes;
}
private static SwapChainSupportDetails querySwapChainSupport(MemoryStack stack) {
SwapChainSupportDetails details = new SwapChainSupportDetails();
details.capabilities = VkSurfaceCapabilitiesKHR.mallocStack(stack);
KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VKConstants.physicalDevice, VKConstants.windowSurface, details.capabilities);
IntBuffer count = stack.ints(0);
KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(VKConstants.physicalDevice, VKConstants.windowSurface, count, null);
if(count.get(0) != 0) {
details.formats = VkSurfaceFormatKHR.mallocStack(count.get(0), stack);
KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(VKConstants.physicalDevice, VKConstants.windowSurface, count, details.formats);
}
KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(VKConstants.physicalDevice,VKConstants.windowSurface, count, null);
if(count.get(0) != 0) {
details.presentModes = stack.mallocInt(count.get(0));
KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(VKConstants.physicalDevice, VKConstants.windowSurface, count, details.presentModes);
}
return details;
}
private static VkSurfaceFormatKHR chooseSwapSurfaceFormat(VkSurfaceFormatKHR.Buffer availableFormats) {
return availableFormats.stream()
.filter(availableFormat -> availableFormat.format() == VK12.VK_FORMAT_B8G8R8_UNORM)
.filter(availableFormat -> availableFormat.colorSpace() == KHRSurface.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
.findAny()
.orElse(availableFormats.get(0));
}
private static int chooseSwapPresentMode(IntBuffer availablePresentModes) {
for(int i = 0;i < availablePresentModes.capacity();i++) {
if(availablePresentModes.get(i) == KHRSurface.VK_PRESENT_MODE_MAILBOX_KHR) {
return availablePresentModes.get(i);
}
}
return KHRSurface.VK_PRESENT_MODE_FIFO_KHR;
}
private static VkExtent2D chooseSwapExtent(VkSurfaceCapabilitiesKHR capabilities) {
if(capabilities.currentExtent().width() != VKConstants.UINT64_MAX) {
return capabilities.currentExtent();
}
VkExtent2D actualExtent = VkExtent2D.mallocStack().set(Window.getWidth(), Window.getHeight());
VkExtent2D minExtent = capabilities.minImageExtent();
VkExtent2D maxExtent = capabilities.maxImageExtent();
actualExtent.width(VKUtils.clamp(minExtent.width(), maxExtent.width(), actualExtent.width()));
actualExtent.height(VKUtils.clamp(minExtent.height(), maxExtent.height(), actualExtent.height()));
return actualExtent;
}
public static void createSwapChain()
{
try(MemoryStack stack = MemoryStack.stackPush()) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(stack);
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
int presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
IntBuffer imageCount = stack.ints(swapChainSupport.capabilities.minImageCount() + 1);
if(swapChainSupport.capabilities.maxImageCount() > 0 && imageCount.get(0) > swapChainSupport.capabilities.maxImageCount()) {
imageCount.put(0, swapChainSupport.capabilities.maxImageCount());
}
VkSwapchainCreateInfoKHR createInfo = VkSwapchainCreateInfoKHR.calloc()
.sType(KHRSwapchain.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR)
.minImageCount(imageCount.get(0))
.imageFormat(surfaceFormat.format())
.imageColorSpace(surfaceFormat.colorSpace())
.imageExtent(extent)
.imageArrayLayers(1)
.imageUsage(VK12.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
QueueFamilyIndices indices = VKUtils.findQueueFamilies();
if(!indices.graphicsFamily.equals(indices.presentFamily)) {
createInfo.imageSharingMode(VK12.VK_SHARING_MODE_CONCURRENT);
createInfo.pQueueFamilyIndices(stack.ints(indices.graphicsFamily, indices.presentFamily));
} else {
createInfo.imageSharingMode(VK12.VK_SHARING_MODE_EXCLUSIVE);
}
createInfo.preTransform(swapChainSupport.capabilities.currentTransform());
createInfo.compositeAlpha(KHRSurface.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
createInfo.presentMode(presentMode);
createInfo.clipped(true);
createInfo.oldSwapchain(1);
LongBuffer pSwapChain = MemoryUtil.memAllocLong(1);
int result = KHRSwapchain.
vkCreateSwapchainKHR
(
VKConstants.device,
createInfo,
null,
pSwapChain
);
if(result != VK12.VK_SUCCESS) {
throw new RuntimeException("Failed to create swap chain reason: " + VKUtils.translateVulkanResult(result));
}
swapChain = pSwapChain.get(0);
KHRSwapchain.vkGetSwapchainImagesKHR(VKConstants.device, swapChain, imageCount, null);
LongBuffer pSwapchainImages = stack.mallocLong(imageCount.get(0));
KHRSwapchain.vkGetSwapchainImagesKHR(VKConstants.device, swapChain, imageCount, pSwapchainImages);
swapChainImages = new ArrayList<>(imageCount.get(0));
for(int i = 0;i < pSwapchainImages.capacity();i++) {
swapChainImages.add(pSwapchainImages.get(i));
}
swapChainImageFormat = surfaceFormat.format();
swapChainExtent = VkExtent2D.create().set(extent);
}
}
}

View File

@ -1,245 +0,0 @@
package com.github.hydos.ginger.engine.vulkan.utils;
import java.nio.IntBuffer;
import java.util.stream.IntStream;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWVulkan;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.*;
import com.github.hydos.ginger.engine.vulkan.VKConstants;
public class VKUtils
{
private static PointerBuffer getRequiredExtensions() {
PointerBuffer glfwExtensions = GLFWVulkan.glfwGetRequiredInstanceExtensions();
return glfwExtensions;
}
public static int clamp(int min, int max, int value) {
return Math.max(min, Math.min(max, value));
}
public static void createInstance() {
try(MemoryStack stack = MemoryStack.stackPush()) {
// Use calloc to initialize the structs with 0s. Otherwise, the program can crash due to random values
VkApplicationInfo appInfo = VkApplicationInfo.callocStack(stack);
appInfo.sType(VK12.VK_STRUCTURE_TYPE_APPLICATION_INFO);
appInfo.pApplicationName(stack.UTF8Safe("GingerGame"));
appInfo.applicationVersion(VK12.VK_MAKE_VERSION(1, 0, 0));
appInfo.pEngineName(stack.UTF8Safe("Ginger2"));
appInfo.engineVersion(VK12.VK_MAKE_VERSION(2, 0, 0));
appInfo.apiVersion(VK12.VK_API_VERSION_1_2);
VkInstanceCreateInfo createInfo = VkInstanceCreateInfo.callocStack(stack);
createInfo.sType(VK12.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
createInfo.pApplicationInfo(appInfo);
// enabledExtensionCount is implicitly set when you call ppEnabledExtensionNames
createInfo.ppEnabledExtensionNames(getRequiredExtensions());
// We need to retrieve the pointer of the created instance
PointerBuffer instancePtr = stack.mallocPointer(1);
if(VK12.vkCreateInstance(createInfo, null, instancePtr) != VK12.VK_SUCCESS) {
throw new RuntimeException("Failed to create instance");
}
VKConstants.vulkanInstance = new VkInstance(instancePtr.get(0), createInfo);
}
}
public static void createPhysicalDevice() {
try(MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer deviceCount = stack.ints(0);
VK12.vkEnumeratePhysicalDevices(VKConstants.vulkanInstance, deviceCount, null);
if(deviceCount.get(0) == 0) {
throw new RuntimeException("Failed to find GPUs with Vulkan support");
}
PointerBuffer ppPhysicalDevices = stack.mallocPointer(deviceCount.get(0));
VK12.vkEnumeratePhysicalDevices(VKConstants.vulkanInstance, deviceCount, ppPhysicalDevices);
VkPhysicalDevice device = null;
for(int i = 0;i < ppPhysicalDevices.capacity();i++) {
device = new VkPhysicalDevice(ppPhysicalDevices.get(i), VKConstants.vulkanInstance);
}
if(device == null) {
throw new RuntimeException("Failed to find a suitable GPU");
}
VKConstants.physicalDevice = device;
}
}
public static class QueueFamilyIndices {
// We use Integer to use null as the empty value
public Integer graphicsFamily;
public Integer presentFamily;
public boolean isComplete() {
return graphicsFamily != null && presentFamily != null;
}
public int[] unique() {
return IntStream.of(graphicsFamily, presentFamily).distinct().toArray();
}
}
public static void createLogicalDevice()
{
try(MemoryStack stack = MemoryStack.stackPush())
{
QueueFamilyIndices indices = findQueueFamilies();
int[] uniqueQueueFamilies = indices.unique();
VkDeviceQueueCreateInfo.Buffer queueCreateInfos = VkDeviceQueueCreateInfo.callocStack(uniqueQueueFamilies.length, stack);
for(int i = 0;i < uniqueQueueFamilies.length;i++) {
VkDeviceQueueCreateInfo queueCreateInfo = queueCreateInfos.get(i);
queueCreateInfo.sType(VK12.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO);
queueCreateInfo.queueFamilyIndex(uniqueQueueFamilies[i]);
queueCreateInfo.pQueuePriorities(stack.floats(1.0f));
}
VkDeviceCreateInfo createInfo = VkDeviceCreateInfo.callocStack(stack);
createInfo.sType(VK12.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO);
createInfo.pQueueCreateInfos(queueCreateInfos);
// queueCreateInfoCount is automatically set
PointerBuffer pDevice = stack.pointers(VK12.VK_NULL_HANDLE);
if(VK12.vkCreateDevice(VKConstants.physicalDevice, createInfo, null, pDevice) != VK12.VK_SUCCESS) {
throw new RuntimeException("Failed to create logical device");
}
VKConstants.device = new VkDevice(pDevice.get(0), VKConstants.physicalDevice, createInfo);
PointerBuffer pQueue = stack.pointers(VK12.VK_NULL_HANDLE);
VK12.vkGetDeviceQueue(VKConstants.device, indices.graphicsFamily, 0, pQueue);
VKConstants.graphicsQueue = new VkQueue(pQueue.get(0), VKConstants.device);
VK12.vkGetDeviceQueue(VKConstants.device, indices.presentFamily, 0, pQueue);
VKConstants.presentQueue = new VkQueue(pQueue.get(0), VKConstants.device);
}
}
public static QueueFamilyIndices findQueueFamilies() {
QueueFamilyIndices indices = new QueueFamilyIndices();
try(MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer queueFamilyCount = stack.ints(0);
VK12.vkGetPhysicalDeviceQueueFamilyProperties(VKConstants.physicalDevice, queueFamilyCount, null);
VkQueueFamilyProperties.Buffer queueFamilies = VkQueueFamilyProperties.mallocStack(queueFamilyCount.get(0), stack);
VK12.vkGetPhysicalDeviceQueueFamilyProperties(VKConstants.physicalDevice, queueFamilyCount, queueFamilies);
IntBuffer presentSupport = stack.ints(VK12.VK_FALSE);
for(int i = 0;i < queueFamilies.capacity() || !indices.isComplete();i++) {
if((queueFamilies.get(i).queueFlags() & VK12.VK_QUEUE_GRAPHICS_BIT) != 0) {
indices.graphicsFamily = i;
}
KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(
VKConstants.physicalDevice,
i, VKConstants.windowSurface,
presentSupport);
if(presentSupport.get(0) == VK12.VK_TRUE) {
indices.presentFamily = i;
}
}
return indices;
}
}
//some code from LWJGL examples for debugging (has changes)
public static String translateVulkanResult(int result) {
switch (result) {
// Success codes
case VK12.VK_SUCCESS:
return "Command successfully completed.";
case VK12.VK_NOT_READY:
return "A fence or query has not yet completed.";
case VK12.VK_TIMEOUT:
return "A wait operation has not completed in the specified time.";
case VK12.VK_EVENT_SET:
return "An event is signaled.";
case VK12.VK_EVENT_RESET:
return "An event is unsignaled.";
case VK12.VK_INCOMPLETE:
return "A return array was too small for the result.";
case KHRSwapchain.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 VK12.VK_ERROR_OUT_OF_HOST_MEMORY:
return "A host memory allocation has failed.";
case VK12.VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "A device memory allocation has failed.";
case VK12.VK_ERROR_INITIALIZATION_FAILED:
return "Initialization of an object could not be completed for implementation-specific reasons.";
case VK12.VK_ERROR_DEVICE_LOST:
return "The logical or physical device has been lost.";
case VK12.VK_ERROR_MEMORY_MAP_FAILED:
return "Mapping of a memory object has failed.";
case VK12.VK_ERROR_LAYER_NOT_PRESENT:
return "A requested layer is not present or could not be loaded.";
case VK12.VK_ERROR_EXTENSION_NOT_PRESENT:
return "A requested extension is not supported.";
case VK12.VK_ERROR_FEATURE_NOT_PRESENT:
return "A requested feature is not supported.";
case VK12.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 VK12.VK_ERROR_TOO_MANY_OBJECTS:
return "Too many objects of the type have already been created.";
case VK12.VK_ERROR_FORMAT_NOT_SUPPORTED:
return "A requested format is not supported on this device.";
case KHRSurface.VK_ERROR_SURFACE_LOST_KHR:
return "A surface is no longer available.";
case KHRSurface.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 KHRSwapchain.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 KHRDisplaySwapchain.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 EXTDebugReport.VK_ERROR_VALIDATION_FAILED_EXT:
return "A validation layer found an error.";
default:
return String.format("%s [%d]", "Unknown", Integer.valueOf(result));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(binding = 1) uniform sampler2D texSampler;
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
void main() {
outColor = texture(texSampler, fragTexCoord);
}

View File

@ -0,0 +1,21 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
fragColor = inColor;
fragTexCoord = inTexCoord;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB