forked from StarOpenSource/Engine
Add render loop
This commit is contained in:
parent
0b4502c3ce
commit
bbde2e9d2d
7 changed files with 241 additions and 73 deletions
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||||
|
* Copyright (c) 2024 The StarOpenSource Engine Contributors
|
||||||
|
* 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.sosengine.graphics.glfw.classes;
|
||||||
|
|
||||||
|
import de.staropensource.sosengine.base.logging.LoggerInstance;
|
||||||
|
import de.staropensource.sosengine.base.types.CodePart;
|
||||||
|
import de.staropensource.sosengine.base.types.logging.LogIssuer;
|
||||||
|
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
||||||
|
import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
|
||||||
|
import de.staropensource.sosengine.graphics.classes.Window;
|
||||||
|
import de.staropensource.sosengine.graphics.glfw.exceptions.NotOnMainThreadException;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.OptionalDouble;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class GlfwManagementClass implements ApiManagementClass {
|
||||||
|
/**
|
||||||
|
* Contains the {@link LoggerInstance} for this class.
|
||||||
|
*
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
protected LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.ENGINE));
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public boolean mustRunOnMainThread() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop() {
|
||||||
|
// Ensure running on the main thread
|
||||||
|
if (!Miscellaneous.onMainThread())
|
||||||
|
throw new NotOnMainThreadException();
|
||||||
|
|
||||||
|
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (Window window : Window.getWindows()) {
|
||||||
|
try {
|
||||||
|
window.updateState();
|
||||||
|
window.render();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||||
|
throwables.put(window, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
return throwables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoopContinuously(@NotNull Runnable frameCode) {
|
||||||
|
// Ensure running on the main thread
|
||||||
|
if (!Miscellaneous.onMainThread())
|
||||||
|
throw new NotOnMainThreadException();
|
||||||
|
|
||||||
|
long renderTime = 0L;
|
||||||
|
LinkedList<Long> renderTimes = new LinkedList<>();
|
||||||
|
AtomicReference<LinkedHashMap<@NotNull Window, @NotNull Throwable>> output = new AtomicReference<>(new LinkedHashMap<>());
|
||||||
|
|
||||||
|
// Run while the output linked map of runRenderLoop is empty
|
||||||
|
while (output.get().isEmpty()) {
|
||||||
|
while (renderTimes.size() >= 10)
|
||||||
|
renderTimes.removeLast();
|
||||||
|
|
||||||
|
renderTimes.add(Miscellaneous.measureExecutionTime(() -> {
|
||||||
|
output.set(runRenderLoop());
|
||||||
|
frameCode.run();
|
||||||
|
}));
|
||||||
|
renderTime += renderTimes.getLast();
|
||||||
|
|
||||||
|
// Report
|
||||||
|
if (renderTime >= 1000) {
|
||||||
|
OptionalDouble average = renderTimes.stream().mapToDouble(a -> a).average();
|
||||||
|
if (average.isPresent())
|
||||||
|
logger.info("Delta average: " + average.getAsDouble() + " | Frames/s: " + 1000 / average.getAsDouble());
|
||||||
|
else
|
||||||
|
logger.error("Unable to get delta average and FPS");
|
||||||
|
|
||||||
|
renderTimes.clear();
|
||||||
|
renderTime = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.get();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import de.staropensource.sosengine.base.exceptions.UnexpectedThrowableException;
|
||||||
import de.staropensource.sosengine.base.types.Tristate;
|
import de.staropensource.sosengine.base.types.Tristate;
|
||||||
import de.staropensource.sosengine.base.types.vectors.Vec2i;
|
import de.staropensource.sosengine.base.types.vectors.Vec2i;
|
||||||
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
||||||
import de.staropensource.sosengine.graphics.GraphicsSubsystemConfiguration;
|
|
||||||
import de.staropensource.sosengine.graphics.classes.Window;
|
import de.staropensource.sosengine.graphics.classes.Window;
|
||||||
import de.staropensource.sosengine.graphics.events.GraphicsApiErrorEvent;
|
import de.staropensource.sosengine.graphics.events.GraphicsApiErrorEvent;
|
||||||
import de.staropensource.sosengine.graphics.events.InputEvent;
|
import de.staropensource.sosengine.graphics.events.InputEvent;
|
||||||
|
@ -144,7 +143,6 @@ public abstract class GlfwWindow extends Window {
|
||||||
glfwWindowHint(GLFW_CENTER_CURSOR, 0);
|
glfwWindowHint(GLFW_CENTER_CURSOR, 0);
|
||||||
glfwWindowHint(GLFW_FOCUSED, Miscellaneous.getIntegerizedBoolean(focused));
|
glfwWindowHint(GLFW_FOCUSED, Miscellaneous.getIntegerizedBoolean(focused));
|
||||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, Miscellaneous.getIntegerizedBoolean(isTransparent()));
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, Miscellaneous.getIntegerizedBoolean(isTransparent()));
|
||||||
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, Miscellaneous.getIntegerizedBoolean(GraphicsSubsystemConfiguration.getInstance().isDisallowIntegratedGraphics()));
|
|
||||||
glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName());
|
glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName());
|
||||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, getName());
|
glfwWindowHintString(GLFW_X11_CLASS_NAME, getName());
|
||||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName());
|
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName());
|
||||||
|
@ -163,8 +161,8 @@ public abstract class GlfwWindow extends Window {
|
||||||
// Own context
|
// Own context
|
||||||
ownContext();
|
ownContext();
|
||||||
|
|
||||||
// Set swap interval based on isDisallowTearing setting
|
// Set swap interval based on V-Sync mode setting
|
||||||
glfwSwapInterval(Miscellaneous.getIntegerizedBoolean(GraphicsSubsystemConfiguration.getInstance().isDisallowTearing()));
|
glfwSwapInterval(getVsyncMode() == VsyncMode.ON ? 1 : 0);
|
||||||
|
|
||||||
// Create callbacks
|
// Create callbacks
|
||||||
keyCallback = GLFWKeyCallback.create(new KeyCallback(this));
|
keyCallback = GLFWKeyCallback.create(new KeyCallback(this));
|
||||||
|
@ -228,12 +226,6 @@ public abstract class GlfwWindow extends Window {
|
||||||
if (!Miscellaneous.onMainThread())
|
if (!Miscellaneous.onMainThread())
|
||||||
throw new NotOnMainThreadException();
|
throw new NotOnMainThreadException();
|
||||||
|
|
||||||
// Own context
|
|
||||||
ownContext();
|
|
||||||
|
|
||||||
// Set swap interval based on isDisallowTearing setting
|
|
||||||
glfwSwapInterval(Miscellaneous.getIntegerizedBoolean(GraphicsSubsystemConfiguration.getInstance().isDisallowTearing()));
|
|
||||||
|
|
||||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||||
IntBuffer width = stack.mallocInt(2);
|
IntBuffer width = stack.mallocInt(2);
|
||||||
IntBuffer height = stack.mallocInt(2);
|
IntBuffer height = stack.mallocInt(2);
|
||||||
|
@ -279,13 +271,15 @@ public abstract class GlfwWindow extends Window {
|
||||||
// Ensure the window is not terminated
|
// Ensure the window is not terminated
|
||||||
if (isTerminated())
|
if (isTerminated())
|
||||||
return;
|
return;
|
||||||
|
// Ensure rendering is enabled
|
||||||
|
if (!isRendering())
|
||||||
|
return;
|
||||||
|
|
||||||
// Ensure running on the main thread
|
// Ensure running on the main thread
|
||||||
if (!Miscellaneous.onMainThread())
|
if (!Miscellaneous.onMainThread())
|
||||||
throw new NotOnMainThreadException();
|
throw new NotOnMainThreadException();
|
||||||
|
|
||||||
glfwSwapBuffers(identifierLong);
|
glfwSwapBuffers(identifierLong);
|
||||||
glfwPollEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------ [ GLFW handling ] ------------------------------------------------ //
|
// ------------------------------------------------ [ GLFW handling ] ------------------------------------------------ //
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package de.staropensource.sosengine.graphics.opengl;
|
package de.staropensource.sosengine.graphics.opengl;
|
||||||
|
|
||||||
import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
|
import de.staropensource.sosengine.graphics.glfw.classes.GlfwManagementClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OpenGL API management class.
|
* The OpenGL API management class.
|
||||||
|
@ -27,15 +27,11 @@ import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
|
||||||
* @since v1-alpha0
|
* @since v1-alpha0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unused" })
|
@SuppressWarnings({ "unused" })
|
||||||
public final class OpenGlManagement implements ApiManagementClass {
|
public final class OpenGlManagement extends GlfwManagementClass {
|
||||||
/**
|
/**
|
||||||
* Constructs this class.
|
* Constructs this class.
|
||||||
|
*
|
||||||
|
* @since v1-alpha2
|
||||||
*/
|
*/
|
||||||
public OpenGlManagement() {}
|
public OpenGlManagement() {}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public boolean mustRunOnMainThread() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,21 @@ public final class GraphicsSubsystemConfiguration implements SubsystemConfigurat
|
||||||
*/
|
*/
|
||||||
private boolean debugInput;
|
private boolean debugInput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, will log the delta time average and FPS count
|
||||||
|
* to the console every second.
|
||||||
|
*
|
||||||
|
* @since v1-alpha2
|
||||||
|
*
|
||||||
|
* -- GETTER --
|
||||||
|
* Gets the value for {@link #debugFrames}.
|
||||||
|
*
|
||||||
|
* @return variable value
|
||||||
|
* @see #debugFrames
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
private boolean debugFrames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If enabled, graphical errors thrown by GLFW will be printed to the log by the subsystem.
|
* If enabled, graphical errors thrown by GLFW will be printed to the log by the subsystem.
|
||||||
*
|
*
|
||||||
|
@ -113,34 +128,21 @@ public final class GraphicsSubsystemConfiguration implements SubsystemConfigurat
|
||||||
private boolean errorGraphicsError;
|
private boolean errorGraphicsError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If enabled, will make the Graphics API try to prevent tearing from happening.
|
* Determines how many frames can be rendered max per second.
|
||||||
*
|
|
||||||
* @since v1-alpha2
|
|
||||||
*
|
|
||||||
* -- GETTER --
|
|
||||||
* Gets the value for {@link #disallowTearing}.
|
|
||||||
*
|
|
||||||
* @return variable value
|
|
||||||
* @see GraphicsSubsystemConfiguration#disallowTearing
|
|
||||||
* @since v1-alpha2
|
|
||||||
*/
|
|
||||||
private boolean disallowTearing;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If enabled, will make the Graphics API try to prevent using the integrated graphics card in your computer.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Will have no effect if no integrated or discrete graphics card is installed in the system.
|
* This value will have no effect on windows with V-Sync enabled.
|
||||||
|
* Set to {@code 0} for no limit.
|
||||||
*
|
*
|
||||||
* @since v1-alpha2
|
* @since v1-alpha2
|
||||||
*
|
*
|
||||||
* -- GETTER --
|
* -- GETTER --
|
||||||
* Gets the value for {@link #disallowIntegratedGraphics}.
|
* Gets the value for {@link #maximumFramesPerSecond}
|
||||||
*
|
*
|
||||||
* @return variable value
|
* @return variable value
|
||||||
* @see GraphicsSubsystemConfiguration#disallowIntegratedGraphics
|
* @see #maximumFramesPerSecond
|
||||||
* @since v1-alpha2
|
* @since v1-alpha2
|
||||||
*/
|
*/
|
||||||
private boolean disallowIntegratedGraphics;
|
private int maximumFramesPerSecond;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this class.
|
* Constructs this class.
|
||||||
|
@ -175,17 +177,20 @@ public final class GraphicsSubsystemConfiguration implements SubsystemConfigurat
|
||||||
switch (property) {
|
switch (property) {
|
||||||
case "debug" -> debug = parser.getBoolean(group + property);
|
case "debug" -> debug = parser.getBoolean(group + property);
|
||||||
case "debugInput" -> debugInput = parser.getBoolean(group + property);
|
case "debugInput" -> debugInput = parser.getBoolean(group + property);
|
||||||
|
case "debugFrames" -> debugFrames = parser.getBoolean(group + property);
|
||||||
|
|
||||||
case "errorGraphicsError" -> errorGraphicsError = parser.getBoolean(group + property);
|
case "errorGraphicsError" -> errorGraphicsError = parser.getBoolean(group + property);
|
||||||
|
|
||||||
case "disallowTearing" -> disallowTearing = parser.getBoolean(group + property);
|
case "maximumFramesPerSecond" -> maximumFramesPerSecond = parser.getInteger(group + property, true);
|
||||||
}
|
}
|
||||||
} catch (NullPointerException ignored) {}
|
} catch (NullPointerException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable all debug options if 'debug' is disabled
|
// Disable all debug options if 'debug' is disabled
|
||||||
if (!debug)
|
if (!debug) {
|
||||||
debugInput = false;
|
debugInput = false;
|
||||||
|
debugFrames = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
|
@ -197,10 +202,11 @@ public final class GraphicsSubsystemConfiguration implements SubsystemConfigurat
|
||||||
public void loadDefaultConfiguration() {
|
public void loadDefaultConfiguration() {
|
||||||
debug = false;
|
debug = false;
|
||||||
debugInput = false;
|
debugInput = false;
|
||||||
|
debugFrames = false;
|
||||||
|
|
||||||
errorGraphicsError = true;
|
errorGraphicsError = true;
|
||||||
|
|
||||||
disallowTearing = false;
|
maximumFramesPerSecond = 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
|
@ -209,10 +215,11 @@ public final class GraphicsSubsystemConfiguration implements SubsystemConfigurat
|
||||||
switch (setting) {
|
switch (setting) {
|
||||||
case "debug" -> { return debug; }
|
case "debug" -> { return debug; }
|
||||||
case "debugInput" -> { return debugInput; }
|
case "debugInput" -> { return debugInput; }
|
||||||
|
case "debugFrames" -> { return debugFrames; }
|
||||||
|
|
||||||
case "errorGraphicsError" -> { return errorGraphicsError; }
|
case "errorGraphicsError" -> { return errorGraphicsError; }
|
||||||
|
|
||||||
case "disallowTearing" -> { return disallowTearing; }
|
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
|
||||||
default -> { return null; }
|
default -> { return null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
package de.staropensource.sosengine.graphics.classes;
|
package de.staropensource.sosengine.graphics.classes;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface for Graphics API management classes.
|
* The interface for Graphics API management classes.
|
||||||
*
|
*
|
||||||
|
@ -33,4 +37,25 @@ public interface ApiManagementClass {
|
||||||
* @since v1-alpha2
|
* @since v1-alpha2
|
||||||
*/
|
*/
|
||||||
boolean mustRunOnMainThread();
|
boolean mustRunOnMainThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the render loop <b>once</b>.
|
||||||
|
* To run the render loop continuously, see {@link #runRenderLoopContinuously()}.
|
||||||
|
*
|
||||||
|
* @return map of windows and their thrown throwables
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the render loop <b>for ever</b>.
|
||||||
|
* To run the render loop only once, see {@link #runRenderLoop()}.
|
||||||
|
* <p>
|
||||||
|
* Immediately returns with when a {@link #runRenderLoop()} call fails.
|
||||||
|
*
|
||||||
|
* @param frameCode code that should be invoked on during a frame. will be counted to frame time
|
||||||
|
* @return see {@link #runRenderLoop()}
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoopContinuously(@NotNull Runnable frameCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,8 +494,8 @@ public abstract class Window implements AutoCloseable {
|
||||||
* @since v1-alpha2
|
* @since v1-alpha2
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public static ImmutableHashSet<@NotNull Window> getWindows() {
|
public static HashSet<@NotNull Window> getWindows() {
|
||||||
return new ImmutableHashSet<>(windows);
|
return new HashSet<>(windows);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,13 +27,22 @@ import de.staropensource.sosengine.base.types.CodePart;
|
||||||
import de.staropensource.sosengine.base.types.logging.LogIssuer;
|
import de.staropensource.sosengine.base.types.logging.LogIssuer;
|
||||||
import de.staropensource.sosengine.base.types.vectors.Vec2i;
|
import de.staropensource.sosengine.base.types.vectors.Vec2i;
|
||||||
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
import de.staropensource.sosengine.base.utility.Miscellaneous;
|
||||||
|
import de.staropensource.sosengine.base.utility.parser.StackTraceParser;
|
||||||
import de.staropensource.sosengine.graphics.GraphicsSubsystem;
|
import de.staropensource.sosengine.graphics.GraphicsSubsystem;
|
||||||
import de.staropensource.sosengine.graphics.classes.ApiMainClass;
|
import de.staropensource.sosengine.graphics.classes.ApiMainClass;
|
||||||
import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
|
import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
|
||||||
import de.staropensource.sosengine.graphics.classes.Window;
|
import de.staropensource.sosengine.graphics.classes.Window;
|
||||||
|
import de.staropensource.sosengine.graphics.events.InputEvent;
|
||||||
|
import de.staropensource.sosengine.graphics.types.input.Key;
|
||||||
|
import de.staropensource.sosengine.graphics.types.input.KeyState;
|
||||||
|
import de.staropensource.sosengine.graphics.types.window.VsyncMode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The initialization class for sos!engine's development application.
|
* The initialization class for sos!engine's development application.
|
||||||
|
@ -75,6 +84,13 @@ public class Main {
|
||||||
*/
|
*/
|
||||||
private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.APPLICATION));
|
private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.APPLICATION));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for terminating the render loop.
|
||||||
|
*
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
private boolean shutdown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this class.
|
* Constructs this class.
|
||||||
*/
|
*/
|
||||||
|
@ -123,40 +139,38 @@ public class Main {
|
||||||
.setTitle("test application window")
|
.setTitle("test application window")
|
||||||
.setSize(new Vec2i(960, 540))
|
.setSize(new Vec2i(960, 540))
|
||||||
.setPosition(new Vec2i(10, 10))
|
.setPosition(new Vec2i(10, 10))
|
||||||
|
.setVsyncMode(VsyncMode.OFF)
|
||||||
.build();
|
.build();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
logger.crash("Window.Builder#build() failed", throwable);
|
logger.crash("Window.Builder#build() failed", throwable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep for 2.5 seconds
|
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = GraphicsSubsystem
|
||||||
logger.diag("Sleeping for 2.5s");
|
.getInstance()
|
||||||
try {
|
.getApi()
|
||||||
Thread.sleep(2500);
|
.getManagement()
|
||||||
} catch (InterruptedException exception) {
|
.runRenderLoopContinuously(() -> {
|
||||||
logger.crash("Was unable to sleep for 2500ms", exception);
|
if (shutdown || window.isClosureRequested())
|
||||||
|
Engine.getInstance().shutdown();
|
||||||
|
});
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
message.append("Render loop failed on some windows:\n");
|
||||||
|
|
||||||
|
for (Window windowFailed : renderLoopFailures.keySet()) {
|
||||||
|
StackTraceParser parser = new StackTraceParser(renderLoopFailures.get(windowFailed));
|
||||||
|
|
||||||
|
message
|
||||||
|
.append("-> ")
|
||||||
|
.append(window)
|
||||||
|
.append(": ")
|
||||||
|
.append(parser.getHeader())
|
||||||
|
.append("\n")
|
||||||
|
.append(parser.getStackTrace())
|
||||||
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state and render window
|
logger.crash(message.toString());
|
||||||
try {
|
|
||||||
logger.diag("Updating state");
|
|
||||||
window.updateState();
|
|
||||||
logger.diag("Rendering");
|
|
||||||
window.render();
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
logger.crash("Window updating or rendering failed", throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep for five seconds
|
|
||||||
logger.diag("Sleeping for 2.5s");
|
|
||||||
try {
|
|
||||||
Thread.sleep(2500);
|
|
||||||
} catch (InterruptedException exception) {
|
|
||||||
logger.crash("Was unable to sleep for 2500ms", exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown
|
|
||||||
Engine.getInstance().shutdown();
|
|
||||||
}, "mainThread");
|
}, "mainThread");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,9 +181,24 @@ public class Main {
|
||||||
* @since v1-alpha2
|
* @since v1-alpha2
|
||||||
*/
|
*/
|
||||||
@EventListener(event = ThrowableCatchEvent.class)
|
@EventListener(event = ThrowableCatchEvent.class)
|
||||||
public static void onThrowable(@NotNull Throwable throwable, @NotNull String identifier) {
|
private static void onThrowable(@NotNull Throwable throwable, @NotNull String identifier) {
|
||||||
if (identifier.equals("mainThread")) {
|
if (identifier.equals("mainThread") && instance != null)
|
||||||
instance.logger.crash("The main thread threw an exception", throwable);
|
instance.logger.crash("The main thread threw an exception", throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles input events.
|
||||||
|
*
|
||||||
|
* @param window origin window
|
||||||
|
* @param key key pressed
|
||||||
|
* @param state key state
|
||||||
|
* @since v1-alpha2
|
||||||
|
*/
|
||||||
|
@EventListener(event = InputEvent.class)
|
||||||
|
private static void onInput(@Nullable Window window, @NotNull Key key, @NotNull KeyState state) {
|
||||||
|
if (key == Key.ESCAPE && instance != null) {
|
||||||
|
instance.logger.diag("ESC pressed, setting shutdown flag");
|
||||||
|
instance.shutdown = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue