Added throttling outside of window focus and update Engine threads to extend a generic thread

This commit is contained in:
Halbear 2026-05-16 18:03:20 +01:00
parent 2f626ba00f
commit 71d3cd400b
11 changed files with 325 additions and 155 deletions

View file

@ -3,8 +3,10 @@ package net.halbear.Terrain4J.EngineCore.Display;
import net.halbear.Terrain4J.EngineCore.Input.KeyboardInput; import net.halbear.Terrain4J.EngineCore.Input.KeyboardInput;
import net.halbear.Terrain4J.EngineCore.Input.MouseListener; import net.halbear.Terrain4J.EngineCore.Input.MouseListener;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import org.lwjgl.glfw.GLFWImage; import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.glfw.GLFWWindowFocusCallback;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import org.tinylog.Logger; import org.tinylog.Logger;
@ -68,7 +70,19 @@ public class Window {
IconBuffer.free(); //free the buffer once icon is set so when the program closes there isn't memory still floating around IconBuffer.free(); //free the buffer once icon is set so when the program closes there isn't memory still floating around
Logger.debug("Freeing icon Buffer"); Logger.debug("Freeing icon Buffer");
glfwSetWindowFocusCallback(handle, new GLFWWindowFocusCallback() {
@Override
public void invoke(long window, boolean focused) {
if(EngineConfig.getInstance().IsEngineThrottledOnUnfocus()) {
if (focused) {
EngineConfig.getInstance().SetEngineThrottle(false);
} else {
EngineConfig.getInstance().SetEngineThrottle(true);
}
}
PrimaryRuntime.UpdateFrameAccuracy();
}
});
glfwMakeContextCurrent(handle); glfwMakeContextCurrent(handle);
if (EngineConfig.VSYNC) glfwSwapInterval(1); if (EngineConfig.VSYNC) glfwSwapInterval(1);
else glfwSwapInterval(0); else glfwSwapInterval(0);

View file

@ -21,35 +21,29 @@ public class EngineConfig {
private int MainTickRate; private int MainTickRate;
private int FastTickRate; private int FastTickRate;
private boolean CapMainToTickRate; private boolean CapMainToTickRate;
private boolean CapFastToTickRate;
private boolean CapPrimaryToTickRate;
private int Width; private int Width;
private int Height; private int Height;
public static int FrameRate_PRIMARYTHREAD =0; public static int FrameRate_PRIMARYTHREAD =0;
public static int TPS =0; public static int TPS =0;
public static int FrameRate_MAINENGINETHREAD =0; public static int FrameRate_MAINENGINETHREAD =0;
public static int FrameRate_FASTENGINETHREAD =0;
public static int FrameRate_RENDERTHREAD =0; public static int FrameRate_RENDERTHREAD =0;
public static int FrameRate_WINDOW =0; public static int FrameRate_WINDOW =0;
public static int RENDERFRAMES =0; public static int RENDERFRAMES =0;
public static boolean VSYNC = false; public static boolean VSYNC = false;
public static boolean ShaderDebug = false; public static boolean ShaderDebug = false;
public static boolean RecompileShaders = false; public static boolean RecompileShaders = false;
private static long ThrottleAccuracy = 100000;
private static boolean ThrottleEngine = false;
private static boolean ThrottleEngineOnUnfocus = true;
private boolean vkValidated; private boolean vkValidated;
private String PhysicalDeviceName; private String PhysicalDeviceName;
private int RequestedImages; private int RequestedImages;
private String IconPath = "/WindowResources/Icon/"; private String IconPath = "/WindowResources/Icon/";
private String IconName = "ProgramIcon.png"; private String IconName = "ProgramIcon.png";
public int GetRequestedImages(){
return RequestedImages;
}
public boolean GetVSYNC(){
return VSYNC;
}
public boolean DebugShaders(){
return ShaderDebug;
}
public boolean RecompileShaders(){
return RecompileShaders;
}
private EngineConfig() { private EngineConfig() {
var EngineConfigVar = new Properties(); var EngineConfigVar = new Properties();
Path path = Paths.get(""); Path path = Paths.get("");
@ -90,12 +84,16 @@ public class EngineConfig {
if(SuccessfulLoad) { if(SuccessfulLoad) {
IconName = (EngineConfigVar.getOrDefault("Software_Icon", "ProgramIcon.png").toString()); IconName = (EngineConfigVar.getOrDefault("Software_Icon", "ProgramIcon.png").toString());
IconPath = (EngineConfigVar.getOrDefault("Software_Icon_Path", "/WindowResources/Icon/").toString()); IconPath = (EngineConfigVar.getOrDefault("Software_Icon_Path", "/WindowResources/Icon/").toString());
CapMainToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_main_to_tickrate", "false").toString()); CapMainToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_main_to_tickrate", true).toString());
CapFastToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_fast_to_tickrate", true).toString());
CapPrimaryToTickRate = Boolean.parseBoolean(EngineConfigVar.getOrDefault("cap_master_to_tickrate", false).toString());
MainTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("main_thread_tickrate", "120").toString()); MainTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("main_thread_tickrate", "120").toString());
FastTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("fast_thread_tickrate", "240").toString()); FastTickRate = Integer.parseInt(EngineConfigVar.getOrDefault("fast_thread_tickrate", "240").toString());
SOFTWARE_TITLE = EngineConfigVar.getOrDefault("Software_Title", "Terrain4J").toString(); SOFTWARE_TITLE = EngineConfigVar.getOrDefault("Software_Title", "Terrain4J").toString();
PhysicalDeviceName = EngineConfigVar.getOrDefault("PhysicalDeviceName", "").toString(); PhysicalDeviceName = EngineConfigVar.getOrDefault("PhysicalDeviceName", "").toString();
accuracy = Long.parseLong(EngineConfigVar.getOrDefault("frame_accuracy", DEFAULT_ACCURACY).toString()); accuracy = Long.parseLong(EngineConfigVar.getOrDefault("frame_accuracy", DEFAULT_ACCURACY).toString());
ThrottleAccuracy = Long.parseLong(EngineConfigVar.getOrDefault("throttle_accuracy", DEFAULT_ACCURACY).toString());
ThrottleEngineOnUnfocus = Boolean.parseBoolean(EngineConfigVar.getOrDefault("throttle_on_unfocus", true).toString());
TickRate = Integer.parseInt(EngineConfigVar.getOrDefault("master_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()); Width = Integer.parseInt(EngineConfigVar.getOrDefault("display_width", "1280").toString());
Height = Integer.parseInt(EngineConfigVar.getOrDefault("display_height", "720").toString()); Height = Integer.parseInt(EngineConfigVar.getOrDefault("display_height", "720").toString());
@ -110,7 +108,9 @@ public class EngineConfig {
try { try {
EngineConfigVar.setProperty("Software_Icon","ProgramIcon.png"); EngineConfigVar.setProperty("Software_Icon","ProgramIcon.png");
EngineConfigVar.setProperty("Software_Icon_Path","/WindowResources/Icon/"); EngineConfigVar.setProperty("Software_Icon_Path","/WindowResources/Icon/");
EngineConfigVar.setProperty("cap_main_to_tickrate","false"); EngineConfigVar.setProperty("cap_master_to_tickrate","false");
EngineConfigVar.setProperty("cap_main_to_tickrate","true");
EngineConfigVar.setProperty("cap_fast_to_tickrate","true");
EngineConfigVar.setProperty("main_thread_tickrate","120"); EngineConfigVar.setProperty("main_thread_tickrate","120");
EngineConfigVar.setProperty("fast_thread_tickrate","240"); EngineConfigVar.setProperty("fast_thread_tickrate","240");
EngineConfigVar.setProperty("Software_Title","Terrain4J Software"); EngineConfigVar.setProperty("Software_Title","Terrain4J Software");
@ -123,6 +123,8 @@ public class EngineConfig {
EngineConfigVar.setProperty("vkValidated","false"); EngineConfigVar.setProperty("vkValidated","false");
EngineConfigVar.setProperty("Debug_Shaders","false"); EngineConfigVar.setProperty("Debug_Shaders","false");
EngineConfigVar.setProperty("ShaderRecompiling","true"); EngineConfigVar.setProperty("ShaderRecompiling","true");
EngineConfigVar.setProperty("throttle_accuracy","100000");
EngineConfigVar.setProperty("throttle_on_unfocus","true");
EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file"); EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file");
Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath()); Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath());
} catch (IOException excp2) { } catch (IOException excp2) {
@ -133,17 +135,33 @@ public class EngineConfig {
} }
} }
public boolean VkValidated(){
return vkValidated;
}
public static synchronized EngineConfig getInstance() { public static synchronized EngineConfig getInstance() {
if (instance == null) { if (instance == null) {instance = new EngineConfig();}
instance = new EngineConfig();
}
return instance; return instance;
} }
public void SetEngineThrottle(boolean isThrottling){ThrottleEngine = isThrottling; }
public boolean IsEngineThrottled(){return ThrottleEngine;}
public boolean IsEngineThrottledOnUnfocus(){return ThrottleEngineOnUnfocus;}
public long GetThrottleAccuracy(){return ThrottleAccuracy;}
public int GetRequestedImages(){
return RequestedImages;
}
public boolean GetVSYNC(){
return VSYNC;
}
public boolean DebugShaders(){
return ShaderDebug;
}
public boolean RecompileShaders(){
return RecompileShaders;
}
public boolean VkValidated(){
return vkValidated;
}
public int getWidth(){ public int getWidth(){
return Width; return Width;
} }
@ -151,6 +169,8 @@ public class EngineConfig {
return Height; return Height;
} }
public boolean GetMainTickCap(){return CapMainToTickRate;} public boolean GetMainTickCap(){return CapMainToTickRate;}
public boolean GetFastTickCap(){return CapFastToTickRate;}
public boolean GetPrimaryTickCap(){return CapPrimaryToTickRate;}
public int GetMainTickrate(){return MainTickRate;} public int GetMainTickrate(){return MainTickRate;}
public int GetFastTickrate(){return FastTickRate;} public int GetFastTickrate(){return FastTickRate;}
public long getAccuracy() { public long getAccuracy() {

View file

@ -5,4 +5,5 @@ public interface GameLogic {
InitData Initialise(EngineInstance engineInstance); InitData Initialise(EngineInstance engineInstance);
void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds); void Input(EngineInstance engineInstance, long FrameDiffNanoSeconds);
void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds); void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds);
void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds);
} }

View file

@ -18,13 +18,18 @@ public class GameCore implements GameLogic {
@Override @Override
public InitData Initialise(EngineInstance engineInstance) { public InitData Initialise(EngineInstance engineInstance) {
var ModelID = "TriangleModel"; 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<>(); List<MeshData> MeshDataList = new ArrayList<>();
MeshDataList.add(meshData); for(int i = 0; i < 5; i++){
float PosXOffset = -1 + (float)Math.random() * 2;
float PosYOffset = -1 + (float)Math.random() * 2;
MeshDataList.add(
new MeshData("triangle-mesh" + i, new float[]{
PosXOffset, PosYOffset, 0.00f,
PosXOffset + 0.05f,PosYOffset + 0.15f, 0.0f,
PosXOffset + 0.1f, PosYOffset, 0.0f},
new int[]{0, 1, 2})
);
}
ModelData modelData = new ModelData(ModelID, MeshDataList); ModelData modelData = new ModelData(ModelID, MeshDataList);
List<ModelData> models = new ArrayList<>(); List<ModelData> models = new ArrayList<>();
models.add(modelData); models.add(modelData);
@ -40,4 +45,9 @@ public class GameCore implements GameLogic {
public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) { public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
} }
@Override
public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
}
} }

View file

@ -2,6 +2,7 @@ package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Display.Window; import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.*; import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Threads.FastTickThread;
import net.halbear.Terrain4J.EngineCore.Threads.MainThread; import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import org.tinylog.Logger; import org.tinylog.Logger;
@ -12,36 +13,60 @@ public class PrimaryRuntime {
private double TickRate = 0; private double TickRate = 0;
private static MainThread PrimaryThread; private static MainThread PrimaryThread;
private static RenderThread DrawingThread; private static RenderThread DrawingThread;
private static FastTickThread FastThread;
private static boolean running = true; private static boolean running = true;
private static Window window; private static Window window;
private static EngineInstance engineInstance; private static EngineInstance engineInstance;
public static int ThreadsOpen = 0; public static int ThreadsOpen = 0;
public static boolean CanClose = false; public static boolean CanClose = false;
public static long ShutDownStart; public static long ShutDownStart;
private static long FrameAccuracy;
private static long OriginalFrameAccuracy;
private static boolean CappedToTickRate = false;
public PrimaryRuntime(String windowTitle, GameLogic appLogic) { public PrimaryRuntime(String windowTitle, GameLogic appLogic) {
window = new Window(windowTitle); window = new Window(windowTitle);
InitData initData = appLogic.Initialise(engineInstance); InitData initData = appLogic.Initialise(engineInstance);
engineInstance = new EngineInstance(window, new Scene(window)); engineInstance = new EngineInstance(window, new Scene(window));
PrimaryThread = new MainThread(engineInstance, appLogic); PrimaryThread = new MainThread(engineInstance, appLogic);
DrawingThread = new RenderThread(window, engineInstance, initData); FastThread = new FastTickThread(engineInstance, appLogic);
DrawingThread = new RenderThread(window, engineInstance, initData, appLogic);
Thread.currentThread().setName("Terrain4J Primary Runtime Thread"); Thread.currentThread().setName("Terrain4J Primary Runtime Thread");
} }
public static void UpdateFrameAccuracy(){
public void UpdateTickRate(){ if(EngineConfig.getInstance().IsEngineThrottled()){
TickRate = (double) 1000000000 / (double) EngineConfig.getInstance().getTickRate(); Logger.debug("ThrottlingEngine");
FrameAccuracy = EngineConfig.getInstance().GetThrottleAccuracy();
} else{
Logger.debug("UnThrottlingEngine");
FrameAccuracy= OriginalFrameAccuracy;
}
PrimaryThread.UpdateFrameAccuracy();
DrawingThread.UpdateFrameAccuracy();
FastThread.UpdateFrameAccuracy();
} }
public void UpdateTickRateThreshold(){
CappedToTickRate = EngineConfig.getInstance().GetPrimaryTickCap();
if (CappedToTickRate){
TickRate = (double) 1000000000 / (double) EngineConfig.getInstance().getTickRate();
} else{
TickRate = 0;
}
}
public void run(){ public void run(){
UpdateTickRateThreshold();
long InitialTime = System.nanoTime(); long InitialTime = System.nanoTime();
long updateTime = InitialTime; long updateTime = InitialTime;
double FrameDiff =0; double FrameDiff =0;
double TickDiff =0; double TickDiff =0;
double SecondCounter =0; double SecondCounter =0;
double cycleDiff = 0; double cycleDiff = 0;
PrimaryThread.StartMainThread(); PrimaryThread.StartThread();
DrawingThread.StartRenderThread(); DrawingThread.StartThread();
TickRate = (double) 1000000000 / (double) EngineConfig.getInstance().getTickRate(); FastThread.StartThread();
long FrameAccuracy = EngineConfig.getInstance().getAccuracy(); FrameAccuracy = EngineConfig.getInstance().getAccuracy();
OriginalFrameAccuracy = FrameAccuracy;
Logger.debug("Primary Thread Started"); Logger.debug("Primary Thread Started");
while (!window.shouldClose() && running){ while (!window.shouldClose() && running){
long now = System.nanoTime(); long now = System.nanoTime();
@ -93,7 +118,7 @@ public class PrimaryRuntime {
if (ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > -1) { if (ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > -1) {
Logger.debug("Waiting to close Primary Thread, making sure vulkan has shut down properly first and Engine Threads are closed\nThreads Open:[{}]\nVulkan Layers Open:[{}]", ThreadsOpen, VulkanContext.VulkanLayersOpen < 0 ? "All processes closed and vulkan instance removed" : VulkanContext.VulkanLayersOpen); Logger.debug("Waiting to close Primary Thread, making sure vulkan has shut down properly first and Engine Threads are closed\nThreads Open:[{}]\nVulkan Layers Open:[{}]", ThreadsOpen, VulkanContext.VulkanLayersOpen < 0 ? "All processes closed and vulkan instance removed" : VulkanContext.VulkanLayersOpen);
while ((ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > 0) && VulkanContext.VulkanLayersOpen > -1 && !CanClose) { while ((ThreadsOpen > 0 || VulkanContext.VulkanLayersOpen > 0) && VulkanContext.VulkanLayersOpen > -1 && !CanClose) {
if (!DrawingThread.FetchRenderThread().isAlive() && !PrimaryThread.FetchMainThread().isAlive() && ThreadsOpen >0){ if (!DrawingThread.FetchThread().isAlive() && !PrimaryThread.FetchThread().isAlive() && ThreadsOpen >0){
break; break;
} }
} }

View file

@ -0,0 +1,123 @@
package net.halbear.Terrain4J.EngineCore.Threads;
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 org.tinylog.Logger;
public class EngineThread implements Runnable{
public boolean CappedToTickRate;
public int TickRate;
public double TickThreshold = 0;
public static boolean running = true;
public final GameLogic gameLogic;
public int Frames = 0;
public int TickFrames = 0;
public final Thread EngineThread;
public long EngineAccuracy;
public long OriginalAccuracy;
public boolean Throttle;
public String ThreadName;
public void KillThread(){
running = false;
}
public void cleanup(){
gameLogic.cleanup();
PrimaryRuntime.ThreadsOpen--;
Logger.debug("Closing "+ThreadName+" Thread, Thread: [{}]",Thread.currentThread().toString());
}
public Thread FetchThread(){
return EngineThread;
}
public void StartThread(){
EngineThread.start();
}
public EngineThread(EngineInstance engineInstance, GameLogic appLogic, String ThreadName){
this.ThreadName = ThreadName;
this.gameLogic = appLogic;
Logger.debug("Starting {} Thread",ThreadName);
EngineThread = new Thread(this);
EngineThread.setName("Terrain4J "+ThreadName+" Engine Thread");
}
public void UpdateFrameAccuracy(){
Throttle = EngineConfig.getInstance().IsEngineThrottled();
if(Throttle){
EngineAccuracy = EngineConfig.getInstance().GetThrottleAccuracy();
} else{
EngineAccuracy= OriginalAccuracy;
}
}
public void TickEvent(long nsTimeDiff){
}
public void FrameEvent(long now, long InitialTime){
}
public void CycleEvent(){
}
public void SecondEvent(){
}
@Override
public void run() {
PrimaryRuntime.ThreadsOpen++;
long InitialTime = System.nanoTime();
long updateTime = InitialTime;
double[] FrameTimes = new double[]{0,0,0,0};
Logger.debug("{} Thread Started",ThreadName);
EngineAccuracy = EngineConfig.getInstance().getAccuracy();
OriginalAccuracy = EngineAccuracy;
while (running){
long now = System.nanoTime();
FrameTimes[0] = now - InitialTime;
FrameTimes[1] += FrameTimes[0];
FrameTimes[2] += FrameTimes[0];
FrameTimes[3] += FrameTimes[0];
if (FrameTimes[1] >= EngineAccuracy){
FrameEvent(now, InitialTime);
Frames++;
if (FrameTimes[2] >= TickThreshold) {
TickFrames++;
long nsTimeDiff = now - updateTime;
TickEvent(nsTimeDiff);
updateTime = now;
FrameTimes[2] = 0;
}
if (FrameTimes[3] >= 1000000000L){
FrameTimes[3] = 0;
SecondEvent();
Frames = 0;
TickFrames = 0;
}
FrameTimes[1] = 0;
}
if(Throttle) {
long endTime = System.nanoTime();
long LoopFrameDiff = endTime - now;
long sleepTime = ((EngineAccuracy - LoopFrameDiff) / 1000000);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
Logger.error("Thread Sleep Exception [{}]", e);
Thread.currentThread().interrupt();
break;
}
}
}
InitialTime = now;
}
cleanup();
}
}

View file

@ -1,4 +1,45 @@
package net.halbear.Terrain4J.EngineCore.Threads; package net.halbear.Terrain4J.EngineCore.Threads;
public class FastTickThread { 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 org.tinylog.Logger;
public class FastTickThread extends EngineThread {
public FastTickThread(EngineInstance engineInstance, GameLogic appLogic){
super(engineInstance, appLogic, "Fast");
UpdateTickRateThreshold();
}
@Override
public void TickEvent(long nsTimeDiff){
gameLogic.UpdateFastThread(PrimaryRuntime.GetEngineInstance(), nsTimeDiff);
}
@Override
public void FrameEvent(long now, long InitialTime){
}
@Override
public void CycleEvent(){
}
@Override
public void SecondEvent(){
EngineConfig.FrameRate_FASTENGINETHREAD = Frames;
Logger.info(ThreadName + " Thread FPS: " + EngineConfig.FrameRate_FASTENGINETHREAD + " TPS: " + TickFrames);
}
public void UpdateTickRateThreshold(){
CappedToTickRate = EngineConfig.getInstance().GetFastTickCap();
TickRate = EngineConfig.getInstance().GetFastTickrate();
if (CappedToTickRate){
TickThreshold = (double) 1000000000 / (double) TickRate;
} else{
TickThreshold = 0;
}
}
} }

View file

@ -9,36 +9,24 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene; import net.halbear.Terrain4J.EngineCore.Main.Scene;
import org.tinylog.Logger; import org.tinylog.Logger;
public class MainThread implements Runnable { public class MainThread extends EngineThread {
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(){ @Override
running = false; public void TickEvent(long nsTimeDiff){
gameLogic.Update(PrimaryRuntime.GetEngineInstance(), nsTimeDiff);
} }
public void cleanup(){ @Override
gameLogic.cleanup(); public void FrameEvent(long now, long InitialTime){
PrimaryRuntime.ThreadsOpen--; gameLogic.Input(PrimaryRuntime.GetEngineInstance(), (now - InitialTime));
Logger.debug("Closing Main Thread, Thread: [{}]",Thread.currentThread().toString());
} }
public Thread FetchMainThread(){ @Override
return mainThread; public void CycleEvent(){
} }
public void StartMainThread(){ @Override
mainThread.start(); public void SecondEvent(){
} EngineConfig.FrameRate_MAINENGINETHREAD = Frames;
public MainThread(EngineInstance engineInstance, GameLogic appLogic){ Logger.info(ThreadName + " Thread FPS: " + EngineConfig.FrameRate_MAINENGINETHREAD + " TPS: " + TickFrames);
this.gameLogic = appLogic;
Logger.debug("Starting Main Thread");
UpdateTickRateThreshold();
mainThread = new Thread(this);
mainThread.setName("Terrain4J Main Engine Thread");
} }
public void UpdateTickRateThreshold(){ public void UpdateTickRateThreshold(){
CappedToTickRate = EngineConfig.getInstance().GetMainTickCap(); CappedToTickRate = EngineConfig.getInstance().GetMainTickCap();
@ -49,44 +37,9 @@ public class MainThread implements Runnable {
TickThreshold = 0; TickThreshold = 0;
} }
} }
@Override public MainThread(EngineInstance engineInstance, GameLogic appLogic){
public void run() { super(engineInstance, appLogic, "Main");
PrimaryRuntime.ThreadsOpen++; UpdateTickRateThreshold();
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 >= 1000000000L){
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();
} }
} }

View file

@ -2,73 +2,44 @@ package net.halbear.Terrain4J.EngineCore.Threads;
import net.halbear.Terrain4J.EngineCore.Display.Render; import net.halbear.Terrain4J.EngineCore.Display.Render;
import net.halbear.Terrain4J.EngineCore.Display.Window; import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; import net.halbear.Terrain4J.EngineCore.Logic.*;
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 net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import org.tinylog.Logger; import org.tinylog.Logger;
public class RenderThread implements Runnable { public class RenderThread extends EngineThread {
private static boolean running = true;
private int Frames = 0;
private final Thread renderThread;
private final Render render; private final Render render;
private Window window; private Window window;
public RenderThread(Window window, EngineInstance engineInstance, InitData initData) { public RenderThread(Window window, EngineInstance engineInstance, InitData initData, GameLogic appLogic) {
//this.window = window; //this.window = window;
render = new Render(engineInstance); render = new Render(engineInstance);
super(engineInstance,appLogic,"Render");
render.Initialise(initData); render.Initialise(initData);
renderThread = new Thread(this);
renderThread.setName("Terrain4J Vulkan Render Thread");
} }
public void KillThread(){ @Override
running = false; public void TickEvent(long nsTimeDiff){
} }
public Thread FetchRenderThread(){ @Override
return renderThread; public void FrameEvent(long now, long InitialTime){
render.render(PrimaryRuntime.GetEngineInstance());
} }
public void StartRenderThread(){ @Override
renderThread.start(); public void CycleEvent(){
} }
private void cleanup() { @Override
public void SecondEvent(){
Logger.info("Render FPS: " + Frames);
}
@Override
public void cleanup() {
render.cleanup(); render.cleanup();
PrimaryRuntime.ThreadsOpen--; PrimaryRuntime.ThreadsOpen--;
Logger.debug("Closing Render Thread, Thread: [{}]",Thread.currentThread().toString()); Logger.debug("Closing Render Thread, Thread: [{}]",Thread.currentThread().toString());
if (PrimaryRuntime.ThreadsOpen <= 0 && VulkanContext.VulkanLayersOpen < 0) PrimaryRuntime.CanClose = true; if (PrimaryRuntime.ThreadsOpen <= 0 && VulkanContext.VulkanLayersOpen < 0) PrimaryRuntime.CanClose = true;
} }
@Override
public void run() {
int TotalFrames = 0;
PrimaryRuntime.ThreadsOpen++;
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 >= 1000000000L){
SecondCounter = 0;
//EngineConfig.FrameRate_RENDERTHREAD = Frames;
Logger.info("Render FPS: " + Frames);
Frames = 0;
}
render.render(PrimaryRuntime.GetEngineInstance());
TotalFrames++;
FrameDiff = 0;
}
InitialTime = now;
}
//PrimaryRuntime.CloseRuntime();
cleanup();
}
} }

View file

@ -0,0 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering;
public class Image {
public static class ImageData{
}
}

View file

@ -8,13 +8,17 @@ display_height=480
display_width=640 display_width=640
#Engine Core Settings #Engine Core Settings
#the precision of frames, set this to 16000 and it will limit the whole engine to 64FPS #the precision of frames, set this to 16000000 and it will limit the whole engine to 64FPS
frame_accuracy=0 frame_accuracy=0
throttle_on_unfocus=true
throttle_accuracy=500000000
#event tick rate for game process #event tick rate for game process
master_tick_rate=120 master_tick_rate=120
main_thread_tickrate=120 main_thread_tickrate=120
fast_thread_tickrate=240 fast_thread_tickrate=240
cap_main_to_tickrate=false cap_master_to_tickrate=false
cap_main_to_tickrate=true
cap_fast_to_tickrate=true
#Renderer Properties #Renderer Properties
Debug_Shaders = false Debug_Shaders = false