Vertex Rendering!

This commit is contained in:
Harrison Corlett 2026-05-14 14:30:48 +01:00
parent 852336e188
commit 2f626ba00f
20 changed files with 450 additions and 25 deletions

View file

@ -0,0 +1,8 @@
#version 450
layout(location = 0) out vec4 outFragColor;
void main()
{
outFragColor = vec4(1, 0, 0, 1);
}

Binary file not shown.

View file

@ -0,0 +1,8 @@
#version 450
layout(location = 0) in vec3 inPos;
void main()
{
gl_Position = vec4(inPos,1);
}

Binary file not shown.

View file

@ -2,7 +2,10 @@ package net.halbear.Terrain4J.EngineCore.Display;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelsCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandPool;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.Fence;
@ -17,12 +20,15 @@ import org.lwjgl.vulkan.VkSemaphoreSubmitInfo;
import org.tinylog.Logger;
import java.util.Arrays;
import java.util.List;
import static org.lwjgl.vulkan.VK13.VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
import static org.lwjgl.vulkan.VK13.VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
public class Render {
private final ModelsCache modelsCache;
private final CommandBuffer[] CommandBuffers;
private final CommandPool[] CommandPools;
private final Fence[] Fences;
@ -55,6 +61,14 @@ public class Render {
RenderCompleteSemaphores[i] = new Semaphore(RendererContext);
}
sceneRender = new SceneRender(RendererContext);
modelsCache = new ModelsCache();
}
public void Initialise(InitData initData){
List<ModelData> Models = initData.Models();
Logger.debug("Loading {} models", Models.size());
modelsCache.loadModels(RendererContext, Models, CommandPools[0], GraphicsQueue);
Logger.debug("Loaded {} models", Models.size());
}
private void RecordingStart(CommandPool commandPool, CommandBuffer commandBuffer){
@ -69,7 +83,7 @@ public class Render {
public void cleanup() {
RendererContext.GetDevice().waitIdle();
Logger.debug("Waiting Vulkan Context");
sceneRender.cleanup();
sceneRender.cleanup(RendererContext);
Logger.debug("Dynamic Renderer Cleaned up");
Arrays.asList(RenderCompleteSemaphores).forEach(i->i.cleanup(RendererContext));
Logger.debug("Render Semaphores cleaned up");
@ -82,6 +96,7 @@ public class Render {
CommandPools[i].cleanup(RendererContext);
}
Logger.debug("Command pools cleaned up");
modelsCache.CleanUp(RendererContext);
RendererContext.cleanup();
}
public void render(EngineInstance engineInstance){
@ -94,7 +109,7 @@ public class Render {
if (ImageIndex < 0){
return;
}
sceneRender.Render(RendererContext,CommandBuffer,ImageIndex);
sceneRender.Render(RendererContext,CommandBuffer, modelsCache,ImageIndex);
RecordingStop(CommandBuffer);
Submit(CommandBuffer, CurrentFrame, ImageIndex);
swapChain.PresentImage(PresentQueue, RenderCompleteSemaphores[ImageIndex],ImageIndex);

View file

@ -30,6 +30,8 @@ public class EngineConfig {
public static int FrameRate_WINDOW =0;
public static int RENDERFRAMES =0;
public static boolean VSYNC = false;
public static boolean ShaderDebug = false;
public static boolean RecompileShaders = false;
private boolean vkValidated;
private String PhysicalDeviceName;
private int RequestedImages;
@ -42,7 +44,12 @@ public class EngineConfig {
public boolean GetVSYNC(){
return VSYNC;
}
public boolean DebugShaders(){
return ShaderDebug;
}
public boolean RecompileShaders(){
return RecompileShaders;
}
private EngineConfig() {
var EngineConfigVar = new Properties();
Path path = Paths.get("");
@ -95,6 +102,8 @@ public class EngineConfig {
VSYNC = Boolean.parseBoolean(EngineConfigVar.getOrDefault("vsync", true).toString());
vkValidated = Boolean.parseBoolean(EngineConfigVar.getOrDefault("vkValidated", false).toString());
RequestedImages = Integer.parseInt(EngineConfigVar.getOrDefault("RequestedImages", 3).toString());
ShaderDebug =Boolean.parseBoolean(EngineConfigVar.getOrDefault("Debug_Shaders", false).toString());
RecompileShaders =Boolean.parseBoolean(EngineConfigVar.getOrDefault("ShaderRecompiling", true).toString());
Logger.debug("\n\nsuccessfully loaded configuration: \n{}\n\n",EngineConfigVar.toString());
}
else if(!ConfigFile.exists()){
@ -112,6 +121,8 @@ public class EngineConfig {
EngineConfigVar.setProperty("display_height","720");
EngineConfigVar.setProperty("vsync","true");
EngineConfigVar.setProperty("vkValidated","false");
EngineConfigVar.setProperty("Debug_Shaders","false");
EngineConfigVar.setProperty("ShaderRecompiling","true");
EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file");
Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath());
} catch (IOException excp2) {

View file

@ -2,7 +2,7 @@ package net.halbear.Terrain4J.EngineCore.Logic;
public interface GameLogic {
void cleanup();
void Initialise(EngineInstance engineInstance);
InitData Initialise(EngineInstance engineInstance);
void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds);
void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds);
}

View file

@ -0,0 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Logic;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import java.util.List;
public record InitData(List<ModelData> Models) {
}

View file

@ -1,6 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Logic;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.Surface;
@ -15,6 +16,7 @@ public class VulkanContext {
private Surface surface;
private final PhysicalDevice PhysDevice;
private SwapChain swapChain;
private final PipelineCache VkPipelineCache;
public VulkanContext(Window window){
var EngineGlobals = EngineConfig.getInstance();
@ -24,20 +26,21 @@ public class VulkanContext {
surface = new Surface(Instance, PhysDevice, window);
swapChain = new SwapChain(window, device, surface, EngineGlobals.GetRequestedImages(), EngineGlobals.GetVSYNC());
VulkanLayersOpen+=5;
VkPipelineCache = new PipelineCache(device);
}
public void cleanup(){
swapChain.cleanup(device);
surface.cleanup(Instance);
VkPipelineCache.CleanUp(device);
device.cleanup();
PhysDevice.cleanup();
Instance.cleanup();
Logger.debug("Render Thread Finished ending Vulkan Processes\nVulkan Layers Open:[{}]", VulkanContext.VulkanLayersOpen < 0 ? "All processes closed and vulkan instance removed" : VulkanContext.VulkanLayersOpen);
VulkanLayersOpen--;
}
public SwapChain GetSwapChain(){
return swapChain;
}
public PipelineCache GetVkPipelineCache(){return VkPipelineCache;}
public SwapChain GetSwapChain(){return swapChain;}
public Device GetDevice(){
return device;
}

View file

@ -2,6 +2,12 @@ package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MeshData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import java.util.ArrayList;
import java.util.List;
public class GameCore implements GameLogic {
@Override
@ -10,8 +16,19 @@ public class GameCore implements GameLogic {
}
@Override
public void Initialise(EngineInstance engineInstance) {
public InitData Initialise(EngineInstance engineInstance) {
var ModelID = "TriangleModel";
MeshData meshData = new MeshData("triangle-mesh", new float[]{
-0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f},
new int[]{0, 1, 2});
List<MeshData> MeshDataList = new ArrayList<>();
MeshDataList.add(meshData);
ModelData modelData = new ModelData(ModelID, MeshDataList);
List<ModelData> models = new ArrayList<>();
models.add(modelData);
return new InitData(models);
}
@Override

View file

@ -1,10 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import org.tinylog.Logger;
@ -24,9 +21,10 @@ public class PrimaryRuntime {
public PrimaryRuntime(String windowTitle, GameLogic appLogic) {
window = new Window(windowTitle);
InitData initData = appLogic.Initialise(engineInstance);
engineInstance = new EngineInstance(window, new Scene(window));
PrimaryThread = new MainThread(engineInstance, appLogic);
DrawingThread = new RenderThread(window, engineInstance);
DrawingThread = new RenderThread(window, engineInstance, initData);
Thread.currentThread().setName("Terrain4J Primary Runtime Thread");
}

View file

@ -4,6 +4,7 @@ import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene;
import org.tinylog.Logger;
@ -34,7 +35,6 @@ public class MainThread implements Runnable {
}
public MainThread(EngineInstance engineInstance, GameLogic appLogic){
this.gameLogic = appLogic;
gameLogic.Initialise(engineInstance);
Logger.debug("Starting Main Thread");
UpdateTickRateThreshold();
mainThread = new Thread(this);

View file

@ -4,6 +4,7 @@ import net.halbear.Terrain4J.EngineCore.Display.Render;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import org.tinylog.Logger;
@ -15,9 +16,10 @@ public class RenderThread implements Runnable {
private final Render render;
private Window window;
public RenderThread(Window window,EngineInstance engineInstance) {
public RenderThread(Window window, EngineInstance engineInstance, InitData initData) {
//this.window = window;
render = new Render(engineInstance);
render.Initialise(initData);
renderThread = new Thread(this);
renderThread.setName("Terrain4J Vulkan Render Thread");
}

View file

@ -0,0 +1,108 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import static org.lwjgl.vulkan.VK10.*;
import static org.lwjgl.vulkan.VK13.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
public class Pipeline {
private final long VulkanPipeline;
private final long VulkanPipelineLayout;
public Pipeline(VulkanContext VkCtx, PipelineBuildInfo BuildInfo){
Logger.debug("Creating Pipeline");
Device device = VkCtx.GetDevice();
try(var MemStack = MemoryStack.stackPush()){
LongBuffer longPtr = MemStack.mallocLong(1);
ByteBuffer main = MemStack.UTF8("main");
ShaderModule[] ShaderModules = BuildInfo.GetShaderModules();
int ModuleCount = ShaderModules.length;
var ShaderStages = VkPipelineShaderStageCreateInfo.calloc(ModuleCount, MemStack);
for(int i = 0; i < ModuleCount; i++){
ShaderModule shaderModule = ShaderModules[i];
ShaderStages.get(i)
.sType$Default()
.stage(shaderModule.GetShaderStage())
.module(shaderModule.GetHandle())
.pName(main);
}
var AssemblyStateCreateInfo = VkPipelineInputAssemblyStateCreateInfo.calloc(MemStack)
.sType$Default()
.topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
var ViewportCreateStateInfo = VkPipelineViewportStateCreateInfo.calloc(MemStack)
.sType$Default()
.viewportCount(1)
.scissorCount(1);
var RasterizationStateCreateInfo = VkPipelineRasterizationStateCreateInfo.calloc(MemStack)
.sType$Default()
.polygonMode(VK_POLYGON_MODE_FILL)
.cullMode(VK_CULL_MODE_NONE)
.frontFace(VK_FRONT_FACE_CLOCKWISE)
.lineWidth(1.0f);
var MultisampleStateCreateInfo = VkPipelineMultisampleStateCreateInfo.calloc(MemStack)
.sType$Default()
.rasterizationSamples(VK_SAMPLE_COUNT_1_BIT);
VkPipelineDynamicStateCreateInfo VulkanPipelineDynamicStateCreateInfo = VkPipelineDynamicStateCreateInfo.calloc(MemStack)
.sType(VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO)
.pDynamicStates(MemStack.ints(
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
));
var BlendAttributeState = VkPipelineColorBlendAttachmentState.calloc(1,MemStack)
.colorWriteMask(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT)
.blendEnable(false);
var ColourBlendState = VkPipelineColorBlendStateCreateInfo.calloc(MemStack)
.sType$Default()
.pAttachments(BlendAttributeState);
IntBuffer ColourFormats = MemStack.mallocInt(1);
ColourFormats.put(0,BuildInfo.GetColourFormat());
var RendererCreateInfo = VkPipelineRenderingCreateInfo.calloc(MemStack)
.sType$Default()
.colorAttachmentCount(1)
.pColorAttachmentFormats(ColourFormats);
var PipelineLayoutCreateInfoPtr = VkPipelineLayoutCreateInfo.calloc(MemStack)
.sType$Default();
VulkanUtils.vkCheck(vkCreatePipelineLayout(device.FetchVulkanDevice(), PipelineLayoutCreateInfoPtr,null,longPtr)
,"Unable to create new pipeline layout");
VulkanPipelineLayout = longPtr.get(0);
var PipelineCreateInfo = VkGraphicsPipelineCreateInfo.calloc(1,MemStack)
.sType$Default()
.renderPass(VK_NULL_HANDLE)
.pStages(ShaderStages)
.pVertexInputState(BuildInfo.GetVertexInputStateCreateInfo())
.pInputAssemblyState(AssemblyStateCreateInfo)
.pViewportState(ViewportCreateStateInfo)
.pRasterizationState(RasterizationStateCreateInfo)
.pColorBlendState(ColourBlendState)
.pMultisampleState(MultisampleStateCreateInfo)
.pDynamicState(VulkanPipelineDynamicStateCreateInfo)
.layout(VulkanPipelineLayout)
.pNext(RendererCreateInfo);
VulkanUtils.vkCheck(vkCreateGraphicsPipelines(device.FetchVulkanDevice(),
VkCtx.GetVkPipelineCache().GetVkPipelineCache(), PipelineCreateInfo,
null, longPtr),"Could not create new pipeline");
VulkanPipeline = longPtr.get(0);
}
}
public long GetVulkanPipeline(){return VulkanPipeline;}
public long GetVulkanPipelineLayout(){return VulkanPipelineLayout;}
public void CleanUp(VulkanContext VkCtx){
Logger.debug("destroying Pipeline");
VkDevice vkDevice = VkCtx.GetDevice().FetchVulkanDevice();
vkDestroyPipelineLayout(vkDevice,VulkanPipelineLayout,null);
vkDestroyPipeline(vkDevice,VulkanPipeline,null);
}
}

View file

@ -0,0 +1,18 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo;
public class PipelineBuildInfo {
private final int ColourFormat;
private final ShaderModule[] ShaderModules;
private final VkPipelineVertexInputStateCreateInfo VertexInput;
public PipelineBuildInfo(ShaderModule[] shaderModules, VkPipelineVertexInputStateCreateInfo VertexInput, int ColourFormat){
this.ColourFormat = ColourFormat;
this.ShaderModules = shaderModules;
this.VertexInput = VertexInput;
}
public int GetColourFormat(){return ColourFormat;}
public ShaderModule[] GetShaderModules(){return ShaderModules;}
public VkPipelineVertexInputStateCreateInfo GetVertexInputStateCreateInfo(){return VertexInput;}
}

View file

@ -0,0 +1,32 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkPipelineCacheCreateInfo;
import org.tinylog.Logger;
import java.nio.LongBuffer;
import static org.lwjgl.vulkan.VK13.vkCreatePipelineCache;
import static org.lwjgl.vulkan.VK13.vkDestroyPipelineCache;
public class PipelineCache {
private final long VulkanPipelineCache;
public PipelineCache(Device device){
Logger.debug("Creating new Vk Pipeline Cache");
try(var MemStack = MemoryStack.stackPush()){
var PipelineCacheCreateInfo = VkPipelineCacheCreateInfo.calloc(MemStack).sType$Default();
LongBuffer longPtr = MemStack.mallocLong(1);
VulkanUtils.vkCheck(vkCreatePipelineCache(device.FetchVulkanDevice(), PipelineCacheCreateInfo
,null, longPtr),"Error Creating New Pipeline Cache");
VulkanPipelineCache = longPtr.get(0);
}
}
public void CleanUp(Device device){
Logger.debug("Destroying Vulkan Pipeline Cache");
vkDestroyPipelineCache(device.FetchVulkanDevice(), VulkanPipelineCache, null);
}
public long GetVkPipelineCache(){return VulkanPipelineCache;}
}

View file

@ -0,0 +1,68 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import org.lwjgl.util.shaderc.Shaderc;
import org.tinylog.Logger;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
public class ShaderCompiler {
private ShaderCompiler(){}
public static byte[] CompileShader(String ShaderCode, int ShaderType, boolean HLSLshader){
long Compiler = 0;
long Options = 0;
byte[] CompiledShader;
try{
Compiler = Shaderc.shaderc_compiler_initialize();
Options = Shaderc.shaderc_compile_options_initialize();
if(EngineConfig.getInstance().DebugShaders()){
Shaderc.shaderc_compile_options_set_generate_debug_info(Options);
Shaderc.shaderc_compile_options_set_optimization_level(Options,0);
}
Shaderc.shaderc_compile_options_set_source_language(Options, HLSLshader ? Shaderc.shaderc_source_language_hlsl : Shaderc.shaderc_source_language_glsl);
long CompilationResult = Shaderc.shaderc_compile_into_spv(
Compiler,
ShaderCode,
ShaderType,
HLSLshader ? "shader.hlsl" : "shader.glsl",
"main",
Options
);
if(Shaderc.shaderc_result_get_compilation_status(CompilationResult) !=
Shaderc.shaderc_compilation_status_success){
throw new RuntimeException("Failed to Compile Shader: " + Shaderc.shaderc_result_get_error_message(CompilationResult));
}
ByteBuffer buffer = Shaderc.shaderc_result_get_bytes(CompilationResult);
assert buffer != null;
CompiledShader = new byte[buffer.remaining()];
buffer.get(CompiledShader);
} finally{
Shaderc.shaderc_compile_options_release(Options);
Shaderc.shaderc_compiler_release(Compiler);
}
return CompiledShader;
}
public static void CompileGLSLShaderOnChange(String ShaderFile, int ShaderType){
byte[] CompiledShader;
try{
var glslFile = new File(ShaderFile);
var spvFile = new File(ShaderFile + ".spv");
if(!spvFile.exists() || glslFile.lastModified() > spvFile.lastModified()){
Logger.debug("Compiling new [{}] shader from [{}] source",spvFile.getPath(),glslFile.getPath());
var ShaderCode = new String(Files.readAllBytes(glslFile.toPath()));
CompiledShader = CompileShader(ShaderCode, ShaderType, false);
Files.write(spvFile.toPath(), CompiledShader);
} else{
Logger.debug("Loading Compiled Shader [{}]",spvFile.getPath());
}
} catch(IOException exception){
Logger.error(exception);
throw new RuntimeException(exception);
}
}
}

View file

@ -0,0 +1,51 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkShaderModuleCreateInfo;
import static org.lwjgl.vulkan.VK13.vkCreateShaderModule;
import static org.lwjgl.vulkan.VK13.vkDestroyShaderModule;
import org.tinylog.Logger;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.nio.file.Files;
public class ShaderModule {
private final long Handle;
private final int ShaderStage;
public ShaderModule(VulkanContext VkCtx, int ShaderStage, String ShaderSPVFile){
try{
byte[] Contents = Files.readAllBytes(new File(ShaderSPVFile).toPath());
Handle = CreateShaderModule(VkCtx, Contents);
this.ShaderStage = ShaderStage;
} catch(IOException exception){
Logger.error("Cannot Read Shader Files",exception);
throw new RuntimeException(exception);
}
}
private static long CreateShaderModule(VulkanContext VkCtx, byte[] Code){
try(var MemStack = MemoryStack.stackPush()){
ByteBuffer CodePointer = MemStack.malloc(Code.length).put(0,Code);
var ShaderModuleCreateInfo = VkShaderModuleCreateInfo.calloc(MemStack)
.sType$Default()
.pCode(CodePointer);
LongBuffer LongPoiner = MemStack.mallocLong(1);
VulkanUtils.vkCheck(vkCreateShaderModule(VkCtx.GetDevice().FetchVulkanDevice(), ShaderModuleCreateInfo, null, LongPoiner),"Failed to create a new Shader Module");
return LongPoiner.get(0);
}
}
public void CleanUp(VulkanContext VkCtx){
vkDestroyShaderModule(VkCtx.GetDevice().FetchVulkanDevice(), Handle, null);
}
public long GetHandle(){return Handle;}
public int GetShaderStage(){return ShaderStage;}
}

View file

@ -3,12 +3,23 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;
import net.halbear.Terrain4J.EngineCore.Display.Render;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Pipeline;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineBuildInfo;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderCompiler;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VertexBufferStructure;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelsCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanMesh;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanModel;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.util.shaderc.Shaderc;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;
import java.nio.LongBuffer;
import java.util.Arrays;
import static org.lwjgl.vulkan.KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
@ -25,12 +36,40 @@ public class SceneRender { //dynamic rendering;
private boolean Rb = false;
private boolean Gb = true;
private boolean Bb = false;
private static final String FRAGMENT_SHADER_FILE_GLSL = "resources/shaders/scene_fragment.glsl";
private static final String FRAGMENT_SHADER_FILE_SPV = FRAGMENT_SHADER_FILE_GLSL + ".spv";
private static final String VERTEX_SHADER_FILE_GLSL = "resources/shaders/scene_vertex.glsl";
private static final String VERTEX_SHADER_FILE_SPV = VERTEX_SHADER_FILE_GLSL + ".spv";
private final Pipeline VkPipeline;
public SceneRender(VulkanContext vulkanContext){
ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f));
AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour);
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour);
}
ShaderModule[] shaderModules = SceneRender.CreateShaderModules(vulkanContext);
VkPipeline = CreatePipeline(vulkanContext, shaderModules);
Arrays.asList(shaderModules).forEach(shaderModule -> shaderModule.CleanUp(vulkanContext));
}
private static ShaderModule[] CreateShaderModules(VulkanContext VkCtx){
if(EngineConfig.getInstance().RecompileShaders()){
ShaderCompiler.CompileGLSLShaderOnChange(VERTEX_SHADER_FILE_GLSL, Shaderc.shaderc_glsl_vertex_shader);
ShaderCompiler.CompileGLSLShaderOnChange(FRAGMENT_SHADER_FILE_GLSL, Shaderc.shaderc_fragment_shader);
}
return new ShaderModule[]{
new ShaderModule(VkCtx, VK_SHADER_STAGE_VERTEX_BIT, VERTEX_SHADER_FILE_SPV),
new ShaderModule(VkCtx, VK_SHADER_STAGE_FRAGMENT_BIT, FRAGMENT_SHADER_FILE_SPV)
};
}
private static Pipeline CreatePipeline(VulkanContext VkCtx, ShaderModule[] ShaderModules){
var vertexBufferStructure = new VertexBufferStructure();
var BuildInfo = new PipelineBuildInfo(ShaderModules,vertexBufferStructure.getVertexInput(),
VkCtx.GetSurface().GetSurfaceFormat().ImageFormat());
var pipeline = new Pipeline(VkCtx, BuildInfo);
vertexBufferStructure.cleanup();
return pipeline;
}
public void UpdateColours(VulkanContext vulkanContext){
if (Gb){
if (G < 1.0f && Math.random() <0.5) G+=0.0005f;
@ -58,7 +97,7 @@ public class SceneRender { //dynamic rendering;
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour);
}
public void Render(VulkanContext vulkanContext, CommandBuffer commandBuffer, int ImageIndex){
public void Render(VulkanContext vulkanContext, CommandBuffer commandBuffer,ModelsCache modelsCache, int ImageIndex){
UpdateColours(vulkanContext);
try(var MemStack = MemoryStack.stackPush()){
SwapChain swapChain = vulkanContext.GetSwapChain();
@ -70,6 +109,35 @@ public class SceneRender { //dynamic rendering;
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_2_NONE, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
vkCmdBeginRendering(CommandHandle, RenderInfo[ImageIndex]);
vkCmdBindPipeline(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, VkPipeline.GetVulkanPipeline());
VkExtent2D swapChainExtent = swapChain.GetSwapChainExtent();
int width = swapChainExtent.width();
int height = swapChainExtent.height();
var Viewport = VkViewport.calloc(1,MemStack)
.x(0)
.y(height)
.height(-height)
.width(width)
.minDepth(0.0F)
.maxDepth(1.0F);
vkCmdSetViewport(CommandHandle, 0, Viewport);
var Scissor = VkRect2D.calloc(1,MemStack)
.extent(it -> it.width(width).height(height))
.offset(it->it.x(0).y(0));
vkCmdSetScissor(CommandHandle, 0, Scissor);
LongBuffer offsets = MemStack.mallocLong(1).put(0,0L);
LongBuffer vertexBuffer = MemStack.mallocLong(1);
var VulkanModels = modelsCache.GetModelMap().values();
for(VulkanModel VkModel : VulkanModels){
for(VulkanMesh mesh : VkModel.GetVkMeshList()){
vertexBuffer.put(0,mesh.VerticesBuffer().GetBuffer());
vkCmdBindVertexBuffers(CommandHandle,0,vertexBuffer,offsets);
vkCmdBindIndexBuffer(CommandHandle,mesh.IndicesBuffer().GetBuffer(),0,VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(CommandHandle,mesh.IndicesCount(),1,0,0,0);
}
}
vkCmdEndRendering(CommandHandle);
VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage,
@ -113,9 +181,11 @@ public class SceneRender { //dynamic rendering;
}
return result;
}
public void cleanup(){
public void cleanup(VulkanContext VkCtx){
VkPipeline.CleanUp(VkCtx);
Arrays.asList(RenderInfo).forEach(VkRenderingInfo::free);
Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
ClearValueColour.free();
}
}

View file

@ -1,6 +1,13 @@
#Default Window dimensions
#Application Properties
#Title that shows up on app window
Software_Title=Terrain4J Game Engine
Software_Icon=ProgramIcon.png
Software_Icon_Path=/WindowResources/Icon/
#Launch Window dimensions
display_height=480
display_width=640
#Engine Core Settings
#the precision of frames, set this to 16000 and it will limit the whole engine to 64FPS
frame_accuracy=0
#event tick rate for game process
@ -8,15 +15,16 @@ master_tick_rate=120
main_thread_tickrate=120
fast_thread_tickrate=240
cap_main_to_tickrate=false
#Renderer Properties
Debug_Shaders = false
ShaderRecompiling = true
#Vulkan Debugging toggle
vkValidated=true
vsync=false
#Vsync images
RequestedImages=3
#This is the name of the GPU you want the engine to use
PhysicalDeviceName=
#Title that shows up on app window
Software_Title=Terrain4J Game Engine
Software_Icon=ProgramIcon.png
Software_Icon_Path=/WindowResources/Icon/