Relocate + split rendering code -> separate class
Some checks failed
build-and-test / build (push) Failing after 1m57s
build-and-test / generate-javadoc (push) Failing after 2m2s
build-and-test / test (push) Failing after 2m2s

This should allow for more JIT optimizations
This commit is contained in:
JeremyStar™ 2024-11-25 02:52:55 +01:00
parent 485b813853
commit d71ff5db0d
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
3 changed files with 303 additions and 117 deletions

View file

@ -19,22 +19,20 @@
package de.staropensource.engine.rendering.renderer; 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.Miscellaneous;
import de.staropensource.engine.base.utility.misc.NumberUtil; import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration; import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException; import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.type.FrameHandler; import de.staropensource.engine.rendering.type.FrameHandler;
import de.staropensource.engine.rendering.type.Window; import lombok.AccessLevel;
import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.*; import java.util.*;
import static org.lwjgl.bgfx.BGFX.*; import static org.lwjgl.bgfx.BGFX.*;
import static org.lwjgl.glfw.GLFW.*;
/** /**
* Renders all windows out. * Renders all windows out.
@ -52,7 +50,18 @@ public final class Renderer {
* to frame renders. * to frame renders.
* *
* @since v1-alpha9 * @since v1-alpha9
* -- GETTER --
* Returns all frame handlers.
* <p>
* 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<>()); 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 * @return amount of frames rendered
* @since v1-alpha9 * @since v1-alpha9
* -- SETTER --
* Sets the frame count aka.
* the amount of frames rendered.
* <p>
* Updated every frame.
*
* @param frameCount new amount of frames rendered
* @since v1-alpha9
*/ */
@Getter @Getter
@Setter(value = AccessLevel.PACKAGE)
private static long frameCount = 0L; private static long frameCount = 0L;
/** /**
@ -120,8 +138,20 @@ public final class Renderer {
* *
* @return delta time * @return delta time
* @since v1-alpha9 * @since v1-alpha9
* -- SETTER --
* Sets the delta time, also
* known as the render time.
* <p>
* Delta time is the time in seconds
* between the last and current frame.
* <p>
* Updated every frame.
*
* @param deltaTime new delta time
* @since v1-alpha9
*/ */
@Getter @Getter
@Setter(value = AccessLevel.PACKAGE)
private static double deltaTime = 0d; private static double deltaTime = 0d;
/** /**
@ -137,8 +167,16 @@ public final class Renderer {
* *
* @return frames per second * @return frames per second
* @since v1-alpha9 * @since v1-alpha9
* -- SETTER --
* Sets the frames per second (FPS) count.
* <p>
* Updated every second.
*
* @param framesPerSecond new frames per second
* @since v1-alpha9
*/ */
@Getter @Getter
@Setter(value = AccessLevel.PACKAGE)
private static double framesPerSecond = 0d; private static double framesPerSecond = 0d;
/** /**
@ -161,8 +199,17 @@ public final class Renderer {
* *
* @return last frame time * @return last frame time
* @since v1-alpha9 * @since v1-alpha9
* -- SETTER --
* Sets the time it took
* to calculate the last frame.
* <p>
* Updated every frame.
*
* @param lastFrameTime new last frame time
* @since v1-alpha9
*/ */
@Getter @Getter
@Setter(value = AccessLevel.PACKAGE)
private static Map<@NotNull String, @NotNull Long> lastFrameTime = Collections.unmodifiableMap(new HashMap<>()); private static Map<@NotNull String, @NotNull Long> lastFrameTime = Collections.unmodifiableMap(new HashMap<>());
@ -287,119 +334,13 @@ public final class Renderer {
* @since v1-alpha9 * @since v1-alpha9
*/ */
@SuppressWarnings({ "InfiniteLoopStatement" }) @SuppressWarnings({ "InfiniteLoopStatement" })
private static void render() throws Throwable { private static void render() {
long previousFrameCount = 0L; // Frame count one second ago
long systemTimeNow; // Current system time
long systemTimePrevious = System.currentTimeMillis(); // Previous system time
LinkedList<Double> deltaTimes = new LinkedList<>(); // Contains all delta time fractions
Map<String, Long> 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
while (true) { while (true) {
// Invoke frame handlers RenderingCode.invokeFrameHandlers();
for (FrameHandler frameHandler : frameHandlers) RenderingCode.renderWindows();
execTimes.put("Frame handler '" + frameHandler.getClass().getName() + "'", Miscellaneous.measureExecutionTime(frameHandler::run)); RenderingCode.waitForNextFrame();
RenderingCode.performPerFrameOperations();
RenderingCode.performPerSecondOperations();
// 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;
}
} }
} }
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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.
* <p>
* 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.
* <p>
* 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<Double> deltaTimes = new LinkedList<>();
/**
* Contains separate timings in one frame.
*
* @since v1-alpha9
*/
private static final Map<String, Long> 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;
}
}
}

View file

@ -149,7 +149,7 @@ public final class Main {
if (shutdown || window.isClosureRequested()) if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown(); 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));
} }
}); });