diff --git a/rendering/src/main/java/de/staropensource/engine/rendering/renderer/Renderer.java b/rendering/src/main/java/de/staropensource/engine/rendering/renderer/Renderer.java
index e73d2f2..a640d19 100644
--- a/rendering/src/main/java/de/staropensource/engine/rendering/renderer/Renderer.java
+++ b/rendering/src/main/java/de/staropensource/engine/rendering/renderer/Renderer.java
@@ -19,22 +19,20 @@
package de.staropensource.engine.rendering.renderer;
-import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.type.FrameHandler;
-import de.staropensource.engine.rendering.type.Window;
-import de.staropensource.engine.rendering.type.window.VsyncMode;
+import lombok.AccessLevel;
import lombok.Getter;
+import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
import java.util.*;
import static org.lwjgl.bgfx.BGFX.*;
-import static org.lwjgl.glfw.GLFW.*;
/**
* Renders all windows out.
@@ -52,7 +50,18 @@ public final class Renderer {
* to frame renders.
*
* @since v1-alpha9
+ * -- GETTER --
+ * Returns all frame handlers.
+ *
+ * Frame handlers are invoked before
+ * all windows are rendered, allowing
+ * the application or game to respond
+ * to frame renders.
+ *
+ * @return frame handlers
+ * @since v1-alpha9
*/
+ @Getter(value = AccessLevel.PACKAGE)
private static final @NotNull List<@NotNull FrameHandler> frameHandlers = Collections.synchronizedList(new ArrayList<>());
/**
@@ -96,8 +105,17 @@ public final class Renderer {
*
* @return amount of frames rendered
* @since v1-alpha9
+ * -- SETTER --
+ * Sets the frame count aka.
+ * the amount of frames rendered.
+ *
+ * Updated every frame.
+ *
+ * @param frameCount new amount of frames rendered
+ * @since v1-alpha9
*/
@Getter
+ @Setter(value = AccessLevel.PACKAGE)
private static long frameCount = 0L;
/**
@@ -120,8 +138,20 @@ public final class Renderer {
*
* @return delta time
* @since v1-alpha9
+ * -- SETTER --
+ * Sets the delta time, also
+ * known as the render time.
+ *
+ * Delta time is the time in seconds
+ * between the last and current frame.
+ *
+ * Updated every frame.
+ *
+ * @param deltaTime new delta time
+ * @since v1-alpha9
*/
@Getter
+ @Setter(value = AccessLevel.PACKAGE)
private static double deltaTime = 0d;
/**
@@ -137,8 +167,16 @@ public final class Renderer {
*
* @return frames per second
* @since v1-alpha9
+ * -- SETTER --
+ * Sets the frames per second (FPS) count.
+ *
+ * Updated every second.
+ *
+ * @param framesPerSecond new frames per second
+ * @since v1-alpha9
*/
@Getter
+ @Setter(value = AccessLevel.PACKAGE)
private static double framesPerSecond = 0d;
/**
@@ -161,8 +199,17 @@ public final class Renderer {
*
* @return last frame time
* @since v1-alpha9
+ * -- SETTER --
+ * Sets the time it took
+ * to calculate the last frame.
+ *
+ * Updated every frame.
+ *
+ * @param lastFrameTime new last frame time
+ * @since v1-alpha9
*/
@Getter
+ @Setter(value = AccessLevel.PACKAGE)
private static Map<@NotNull String, @NotNull Long> lastFrameTime = Collections.unmodifiableMap(new HashMap<>());
@@ -287,119 +334,13 @@ public final class Renderer {
* @since v1-alpha9
*/
@SuppressWarnings({ "InfiniteLoopStatement" })
- private static void render() throws Throwable {
- long previousFrameCount = 0L; // Frame count one second ago
- long systemTimeNow; // Current system time
- long systemTimePrevious = System.currentTimeMillis(); // Previous system time
- LinkedList deltaTimes = new LinkedList<>(); // Contains all delta time fractions
- Map execTimes = new LinkedHashMap<>(); // Contains the amount of time of all rendering operations
- long timesWait; // Time to wait until the next frame
- long timesPSO = System.currentTimeMillis() + 1000; // Time to wait until invoking per-second operations
-
+ private static void render() {
while (true) {
- // Invoke frame handlers
- for (FrameHandler frameHandler : frameHandlers)
- execTimes.put("Frame handler '" + frameHandler.getClass().getName() + "'", Miscellaneous.measureExecutionTime(frameHandler::run));
-
-
- // Perform rendering
- execTimes.put("Rendering", Miscellaneous.measureExecutionTime(() -> {
- // Poll for events
- glfwPollEvents();
-
- // Reset backbuffer
- int resetSettings = 0;
- if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON)
- resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
- for (Window window : Window.getWindows())
- if (window.isTransparent())
- resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
-
- bgfx_reset(
- Window.getWindows().getFirst().getSize().getX(),
- Window.getWindows().getFirst().getSize().getY(),
- resetSettings,
- BGFX_TEXTURE_FORMAT_RGBA4
- );
-
- // Render all windows
- for (Window window : Window.getWindows())
- if (window.isRendering()) {
- window.updateState();
- window.render();
- }
-
- // Advance to next frame
- bgfx_frame(false);
- }));
-
-
- // Determine time to wait for the next frame
- execTimes.put("Waiting", 0L);
- if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() > 0) {
- execTimes.replace("Waiting", (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d));
-
- for (String time : execTimes.keySet())
- if (!time.equals("Waiting"))
- execTimes.replace("Waiting", execTimes.get("Waiting") - execTimes.get(time));
-
- // Wait until next frame
- if (execTimes.get("Waiting") > 0) {
- timesWait = execTimes.get("Waiting") + System.currentTimeMillis();
-
- while (System.currentTimeMillis() < timesWait)
- Thread.onSpinWait();
- }
- }
-
-
- // Perform per-frame operations
- frameCount += 1;
- lastFrameTime = new HashMap<>(execTimes);
- execTimes.clear();
- systemTimeNow = System.currentTimeMillis();
- deltaTime = (double) (systemTimeNow - systemTimePrevious) / 1000;
- systemTimePrevious = systemTimeNow;
- deltaTimes.add(deltaTime);
-
-
- // Perform per-second operations
- if (System.currentTimeMillis() >= timesPSO) {
- // Calculate FPS count
- framesPerSecond = 1 / NumberUtil.calculateMeanDouble(deltaTimes);
-
- // Log frame count
- if (RenderingSubsystemConfiguration.getInstance().isDebugFrames())
- Logger.diag("Frames " + previousFrameCount + "-" + frameCount + "\n-> Frames/s: " + framesPerSecond + "\n-> Delta time: " + deltaTime);
-
- // Log window states
- if (RenderingSubsystemConfiguration.getInstance().isDebugWindowStates())
- for (Window window : Window.getWindows())
- Logger.diag(
- "Window state for " + window.getUniqueIdentifier() + "\n" +
- "-> Terminated: " + window.isTerminated() + "\n" +
- "-> Name: " + window.getName() + "\n" +
- "-> Title: " + window.getTitle() + "\n" +
- "-> Size: " + window.getSize() + "\n" +
- " -> Minimum: " + window.getMinimumSize() + "\n" +
- " -> Maximum: " + window.getMaximumSize() + "\n" +
- "-> Position: " + window.getPosition() + "\n" +
- "-> Mode: " + window.getMode() + "\n" +
- "-> Resizable: " + window.isResizable() + "\n" +
- "-> Borderless: " + window.isBorderless() + "\n" +
- "-> Focused: " + window.isFocused() + "\n" +
- "-> On top: " + window.isOnTop() + "\n" +
- "-> Transparent: " + window.isTransparent() + "\n" +
- "-> Rendering: " + window.isRendering()
- );
-
-
-
- // Reset per-second variables
- previousFrameCount = frameCount;
- deltaTimes.clear();
- timesPSO = System.currentTimeMillis() + 1000;
- }
+ RenderingCode.invokeFrameHandlers();
+ RenderingCode.renderWindows();
+ RenderingCode.waitForNextFrame();
+ RenderingCode.performPerFrameOperations();
+ RenderingCode.performPerSecondOperations();
}
}
}
diff --git a/rendering/src/main/java/de/staropensource/engine/rendering/renderer/RenderingCode.java b/rendering/src/main/java/de/staropensource/engine/rendering/renderer/RenderingCode.java
new file mode 100644
index 0000000..a4f5b9d
--- /dev/null
+++ b/rendering/src/main/java/de/staropensource/engine/rendering/renderer/RenderingCode.java
@@ -0,0 +1,245 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.engine.rendering.renderer;
+
+import de.staropensource.engine.base.logging.Logger;
+import de.staropensource.engine.base.utility.misc.Miscellaneous;
+import de.staropensource.engine.base.utility.misc.NumberUtil;
+import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
+import de.staropensource.engine.rendering.type.FrameHandler;
+import de.staropensource.engine.rendering.type.Window;
+import de.staropensource.engine.rendering.type.window.VsyncMode;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import static org.lwjgl.bgfx.BGFX.*;
+import static org.lwjgl.bgfx.BGFX.bgfx_frame;
+import static org.lwjgl.glfw.GLFW.glfwPollEvents;
+
+/**
+ * Contains the rendering code.
+ *
+ * @since v1-alpha9
+ */
+@SuppressWarnings("FieldCanBeLocal")
+final class RenderingCode {
+ /**
+ * Contains the frame count of second ago.
+ *
+ * @since v1-alpha9
+ */
+ private static long previousFrameCount = 0L;
+
+ /**
+ * Contains the current system time.
+ *
+ * This variable is used for calculations where
+ * system time is used over multiple instructions,
+ * potentially allowing unwanted deviations in
+ * time and therefore the calculations.
+ *
+ * @since v1-alpha9
+ */
+ private static long systemTimeNow;
+
+ /**
+ * Contains the system time at
+ * the end of the last frame.
+ *
+ * Used for delta time calculation.
+ *
+ * @since v1-alpha9
+ */
+ private static long systemTimePrevious = System.currentTimeMillis();
+
+ /**
+ * Contains a list of delta
+ * time values over one second.
+ *
+ * @since v1-alpha9
+ */
+ private static final LinkedList deltaTimes = new LinkedList<>();
+
+ /**
+ * Contains separate timings in one frame.
+ *
+ * @since v1-alpha9
+ */
+ private static final Map execTimes = new LinkedHashMap<>();
+
+ /**
+ * Contains the amount to wait until the next frame is allowed to pass.
+ *
+ * @since v1-alpha9
+ */
+ private static long timesWait;
+
+ /**
+ * Contains the time to wait until per-second
+ * operations can be executed again.
+ *
+ * @since v1-alpha9
+ */
+ private static long timesPSO = System.currentTimeMillis() + 1000;
+
+
+ /**
+ * Invokes all frame handlers.
+ *
+ * @since v1-alpha9
+ */
+ public static void invokeFrameHandlers() {
+ for (FrameHandler frameHandler : Renderer.getFrameHandlers())
+ execTimes.put("Frame handler '" + frameHandler.getClass().getName() + "'", Miscellaneous.measureExecutionTime(frameHandler::run));
+ }
+
+ /**
+ * Renders all windows out.
+ *
+ * @since v1-alpha9
+ */
+ public static void renderWindows() {
+ execTimes.put("Rendering", Miscellaneous.measureExecutionTime(() -> {
+ // Poll for events
+ glfwPollEvents();
+
+ // Reset backbuffer
+ resetBackBuffer();
+
+ // Render all windows
+ for (Window window : Window.getWindows())
+ if (window.isRendering()) {
+ window.updateState();
+ window.render();
+ }
+
+ // Advance to next frame
+ bgfx_frame(false);
+ }));
+ }
+
+ /**
+ * Resets bgfx's backbuffer.
+ *
+ * @since v1-alpha9
+ */
+ private static void resetBackBuffer() {
+ int resetSettings = 0;
+ if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON)
+ resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
+ for (Window window : Window.getWindows())
+ if (window.isTransparent())
+ resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
+
+ bgfx_reset(
+ Window.getWindows().getFirst().getSize().getX(),
+ Window.getWindows().getFirst().getSize().getY(),
+ resetSettings,
+ BGFX_TEXTURE_FORMAT_RGBA4
+ );
+ }
+
+ /**
+ * Waits for the next frame.
+ *
+ * @since v1-alpha9
+ */
+ public static void waitForNextFrame() {
+ execTimes.put("Waiting", 0L);
+ if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() > 0) {
+ execTimes.replace("Waiting", (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d));
+
+ for (String time : execTimes.keySet())
+ if (!time.equals("Waiting"))
+ execTimes.replace("Waiting", execTimes.get("Waiting") - execTimes.get(time));
+
+ // Wait until next frame
+ if (execTimes.get("Waiting") > 0) {
+ timesWait = execTimes.get("Waiting") + System.currentTimeMillis();
+
+ while (System.currentTimeMillis() < timesWait)
+ Thread.onSpinWait();
+ }
+ }
+ }
+
+ /**
+ * Performs per-frame operations.
+ *
+ * @since v1-alpha9
+ */
+ public static void performPerFrameOperations() {
+ Renderer.setFrameCount(Renderer.getFrameCount() + 1);
+ Renderer.setLastFrameTime(new HashMap<>(execTimes));
+ systemTimeNow = System.currentTimeMillis();
+ Renderer.setDeltaTime((double) (systemTimeNow - systemTimePrevious) / 1000);
+ systemTimePrevious = systemTimeNow;
+ deltaTimes.add(Renderer.getDeltaTime());
+ execTimes.clear();
+ }
+
+ /**
+ * Performs per-second operations.
+ *
+ * @since v1-alpha9
+ */
+ public static void performPerSecondOperations() {
+ // Perform per-second operations
+ if (System.currentTimeMillis() >= timesPSO) {
+ // Calculate FPS count
+ Renderer.setFramesPerSecond( 1 / NumberUtil.calculateMeanDouble(deltaTimes));
+
+ // Log frame count
+ if (RenderingSubsystemConfiguration.getInstance().isDebugFrames())
+ Logger.diag("Frames " + previousFrameCount + "-" + Renderer.getFrameCount() + "\n-> Frames/s: " + Renderer.getFramesPerSecond() + "\n-> Delta time: " + Renderer.getDeltaTime());
+
+ // Log window states
+ if (RenderingSubsystemConfiguration.getInstance().isDebugWindowStates())
+ for (Window window : Window.getWindows())
+ Logger.diag(
+ "Window state for " + window.getUniqueIdentifier() + "\n" +
+ "-> Terminated: " + window.isTerminated() + "\n" +
+ "-> Name: " + window.getName() + "\n" +
+ "-> Title: " + window.getTitle() + "\n" +
+ "-> Size: " + window.getSize() + "\n" +
+ " -> Minimum: " + window.getMinimumSize() + "\n" +
+ " -> Maximum: " + window.getMaximumSize() + "\n" +
+ "-> Position: " + window.getPosition() + "\n" +
+ "-> Mode: " + window.getMode() + "\n" +
+ "-> Resizable: " + window.isResizable() + "\n" +
+ "-> Borderless: " + window.isBorderless() + "\n" +
+ "-> Focused: " + window.isFocused() + "\n" +
+ "-> On top: " + window.isOnTop() + "\n" +
+ "-> Transparent: " + window.isTransparent() + "\n" +
+ "-> Rendering: " + window.isRendering()
+ );
+
+
+
+ // Reset per-second variables
+ previousFrameCount = Renderer.getFrameCount();
+ deltaTimes.clear();
+ timesPSO = System.currentTimeMillis() + 1000;
+ }
+ }
+}
diff --git a/testapp/src/main/java/de/staropensource/engine/testapp/Main.java b/testapp/src/main/java/de/staropensource/engine/testapp/Main.java
index 5ca6461..859d420 100644
--- a/testapp/src/main/java/de/staropensource/engine/testapp/Main.java
+++ b/testapp/src/main/java/de/staropensource/engine/testapp/Main.java
@@ -149,7 +149,7 @@ public final class Main {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
- //window.setPosition(new Vec2i((int) Renderer.getFrameCount() / 10, (int) Renderer.getFrameCount() / 10));
+ window.setPosition(new Vec2i((int) Renderer.getFrameCount() / 10, (int) Renderer.getFrameCount() / 10));
}
});