Engine architecture + Multithreading
This commit is contained in:
parent
44845f781d
commit
d15d1a75af
8 changed files with 223 additions and 42 deletions
|
|
@ -57,6 +57,7 @@ public class Window {
|
|||
glfwFreeCallbacks(handle);
|
||||
glfwDestroyWindow(handle);
|
||||
glfwTerminate();
|
||||
Logger.debug("Destroyed Window");
|
||||
}
|
||||
public long getHandle(){
|
||||
return handle;
|
||||
|
|
@ -78,7 +79,7 @@ public class Window {
|
|||
EngineConfig.RENDERFRAMES++;
|
||||
}
|
||||
public void UpdateWindowTitle(){
|
||||
if (displayFPS) glfwSetWindowTitle(handle, EngineConfig.SOFTWARE_TITLE + " - FPS: " + EngineConfig.FrameRate_PRIMARYTHREAD + " TPS: " + EngineConfig.TPS + " RenderFPS: " + EngineConfig.FrameRate_RENDER + " VSYNC: " + EngineConfig.VSYNC);
|
||||
if (displayFPS) glfwSetWindowTitle(handle, EngineConfig.SOFTWARE_TITLE + " - FPS: " + EngineConfig.FrameRate_PRIMARYTHREAD + " TPS: " + EngineConfig.TPS + " RenderFPS: " + EngineConfig.FrameRate_RENDERTHREAD + " VSYNC: " + EngineConfig.VSYNC);
|
||||
}
|
||||
public void SetVsync(){
|
||||
Logger.info("Vsync Swap");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package net.halbear.Terrain4J.EngineCore.Logic;
|
||||
|
||||
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
|
||||
import org.tinylog.Logger;
|
||||
import org.tinylog.core.TinylogLoggingConfiguration;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
|
|
@ -18,18 +18,21 @@ public class EngineConfig {
|
|||
private static EngineConfig instance;
|
||||
private long accuracy;
|
||||
private int TickRate;
|
||||
private int MainTickRate;
|
||||
private int FastTickRate;
|
||||
private boolean CapMainToTickRate;
|
||||
private int Width;
|
||||
private int Height;
|
||||
public static int FrameRate_PRIMARYTHREAD =0;
|
||||
public static int TPS =0;
|
||||
public static int FrameRate_MAINENGINETHREAD =0;
|
||||
public static int FrameRate_RENDER =0;
|
||||
public static int FrameRate_RENDERTHREAD =0;
|
||||
public static int FrameRate_WINDOW =0;
|
||||
public static int RENDERFRAMES =0;
|
||||
public static boolean VSYNC = false;
|
||||
private boolean vkValidated;
|
||||
private String PhysicalDeviceName;
|
||||
|
||||
|
||||
private EngineConfig() {
|
||||
var EngineConfigVar = new Properties();
|
||||
Path path = Paths.get("");
|
||||
|
|
@ -68,10 +71,13 @@ public class EngineConfig {
|
|||
}
|
||||
}
|
||||
if(SuccessfulLoad) {
|
||||
CapMainToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_main_to_tickrate", "false").toString());
|
||||
MainTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("main_thread_tickrate", "120").toString());
|
||||
FastTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("fast_thread_tickrate", "240").toString());
|
||||
SOFTWARE_TITLE = EngineConfigVar.getOrDefault("Software_Title", "Terrain4J").toString();
|
||||
PhysicalDeviceName = EngineConfigVar.getOrDefault("PhysicalDeviceName", "").toString();
|
||||
accuracy = Long.parseLong(EngineConfigVar.getOrDefault("frame_accuracy", DEFAULT_ACCURACY).toString());
|
||||
TickRate = Integer.parseInt(EngineConfigVar.getOrDefault("main_tick_rate", DEFAULT_TICKRATE).toString());
|
||||
TickRate = Integer.parseInt(EngineConfigVar.getOrDefault("master_tick_rate", DEFAULT_TICKRATE).toString());
|
||||
Width = Integer.parseInt(EngineConfigVar.getOrDefault("display_width", "1280").toString());
|
||||
Height = Integer.parseInt(EngineConfigVar.getOrDefault("display_height", "720").toString());
|
||||
VSYNC = Boolean.parseBoolean(EngineConfigVar.getOrDefault("vsync", false).toString());
|
||||
|
|
@ -80,10 +86,13 @@ public class EngineConfig {
|
|||
}
|
||||
else if(!ConfigFile.exists()){
|
||||
try {
|
||||
EngineConfigVar.setProperty("cap_main_to_tickrate","false");
|
||||
EngineConfigVar.setProperty("main_thread_tickrate","120");
|
||||
EngineConfigVar.setProperty("fast_thread_tickrate","240");
|
||||
EngineConfigVar.setProperty("Software_Title","Terrain4J Software");
|
||||
EngineConfigVar.setProperty("PhysicalDeviceName",null);
|
||||
EngineConfigVar.setProperty("frame_accuracy", String.valueOf(DEFAULT_ACCURACY));
|
||||
EngineConfigVar.setProperty("main_tick_rate", String.valueOf(DEFAULT_TICKRATE));
|
||||
EngineConfigVar.setProperty("master_tick_rate", String.valueOf(DEFAULT_TICKRATE));
|
||||
EngineConfigVar.setProperty("display_width","1280");
|
||||
EngineConfigVar.setProperty("display_height","720");
|
||||
EngineConfigVar.setProperty("vsync","true");
|
||||
|
|
@ -115,6 +124,9 @@ public class EngineConfig {
|
|||
public int getHeight(){
|
||||
return Height;
|
||||
}
|
||||
public boolean GetMainTickCap(){return CapMainToTickRate;}
|
||||
public int GetMainTickrate(){return MainTickRate;}
|
||||
public int GetFastTickrate(){return FastTickRate;}
|
||||
public long getAccuracy() {
|
||||
return accuracy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,30 @@
|
|||
package net.halbear.Terrain4J.EngineCore.Main;
|
||||
|
||||
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.GameLogic;
|
||||
import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
|
||||
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
public class PrimaryRuntime {
|
||||
private final EngineInstance engineInstance;
|
||||
private final GameLogic gameLogic;
|
||||
private final Render render;
|
||||
private Window window;
|
||||
private int Frames = 0;
|
||||
private int TickFrames = 0;
|
||||
private double TickRate = 0;
|
||||
private static MainThread PrimaryThread;
|
||||
private static RenderThread DrawingThread;
|
||||
private static boolean running = true;
|
||||
private static Window window;
|
||||
private static EngineInstance engineInstance;
|
||||
|
||||
public PrimaryRuntime(String windowTitle, GameLogic appLogic) {
|
||||
this.gameLogic = appLogic;
|
||||
window = new Window(windowTitle);
|
||||
engineInstance = new EngineInstance(window, new Scene(window));
|
||||
render = new Render(engineInstance);
|
||||
gameLogic.Initialise(engineInstance);
|
||||
}
|
||||
private void cleanup() {
|
||||
gameLogic.cleanup();
|
||||
render.cleanup();
|
||||
engineInstance.cleanup();
|
||||
}
|
||||
public EngineInstance getEngineInstance() {
|
||||
return engineInstance;
|
||||
PrimaryThread = new MainThread(engineInstance, appLogic);
|
||||
DrawingThread = new RenderThread(window, engineInstance);
|
||||
}
|
||||
|
||||
public void UpdateTickRate(){
|
||||
TickRate = (double) 1000000000 / (double) EngineConfig.getInstance().getTickRate();
|
||||
}
|
||||
|
|
@ -40,41 +34,56 @@ public class PrimaryRuntime {
|
|||
double FrameDiff =0;
|
||||
double TickDiff =0;
|
||||
double SecondCounter =0;
|
||||
double tickDiff = 0;
|
||||
double cycleDiff = 0;
|
||||
PrimaryThread.StartMainThread();
|
||||
DrawingThread.StartRenderThread();
|
||||
TickRate = (double) 1000000000 / (double) EngineConfig.getInstance().getTickRate();
|
||||
long FrameAccuracy = EngineConfig.getInstance().getAccuracy();
|
||||
Logger.debug("Primary Thread Started");
|
||||
while (!window.shouldClose()){
|
||||
long now = System.nanoTime();
|
||||
tickDiff = now - InitialTime;
|
||||
FrameDiff += tickDiff;
|
||||
TickDiff += tickDiff;
|
||||
SecondCounter += tickDiff;
|
||||
if (FrameDiff >= EngineConfig.getInstance().getAccuracy()){
|
||||
cycleDiff = now - InitialTime;
|
||||
FrameDiff += cycleDiff;
|
||||
TickDiff += cycleDiff;
|
||||
SecondCounter += cycleDiff;
|
||||
if (FrameDiff >= FrameAccuracy){
|
||||
window.pollEvents();
|
||||
gameLogic.Input(engineInstance, (now - InitialTime));
|
||||
Frames++;
|
||||
if (TickDiff >= TickRate) {
|
||||
TickFrames++;
|
||||
long nsTimeDiff = now - updateTime;
|
||||
gameLogic.Update(engineInstance, nsTimeDiff);
|
||||
updateTime = now;
|
||||
TickDiff = 0;
|
||||
}
|
||||
if (SecondCounter >= 1000000000){
|
||||
SecondCounter = 0;
|
||||
EngineConfig.FrameRate_WINDOW = EngineConfig.RENDERFRAMES;
|
||||
EngineConfig.FrameRate_PRIMARYTHREAD = Frames;
|
||||
EngineConfig.FrameRate_RENDER = EngineConfig.RENDERFRAMES;
|
||||
EngineConfig.TPS = TickFrames;
|
||||
Logger.info("Primary Thread FPS: " + Frames + " TPS: " + TickFrames + " Window Draw FPS: " + EngineConfig.RENDERFRAMES);
|
||||
Frames = 0;
|
||||
TickFrames = 0;
|
||||
EngineConfig.RENDERFRAMES = 0;
|
||||
Logger.info("FPS: " + EngineConfig.FrameRate_PRIMARYTHREAD + " TPS: " + EngineConfig.TPS + " RenderFPS: " + EngineConfig.FrameRate_RENDER);
|
||||
}
|
||||
render.render(engineInstance);
|
||||
FrameDiff = 0;
|
||||
}
|
||||
InitialTime = now;
|
||||
}
|
||||
PrimaryThread.KillThread();
|
||||
DrawingThread.KillThread();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
public static EngineInstance GetEngineInstance(){
|
||||
return engineInstance;
|
||||
}
|
||||
public static void CloseRuntime(){
|
||||
running = false;
|
||||
}
|
||||
private void cleanup() {
|
||||
engineInstance.cleanup();
|
||||
window.cleanup();
|
||||
Logger.debug("Closing Primary Thread");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
package net.halbear.Terrain4J.EngineCore.Threads;
|
||||
|
||||
public class FastTickThread {
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package net.halbear.Terrain4J.EngineCore.Threads;
|
||||
|
||||
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.Main.PrimaryRuntime;
|
||||
import net.halbear.Terrain4J.EngineCore.Main.Scene;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
public class MainThread implements Runnable {
|
||||
private static boolean CappedToTickRate;
|
||||
private static int TickRate;
|
||||
private static double TickThreshold;
|
||||
private static boolean running = true;
|
||||
private final GameLogic gameLogic;
|
||||
private int Frames = 0;
|
||||
private int TickFrames = 0;
|
||||
private final Thread mainThread;
|
||||
|
||||
public void KillThread(){
|
||||
running = false;
|
||||
}
|
||||
public void cleanup(){
|
||||
gameLogic.cleanup();
|
||||
Logger.debug("Closing Main Thread");
|
||||
}
|
||||
public Thread FetchMainThread(){
|
||||
return mainThread;
|
||||
}
|
||||
public void StartMainThread(){
|
||||
mainThread.start();
|
||||
}
|
||||
public MainThread(EngineInstance engineInstance, GameLogic appLogic){
|
||||
this.gameLogic = appLogic;
|
||||
gameLogic.Initialise(engineInstance);
|
||||
Logger.debug("Starting Main Thread");
|
||||
UpdateTickRateThreshold();
|
||||
mainThread = new Thread(this);
|
||||
}
|
||||
public void UpdateTickRateThreshold(){
|
||||
CappedToTickRate = EngineConfig.getInstance().GetMainTickCap();
|
||||
TickRate = EngineConfig.getInstance().GetMainTickrate();
|
||||
if (CappedToTickRate){
|
||||
TickThreshold = (double) 1000000000 / (double) TickRate;
|
||||
} else{
|
||||
TickThreshold = 0;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
long InitialTime = System.nanoTime();
|
||||
long updateTime = InitialTime;
|
||||
double FrameDiff =0;
|
||||
double TickDiff =0;
|
||||
double SecondCounter =0;
|
||||
double cycleDiff = 0;
|
||||
Logger.debug("Main Thread Started");
|
||||
long EngineAccuracy = EngineConfig.getInstance().getAccuracy();
|
||||
while (running){
|
||||
long now = System.nanoTime();
|
||||
cycleDiff = now - InitialTime;
|
||||
FrameDiff += cycleDiff;
|
||||
TickDiff += cycleDiff;
|
||||
SecondCounter += cycleDiff;
|
||||
if (FrameDiff >= EngineAccuracy){
|
||||
gameLogic.Input(PrimaryRuntime.GetEngineInstance(), (now - InitialTime));
|
||||
Frames++;
|
||||
if (TickDiff >= TickThreshold) {
|
||||
TickFrames++;
|
||||
long nsTimeDiff = now - updateTime;
|
||||
gameLogic.Update(PrimaryRuntime.GetEngineInstance(), nsTimeDiff);
|
||||
updateTime = now;
|
||||
TickDiff = 0;
|
||||
}
|
||||
if (SecondCounter >= 1000000000){
|
||||
SecondCounter = 0;
|
||||
EngineConfig.FrameRate_MAINENGINETHREAD = Frames;
|
||||
Logger.info("Main Thread FPS: " + EngineConfig.FrameRate_MAINENGINETHREAD + " TPS: " + TickFrames);
|
||||
Frames = 0;
|
||||
TickFrames = 0;
|
||||
}
|
||||
FrameDiff = 0;
|
||||
}
|
||||
InitialTime = now;
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.halbear.Terrain4J.EngineCore.Threads;
|
||||
|
||||
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.Main.PrimaryRuntime;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
public class RenderThread implements Runnable {
|
||||
private static boolean running = true;
|
||||
private int Frames = 0;
|
||||
private final Thread renderThread;
|
||||
private final Render render;
|
||||
private Window window;
|
||||
|
||||
public RenderThread(Window window,EngineInstance engineInstance) {
|
||||
//this.window = window;
|
||||
render = new Render(engineInstance);
|
||||
renderThread = new Thread(this);
|
||||
}
|
||||
|
||||
public void KillThread(){
|
||||
running = false;
|
||||
}
|
||||
public Thread FetchRenderThread(){
|
||||
return renderThread;
|
||||
}
|
||||
public void StartRenderThread(){
|
||||
renderThread.start();
|
||||
}
|
||||
private void cleanup() {
|
||||
render.cleanup();
|
||||
Logger.debug("Closing Render Thread");
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
long InitialTime = System.nanoTime();
|
||||
double FrameDiff =0;
|
||||
double SecondCounter =0;
|
||||
double cycleDiff = 0;
|
||||
long FrameAccuracy = EngineConfig.getInstance().getAccuracy();
|
||||
Logger.debug("Render Thread Started");
|
||||
while (running){
|
||||
long now = System.nanoTime();
|
||||
cycleDiff = now - InitialTime;
|
||||
FrameDiff += cycleDiff;
|
||||
SecondCounter += cycleDiff;
|
||||
if (FrameDiff >= FrameAccuracy){
|
||||
Frames++;
|
||||
if (SecondCounter >= 1000000000){
|
||||
SecondCounter = 0;
|
||||
EngineConfig.FrameRate_RENDERTHREAD = Frames;
|
||||
Logger.info("Render FPS: " + Frames);
|
||||
Frames = 0;
|
||||
}
|
||||
render.render(PrimaryRuntime.GetEngineInstance());
|
||||
FrameDiff = 0;
|
||||
}
|
||||
InitialTime = now;
|
||||
}
|
||||
//PrimaryRuntime.CloseRuntime();
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
#created new properties file
|
||||
#Tue Jan 20 08:16:32 GMT 2026
|
||||
#Default Window dimensions
|
||||
display_height=480
|
||||
display_width=640
|
||||
#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
|
||||
main_tick_rate=120
|
||||
master_tick_rate=120
|
||||
main_thread_tickrate=120
|
||||
fast_thread_tickrate=240
|
||||
cap_main_to_tickrate=false
|
||||
#Vulkan Debugging toggle
|
||||
vkValidated=true
|
||||
|
||||
vsync=false
|
||||
vsync=true
|
||||
#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_Title=Terrain4J Game Engine
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
#created new properties file
|
||||
#Tue Jan 20 08:16:32 GMT 2026
|
||||
#Default Window dimensions
|
||||
display_height=480
|
||||
display_width=640
|
||||
#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
|
||||
main_tick_rate=120
|
||||
master_tick_rate=120
|
||||
main_thread_tickrate=120
|
||||
fast_thread_tickrate=240
|
||||
cap_main_to_tickrate=true
|
||||
#Vulkan Debugging toggle
|
||||
vkValidated=true
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue