diff --git a/src/main/java/speiger/src/coreengine/application/Application.java b/src/main/java/speiger/src/coreengine/application/Application.java new file mode 100644 index 0000000..650cc2e --- /dev/null +++ b/src/main/java/speiger/src/coreengine/application/Application.java @@ -0,0 +1,183 @@ +package speiger.src.coreengine.application; + +import java.io.File; +import java.util.function.IntConsumer; +import java.util.function.ObjLongConsumer; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWErrorCallback; + +import speiger.src.coreengine.assets.AssetManager; +import speiger.src.coreengine.assets.reloader.ResourceReloader; +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.input.Keyboard; +import speiger.src.coreengine.rendering.input.Mouse; +import speiger.src.coreengine.rendering.input.camera.Camera; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.input.window.WindowProvider; +import speiger.src.coreengine.rendering.shader.ProjectionBuffer; +import speiger.src.coreengine.rendering.shader.ShaderTracker; +import speiger.src.coreengine.rendering.textures.TextureManager; +import speiger.src.coreengine.rendering.utils.Cursor; +import speiger.src.coreengine.utils.counters.timers.FPSTimer; +import speiger.src.coreengine.utils.eventbus.EventBus; +import speiger.src.coreengine.utils.helpers.FileUtils; +import speiger.src.coreengine.utils.profiler.GPUProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler; +import speiger.src.coreengine.utils.profiler.Profiler; + +public abstract class Application +{ + protected FPSTimer timer; + protected ApplicationExecutor executor = new ApplicationExecutor(this); + protected IProfiler clientProfiler = new Profiler("Client Thread"); + protected IProfiler gpuProfiler = new GPUProfiler("GPU", clientProfiler); + protected boolean showProfilingInfo = true; + protected WindowProvider provider = new WindowProvider(); + protected Window mainWindow; + protected Camera camera; + protected EventBus eventBus = new EventBus(); + + protected ResourceReloader reloader = new ResourceReloader(); + protected AssetManager assetManager; + protected ProjectionBuffer projectionBuffer; + protected GuiManager uiManager; + + public void run() + { + GLFWErrorCallback.createPrint(System.err).set(); + if(!GLFW.glfwInit()) throw new IllegalStateException("OpenGL can't be loaded"); + provider.init(); + try + { + mainWindow = createWindow(provider); + mainWindow.finishWindow(); + } + catch(Exception e) + { + System.err.println("Could not create a Window!"); + e.printStackTrace(); + if(mainWindow != null) mainWindow.destroy(); + provider.destroy(); + System.exit(0); + return; + } + Thread.currentThread().setName("Client Thread"); + File file = FileUtils.getBase(); + file = file.getName().endsWith(".jar") ? file : new File("bin/main"); + internalInit(file); + init(file); + executor.start(mainWindow); + mainWindow.destroy(); + reloader.deleteResources(); + destroy(); + System.exit(0); + } + + protected void internalInit(File file) + { + assetManager = reloader.addReloadableResource(new AssetManager(file), true); + ShaderTracker.INSTANCE.init(assetManager); + TextureManager.INSTANCE.init(assetManager); + reloader.addReloadableResource(ShaderTracker.INSTANCE); + reloader.addReloadableResource(TextureManager.INSTANCE); + camera = new Camera(mainWindow); + if(initUI()) uiManager = BaseUIManager.create(this); + Keyboard.INSTANCE.init(eventBus, mainWindow); + Mouse.INSTANCE.init(eventBus, mainWindow, camera); + reloader.addReloadableResource(Cursor.INSTANCE, true); + mainWindow.addListener(camera.getFrustrum(), true); + projectionBuffer = new ProjectionBuffer(camera, mainWindow); + } + + public abstract String getMainWindowName(); + public void addExtraTickRates(IntConsumer ticks) {}; + public void addExtraTimers(ObjLongConsumer profiler) {}; + public boolean initUI() { return true; } + public DebugOverlay createCustomDebug() { return null; } + public abstract Window createWindow(WindowProvider provider) throws Exception; + public abstract void init(File file); + public abstract void update(); + public abstract void render(float particalTicks); + public abstract void destroy(); + + protected final void updateInternal() + { + if(uiManager != null) + { + clientProfiler.start("UI"); + uiManager.onFixedUpdate(); + clientProfiler.stop(); + } + clientProfiler.start("Input"); + camera.onInput(); + Mouse.update(); + clientProfiler.stop(); + update(); + } + + protected final void renderInternal(float particalTicks) + { + render(particalTicks); + if(uiManager != null) + { + gpuProfiler.start("UI"); + uiManager.render(particalTicks); + gpuProfiler.stop(); + } + gpuProfiler.start("camera"); + camera.update(particalTicks); + gpuProfiler.stop(); + } + + public AssetManager getAssetManager() + { + return assetManager; + } + + public ResourceReloader getReloader() + { + return reloader; + } + + public Window getMainWindow() + { + return mainWindow; + } + + public Camera getCamera() + { + return camera; + } + + public EventBus getEventBus() + { + return eventBus; + } + + public ProjectionBuffer getProjectionBuffer() + { + return projectionBuffer; + } + + public FPSTimer getTimer() + { + return timer; + } + + public GuiManager getUiManager() + { + return uiManager; + } + + public IProfiler getClientProfiler() + { + return clientProfiler; + } + + public IProfiler getGPUProfiler() + { + return gpuProfiler; + } +} diff --git a/src/main/java/speiger/src/coreengine/application/ApplicationExecutor.java b/src/main/java/speiger/src/coreengine/application/ApplicationExecutor.java new file mode 100644 index 0000000..d430402 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/application/ApplicationExecutor.java @@ -0,0 +1,143 @@ +package speiger.src.coreengine.application; + +import java.text.DecimalFormat; + +import org.lwjgl.opengl.GL11; + +import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; +import speiger.src.collections.objects.queues.ObjectPriorityQueue; +import speiger.src.coreengine.rendering.input.window.Window; +import speiger.src.coreengine.rendering.utils.AllocationTracker; +import speiger.src.coreengine.rendering.utils.GLStamper; +import speiger.src.coreengine.rendering.utils.GLStamper.GLStamp; +import speiger.src.coreengine.rendering.utils.GLUtils; +import speiger.src.coreengine.utils.counters.averager.TimeAverager; +import speiger.src.coreengine.utils.counters.timers.FPSTimer; +import speiger.src.coreengine.utils.counters.timers.FrameSyncer; +import speiger.src.coreengine.utils.counters.timers.TimerTarget; +import speiger.src.coreengine.utils.helpers.FileUtils; +import speiger.src.coreengine.utils.helpers.TextUtil; +import speiger.src.coreengine.utils.io.GameLog; +import speiger.src.coreengine.utils.io.GameLog.LogLevel; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public class ApplicationExecutor +{ + public static final DecimalFormat FORMATTER = new DecimalFormat("###"); + FPSTimer timer = new FPSTimer(); + TimerTarget fps = timer.getFPS(); + TimerTarget ups = timer.getUPS(); + FrameSyncer sync = new FrameSyncer(); + + TimeAverager gpuTime = new TimeAverager(); + ObjectPriorityQueue stamps = new ObjectArrayFIFOQueue<>(); + StringBuilder builder = new StringBuilder(); + + long frame = 0L; + + Application owner; + + public ApplicationExecutor(Application owner) + { + this.owner = owner; + owner.timer = timer; + } + + private IProfiler gpu() + { + return owner.gpuProfiler; + } + + public void start(Window window) + { + createTimeHacker(); + timer.init(); + try + { + while(window.shouldRun()) loop(window); + } + catch(Exception e) + { + GameLog.error("Client Has Crashed", e, LogLevel.ERROR); + } + } + + private void loop(Window window) + { + GLStamp stamp = GLStamper.INSTANCE.createStamp("MainStamp").start(); + stamps.enqueue(stamp); + gpu().start("Client").start("Update"); + window.update(); + for(long f = timer.getDelta();f >= ups.getTargetNS();f = timer.consumeDelta()) + { + ups.tick(); + owner.updateInternal(); + builder.setLength(0); + builder.append(owner.getMainWindowName()); + if(owner.showProfilingInfo) addDebugInfo(builder); + window.setTitle(builder.toString()); + } + gpu().next("Render"); + render(timer.getParticalTime()); + gpu().next("Trackers"); + boolean end = timer.update(); + stamp.stop(); + AllocationTracker.INSTANCE.update(); + GLUtils.onFrameEnded(); + gpu().next("Window").start("V-Sync"); + window.finishFrame(); + if(window.isCPULimited()) sync.sync(fps.getTargetTicks()); + gpu().next("Input"); + window.gatherInputs(); + gpu().stop().stop().stop().onFrameEnded(end); + while(!stamps.isEmpty() && stamps.first().isFinished()) + { + stamp = stamps.dequeue(); + gpuTime.addEntry(stamp.getResult()); + stamp.release(); + } + frame++; + } + + private void render(float particalTicks) + { + gpu().start("Prep"); + fps.tick(); + GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + gpu().stop(); + owner.renderInternal(particalTicks); + } + + public void createTimeHacker() + { + Thread thread = new Thread(() -> { + while (true){ + try {Thread.sleep(2147483647L);} + catch (InterruptedException e){} + } + }, "Time Fixer Thread"); + thread.setDaemon(true); + thread.start(); + } + + protected void addDebugInfo(StringBuilder builder) + { + builder.append("("); + builder.append(fps.getTicks()).append(":").append(ups.getTicks()); + owner.addExtraTickRates(T -> builder.append(":").append(T)); + builder.append(",").append(getMemoryUsage()); + builder.append(",").append("CPU-A: "+FileUtils.convertBytes(AllocationTracker.INSTANCE.getCPUAllocatedBytes())); + builder.append(",").append("GPU-A: "+FileUtils.convertBytes(AllocationTracker.INSTANCE.getGPUAllocatedBytes())); + builder.append(",").append(TextUtil.convertTime(timer.getUsage(), "Client:", FORMATTER)); + builder.append(",").append(TextUtil.convertTime(gpuTime.getAverage(), "GPU:", FORMATTER)); + owner.addExtraTimers((N, T) -> builder.append(",").append(TextUtil.convertTime(T, N, FORMATTER))); + builder.append(")"); + } + + private String getMemoryUsage() + { + Runtime time = Runtime.getRuntime(); + long aviable = time.totalMemory(); + return "Memory:"+((aviable - time.freeMemory()) >> 20)+"MB/"+(aviable >> 20)+"MB"; + } +} diff --git a/src/main/java/speiger/src/coreengine/application/BaseUIManager.java b/src/main/java/speiger/src/coreengine/application/BaseUIManager.java new file mode 100644 index 0000000..4664662 --- /dev/null +++ b/src/main/java/speiger/src/coreengine/application/BaseUIManager.java @@ -0,0 +1,81 @@ +package speiger.src.coreengine.application; + +import speiger.src.coreengine.rendering.gui.GuiManager; +import speiger.src.coreengine.rendering.gui.base.DebugOverlay; +import speiger.src.coreengine.rendering.models.UniformBuffer; +import speiger.src.coreengine.utils.collections.FlagHolder; +import speiger.src.coreengine.utils.profiler.EmptyProfiler; +import speiger.src.coreengine.utils.profiler.IProfiler; + +public class BaseUIManager extends GuiManager +{ + Application application; + + public BaseUIManager(Application application) + { + super(application.mainWindow, application.eventBus); + this.application = application; + } + + public static BaseUIManager create(Application app) + { + return new BaseUIManager(app) { + @Override + public DebugOverlay createOverlay() + { + DebugOverlay overlay = app.createCustomDebug(); + return overlay != null ? overlay : new InternalDebugOverlay(); + } + }; + } + + @Override + public IProfiler getGPUProfiler() + { + return application.gpuProfiler; + } + + @Override + public IProfiler getCPUProfiler() + { + return application.clientProfiler; + } + + @Override + public IProfiler getServerProfiler() + { + return EmptyProfiler.INSTANCE; + } + + @Override + public UniformBuffer getOrthoMatrixBuffer() + { + return application.projectionBuffer.getOrthoViewMatrixBuffer(); + } + + @Override + public DebugOverlay createOverlay() + { + DebugOverlay overlay = application.createCustomDebug(); + return overlay != null ? overlay : new InternalDebugOverlay(); + } + + public static class InternalDebugOverlay extends DebugOverlay + { + public static final int UPDATING = 1; + public static final int RENDERING = 2; + FlagHolder flags = new FlagHolder(RENDERING); + @Override + public void toggleUI() { flags.flipFlag(RENDERING); } + @Override + public void toggleUpdate() { flags.flipFlag(UPDATING); } + @Override + public void toggleDebug() {} + @Override + public boolean toggleFPS() { return false; } + @Override + public boolean isUpdating() { return flags.isFlagSet(UPDATING); } + @Override + public boolean isRendering() { return flags.isFlagSet(RENDERING); } + } +} diff --git a/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java index 0ed3b84..70216ae 100644 --- a/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java +++ b/src/main/java/speiger/src/coreengine/rendering/utils/AllocationTracker.java @@ -55,7 +55,7 @@ public class AllocationTracker return cpuAllocation.getAverage(); } - public long getGPUAllocationBytes() + public long getGPUAllocatedBytes() { return gpuAllocation.getAverage(); }