Working high fps vulkan example
parent
14db9b4a22
commit
a9d0fdd90c
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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 |
Loading…
Reference in New Issue