From eeb3d31736986e7103e4cd5c8f424ea97a5805a3 Mon Sep 17 00:00:00 2001
From: Svjatoslav Agejenko
Date: Sun, 15 Mar 2026 06:22:54 +0200
Subject: [PATCH] feat(benchmark): add keyboard shortcut to skip tests
Allow pressing Space to skip to the next benchmark test immediately.
Also improve cleanup by properly removing listeners and stopping the
render loop when the benchmark finishes.
---
doc/index.org | 1 +
.../examples/benchmark/GraphicsBenchmark.java | 70 ++++++++++++++++---
2 files changed, 62 insertions(+), 9 deletions(-)
diff --git a/doc/index.org b/doc/index.org
index 2ad0fd7..c6606cb 100644
--- a/doc/index.org
+++ b/doc/index.org
@@ -212,6 +212,7 @@ three tests sequentially, each for 30 seconds:
The camera follows a deterministic orbital path around the scene,
ensuring reproducible results across runs.
+Example benchmark results:
#+begin_example
================================================================================
GRAPHICS BENCHMARK RESULTS
diff --git a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java
index 14e4d1b..d336dee 100644
--- a/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java
+++ b/src/main/java/eu/svjatoslav/sixth/e3d/examples/benchmark/GraphicsBenchmark.java
@@ -10,8 +10,11 @@ import eu.svjatoslav.sixth.e3d.gui.Camera;
import eu.svjatoslav.sixth.e3d.gui.FrameListener;
import eu.svjatoslav.sixth.e3d.gui.ViewFrame;
import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
+import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardInputHandler;
import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import javax.swing.SwingUtilities;
+import java.awt.event.KeyEvent;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
@@ -23,7 +26,9 @@ import java.util.List;
* reproducible benchmark results to standard output.
*
* The benchmark creates a 16x16x16 grid of cubes (4096 total) with the camera
- * following a deterministic orbital path. Each test runs for 30 seconds.
+ * following a deterministic orbital path. Each test runs for 30 seconds by default.
+ *
+ * Press Space to skip to the next test immediately.
*
* Available tests:
*
@@ -32,7 +37,7 @@ import java.util.List;
* - {@link WireframeCubesTest} - Line rendering
*
*/
-public class GraphicsBenchmark implements FrameListener {
+public class GraphicsBenchmark implements FrameListener, KeyboardInputHandler {
private static final int WINDOW_WIDTH = 1920;
private static final int WINDOW_HEIGHT = 1080;
@@ -52,6 +57,7 @@ public class GraphicsBenchmark implements FrameListener {
private long frameCount;
private BenchmarkTest currentTest;
private boolean testFinished = false;
+ private boolean benchmarkFinished = false;
private final List results = new ArrayList<>();
private final List tests = new ArrayList<>();
@@ -78,6 +84,7 @@ public class GraphicsBenchmark implements FrameListener {
viewPanel.setFrameRate(0);
shapes = viewPanel.getRootShapeCollection();
viewPanel.addFrameListener(this);
+ viewPanel.getKeyboardFocusStack().pushFocusOwner(this);
camera = viewPanel.getCamera();
}
@@ -90,7 +97,7 @@ public class GraphicsBenchmark implements FrameListener {
private void startNextTest() {
int nextIndex = results.size();
if (nextIndex >= tests.size()) {
- finishBenchmark();
+ scheduleBenchmarkFinish();
return;
}
@@ -103,8 +110,30 @@ public class GraphicsBenchmark implements FrameListener {
currentTest.setup(shapes);
}
+ private void finishCurrentTest() {
+ if (currentTest == null || testFinished) {
+ return;
+ }
+
+ testFinished = true;
+ long elapsed = System.currentTimeMillis() - testStartTime;
+ double durationSeconds = elapsed / 1000.0;
+ results.add(new TestResult(currentTest.getName(), frameCount, durationSeconds));
+
+ currentTest.teardown(shapes);
+ startNextTest();
+ }
+
+ private void scheduleBenchmarkFinish() {
+ benchmarkFinished = true;
+ SwingUtilities.invokeLater(this::finishBenchmark);
+ }
+
private void finishBenchmark() {
currentTest = null;
+ viewPanel.getKeyboardFocusStack().popFocusOwner();
+ viewPanel.removeFrameListener(this);
+ viewPanel.stop();
viewFrame.dispose();
printResults();
}
@@ -138,6 +167,10 @@ public class GraphicsBenchmark implements FrameListener {
@Override
public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
+ if (benchmarkFinished) {
+ return false;
+ }
+
if (currentTest == null) {
return true;
}
@@ -155,14 +188,33 @@ public class GraphicsBenchmark implements FrameListener {
long elapsed = System.currentTimeMillis() - testStartTime;
if (elapsed >= TEST_DURATION_MS && !testFinished) {
- testFinished = true;
- double durationSeconds = elapsed / 1000.0;
- results.add(new TestResult(currentTest.getName(), frameCount, durationSeconds));
-
- currentTest.teardown(shapes);
- startNextTest();
+ finishCurrentTest();
}
return true;
}
+
+ @Override
+ public boolean keyPressed(KeyEvent event, ViewPanel viewPanel) {
+ if (event.getKeyChar() == ' ') {
+ finishCurrentTest();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean keyReleased(KeyEvent event, ViewPanel viewPanel) {
+ return false;
+ }
+
+ @Override
+ public boolean focusLost(ViewPanel viewPanel) {
+ return false;
+ }
+
+ @Override
+ public boolean focusReceived(ViewPanel viewPanel) {
+ return false;
+ }
}
\ No newline at end of file
--
2.20.1