Very experimental rendering subsystem changes
Some checks failed
build-and-test / build (push) Failing after 2m4s
build-and-test / test (push) Failing after 2m4s
build-and-test / generate-javadoc (push) Failing after 2m8s

This commit is contained in:
JeremyStar™ 2024-11-24 16:39:11 +01:00
parent 45ec057457
commit c89e65882c
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
12 changed files with 997 additions and 228 deletions

View file

@ -21,25 +21,30 @@ package de.staropensource.engine.rendering;
import de.staropensource.engine.base.annotation.EngineSubsystem; import de.staropensource.engine.base.annotation.EngineSubsystem;
import de.staropensource.engine.base.annotation.EventListener; import de.staropensource.engine.base.annotation.EventListener;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.SubsystemClass; import de.staropensource.engine.base.implementable.SubsystemClass;
import de.staropensource.engine.base.implementable.helper.EventHelper; import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.type.EventPriority; import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import de.staropensource.engine.base.utility.information.EngineInformation; import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem; import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
import de.staropensource.engine.base.event.InternalEngineShutdownEvent; import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
import de.staropensource.engine.base.type.DependencyVector; import de.staropensource.engine.base.type.DependencyVector;
import de.staropensource.engine.base.utility.Miscellaneous; import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.rendering.event.InputEvent; import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.event.RenderingErrorEvent; import de.staropensource.engine.rendering.event.RenderingErrorEvent;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException; import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.renderer.Renderer;
import de.staropensource.engine.rendering.type.Window; import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.window.RenderingPlatform;
import de.staropensource.engine.rendering.type.window.VsyncMode; import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -117,8 +122,13 @@ public final class RenderingSubsystem extends SubsystemClass {
// Precompute event listeners // Precompute event listeners
cacheEvents(); cacheEvents();
// Initialize GLFW
initGlfw(); initGlfw();
// Initialize renderer
Renderer.initialize();
// Warn about subsystem and API instability // Warn about subsystem and API instability
Logger.warn("The rendering subsystem is experimental. Subsystem and API stability are not guaranteed."); Logger.warn("The rendering subsystem is experimental. Subsystem and API stability are not guaranteed.");
} }
@ -150,10 +160,10 @@ public final class RenderingSubsystem extends SubsystemClass {
Logger.diag("Setting initialization hints"); Logger.diag("Setting initialization hints");
switch (RenderingSubsystemConfiguration.getInstance().getInitialPlatform()) { switch (RenderingSubsystemConfiguration.getInstance().getInitialPlatform()) {
case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM); case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND); case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND, RenderingPlatform.WAYLAND);
case X11 -> tryPlatform(GLFW_PLATFORM_X11); case X11 -> tryPlatform(GLFW_PLATFORM_X11, RenderingPlatform.X11);
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32); case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32, RenderingPlatform.WIN32);
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA); case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA, RenderingPlatform.COCOA);
case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL); case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL);
} }
glfwInitHint(GLFW_WAYLAND_LIBDECOR, RenderingSubsystemConfiguration.getInstance().isInitialDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR); glfwInitHint(GLFW_WAYLAND_LIBDECOR, RenderingSubsystemConfiguration.getInstance().isInitialDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR);
@ -182,7 +192,7 @@ public final class RenderingSubsystem extends SubsystemClass {
long shutdownTime = Miscellaneous.measureExecutionTime(() -> { long shutdownTime = Miscellaneous.measureExecutionTime(() -> {
// Close all windows // Close all windows
for (Window window : Window.getWindows()) for (Window window : new ArrayList<>(Window.getWindows()))
window.close(); window.close();
instance.errorCallback.free(); instance.errorCallback.free();
@ -198,7 +208,7 @@ public final class RenderingSubsystem extends SubsystemClass {
* @see RenderingSubsystemConfiguration#errorRenderingFailures * @see RenderingSubsystemConfiguration#errorRenderingFailures
* @since v1-alpha9 * @since v1-alpha9
*/ */
@EventListener(event = RenderingErrorEvent.class, priority = EventPriority.EXCLUSIVELY_IMPORTANT) @EventListener(event = RenderingErrorEvent.class, priority = Event.Priority.EXCLUSIVELY_IMPORTANT)
private static void logRenderingError(@NotNull String error) { private static void logRenderingError(@NotNull String error) {
Logger.error("Rendering error occurred: " + error); Logger.error("Rendering error occurred: " + error);
} }
@ -220,6 +230,9 @@ public final class RenderingSubsystem extends SubsystemClass {
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>(); LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
// Poll for events
glfwPollEvents();
// Update and render all windows // Update and render all windows
for (Window window : Window.getWindows()) { for (Window window : Window.getWindows()) {
if (!window.isRendering()) if (!window.isRendering())
@ -232,14 +245,14 @@ public final class RenderingSubsystem extends SubsystemClass {
Logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage())); Logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
throwables.put(window, throwable); throwables.put(window, throwable);
} }
}
// Poll for events //bgfx_frame(false);
glfwPollEvents(); }
return throwables; return throwables;
} }
/** /**
*
* Renders all windows continuously. * Renders all windows continuously.
* To render all windows just once, invoke * To render all windows just once, invoke
* {@link #renderWindows()} instead. * {@link #renderWindows()} instead.
@ -270,6 +283,8 @@ public final class RenderingSubsystem extends SubsystemClass {
if (!RenderingSubsystemConfiguration.getInstance().isDebugFrames()) if (!RenderingSubsystemConfiguration.getInstance().isDebugFrames())
reportDuration = Long.MAX_VALUE; reportDuration = Long.MAX_VALUE;
Logger.info("Entering render loop");
// Run while the 'output' is empty // Run while the 'output' is empty
while (output.get().isEmpty()) { while (output.get().isEmpty()) {
renderTime = Miscellaneous.measureExecutionTime(() -> { renderTime = Miscellaneous.measureExecutionTime(() -> {
@ -293,11 +308,33 @@ public final class RenderingSubsystem extends SubsystemClass {
Thread.onSpinWait(); Thread.onSpinWait();
} }
// Calculate delta time and frame count every second // Calculate delta time and frame count every second
if (System.currentTimeMillis() >= reportDuration) { if (System.currentTimeMillis() >= reportDuration) {
deltaTime = Math.getMeanLong(splitDeltaTime); // Calculate delta time deltaTime = NumberUtil.calculateMeanLong(splitDeltaTime); // Calculate delta time
Logger.diag("Delta time average: " + deltaTime + " | Frames/s: " + 1000 / deltaTime); // Print delta time and frame count to console Logger.diag("Delta time average: " + deltaTime + " | Frames/s: " + 1000 / deltaTime); // Print delta time and frame count to console
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" +
"-> Monitor: " + window.getMonitor() + "\n" +
"-> Resizable: " + window.isResizable() + "\n" +
"-> Borderless: " + window.isBorderless() + "\n" +
"-> Focusable: " + window.isFocusable() + "\n" +
"-> On top: " + window.isOnTop() + "\n" +
"-> Transparent: " + window.isTransparent() + "\n" +
"-> Rendering: " + window.isRendering()
);
reportDuration = System.currentTimeMillis() + 1000; // Update 'reportDuration' reportDuration = System.currentTimeMillis() + 1000; // Update 'reportDuration'
splitDeltaTime.clear(); // Clear 'splitDeltaTime' list splitDeltaTime.clear(); // Clear 'splitDeltaTime' list
} }
@ -312,12 +349,21 @@ public final class RenderingSubsystem extends SubsystemClass {
* and if so, specifies it as the platform to use. * and if so, specifies it as the platform to use.
* *
* @param platform platform to try * @param platform platform to try
* @param renderingPlatform {@link RenderingPlatform} used to log that the platform is unsupported (set to {@code null} to disable)
* @since v1-alpha9 * @since v1-alpha9
*/ */
private void tryPlatform(int platform) { private void tryPlatform(int platform, @Nullable RenderingPlatform renderingPlatform) {
if (glfwPlatformSupported(platform)) if (glfwPlatformSupported(platform))
glfwInitHint(GLFW_PLATFORM, platform); if (platform != GLFW_PLATFORM_WAYLAND)
else glfwInitHint(GLFW_PLATFORM, platform);
else {
Logger.warn("Wayland is not supported by the StarOpenSource Engine due to various issues with it, sorry.");
tryPlatform(GLFW_PLATFORM_X11, RenderingPlatform.X11);
}
else {
if (renderingPlatform != null)
Logger.warn("Platform RenderingPlatform." + renderingPlatform.name() + " is not supported GLFW. Using RenderingPlatform.ANY instead");
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM); glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
}
} }
} }

View file

@ -23,6 +23,7 @@ import de.staropensource.engine.base.implementable.Configuration;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.PropertiesReader; import de.staropensource.engine.base.utility.PropertiesReader;
import de.staropensource.engine.rendering.event.RenderingErrorEvent; import de.staropensource.engine.rendering.event.RenderingErrorEvent;
import de.staropensource.engine.rendering.type.window.RenderingAdapter;
import de.staropensource.engine.rendering.type.window.RenderingPlatform; import de.staropensource.engine.rendering.type.window.RenderingPlatform;
import de.staropensource.engine.rendering.type.window.VsyncMode; import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter; import lombok.Getter;
@ -97,24 +98,48 @@ public final class RenderingSubsystemConfiguration extends Configuration {
* Contains whether or not the delta time and * Contains whether or not the delta time and
* FPS count should be logged to the console * FPS count should be logged to the console
* every second. * every second.
* <p>
* Changes will no longer be picked up as
* soon as the rendering loop is running.
* *
* @since v1-alpha9 * @since v1-alpha9
* -- GETTER -- * -- GETTER --
* Returns whether or not the delta time and * Returns whether or not the delta time and
* FPS count should be logged to the console * FPS count should be logged to the console
* every second. * every second.
* <p>
* Changes will no longer be picked up as
* soon as the rendering loop is running.
* *
* @return print delta time and FPS count? * @return print delta time and FPS count?
* @since v1-alpha9 * @since v1-alpha9
*/ */
private boolean debugFrames; private boolean debugFrames;
/**
* Contains if to log the state of all
* windows every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns if to log the state of all
* windows every second.
*
* @return log states of all windows?
* @since v1-alpha9
*/
private boolean debugWindowStates;
/**
* Contains if to allow updates to a window's
* position. May cause errors and crashes to
* appear, we do not know why.
*
* @since v1-alpha9
* -- GETTER --
* Returns if to allow updates to a window's
* position. May cause errors and crashes to
* appear, we do not know why.
*
* @return allow window position updates?
* @since v1-alpha9
*/
private boolean debugAllowPositionUpdates;
/** /**
* Contains the platform GLFW shall try initialising. * Contains the platform GLFW shall try initialising.
@ -172,6 +197,18 @@ public final class RenderingSubsystemConfiguration extends Configuration {
private boolean errorRenderingFailures; private boolean errorRenderingFailures;
/**
* Contains the adapter bgfx shall use.
*
* @since v1-alpha9
* -- GETTER --
* Returns the adapter bgfx shall use.
*
* @return bgfx adapter
* @since v1-alpha9
*/
private RenderingAdapter renderingAdapter;
/** /**
* Contains which {@link VsyncMode} to use. * Contains which {@link VsyncMode} to use.
* <p> * <p>
@ -241,18 +278,27 @@ public final class RenderingSubsystemConfiguration extends Configuration {
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 "debugFrames" -> debugFrames = parser.getBoolean(group + property);
case "debugWindowStates" -> debugWindowStates = parser.getBoolean(group + property);
case "debugAllowPositionUpdates" -> debugAllowPositionUpdates = parser.getBoolean(group + property);
case "initialPlatform" -> { case "initialPlatform" -> {
try { try {
initialPlatform = RenderingPlatform.valueOf(parser.getString(group + property).toUpperCase()); initialPlatform = RenderingPlatform.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
Logger.error("Platform " + parser.getString(group + property) + " is not valid"); Logger.error("Rendering platform " + parser.getString(group + property) + " is not valid");
} }
} }
case "initialDisableLibdecor" -> initialDisableLibdecor = parser.getBoolean(group + property); case "initialDisableLibdecor" -> initialDisableLibdecor = parser.getBoolean(group + property);
case "errorRenderingFailures" -> errorRenderingFailures = parser.getBoolean(group + property); case "errorRenderingFailures" -> errorRenderingFailures = parser.getBoolean(group + property);
case "renderingAdapter" -> {
try {
renderingAdapter = RenderingAdapter.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException exception) {
Logger.error("Rendering adapter " + parser.getString(group + property) + " is not valid");
}
}
case "vsyncMode" -> { case "vsyncMode" -> {
try { try {
vsyncMode = VsyncMode.valueOf(parser.getString(group + property).toUpperCase()); vsyncMode = VsyncMode.valueOf(parser.getString(group + property).toUpperCase());
@ -271,21 +317,26 @@ public final class RenderingSubsystemConfiguration extends Configuration {
if (!debug) { if (!debug) {
debugInput = false; debugInput = false;
debugFrames = false; debugFrames = false;
debugWindowStates = false;
debugAllowPositionUpdates = false;
} }
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void loadDefaultConfiguration() { public void loadDefaultConfiguration() {
debug = false; debug = true;
debugInput = false; debugInput = false;
debugFrames = false; debugFrames = true;
debugWindowStates = true;
debugAllowPositionUpdates = true;
initialPlatform = RenderingPlatform.ANY; initialPlatform = RenderingPlatform.ANY;
initialDisableLibdecor = false; initialDisableLibdecor = false;
errorRenderingFailures = true; errorRenderingFailures = true;
renderingAdapter = RenderingAdapter.ANY;
vsyncMode = VsyncMode.ON; vsyncMode = VsyncMode.ON;
maximumFramesPerSecond = 60; maximumFramesPerSecond = 60;
} }
@ -297,12 +348,15 @@ public final class RenderingSubsystemConfiguration extends Configuration {
case "debug" -> { return debug; } case "debug" -> { return debug; }
case "debugInput" -> { return debugInput; } case "debugInput" -> { return debugInput; }
case "debugFrames" -> { return debugFrames; } case "debugFrames" -> { return debugFrames; }
case "debugWindowStates" -> { return debugWindowStates; }
case "debugAllowPositionUpdates" -> { return debugAllowPositionUpdates; }
case "initialPlatform" -> { return initialPlatform; } case "initialPlatform" -> { return initialPlatform; }
case "disableLibdecor" -> { return initialDisableLibdecor; } case "disableLibdecor" -> { return initialDisableLibdecor; }
case "errorRenderingFailures" -> { return errorRenderingFailures; } case "errorRenderingFailures" -> { return errorRenderingFailures; }
case "renderingAdapter" -> { return renderingAdapter; }
case "vsyncMode" -> { return vsyncMode; } case "vsyncMode" -> { return vsyncMode; }
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; } case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
default -> { return null; } default -> { return null; }

View file

@ -21,7 +21,6 @@ package de.staropensource.engine.rendering.callback;
import de.staropensource.engine.rendering.type.Window; import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.event.InputEvent; import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.callback.WindowCallback;
import de.staropensource.engine.rendering.type.input.Key; import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState; import de.staropensource.engine.rendering.type.input.KeyState;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View file

@ -0,0 +1,58 @@
/*
* 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.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import org.jetbrains.annotations.NotNull;
/**
* Emitted when the rendering thread exits due to an unhandled exception.
*
* @since v1-alpha9
*/
public final class RenderingThreadThrowableEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha9
*/
public RenderingThreadThrowableEvent() {}
/**
* {@inheritDoc}
*
* @deprecated use the {@code callEvent} method with arguments
* @see #callEvent(Throwable)
*/
@Deprecated
@Override
public void callEvent() {}
/**
* Emits the event and calls all event listeners.
*
* @param throwable thrown {@link Throwable}
* @since v1-alpha9
*/
public void callEvent(@NotNull Throwable throwable) {
EventHelper.invokeAnnotatedMethods(getClass(), throwable);
}
}

View file

@ -0,0 +1,379 @@
/*
* 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.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.type.FrameHandler;
import de.staropensource.engine.rendering.type.Monitor;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter;
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.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "JavadocDeclaration" })
public final class Renderer {
/**
* Contains all frame handlers.
* <p>
* Frame handlers are invoked before
* all windows are rendered, allowing
* the application or game to respond
* to frame renders.
*
* @since v1-alpha9
*/
private static final @NotNull List<@NotNull FrameHandler> frameHandlers = Collections.synchronizedList(new ArrayList<>());
/**
* Contains if the renderer has been initialized.
*
* @since v1-alpha9
* -- GETTER --
* Returns if the renderer has been initialized.
*
* @return renderer initialized?
* @since v1-alpha9
*/
@Getter
private static boolean initialized = false;
/**
* Contains if the renderer is running.
*
* @since v1-alpha9
* -- GETTER --
* Returns if the renderer is running.
*
* @return renderer running?
* @since v1-alpha9
*/
@Getter
private static boolean running = false;
/**
* Contains the frame count aka.
* the amount of frames rendered.
* <p>
* Updated every frame.
*
* @since v1-alpha9
* -- GETTER --
* Returns the frame count aka.
* the amount of frames rendered.
* <p>
* Updated every frame.
*
* @return amount of frames rendered
* @since v1-alpha9
*/
@Getter
private static long frameCount = 0L;
/**
* Contains the delta time, also
* known as the render time.
* <p>
* Updated every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns the delta time, also
* known as the render time.
* <p>
* Updated every second.
*
* @return delta time
* @since v1-alpha9
*/
@Getter
private static double deltaTime = 0d;
/**
* Contains the frames per second (FPS) count.
* <p>
* Updated every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns the frames per second (FPS) count.
* <p>
* Updated every second.
*
* @return frames per second
* @since v1-alpha9
*/
@Getter
private static double framesPerSecond = 0d;
/**
* Contains the time it took
* to calculate the last frame.
* <p>
* Updated every frame.
*
* @since v1-alpha9
* -- GETTER --
* Contains the time it took
* to calculate the last frame.
* <p>
* Updated every frame.
*
* @return last frame time
* @since v1-alpha9
*/
@Getter
private static Map<@NotNull String, @NotNull Long> lastFrameTime = Collections.unmodifiableMap(new HashMap<>());
// ----> Initialization
/**
* Initializes the renderer.
*
* @since v1-alpha9
*/
public static void initialize() {
if (initialized)
return;
addFrameHandler(new FrameHandler() {
@Override
public @NotNull FrameHandler.Priority getPriority() {
return Priority.VERY_IMPORTANT;
}
@Override
public void run() {
bgfx_set_view_clear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000, 1.0f, 0);
}
});
addFrameHandler(new FrameHandler() {
/** {@inheritDoc} */
@Override
public @NotNull FrameHandler.Priority getPriority() {
return Priority.VERY_UNIMPORTANT;
}
/** {@inheritDoc} */
@Override
public void run() {
int offset = 2;
bgfx_dbg_text_clear(0, false);
bgfx_dbg_text_printf(0, 0, 0x0f, "It's " + NumberUtil.padNumbers(LocalTime.now().getHour(), 2) + ":" + NumberUtil.padNumbers(LocalTime.now().getMinute(), 2) + ":" + NumberUtil.padNumbers(LocalTime.now().getSecond(), 2));
bgfx_dbg_text_printf(0, 1, 0x0f, "LFT.size(): " + lastFrameTime.size());
for (String item : lastFrameTime.keySet()) {
bgfx_dbg_text_printf(0, offset, 0x0f, item + ": " + lastFrameTime.get(item) + "ms");
offset += 1;
}
}
});
initialized = true;
}
// -----> Frame handler management
/**
* Adds the specified frame handler.
*
* @param frameHandler {@link FrameHandler} to add
* @since v1-alpha9
*/
public static void addFrameHandler(@NotNull FrameHandler frameHandler) {
frameHandlers.add(frameHandler);
frameHandlers.sort(Comparator.comparing(FrameHandler::getPriority));
}
/**
* Removes the specified frame handler.
*
* @param frameHandler {@link FrameHandler} to remove
* @since v1-alpha9
*/
public static void removeFrameHandler(@NotNull FrameHandler frameHandler) {
frameHandlers.remove(frameHandler);
}
// -----> Rendering logic
/**
* Starts the renderer.
*
* @throws NotOnMainThreadException if not running on the main thread
* @throws RuntimeException on major rendering error
* @since v1-alpha9
*/
public static void start() throws RuntimeException {
// Check if running on main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
String threadName = Thread.currentThread().getName();
int threadPriority = Thread.currentThread().getPriority();
RuntimeException exception = null;
// Update thread
Thread.currentThread().setName("Rendering thread");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
// Start renderer
running = true;
try {
render();
} catch (Throwable throwable) {
exception = new RuntimeException("Renderer failed", throwable);
}
// Revert thread changes
Thread.currentThread().setName(threadName);
Thread.currentThread().setPriority(threadPriority);
// Throw if necessary
if (exception != null)
throw exception;
}
/**
* Renders all windows.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "InfiniteLoopStatement" })
private static void render() throws Throwable {
long previousFrameCount = 0L; // Frame count one second ago
LinkedList<Long> deltaTimeFractions = 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) {
// 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
bgfx_reset(Window.getWindows().getFirst().getSize().getX(), Window.getWindows().getFirst().getSize().getY(), RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON ? BGFX_RESET_VSYNC : BGFX_RESET_NONE, 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);
switch (RenderingSubsystemConfiguration.getInstance().getVsyncMode()) {
case OFF -> execTimes.replace("Waiting", (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d));
case ON -> {
for (Monitor monitor : Monitor.getMonitors())
if (monitor.getRefreshRate() > execTimes.get("Waiting"))
execTimes.replace("Waiting", (long) monitor.getRefreshRate());
}
default -> {}
}
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);
deltaTimeFractions.add(execTimes.get("Rendering") + execTimes.get("Waiting"));
execTimes.clear();
// Perform per-second operations
if (System.currentTimeMillis() >= timesPSO) {
// Calculate delta time and FPS count
deltaTime = NumberUtil.calculateMeanLong(deltaTimeFractions);
framesPerSecond = 1000 / deltaTime;
// 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" +
"-> Monitor: " + window.getMonitor() + "\n" +
"-> Resizable: " + window.isResizable() + "\n" +
"-> Borderless: " + window.isBorderless() + "\n" +
"-> Focusable: " + window.isFocusable() + "\n" +
"-> On top: " + window.isOnTop() + "\n" +
"-> Transparent: " + window.isTransparent() + "\n" +
"-> Rendering: " + window.isRendering()
);
// Reset per-second variables
previousFrameCount = frameCount;
deltaTimeFractions.clear();
timesPSO = System.currentTimeMillis() + 1000;
}
}
}
}

View file

@ -0,0 +1,93 @@
/*
* 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.type;
import org.jetbrains.annotations.NotNull;
/**
* Used for performing actions
* before a frame is rendered.
*
* @since v1-alpha9
*/
public interface FrameHandler {
/**
* Returns the priority this frame handler has.
*
* @return priority
* @since v1-alpha9
*/
@NotNull FrameHandler.Priority getPriority();
/**
* Invokes this frame handler.
*
* @since v1-alpha9
*/
void run();
/**
* Contains all priority levels
* a {@link FrameHandler} can have.
*
* @since v1-alpha9
*/
enum Priority {
/**
* {@link FrameHandler}s with
* this priority are called first.
*
* @since v1-alpha9
*/
VERY_IMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called 2nd.
*
* @since v1-alpha9
*/
IMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called 3rd.
*
* @since v1-alpha9
*/
DEFAULT,
/**
* {@link FrameHandler}s with
* this priority are called 4th.
*
* @since v1-alpha9
*/
UNIMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called last.
*
* @since v1-alpha9
*/
VERY_UNIMPORTANT,
}
}

View file

@ -22,19 +22,21 @@ package de.staropensource.engine.rendering.type;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.type.Tristate; import de.staropensource.engine.base.type.Tristate;
import de.staropensource.engine.base.type.vector.Vec2i; import de.staropensource.engine.base.type.vector.Vec2i;
import de.staropensource.engine.base.utility.Miscellaneous; import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.base.utility.misc.TypeConversion;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.rendering.callback.KeyCallback; import de.staropensource.engine.rendering.callback.KeyCallback;
import de.staropensource.engine.rendering.callback.MouseButtonCallback; import de.staropensource.engine.rendering.callback.MouseButtonCallback;
import de.staropensource.engine.rendering.event.InputEvent; import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException; import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.exception.WindowCreationFailureException; import de.staropensource.engine.rendering.exception.WindowCreationFailureException;
import de.staropensource.engine.rendering.type.window.WindowMode;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.bgfx.BGFX;
import org.lwjgl.bgfx.BGFXInit; import org.lwjgl.bgfx.BGFXInit;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import org.lwjgl.stb.STBImage; import org.lwjgl.stb.STBImage;
@ -60,7 +62,7 @@ import static org.lwjgl.system.MemoryUtil.*;
* @since v1-alpha9 * @since v1-alpha9
*/ */
@Getter @Getter
@SuppressWarnings({ "JavadocDeclaration" }) @SuppressWarnings({ "unused", "JavadocDeclaration" })
public final class Window implements AutoCloseable { public final class Window implements AutoCloseable {
/** /**
* A set of all active windows. * A set of all active windows.
@ -69,18 +71,7 @@ public final class Window implements AutoCloseable {
*/ */
private static final @NotNull List<@NotNull Window> windows = new ArrayList<>(); private static final @NotNull List<@NotNull Window> windows = new ArrayList<>();
/** // -----> Internal fields
* Contains the internal window identifier.
*
* @since v1-alpha9
* -- GETTER --
* Returns the internal window identifier.
*
* @return window identifier
* @since v1-alpha9
*/
private long identifier;
/** /**
* Contains if this window is fresh. * Contains if this window is fresh.
* <p> * <p>
@ -99,8 +90,39 @@ public final class Window implements AutoCloseable {
* @return fresh flag state * @return fresh flag state
* @since v1-alpha9 * @since v1-alpha9
*/ */
private boolean fresh = true; @Getter(value = AccessLevel.NONE)
@Setter(value = AccessLevel.NONE)
private boolean fresh;
/**
* Contains the internal GLFW window identifier.
*
* @since v1-alpha9
* -- GETTER --
* Returns the internal GLFW window identifier.
*
* @return GLFW window identifier
* @since v1-alpha9
*/
@Getter(value = AccessLevel.NONE)
@Setter(value = AccessLevel.NONE)
private long internalWindowIdentifier;
/**
* Contains the internal bgfx view identifier.
*
* @since v1-alpha9
* -- GETTER --
* Returns the internal bgfx view identifier.
*
* @return bgfx view identifier
* @since v1-alpha9
*/
@Getter(value = AccessLevel.NONE)
@Setter(value = AccessLevel.NONE)
private int internalViewIdentifier;
// -----> Window properties
/** /**
* Contains if this window can be interacted with or not. * Contains if this window can be interacted with or not.
* *
@ -258,7 +280,7 @@ public final class Window implements AutoCloseable {
private @NotNull Vec2i position; private @NotNull Vec2i position;
/** /**
* Contains in which {@link WindowMode} this window is in. * Contains in which {@link Mode} this window is in.
* *
* @since v1-alpha9 * @since v1-alpha9
* -- GETTER -- * -- GETTER --
@ -267,7 +289,7 @@ public final class Window implements AutoCloseable {
* @return window mode * @return window mode
* @since v1-alpha9 * @since v1-alpha9
*/ */
private @NotNull WindowMode windowMode; private @NotNull Window.Mode mode;
/** /**
* Contains on which {@link Monitor} the window is displayed on. * Contains on which {@link Monitor} the window is displayed on.
@ -287,24 +309,6 @@ public final class Window implements AutoCloseable {
@Setter @Setter
private @NotNull Monitor monitor; private @NotNull Monitor monitor;
/**
* Contains how fast the window may update it's contents.
*
* @since v1-alpha9
* -- GETTER --
* Returns how fast the window may update it's contents.
*
* @return new window frame limit
* @since v1-alpha9
* -- SETTER --
* Sets how fast the window may update it's contents.
*
* @param framerate new frame limit
* @since v1-alpha9
*/
@Setter
private int framerate;
/** /**
* Contains if this window can be resized by the user. * Contains if this window can be resized by the user.
* *
@ -403,16 +407,20 @@ public final class Window implements AutoCloseable {
*/ */
private GLFWMouseButtonCallback mouseButtonCallback; private GLFWMouseButtonCallback mouseButtonCallback;
// -----> Static methods
/** /**
* Returns a set of active windows. * Returns a set of active windows.
* *
* @return active windows * @return active windows
* @since v1-alpha9 * @since v1-alpha9
*/ */
public static @NotNull HashSet<@NotNull Window> getWindows() { public static @NotNull List<@NotNull Window> getWindows() {
return new HashSet<>(windows); return Collections.unmodifiableList(windows);
} }
// -----> Initialization
/** /**
* Creates and initializes an instance of this abstract class. * Creates and initializes an instance of this abstract class.
* *
@ -423,7 +431,7 @@ public final class Window implements AutoCloseable {
* @param minimumSize minimum size * @param minimumSize minimum size
* @param maximumSize maximum size * @param maximumSize maximum size
* @param position position * @param position position
* @param windowMode window mode * @param mode window mode
* @param monitor monitor * @param monitor monitor
* @param resizable resizable flag * @param resizable resizable flag
* @param borderless borderless flag * @param borderless borderless flag
@ -441,7 +449,7 @@ public final class Window implements AutoCloseable {
@NotNull Vec2i minimumSize, @NotNull Vec2i minimumSize,
@NotNull Vec2i maximumSize, @NotNull Vec2i maximumSize,
@NotNull Vec2i position, @NotNull Vec2i position,
@NotNull WindowMode windowMode, @NotNull Window.Mode mode,
@NotNull Monitor monitor, @NotNull Monitor monitor,
boolean resizable, boolean resizable,
boolean borderless, boolean borderless,
@ -450,7 +458,11 @@ public final class Window implements AutoCloseable {
boolean transparent, boolean transparent,
boolean rendering) throws NotOnMainThreadException { boolean rendering) throws NotOnMainThreadException {
// Initialize variables // Initialize variables
this.fresh = true;
this.internalWindowIdentifier = 0L;
this.internalViewIdentifier = 0;
this.uniqueIdentifier = UUID.randomUUID(); this.uniqueIdentifier = UUID.randomUUID();
this.terminated = false;
this.name = name; this.name = name;
this.title = title; this.title = title;
this.icons = icons; this.icons = icons;
@ -458,7 +470,7 @@ public final class Window implements AutoCloseable {
this.minimumSize = minimumSize; this.minimumSize = minimumSize;
this.maximumSize = maximumSize; this.maximumSize = maximumSize;
this.position = position; this.position = position;
this.windowMode = windowMode; this.mode = mode;
this.monitor = monitor; this.monitor = monitor;
this.resizable = resizable; this.resizable = resizable;
this.borderless = borderless; this.borderless = borderless;
@ -468,25 +480,45 @@ public final class Window implements AutoCloseable {
this.rendering = rendering; this.rendering = rendering;
// Log about window creation // Log about window creation
Logger.diag("Creating new window with properties: uniqueIdentifier=" + uniqueIdentifier + " name=\"" + name + "\" title=\"" + title + "\" icons=" + Arrays.toString(icons) + " size=" + size + " minimumSize=" + minimumSize + " maximumSize=" + maximumSize + " position=" + position + " windowMode=" + windowMode + " monitor=" + monitor.getUniqueIdentifier() + " (" + monitor.getName() + ") resizable=" + resizable + " borderless=" + borderless + " focusable=" + focusable + " onTop=" + onTop + " transparent=" + transparent + " rendering=" + rendering); Logger.verb("Creating new window " + this.uniqueIdentifier + "\n" +
"-> Name: " + this.name + "\n" +
"-> Title: " + this.title + "\n" +
"-> Size: " + this.size + "\n" +
" -> Minimum: " + this.minimumSize + "\n" +
" -> Maximum: " + this.maximumSize + "\n" +
"-> Position: " + this.position + "\n" +
"-> Mode: " + this.mode + "\n" +
"-> Monitor: " + this.monitor + "\n" +
"-> Resizable: " + this.resizable + "\n" +
"-> Borderless: " + this.borderless + "\n" +
"-> Focusable: " + this.focusable + "\n" +
"-> On top: " + this.onTop + "\n" +
"-> Transparent: " + this.transparent + "\n" +
"-> Rendering: " + this.rendering
);
// Allow windowing API to initialize window // Check if another window already exists
if (!Window.getWindows().isEmpty())
Logger.crash("Window already present\nThe StarOpenSource Engine is unable to initialize more than one window at the moment, sorry.");
// Initialize window
initializeWindow(); initializeWindow();
// Update state // Update state
updateState(); updateState();
// Initialize bgfx
if (glfwGetPlatform() != GLFW_PLATFORM_NULL)
initBgfx();
// Add to window set // Add to window set
windows.add(this); windows.add(this);
fresh = false; fresh = false;
} }
/** /**
* Allows the windowing API to initialize the window. * Initializes this window.
* <p>
* NEVER place any code in the constructor. Instead, write
* API-specific window initialization code in here
* or stuff may break unexpectedly.
* *
* @throws NotOnMainThreadException if not running on the main thread * @throws NotOnMainThreadException if not running on the main thread
* @since v1-alpha9 * @since v1-alpha9
@ -504,12 +536,13 @@ public final class Window implements AutoCloseable {
} }
// Set window hints // Set window hints
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // The window's visibility is later changed in setWindowState, this is just for setting up the window if (RenderingSubsystemConfiguration.getInstance().isDebugAllowPositionUpdates()) {
glfwWindowHint(GLFW_POSITION_X, getPosition().getX()); glfwWindowHint(GLFW_POSITION_X, getPosition().getX());
glfwWindowHint(GLFW_POSITION_Y, getPosition().getY()); glfwWindowHint(GLFW_POSITION_Y, getPosition().getY());
}
glfwWindowHint(GLFW_CENTER_CURSOR, 0); glfwWindowHint(GLFW_CENTER_CURSOR, 0);
glfwWindowHint(GLFW_FOCUSED, Miscellaneous.getIntegerizedBoolean(focused)); glfwWindowHint(GLFW_FOCUSED, TypeConversion.booleanToInteger(focused));
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, Miscellaneous.getIntegerizedBoolean(isTransparent())); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, TypeConversion.booleanToInteger(transparent));
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName()); glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName());
@ -517,8 +550,8 @@ public final class Window implements AutoCloseable {
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName()); glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName());
// Create window // Create window
identifier = glfwCreateWindow(getSize().getX(), getSize().getY(), getTitle(), NULL, NULL); internalWindowIdentifier = glfwCreateWindow(getSize().getX(), getSize().getY(), getTitle(), NULL, NULL);
if (identifier == NULL) if (internalWindowIdentifier == NULL)
throw new WindowCreationFailureException(); throw new WindowCreationFailureException();
// Own context // Own context
@ -532,18 +565,15 @@ public final class Window implements AutoCloseable {
mouseButtonCallback = GLFWMouseButtonCallback.create(new MouseButtonCallback(this)); mouseButtonCallback = GLFWMouseButtonCallback.create(new MouseButtonCallback(this));
// Set callback // Set callback
glfwSetKeyCallback(identifier, keyCallback); glfwSetKeyCallback(internalWindowIdentifier, keyCallback);
glfwSetMouseButtonCallback(identifier, mouseButtonCallback); glfwSetMouseButtonCallback(internalWindowIdentifier, mouseButtonCallback);
// Update the window state // Update the window state
setIcons(getIcons()); setIcons(getIcons());
setSize(getSize()); setSize(getSize());
setMinimumSize(getMinimumSize()); setMinimumSize(getMinimumSize());
setMaximumSize(getMaximumSize()); setMaximumSize(getMaximumSize());
setWindowMode(getWindowMode()); setMode(getMode());
// Initialize bgfx
initBgfx();
} }
/** /**
@ -552,6 +582,10 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
private void initBgfx() { private void initBgfx() {
// Ensure the window is not terminated
if (terminated)
return;
try (MemoryStack stack = stackPush()) { try (MemoryStack stack = stackPush()) {
Logger.verb("Initializing bgfx"); Logger.verb("Initializing bgfx");
@ -560,13 +594,29 @@ public final class Window implements AutoCloseable {
BGFXInit init = BGFXInit.calloc(stack); BGFXInit init = BGFXInit.calloc(stack);
bgfx_init_ctor(init); bgfx_init_ctor(init);
// Set adapter
Logger.diag("Setting adapter");
init.vendorId(switch (RenderingSubsystemConfiguration.getInstance().getRenderingAdapter()) {
case ANY -> BGFX_PCI_ID_NONE;
case SOFTWARE -> BGFX_PCI_ID_SOFTWARE_RASTERIZER;
case AMD -> BGFX_PCI_ID_AMD;
case APPLE -> BGFX_PCI_ID_APPLE;
case INTEL -> BGFX_PCI_ID_INTEL;
case NVIDIA -> BGFX_PCI_ID_NVIDIA;
case MICROSOFT -> BGFX_PCI_ID_MICROSOFT;
});
// Set initial resolution // Set initial resolution
Logger.diag("Setting initial resolution"); Logger.diag("Setting initial resolution");
init init
.resolution(it -> it .resolution(
.width(size.getX()) it -> it
.height(size.getY()) .width(size.getX())
.reset(BGFX_RESET_VSYNC)); .height(size.getY())
.reset(
BGFX_RESET_VSYNC
)
);
// Determine platform to render for // Determine platform to render for
Logger.diag("Setting platform"); Logger.diag("Setting platform");
@ -574,18 +624,17 @@ public final class Window implements AutoCloseable {
case GLFW_PLATFORM_X11 -> init case GLFW_PLATFORM_X11 -> init
.platformData() .platformData()
.ndt(GLFWNativeX11.glfwGetX11Display()) .ndt(GLFWNativeX11.glfwGetX11Display())
.nwh(GLFWNativeX11.glfwGetX11Window(identifier)); .nwh(GLFWNativeX11.glfwGetX11Window(internalWindowIdentifier));
case GLFW_PLATFORM_WAYLAND -> init case GLFW_PLATFORM_WAYLAND -> init
.platformData() .platformData()
.ndt(GLFWNativeWayland.glfwGetWaylandDisplay()) .ndt(GLFWNativeWayland.glfwGetWaylandDisplay())
.nwh(GLFWNativeWayland.glfwGetWaylandWindow(identifier)); .nwh(GLFWNativeWayland.glfwGetWaylandWindow(internalWindowIdentifier));
case GLFW_PLATFORM_WIN32 -> init case GLFW_PLATFORM_WIN32 -> init
.platformData() .platformData()
.nwh(GLFWNativeWin32.glfwGetWin32Window(identifier)); .nwh(GLFWNativeWin32.glfwGetWin32Window(internalWindowIdentifier));
case GLFW_PLATFORM_COCOA -> init case GLFW_PLATFORM_COCOA -> init
.platformData() .platformData()
.nwh(GLFWNativeCocoa.glfwGetCocoaWindow(identifier)); .nwh(GLFWNativeCocoa.glfwGetCocoaWindow(internalWindowIdentifier));
case GLFW_PLATFORM_NULL -> {}
default -> Logger.crash("Invalid GLFW platform \"" + glfwGetPlatform() + "\""); default -> Logger.crash("Invalid GLFW platform \"" + glfwGetPlatform() + "\"");
} }
@ -595,8 +644,6 @@ public final class Window implements AutoCloseable {
Logger.crash("Unable to initialize bgfx"); Logger.crash("Unable to initialize bgfx");
bgfx_set_debug(BGFX_DEBUG_TEXT); bgfx_set_debug(BGFX_DEBUG_TEXT);
bgfx_set_view_rect(0, 0, 0, size.getX(), size.getY());
bgfx_touch(0);
} catch (UnsatisfiedLinkError error) { } catch (UnsatisfiedLinkError error) {
Logger.crash("Failed to load LWJGL native libraries", error); Logger.crash("Failed to load LWJGL native libraries", error);
} }
@ -613,7 +660,7 @@ public final class Window implements AutoCloseable {
*/ */
public void updateState() throws NotOnMainThreadException { public void updateState() throws NotOnMainThreadException {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
// Ensure running on the main thread // Ensure running on the main thread
@ -621,14 +668,14 @@ public final class Window implements AutoCloseable {
throw new NotOnMainThreadException(); throw new NotOnMainThreadException();
// Update window mode // Update window mode
if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_ICONIFIED))) if (TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_ICONIFIED)))
setWindowMode(WindowMode.MINIMIZED); mode = Mode.MINIMIZED;
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_MAXIMIZED))) else if (TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_MAXIMIZED)))
setWindowMode(WindowMode.MAXIMIZED); mode = Mode.MAXIMIZED;
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_VISIBLE))) else if (TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_VISIBLE)))
setWindowMode(WindowMode.WINDOWED); mode = Mode.WINDOWED;
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_VISIBLE))) else if (TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_VISIBLE)))
setWindowMode(WindowMode.HIDDEN); mode = Mode.HIDDEN;
// Update monitor // Update monitor
if (!getMonitor().isConnected()) { if (!getMonitor().isConnected()) {
@ -638,10 +685,12 @@ public final class Window implements AutoCloseable {
if (monitor.isConnected()) if (monitor.isConnected())
newMonitor = monitor; newMonitor = monitor;
if (newMonitor == null) if (newMonitor == null) {
Logger.crash("Unable to set a new target monitor for window " + getUniqueIdentifier() + " as no monitors are connected to the system"); Logger.crash("Unable to set a new target monitor for window " + getUniqueIdentifier() + " as no monitors are connected to the system");
return;
}
setMonitor(Objects.requireNonNull(newMonitor)); monitor = newMonitor;
} }
// Update vectors // Update vectors
@ -649,17 +698,17 @@ public final class Window implements AutoCloseable {
IntBuffer width = stack.mallocInt(2); IntBuffer width = stack.mallocInt(2);
IntBuffer height = stack.mallocInt(2); IntBuffer height = stack.mallocInt(2);
glfwGetWindowSize(identifier, width, height); glfwGetWindowSize(internalWindowIdentifier, width, height);
setSize(new Vec2i(width.get(), height.get())); size = new Vec2i(width.get(), height.get());
glfwGetWindowPos(identifier, width, height); glfwGetWindowPos(internalWindowIdentifier, width, height);
setPosition(new Vec2i(width.get(), height.get())); position = new Vec2i(width.get(), height.get());
} }
// Update booleans // Update booleans
setResizable(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_RESIZABLE))); resizable = TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_RESIZABLE));
setOnTop(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_FLOATING))); onTop = TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_FLOATING));
setTransparent(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifier, GLFW_TRANSPARENT_FRAMEBUFFER))); transparent = TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_TRANSPARENT_FRAMEBUFFER));
} }
/** /**
@ -672,18 +721,15 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
public void render() throws NotOnMainThreadException { public void render() throws NotOnMainThreadException {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
// Ensure rendering is enabled // Ensure rendering is enabled
if (!isRendering()) if (!isRendering())
return; return;
glfwSwapBuffers(identifier); bgfx_set_view_rect(0, 0, 0, size.getX(), size.getY());
} }
/** /**
@ -696,8 +742,13 @@ public final class Window implements AutoCloseable {
if (!Miscellaneous.onMainThread()) if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException(); throw new NotOnMainThreadException();
closeInternal(); // Ensure the window is not terminated
if (terminated)
return;
windows.remove(this);
terminated = true; terminated = true;
closeInternal();
} }
/** /**
@ -709,11 +760,12 @@ public final class Window implements AutoCloseable {
*/ */
private void closeInternal() { private void closeInternal() {
// Terminate bgfx // Terminate bgfx
bgfx_shutdown(); if (glfwGetPlatform() != GLFW_PLATFORM_NULL)
bgfx_shutdown();
// Destroy the window // Destroy the window
Callbacks.glfwFreeCallbacks(identifier); Callbacks.glfwFreeCallbacks(internalWindowIdentifier);
glfwDestroyWindow(identifier); glfwDestroyWindow(internalWindowIdentifier);
} }
/** /**
@ -724,11 +776,15 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
public boolean isClosureRequested() { public boolean isClosureRequested() {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return false; return false;
return glfwWindowShouldClose(identifier); return glfwWindowShouldClose(internalWindowIdentifier);
} }
/** /**
@ -738,11 +794,15 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
public boolean isFocused() { public boolean isFocused() {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return false; return false;
return Miscellaneous.getTristatedInteger(glfwGetWindowAttrib(identifier, GLFW_FOCUSED)).toBoolean(); return TypeConversion.integerToBoolean(glfwGetWindowAttrib(internalWindowIdentifier, GLFW_FOCUSED));
} }
/** /**
@ -752,11 +812,15 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
public void focus() { public void focus() {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
glfwFocusWindow(identifier); glfwFocusWindow(internalWindowIdentifier);
} }
/** /**
@ -770,11 +834,15 @@ public final class Window implements AutoCloseable {
* @since v1-alpha9 * @since v1-alpha9
*/ */
public void requestAttention() { public void requestAttention() {
// Ensure running on the main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
glfwRequestWindowAttention(identifier); glfwRequestWindowAttention(internalWindowIdentifier);
} }
// -----> Setters & getters // -----> Setters & getters
@ -801,7 +869,7 @@ public final class Window implements AutoCloseable {
*/ */
public void setName(@NotNull String name) { public void setName(@NotNull String name) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.name = name; this.name = name;
@ -816,11 +884,11 @@ public final class Window implements AutoCloseable {
*/ */
public void setTitle(@NotNull String title) { public void setTitle(@NotNull String title) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.title = title; this.title = title;
glfwSetWindowTitle(identifier, title); glfwSetWindowTitle(internalWindowIdentifier, title);
} }
/** /**
@ -833,7 +901,7 @@ public final class Window implements AutoCloseable {
@ApiStatus.Experimental @ApiStatus.Experimental
public void setIcons(@NotNull Path @Nullable [] icons) { public void setIcons(@NotNull Path @Nullable [] icons) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.icons = icons; this.icons = icons;
@ -873,7 +941,7 @@ public final class Window implements AutoCloseable {
iconsBuffer.position(0); iconsBuffer.position(0);
Logger.diag("setting icons"); Logger.diag("setting icons");
Logger.flush(); Logger.flush();
glfwSetWindowIcon(identifier, iconsBuffer); glfwSetWindowIcon(internalWindowIdentifier, iconsBuffer);
// Free icons // Free icons
Logger.diag("freeing icons"); Logger.diag("freeing icons");
@ -894,11 +962,11 @@ public final class Window implements AutoCloseable {
*/ */
public void setSize(@NotNull Vec2i size) { public void setSize(@NotNull Vec2i size) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.size = size; this.size = size;
glfwSetWindowSize(identifier, size.getX(), size.getY()); glfwSetWindowSize(internalWindowIdentifier, size.getX(), size.getY());
} }
/** /**
@ -911,11 +979,11 @@ public final class Window implements AutoCloseable {
*/ */
public void setMinimumSize(@NotNull Vec2i minimumSize) { public void setMinimumSize(@NotNull Vec2i minimumSize) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.minimumSize = minimumSize; this.minimumSize = minimumSize;
glfwSetWindowSizeLimits(identifier, minimumSize.getX(), minimumSize.getY(), getMaximumSize().getX(), getMaximumSize().getY()); glfwSetWindowSizeLimits(internalWindowIdentifier, minimumSize.getX(), minimumSize.getY(), getMaximumSize().getX(), getMaximumSize().getY());
} }
/** /**
@ -928,11 +996,11 @@ public final class Window implements AutoCloseable {
*/ */
public void setMaximumSize(@NotNull Vec2i maximumSize) { public void setMaximumSize(@NotNull Vec2i maximumSize) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.maximumSize = maximumSize; this.maximumSize = maximumSize;
glfwSetWindowSizeLimits(identifier, getMinimumSize().getX(), getMinimumSize().getY(), maximumSize.getX(), maximumSize.getY()); glfwSetWindowSizeLimits(internalWindowIdentifier, getMinimumSize().getX(), getMinimumSize().getY(), maximumSize.getX(), maximumSize.getY());
} }
/** /**
@ -943,49 +1011,50 @@ public final class Window implements AutoCloseable {
*/ */
public void setPosition(@NotNull Vec2i position) { public void setPosition(@NotNull Vec2i position) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (isTerminated() || !RenderingSubsystemConfiguration.getInstance().isDebugAllowPositionUpdates())
return; return;
this.position = position; this.position = position;
glfwSetWindowSize(identifier, position.getX(), position.getY()); glfwSetWindowSize(internalWindowIdentifier, position.getX(), position.getY());
} }
/** /**
* Sets the window mode. * Sets the window mode.
* *
* @param windowMode new mode * @param mode new mode
* @since v1-alpha9 * @since v1-alpha9
*/ */
public void setWindowMode(@NotNull WindowMode windowMode) { public void setMode(@NotNull Window.Mode mode) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.windowMode = windowMode; this.mode = mode;
switch (windowMode) { switch (mode) {
case HIDDEN -> glfwHideWindow(identifier); case HIDDEN -> glfwHideWindow(internalWindowIdentifier);
case WINDOWED -> { case WINDOWED -> {
glfwShowWindow(identifier); glfwShowWindow(internalWindowIdentifier);
glfwRestoreWindow(identifier); glfwRestoreWindow(internalWindowIdentifier);
} }
case MINIMIZED -> { case MINIMIZED -> {
glfwShowWindow(identifier); glfwShowWindow(internalWindowIdentifier);
glfwIconifyWindow(identifier); glfwIconifyWindow(internalWindowIdentifier);
} }
case MAXIMIZED -> { case MAXIMIZED -> {
glfwShowWindow(identifier); glfwShowWindow(internalWindowIdentifier);
glfwRestoreWindow(identifier); glfwRestoreWindow(internalWindowIdentifier);
glfwMaximizeWindow(identifier); glfwMaximizeWindow(internalWindowIdentifier);
} }
case BORDERLESS_FULLSCREEN -> { case BORDERLESS_FULLSCREEN -> {
glfwShowWindow(identifier); glfwShowWindow(internalWindowIdentifier);
glfwRestoreWindow(identifier); glfwRestoreWindow(internalWindowIdentifier);
glfwSetWindowPos(internalWindowIdentifier, 0, 0);
// TODO // TODO
} }
case EXCLUSIVE_FULLSCREEN -> { case EXCLUSIVE_FULLSCREEN -> {
glfwShowWindow(identifier); glfwShowWindow(internalWindowIdentifier);
glfwRestoreWindow(identifier); glfwRestoreWindow(internalWindowIdentifier);
// TODO glfwSetWindowPos(internalWindowIdentifier, 0, 0);
} }
} }
} }
@ -998,7 +1067,7 @@ public final class Window implements AutoCloseable {
*/ */
public void setResizable(boolean resizable) { public void setResizable(boolean resizable) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.resizable = resizable; this.resizable = resizable;
@ -1012,7 +1081,7 @@ public final class Window implements AutoCloseable {
*/ */
public void setBorderless(boolean borderless) { public void setBorderless(boolean borderless) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.borderless = borderless; this.borderless = borderless;
@ -1026,7 +1095,7 @@ public final class Window implements AutoCloseable {
*/ */
public void setFocusable(boolean focusable) { public void setFocusable(boolean focusable) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.focusable = focusable; this.focusable = focusable;
@ -1040,7 +1109,7 @@ public final class Window implements AutoCloseable {
*/ */
public void setOnTop(boolean onTop) { public void setOnTop(boolean onTop) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.onTop = onTop; this.onTop = onTop;
@ -1057,14 +1126,79 @@ public final class Window implements AutoCloseable {
*/ */
public void setTransparent(boolean transparent) { public void setTransparent(boolean transparent) {
// Ensure the window is not terminated // Ensure the window is not terminated
if (isTerminated()) if (terminated)
return; return;
this.transparent = transparent; this.transparent = transparent;
initializeWindow(); initializeWindow();
} }
// -----> Builder inner class // -----> Inner classes
/**
* How a window can be displayed.
*
* @since v1-alpha9
*/
public enum Mode {
/**
* Marks the window as hidden, making it invisible and unable to be interacted with.
*
* @since v1-alpha9
*/
HIDDEN,
/**
* Marks the window as windowed, which
* will allow the user to drag around the window freely.
*
* @since v1-alpha9
*/
WINDOWED,
/**
* Same as {@link #HIDDEN} mode, but the window can be
* summoned back into {@link #WINDOWED} mode by the user
* by (for example) clicking an icon or {@code ALT+TAB}-ing.
*
* @since v1-alpha9
*/
MINIMIZED,
/**
* Same as {@link #WINDOWED}, but will make the window occupy
* most of the screen space, except for windows/bars/docks.
*
* @since v1-alpha9
*/
MAXIMIZED,
/**
* Makes the window will have the same
* size as the monitor it is currently on.
*
* @since v1-alpha9
*/
BORDERLESS_FULLSCREEN,
/**
* Makes the window occupy the entire
* monitor it is currently on without
* allowing other windows to occupy
* the same space.
* <p>
* This will increase rendering
* throughput as the window manager
* or compositor does not need to
* care about other windows occupying
* the same monitor. Use (and recommend)
* this mode if you/your users
* want more frames per second.
*
* @since v1-alpha9
*/
EXCLUSIVE_FULLSCREEN
}
/** /**
* Provides an API for building {@link Window}s more easily. * Provides an API for building {@link Window}s more easily.
* *
@ -1131,10 +1265,10 @@ public final class Window implements AutoCloseable {
/** /**
* Contains the window mode. * Contains the window mode.
* *
* @see Window#windowMode * @see Window#mode
* @since v1-alpha9 * @since v1-alpha9
*/ */
private @Nullable WindowMode windowMode = null; private @Nullable Window.Mode mode = null;
/** /**
* Contains the target monitor. * Contains the target monitor.
@ -1235,8 +1369,8 @@ public final class Window implements AutoCloseable {
minimumSize = new Vec2i(-1, -1); minimumSize = new Vec2i(-1, -1);
if (maximumSize == null) if (maximumSize == null)
maximumSize = new Vec2i(-1, -1); maximumSize = new Vec2i(-1, -1);
if (windowMode == null) if (mode == null)
windowMode = WindowMode.WINDOWED; mode = Mode.WINDOWED;
if (monitor == null) if (monitor == null)
monitor = Monitor.getMonitors().getFirst(); monitor = Monitor.getMonitors().getFirst();
@ -1255,7 +1389,7 @@ public final class Window implements AutoCloseable {
renderingBoolean = false; renderingBoolean = false;
// Create new Window instance // Create new Window instance
return new Window(name, title, icons, size, minimumSize, maximumSize, position, windowMode, monitor, resizableBoolean, borderlessBoolean, focusableBoolean, onTopBoolean, transparentBoolean, renderingBoolean); return new Window(name, title, icons, size, minimumSize, maximumSize, position, mode, monitor, resizableBoolean, borderlessBoolean, focusableBoolean, onTopBoolean, transparentBoolean, renderingBoolean);
} }
/** /**
@ -1340,11 +1474,11 @@ public final class Window implements AutoCloseable {
* Returns the window mode. * Returns the window mode.
* *
* @return window mode * @return window mode
* @see Window#windowMode * @see Window#mode
* @since v1-alpha9 * @since v1-alpha9
*/ */
public @Nullable WindowMode getWindowMode() { public @Nullable Window.Mode getWindowMode() {
return windowMode; return mode;
} }
/** /**
@ -1519,13 +1653,13 @@ public final class Window implements AutoCloseable {
/** /**
* Sets the window mode. * Sets the window mode.
* *
* @param windowMode new window mode * @param mode new window mode
* @return builder instance * @return builder instance
* @see Window#windowMode * @see Window#mode
* @since v1-alpha9 * @since v1-alpha9
*/ */
public @NotNull Builder setWindowMode(@Nullable WindowMode windowMode) { public @NotNull Builder setWindowMode(@Nullable Window.Mode mode) {
this.windowMode = windowMode; this.mode = mode;
return this; return this;
} }

View file

@ -20,66 +20,57 @@
package de.staropensource.engine.rendering.type.window; package de.staropensource.engine.rendering.type.window;
/** /**
* Contains how a window should be displayed. * Represents all available rendering adapters.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
public enum WindowMode { public enum RenderingAdapter {
/** /**
* Marks the window as hidden, making it invisible and unable to be interacted with. * Allows the bgfx to autodetect the adapter to use.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
HIDDEN, ANY,
/** /**
* Marks the window as windowed, which * Tells bgfx to use the CPU for rendering.
* will allow the user to drag around the window freely.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
WINDOWED, SOFTWARE,
/** /**
* Same as {@link #HIDDEN} mode, but the window can be * Tells bgfx to use AMD graphics cards for rendering.
* summoned back into {@link #WINDOWED} mode by the user
* by (for example) clicking an icon or {@code ALT+TAB}-ing.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
MINIMIZED, AMD,
/** /**
* Same as {@link #WINDOWED}, but will make the window occupy * Tells bgfx to use Apple's SOC for rendering.
* most of the screen space, except for windows/bars/docks.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
MAXIMIZED, APPLE,
/** /**
* Makes the window will have the same * Tells bgfx to use Intel graphics cards for rendering.
* size as the monitor it is currently on.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
BORDERLESS_FULLSCREEN, INTEL,
/** /**
* Makes the window occupy the entire * Tells bgfx to use NVIDIA graphics cards for rendering.
* monitor it is currently on without
* allowing other windows to occupy
* the same space.
* <p>
* This will increase rendering
* throughput as the window manager
* or compositor does not need to
* care about other windows occupying
* the same monitor. Use (and recommend)
* this mode if you/your users
* want more frames per second.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
EXCLUSIVE_FULLSCREEN NVIDIA,
/**
* Tells bgfx to use Windows' integrated graphics driver for rendering.
*
* @since v1-alpha9
*/
MICROSOFT,
} }

View file

@ -20,13 +20,13 @@
package de.staropensource.engine.rendering.type.window; package de.staropensource.engine.rendering.type.window;
/** /**
* Represents all available platforms. * Represents all available rendering platforms.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
public enum RenderingPlatform { public enum RenderingPlatform {
/** /**
* Allows the subsystem to autodetect the platform to use. * Allows GLFW to autodetect the platform to use.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
@ -61,7 +61,7 @@ public enum RenderingPlatform {
COCOA, COCOA,
/** /**
* Prefer initializing without any platform. * Prefer initializing headless.
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */

View file

@ -42,5 +42,10 @@ public enum VsyncMode {
* *
* @since v1-alpha9 * @since v1-alpha9
*/ */
ON ON,
/**
* Party
*/
PARTY
} }

View file

@ -14,11 +14,13 @@ module sosengine.rendering {
requires org.lwjgl.stb; requires org.lwjgl.stb;
requires org.lwjgl.glfw; requires org.lwjgl.glfw;
requires org.lwjgl.bgfx; requires org.lwjgl.bgfx;
requires java.desktop;
// API access // API access
exports de.staropensource.engine.rendering; exports de.staropensource.engine.rendering;
exports de.staropensource.engine.rendering.event; exports de.staropensource.engine.rendering.event;
exports de.staropensource.engine.rendering.exception; exports de.staropensource.engine.rendering.exception;
exports de.staropensource.engine.rendering.renderer;
exports de.staropensource.engine.rendering.type; exports de.staropensource.engine.rendering.type;
exports de.staropensource.engine.rendering.type.input; exports de.staropensource.engine.rendering.type.input;
exports de.staropensource.engine.rendering.type.window; exports de.staropensource.engine.rendering.type.window;
@ -27,6 +29,7 @@ module sosengine.rendering {
opens de.staropensource.engine.rendering; opens de.staropensource.engine.rendering;
opens de.staropensource.engine.rendering.event; opens de.staropensource.engine.rendering.event;
opens de.staropensource.engine.rendering.exception; opens de.staropensource.engine.rendering.exception;
opens de.staropensource.engine.rendering.renderer;
opens de.staropensource.engine.rendering.type; opens de.staropensource.engine.rendering.type;
opens de.staropensource.engine.rendering.type.input; opens de.staropensource.engine.rendering.type.input;
opens de.staropensource.engine.rendering.type.window; opens de.staropensource.engine.rendering.type.window;

View file

@ -25,9 +25,9 @@ import de.staropensource.engine.base.implementable.EventListenerCode;
import de.staropensource.engine.base.implementable.helper.EventHelper; import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.logging.Logger; import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.type.vector.Vec2i; import de.staropensource.engine.base.type.vector.Vec2i;
import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.rendering.RenderingSubsystem;
import de.staropensource.engine.rendering.event.InputEvent; import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.renderer.Renderer;
import de.staropensource.engine.rendering.type.FrameHandler;
import de.staropensource.engine.rendering.type.Window; import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.input.Key; import de.staropensource.engine.rendering.type.input.Key;
import de.staropensource.engine.rendering.type.input.KeyState; import de.staropensource.engine.rendering.type.input.KeyState;
@ -35,8 +35,6 @@ import lombok.Getter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
/** /**
* The main class of the sos!engine development application. * The main class of the sos!engine development application.
* *
@ -140,14 +138,23 @@ public final class Main {
return; return;
} }
// Render loop // Add frame handlers
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = RenderingSubsystem Renderer.addFrameHandler(new FrameHandler() {
.getInstance() @Override
.runRenderLoop(() -> { public @NotNull FrameHandler.Priority getPriority() {
if (shutdown || window.isClosureRequested()) return Priority.DEFAULT;
Engine.getInstance().shutdown(); }
}); @Override
public void run() {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
//window.setPosition(new Vec2i((int) Renderer.getFrameCount() / 10, (int) Renderer.getFrameCount() / 10));
}
});
// Start renderer
Renderer.start();
} catch (Exception exception) { } catch (Exception exception) {
Logger.crash("Caught throwable in main thread:", exception); Logger.crash("Caught throwable in main thread:", exception);
} }