Relocate + split rendering code -> separate class
This should allow for more JIT optimizations
This commit is contained in:
parent
485b813853
commit
d71ff5db0d
3 changed files with 303 additions and 117 deletions
|
@ -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.
|
||||
* <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<>());
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <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
|
||||
@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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<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
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue