Renderer full life cycle;

This commit is contained in:
extio-de
2025-12-21 19:28:37 +01:00
parent f5ef32dea6
commit 9b73577243
8 changed files with 172 additions and 22 deletions

View File

@@ -12,6 +12,8 @@ public interface Renderer {
void reset();
void shutdown();
void takeScreenshot();
void setTitle(final String title);

View File

@@ -8,6 +8,7 @@ import java.util.function.Consumer;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import de.extio.game_engine.renderer.model.RenderingBo;
@@ -32,8 +33,8 @@ public class RendererConfiguration {
}
@Bean
public RendererData rendererData(final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, @Qualifier("gameEngineEventConsumer") final Consumer<Object> eventConsumer) {
final var rendererData = new RendererData(renderer, rendererControl, renderingBoPool, eventConsumer);
public RendererData rendererData(final ApplicationContext applicationContext, final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, @Qualifier("gameEngineEventConsumer") final Consumer<Object> eventConsumer) {
final var rendererData = new RendererData(applicationContext, renderer, rendererControl, renderingBoPool, eventConsumer);
renderer.setRendererData(rendererData);
rendererControl.setRendererData(rendererData);
renderingBoPool.setRendererData(rendererData);
@@ -41,11 +42,8 @@ public class RendererConfiguration {
}
@Bean
public RendererLoop rendererLoop(final RendererData rendererData) {
final var rendererLoop = new RendererLoop(rendererData);
rendererData.setRendererLoop(rendererLoop);
rendererLoop.start();
return rendererLoop;
public RendererLauncher rendererLauncher(final ApplicationContext applicationContext, final RendererData rendererData) {
return new RendererLauncher(applicationContext, rendererData);
}
}

View File

@@ -3,11 +3,15 @@ package de.extio.game_engine.renderer;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import org.springframework.context.ApplicationContext;
import de.extio.game_engine.renderer.model.options.UiOptions;
import de.extio.game_engine.renderer.model.options.VideoOptions;
public class RendererData {
private final ApplicationContext applicationContext;
private final Renderer renderer;
private final RendererControl rendererControl;
@@ -24,13 +28,18 @@ public class RendererData {
private RendererLoop rendererLoop;
public RendererData(final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, final Consumer<Object> eventConsumer) {
public RendererData(final ApplicationContext applicationContext, final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, final Consumer<Object> eventConsumer) {
this.applicationContext = applicationContext;
this.renderer = renderer;
this.rendererControl = rendererControl;
this.renderingBoPool = renderingBoPool;
this.eventConsumer = eventConsumer;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public RendererControl getRendererControl() {
return this.rendererControl;
}

View File

@@ -0,0 +1,113 @@
package de.extio.game_engine.renderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
public class RendererLauncher implements InitializingBean, DisposableBean {
private static final Logger LOGGER = LoggerFactory.getLogger(RendererLauncher.class);
private final ApplicationContext applicationContext;
private final RendererData rendererData;
private RendererLoop rendererLoop;
private Thread rendererLoopThread;
private Thread watchDogThread;
public RendererLauncher(final ApplicationContext applicationContext, final RendererData rendererData) {
this.applicationContext = applicationContext;
this.rendererData = rendererData;
}
// @Override
// public void start() {
// this.launch();
// }
@Override
public void afterPropertiesSet() throws Exception {
this.launch();
}
@Override
public void destroy() throws Exception {
this.shutdown();
}
// @Override
// public void stop() {
// this.shutdown();
// }
// @Override
// public boolean isRunning() {
// return this.rendererLoopThread != null && this.rendererLoopThread.isAlive();
// }
private void launch() {
if (this.rendererLoopThread != null && this.rendererLoopThread.isAlive()) {
return;
}
LOGGER.info("Launching RendererLoop");
this.rendererLoop = new RendererLoop(this.rendererData);
rendererData.setRendererLoop(rendererLoop);
this.rendererLoopThread = Thread.ofPlatform()
.name("RendererLoop")
.daemon(false)
.start(this.rendererLoop);
LOGGER.info("Launching RendererWatchDog");
this.watchDogThread = Thread.ofPlatform()
.name("RendererWatchDog")
.daemon(true)
.start(() -> {
try {
this.rendererLoopThread.join();
LOGGER.error("RendererLoop thread has exited unexpectedly, shutting down application");
SpringApplication.exit(this.applicationContext, () -> 1);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
private void shutdown() {
if (this.watchDogThread != null && this.watchDogThread.isAlive()) {
LOGGER.info("Shutting down RendererWatchDog");
this.watchDogThread.interrupt();
try {
this.watchDogThread.join(5000);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.watchDogThread = null;
if (this.rendererLoopThread != null && this.rendererLoopThread.isAlive() && this.rendererLoop != null) {
LOGGER.info("Shutting down RendererLoop");
this.rendererLoop.shutdown();
try {
this.rendererLoopThread.join(5000);
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.rendererLoopThread = null;
this.rendererLoop = null;
this.rendererData.setRendererLoop(null);
this.rendererData.getRenderer().shutdown();
}
}

View File

@@ -5,28 +5,28 @@ import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RendererLoop extends Thread {
public class RendererLoop implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(RendererLoop.class);
private final RendererData rendererData;
private volatile boolean shutdown;
private volatile boolean running = true;
private final RendererData rendererData;
public RendererLoop(final RendererData rendererData) {
this.rendererData = rendererData;
this.setName("Renderer-Loop");
this.setDaemon(false);
}
@Override
public void run() {
this.shutdown = false;
try {
this.rendererData.getRenderer().show();
LOGGER.info("Renderer loop started");
int exceptionCount = 0;
while (this.running) {
while (!this.shutdown) {
try {
this.rendererData.nextFrame();
this.rendererData.getRenderer().run(new ArrayList<>()); // TODO: Pass actual renderables
@@ -54,7 +54,8 @@ public class RendererLoop extends Thread {
}
public void shutdown() {
this.running = false;
LOGGER.info("RendererLoop shutdown requested");
this.shutdown = true;
}
}

View File

@@ -27,6 +27,7 @@ import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import de.extio.game_engine.keyboard.KeyModifiers;
import de.extio.game_engine.renderer.RendererControl;
@@ -93,7 +94,8 @@ public class G2DMainFrame extends Frame {
@Override
public void windowClosing(final WindowEvent e) {
G2DMainFrame.this.rendererData.getRendererLoop().shutdown();
LOGGER.info("Window closing requested");
SpringApplication.exit(G2DMainFrame.this.rendererData.getApplicationContext(), () -> 0);
}
@Override

View File

@@ -99,6 +99,25 @@ public class G2DRenderer implements Renderer {
G2DDrawControl.reset();
}
@Override
public void shutdown() {
LOGGER.info("shutdown()");
this.rwLock.writeLock().lock();
try {
if (this.mainFrame != null) {
this.mainFrame.unregisterFullScreenWindow();
this.reset();
this.mainFrame.setVisible(false);
this.mainFrame.dispose();
this.mainFrame = null;
}
}
finally {
this.rwLock.writeLock().unlock();
}
}
@Override
public void run(final List<RenderingBo> renderingBOs) throws InterruptedException {
try {

View File

@@ -163,8 +163,11 @@ public class G2DRendererControl implements RendererControl {
if (this.updateAbsoluteViewportPortDimension()) {
this.recalculate();
}
this.renderer.getMainFrame().createBufferStrategy();
this.renderer.getMainFrame().resizeListenerEnabled = true;
final var mainFrame = this.renderer.getMainFrame();
if (mainFrame != null) {
mainFrame.createBufferStrategy();
mainFrame.resizeListenerEnabled = true;
}
}
finally {
if (lockRenderer) {
@@ -174,7 +177,10 @@ public class G2DRendererControl implements RendererControl {
};
if (async) {
EventQueue.invokeLater(run);
// EventQueue.invokeLater(run); // Using EventQueue here can clutter the AWT event queue with too many events when resizing on a system with high event rate
Thread.ofVirtual()
.name("G2DRendererControl-ViewportUpdater", 0)
.start(run);
}
else {
run.run();
@@ -189,7 +195,7 @@ public class G2DRendererControl implements RendererControl {
private boolean updateAbsoluteViewportPortDimension() {
boolean changed = false;
final var absoluteDim = this.absoluteViewportDimension;
if (absoluteDim == null || absoluteDim.getX() != this.renderer.getMainFrame().getWidth() || absoluteDim.getY() != this.renderer.getMainFrame().getHeight()) {
if (absoluteDim == null || (this.renderer.getMainFrame() != null && (absoluteDim.getX() != this.renderer.getMainFrame().getWidth() || absoluteDim.getY() != this.renderer.getMainFrame().getHeight()))) {
this.absoluteViewportDimension = ImmutableCoordI2.create(this.renderer.getMainFrame().getWidth(), this.renderer.getMainFrame().getHeight());
changed = true;
}