the scene to raytrace current view through compressed voxel
datastructure.
-** Fill-rate test
+** Graphics Benchmark
:PROPERTIES:
-:CUSTOM_ID: fill-rate-test
+:CUSTOM_ID: graphics-benchmark
:ID: e5f6a7b8-c9d0-1234-ef01-345678901234
:END:
-A benchmark for polygon rasterization performance testing. This demo
-tests pixel fill throughput by rendering 40 large screen-filling quads
-(600x600) layered at different Z depths.
+An automated graphics benchmark that measures the engine's rendering
+performance across different rendering modes.
-The low vertex count (4 vertices per quad) combined with high overdraw
-isolates the pixel fill cost from vertex processing overhead, allowing
-you to measure pure rasterization performance.
+The benchmark creates a 16x16x16 grid of cubes (4096 total) and runs
+three tests sequentially, each for 30 seconds:
-Controls:
-| key | result |
-|-----+---------------------|
-| 1 | Solid colored quads |
-| 2 | Textured quads |
+- *Solid Cubes* - Tests solid-color polygon rasterization
+- *Textured Cubes* - Tests textured polygon rendering with texture sampling
+- *Wireframe Cubes* - Tests line rendering performance
-Compare FPS between solid and textured modes to see exactly what
-texture sampling costs on your hardware.
+The camera follows a deterministic orbital path around the scene,
+ensuring reproducible results across runs.
-
-Example test run:
#+begin_example
================================================================================
GRAPHICS BENCHMARK RESULTS
+++ /dev/null
-/*
- * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
- * This project is released under Creative Commons Zero (CC0) license.
- */
-
-package eu.svjatoslav.sixth.e3d.examples;
-
-import eu.svjatoslav.sixth.e3d.geometry.Point2D;
-import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-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.math.Vertex;
-import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
-import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
-import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox;
-import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Automated graphics benchmark that tests the engine's rendering performance.
- * Runs multiple tests sequentially, each for a fixed duration, and outputs
- * reproducible benchmark results to standard output.
- *
- * <p>The benchmark creates a 16x16x16 grid of cubes (4096 total) with the camera
- * following a deterministic orbital path. Each test runs for 30 seconds.</p>
- *
- * @see SolidPolygonCube
- * @see TexturedPolygon
- */
-public class GraphicsBenchmark implements FrameListener {
-
- private static final int WINDOW_WIDTH = 1920;
- private static final int WINDOW_HEIGHT = 1080;
- private static final int GRID_SIZE = 16;
- private static final double SPACING = 80;
- private static final double CUBE_SIZE = 25;
- private static final double ORBIT_DISTANCE = 1200;
- private static final double ORBIT_SPEED = 0.0003;
- private static final double WOBBLE_AMPLITUDE = 800;
- private static final int TEXTURE_COUNT = 20;
- private static final int TEST_DURATION_MS = 30000;
- private static final long RANDOM_SEED = 42;
-
- private ViewFrame viewFrame;
- private ViewPanel viewPanel;
- private ShapeCollection shapes;
- private final Random random = new Random(RANDOM_SEED);
- private Camera camera;
-
- private final Texture[] textures = new Texture[TEXTURE_COUNT];
- private final int[] cubeTextureIndices = new int[GRID_SIZE * GRID_SIZE * GRID_SIZE];
-
- private double orbitAngle = 0;
- private long testStartTime;
- private long frameCount;
- private BenchmarkTest currentTest;
- private boolean testFinished = false;
- private final List<TestResult> results = new ArrayList<>();
- private final List<BenchmarkTest> tests = new ArrayList<>();
-
- /**
- * Interface for a single benchmark test.
- * Implementations define how to set up and tear down the test scene.
- */
- public interface BenchmarkTest {
- String getName();
- void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices);
- void teardown(ShapeCollection shapes);
- }
-
- /**
- * Holds the results of a single benchmark test.
- */
- public static class TestResult {
- final String testName;
- final long frameCount;
- final double durationSeconds;
- final double averageFps;
-
- TestResult(String testName, long frameCount, double durationSeconds) {
- this.testName = testName;
- this.frameCount = frameCount;
- this.durationSeconds = durationSeconds;
- this.averageFps = frameCount / durationSeconds;
- }
- }
-
- /**
- * Benchmark test for solid-color cubes.
- */
- public static class SolidCubesTest implements BenchmarkTest {
- private final Random random = new Random(RANDOM_SEED);
- private final List<Object> cubes = new ArrayList<>();
-
- @Override
- public String getName() {
- return "Solid Cubes";
- }
-
- @Override
- public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
- random.setSeed(RANDOM_SEED);
- double offset = -(GRID_SIZE - 1) * SPACING / 2;
-
- for (int x = 0; x < GRID_SIZE; x++) {
- for (int y = 0; y < GRID_SIZE; y++) {
- for (int z = 0; z < GRID_SIZE; z++) {
- double px = offset + x * SPACING;
- double py = offset + y * SPACING;
- double pz = offset + z * SPACING;
-
- Color color = new Color(
- 50 + random.nextInt(150),
- 100 + random.nextInt(155),
- 50 + random.nextInt(150),
- 40
- );
- SolidPolygonCube cube = new SolidPolygonCube(
- new Point3D(px, py, pz),
- CUBE_SIZE,
- color
- );
- shapes.addShape(cube);
- cubes.add(cube);
- }
- }
- }
- }
-
- @Override
- public void teardown(ShapeCollection shapes) {
- for (Object cube : cubes) {
- shapes.getShapes().remove(cube);
- }
- cubes.clear();
- }
- }
-
- /**
- * Benchmark test for textured cubes.
- */
- public static class TexturedCubesTest implements BenchmarkTest {
- private final List<Object> cubes = new ArrayList<>();
-
- @Override
- public String getName() {
- return "Textured Cubes";
- }
-
- @Override
- public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
- double offset = -(GRID_SIZE - 1) * SPACING / 2;
- int idx = 0;
-
- for (int x = 0; x < GRID_SIZE; x++) {
- for (int y = 0; y < GRID_SIZE; y++) {
- for (int z = 0; z < GRID_SIZE; z++) {
- double px = offset + x * SPACING;
- double py = offset + y * SPACING;
- double pz = offset + z * SPACING;
-
- Texture tex = textures[cubeTextureIndices[idx]];
- TexturedCube cube = new TexturedCube(
- new Point3D(px, py, pz),
- CUBE_SIZE,
- tex
- );
- shapes.addShape(cube);
- cubes.add(cube);
- idx++;
- }
- }
- }
- }
-
- @Override
- public void teardown(ShapeCollection shapes) {
- for (Object cube : cubes) {
- shapes.getShapes().remove(cube);
- }
- cubes.clear();
- }
- }
-
- /**
- * Benchmark test for wireframe cubes.
- */
- public static class WireframeCubesTest implements BenchmarkTest {
- private final Random random = new Random(RANDOM_SEED);
- private final List<Object> cubes = new ArrayList<>();
-
- @Override
- public String getName() {
- return "Wireframe Cubes";
- }
-
- @Override
- public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
- random.setSeed(RANDOM_SEED);
- double offset = -(GRID_SIZE - 1) * SPACING / 2;
-
- for (int x = 0; x < GRID_SIZE; x++) {
- for (int y = 0; y < GRID_SIZE; y++) {
- for (int z = 0; z < GRID_SIZE; z++) {
- double px = offset + x * SPACING;
- double py = offset + y * SPACING;
- double pz = offset + z * SPACING;
-
- Color color = new Color(
- 100 + random.nextInt(155),
- 100 + random.nextInt(155),
- 100 + random.nextInt(155)
- );
- LineAppearance appearance = new LineAppearance(1.5, color);
-
- Point3D center = new Point3D(px, py, pz);
- Point3D p1 = new Point3D(center.x - CUBE_SIZE, center.y - CUBE_SIZE, center.z - CUBE_SIZE);
- Point3D p2 = new Point3D(center.x + CUBE_SIZE, center.y + CUBE_SIZE, center.z + CUBE_SIZE);
-
- WireframeBox cube = new WireframeBox(p1, p2, appearance);
- shapes.addShape(cube);
- cubes.add(cube);
- }
- }
- }
- }
-
- @Override
- public void teardown(ShapeCollection shapes) {
- for (Object cube : cubes) {
- shapes.getShapes().remove(cube);
- }
- cubes.clear();
- }
- }
-
- /**
- * Entry point for the graphics benchmark.
- * @param args command line arguments (ignored)
- */
- public static void main(String[] args) {
- new GraphicsBenchmark();
- }
-
- /**
- * Constructs and runs the graphics benchmark.
- */
- public GraphicsBenchmark() {
- initializeWindow();
- initializeTextures();
- initializeCubeTextureIndices();
- registerTests();
- startNextTest();
- }
-
- private void initializeWindow() {
- viewFrame = new ViewFrame(WINDOW_WIDTH, WINDOW_HEIGHT);
- viewPanel = viewFrame.getViewPanel();
- viewPanel.setFrameRate(0);
- shapes = viewPanel.getRootShapeCollection();
- viewPanel.addFrameListener(this);
- camera = viewPanel.getCamera();
- }
-
- private void initializeTextures() {
- for (int i = 0; i < TEXTURE_COUNT; i++) {
- textures[i] = createGlowTexture(
- 50 + random.nextInt(200),
- 50 + random.nextInt(200),
- 50 + random.nextInt(200)
- );
- }
- }
-
- private void initializeCubeTextureIndices() {
- random.setSeed(RANDOM_SEED);
- for (int i = 0; i < cubeTextureIndices.length; i++) {
- cubeTextureIndices[i] = random.nextInt(TEXTURE_COUNT);
- }
- }
-
- private void registerTests() {
- tests.add(new SolidCubesTest());
- tests.add(new TexturedCubesTest());
- tests.add(new WireframeCubesTest());
- }
-
- private void startNextTest() {
- int nextIndex = results.size();
- if (nextIndex >= tests.size()) {
- finishBenchmark();
- return;
- }
-
- currentTest = tests.get(nextIndex);
- testFinished = false;
- orbitAngle = 0;
- frameCount = 0;
- testStartTime = System.currentTimeMillis();
-
- currentTest.setup(shapes, textures, cubeTextureIndices);
- }
-
- private void finishBenchmark() {
- currentTest = null;
- viewFrame.dispose();
- printResults();
- System.exit(0);
- }
-
- private void printResults() {
- String separator = "================================================================================";
- String thinSeparator = "--------------------------------------------------------------------------------";
-
- System.out.println(separator);
- System.out.println(" GRAPHICS BENCHMARK RESULTS");
- System.out.println(separator);
- System.out.println("Date: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
- System.out.println("Resolution: " + WINDOW_WIDTH + "x" + WINDOW_HEIGHT);
- System.out.println("Cubes: " + (GRID_SIZE * GRID_SIZE * GRID_SIZE) + " (" + GRID_SIZE + "x" + GRID_SIZE + "x" + GRID_SIZE + " grid)");
- System.out.println("Duration: " + (TEST_DURATION_MS / 1000) + " seconds per test");
- System.out.println();
- System.out.println(thinSeparator);
- System.out.printf("%-28s %-11s %-9s %-10s%n", "Test", "Duration", "Frames", "Avg FPS");
- System.out.println(thinSeparator);
-
- for (TestResult result : results) {
- System.out.printf("%-28s %-11s %-9d %-10.2f%n",
- result.testName,
- String.format("%.2fs", result.durationSeconds),
- result.frameCount,
- result.averageFps);
- }
-
- System.out.println(separator);
- }
-
- private Texture createGlowTexture(int r, int g, int b) {
- int texSize = 64;
- Texture texture = new Texture(texSize, texSize, 2);
-
- java.awt.Graphics2D gr = texture.graphics;
- gr.setBackground(new java.awt.Color(r, g, b, 80));
- gr.clearRect(0, 0, texSize, texSize);
-
- int glowWidth = 6;
- for (int i = 0; i < glowWidth; i++) {
- int intensity = (int) (255.0 * (glowWidth - i) / glowWidth);
- java.awt.Color glowColor = new java.awt.Color(
- Math.min(255, r + intensity),
- Math.min(255, g + intensity),
- Math.min(255, b + intensity),
- 200 - i * 30
- );
- gr.setColor(glowColor);
- gr.drawRect(i, i, texSize - 1 - 2 * i, texSize - 1 - 2 * i);
- }
-
- gr.dispose();
- texture.resetResampledBitmapCache();
- return texture;
- }
-
- @Override
- public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
- if (currentTest == null) {
- return true;
- }
-
- orbitAngle += ORBIT_SPEED * millisecondsSinceLastFrame;
-
- double x = Math.sin(orbitAngle) * ORBIT_DISTANCE;
- double z = Math.cos(orbitAngle) * ORBIT_DISTANCE;
- double y = Math.sin(orbitAngle * 1.8934) * WOBBLE_AMPLITUDE;
-
- camera.getTransform().setTranslation(new Point3D(x, y, z));
- camera.lookAt(new Point3D(0, 0, 0));
-
- frameCount++;
-
- 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();
- }
-
- return true;
- }
-
- /**
- * A cube composed of textured polygons.
- */
- private static class TexturedCube extends AbstractCompositeShape {
-
- public TexturedCube(Point3D center, double size, Texture texture) {
- double s = size;
- Point3D p1 = new Point3D(center.x - s, center.y - s, center.z - s);
- Point3D p7 = new Point3D(center.x + s, center.y + s, center.z + s);
-
- Point3D p2 = new Point3D(p7.x, p1.y, p1.z);
- Point3D p3 = new Point3D(p7.x, p1.y, p7.z);
- Point3D p4 = new Point3D(p1.x, p1.y, p7.z);
- Point3D p5 = new Point3D(p1.x, p7.y, p1.z);
- Point3D p6 = new Point3D(p7.x, p7.y, p1.z);
- Point3D p8 = new Point3D(p1.x, p7.y, p7.z);
-
- Point2D t00 = new Point2D(0, 0);
- Point2D t10 = new Point2D(64, 0);
- Point2D t01 = new Point2D(0, 64);
- Point2D t11 = new Point2D(64, 64);
-
- addTexturedFace(p1, p2, p3, p4, t00, t10, t11, t01, texture);
- addTexturedFace(p5, p8, p7, p6, t00, t01, t11, t10, texture);
- addTexturedFace(p1, p5, p6, p2, t00, t01, t11, t10, texture);
- addTexturedFace(p3, p7, p8, p4, t00, t10, t11, t01, texture);
- addTexturedFace(p1, p4, p8, p5, t00, t10, t11, t01, texture);
- addTexturedFace(p2, p6, p7, p3, t00, t01, t11, t10, texture);
-
- setBackfaceCulling(true);
- }
-
- private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
- Point2D t1, Point2D t2, Point2D t3, Point2D t4,
- Texture texture) {
- TexturedPolygon tri1 = new TexturedPolygon(
- new Vertex(p1, t1),
- new Vertex(p2, t2),
- new Vertex(p3, t3),
- texture
- );
- tri1.setBackfaceCulling(true);
-
- TexturedPolygon tri2 = new TexturedPolygon(
- new Vertex(p1, t1),
- new Vertex(p3, t3),
- new Vertex(p4, t4),
- texture
- );
- tri2.setBackfaceCulling(true);
-
- addShape(tri1);
- addShape(tri2);
- }
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+/**
+ * Interface for a single benchmark test.
+ * Implementations define how to set up and tear down the test scene.
+ */
+public interface BenchmarkTest {
+
+ /**
+ * Returns the display name for this benchmark test.
+ * @return the test name
+ */
+ String getName();
+
+ /**
+ * Sets up the test scene by adding shapes to the collection.
+ * @param shapes the shape collection to populate
+ * @param textures available textures for textured tests
+ * @param cubeTextureIndices texture index for each cube
+ */
+ void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices);
+
+ /**
+ * Tears down the test scene by removing all shapes added during setup.
+ * @param shapes the shape collection to clean up
+ */
+ void teardown(ShapeCollection shapes);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+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.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Automated graphics benchmark that tests the engine's rendering performance.
+ * Runs multiple tests sequentially, each for a fixed duration, and outputs
+ * reproducible benchmark results to standard output.
+ *
+ * <p>The benchmark creates a 16x16x16 grid of cubes (4096 total) with the camera
+ * following a deterministic orbital path. Each test runs for 30 seconds.</p>
+ *
+ * <p>Available tests:</p>
+ * <ul>
+ * <li>{@link SolidCubesTest} - Solid-color polygon rendering</li>
+ * <li>{@link TexturedCubesTest} - Textured polygon rendering</li>
+ * <li>{@link WireframeCubesTest} - Line rendering</li>
+ * </ul>
+ */
+public class GraphicsBenchmark implements FrameListener {
+
+ private static final int WINDOW_WIDTH = 1920;
+ private static final int WINDOW_HEIGHT = 1080;
+ private static final int GRID_SIZE = 16;
+ private static final double ORBIT_DISTANCE = 1200;
+ private static final double ORBIT_SPEED = 0.0003;
+ private static final double WOBBLE_AMPLITUDE = 800;
+ private static final int TEXTURE_COUNT = 20;
+ private static final int TEST_DURATION_MS = 30000;
+ private static final long RANDOM_SEED = 42;
+
+ private ViewFrame viewFrame;
+ private ViewPanel viewPanel;
+ private ShapeCollection shapes;
+ private final Random random = new Random(RANDOM_SEED);
+ private Camera camera;
+
+ private final Texture[] textures = new Texture[TEXTURE_COUNT];
+ private final int[] cubeTextureIndices = new int[GRID_SIZE * GRID_SIZE * GRID_SIZE];
+
+ private double orbitAngle = 0;
+ private long testStartTime;
+ private long frameCount;
+ private BenchmarkTest currentTest;
+ private boolean testFinished = false;
+ private final List<TestResult> results = new ArrayList<>();
+ private final List<BenchmarkTest> tests = new ArrayList<>();
+
+ /**
+ * Entry point for the graphics benchmark.
+ * @param args command line arguments (ignored)
+ */
+ public static void main(String[] args) {
+ new GraphicsBenchmark();
+ }
+
+ /**
+ * Constructs and runs the graphics benchmark.
+ */
+ public GraphicsBenchmark() {
+ initializeWindow();
+ initializeTextures();
+ initializeCubeTextureIndices();
+ registerTests();
+ startNextTest();
+ }
+
+ private void initializeWindow() {
+ viewFrame = new ViewFrame(WINDOW_WIDTH, WINDOW_HEIGHT);
+ viewPanel = viewFrame.getViewPanel();
+ viewPanel.setFrameRate(0);
+ shapes = viewPanel.getRootShapeCollection();
+ viewPanel.addFrameListener(this);
+ camera = viewPanel.getCamera();
+ }
+
+ private void initializeTextures() {
+ for (int i = 0; i < TEXTURE_COUNT; i++) {
+ textures[i] = createGlowTexture(
+ 50 + random.nextInt(200),
+ 50 + random.nextInt(200),
+ 50 + random.nextInt(200)
+ );
+ }
+ }
+
+ private void initializeCubeTextureIndices() {
+ random.setSeed(RANDOM_SEED);
+ for (int i = 0; i < cubeTextureIndices.length; i++) {
+ cubeTextureIndices[i] = random.nextInt(TEXTURE_COUNT);
+ }
+ }
+
+ private void registerTests() {
+ tests.add(new SolidCubesTest());
+ tests.add(new TexturedCubesTest());
+ tests.add(new WireframeCubesTest());
+ }
+
+ private void startNextTest() {
+ int nextIndex = results.size();
+ if (nextIndex >= tests.size()) {
+ finishBenchmark();
+ return;
+ }
+
+ currentTest = tests.get(nextIndex);
+ testFinished = false;
+ orbitAngle = 0;
+ frameCount = 0;
+ testStartTime = System.currentTimeMillis();
+
+ currentTest.setup(shapes, textures, cubeTextureIndices);
+ }
+
+ private void finishBenchmark() {
+ currentTest = null;
+ viewFrame.dispose();
+ printResults();
+ }
+
+ private void printResults() {
+ String separator = "================================================================================";
+ String thinSeparator = "--------------------------------------------------------------------------------";
+
+ System.out.println(separator);
+ System.out.println(" GRAPHICS BENCHMARK RESULTS");
+ System.out.println(separator);
+ System.out.println("Date: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+ System.out.println("Resolution: " + WINDOW_WIDTH + "x" + WINDOW_HEIGHT);
+ System.out.println("Cubes: " + (GRID_SIZE * GRID_SIZE * GRID_SIZE) + " (" + GRID_SIZE + "x" + GRID_SIZE + "x" + GRID_SIZE + " grid)");
+ System.out.println("Duration: " + (TEST_DURATION_MS / 1000) + " seconds per test");
+ System.out.println();
+ System.out.println(thinSeparator);
+ System.out.printf("%-28s %-11s %-9s %-10s%n", "Test", "Duration", "Frames", "Avg FPS");
+ System.out.println(thinSeparator);
+
+ for (TestResult result : results) {
+ System.out.printf("%-28s %-11s %-9d %-10.2f%n",
+ result.testName,
+ String.format("%.2fs", result.durationSeconds),
+ result.frameCount,
+ result.averageFps);
+ }
+
+ System.out.println(separator);
+ }
+
+ private Texture createGlowTexture(int r, int g, int b) {
+ int texSize = 64;
+ Texture texture = new Texture(texSize, texSize, 2);
+
+ java.awt.Graphics2D gr = texture.graphics;
+ gr.setBackground(new java.awt.Color(r, g, b, 80));
+ gr.clearRect(0, 0, texSize, texSize);
+
+ int glowWidth = 6;
+ for (int i = 0; i < glowWidth; i++) {
+ int intensity = (int) (255.0 * (glowWidth - i) / glowWidth);
+ java.awt.Color glowColor = new java.awt.Color(
+ Math.min(255, r + intensity),
+ Math.min(255, g + intensity),
+ Math.min(255, b + intensity),
+ 200 - i * 30
+ );
+ gr.setColor(glowColor);
+ gr.drawRect(i, i, texSize - 1 - 2 * i, texSize - 1 - 2 * i);
+ }
+
+ gr.dispose();
+ texture.resetResampledBitmapCache();
+ return texture;
+ }
+
+ @Override
+ public boolean onFrame(ViewPanel viewPanel, int millisecondsSinceLastFrame) {
+ if (currentTest == null) {
+ return true;
+ }
+
+ orbitAngle += ORBIT_SPEED * millisecondsSinceLastFrame;
+
+ double x = Math.sin(orbitAngle) * ORBIT_DISTANCE;
+ double z = Math.cos(orbitAngle) * ORBIT_DISTANCE;
+ double y = Math.sin(orbitAngle * 1.8934) * WOBBLE_AMPLITUDE;
+
+ camera.getTransform().setTranslation(new Point3D(x, y, z));
+ camera.lookAt(new Point3D(0, 0, 0));
+
+ frameCount++;
+
+ 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();
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.solid.SolidPolygonCube;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for solid-color cubes.
+ * Renders a grid of cubes with random semi-transparent colors to test
+ * solid polygon rasterization performance.
+ */
+public class SolidCubesTest implements BenchmarkTest {
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> cubes = new ArrayList<>();
+
+ @Override
+ public String getName() {
+ return "Solid Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
+ random.setSeed(RANDOM_SEED);
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Color color = new Color(
+ 50 + random.nextInt(150),
+ 100 + random.nextInt(155),
+ 50 + random.nextInt(150),
+ 40
+ );
+ SolidPolygonCube cube = new SolidPolygonCube(
+ new Point3D(px, py, pz),
+ CUBE_SIZE,
+ color
+ );
+ shapes.addShape(cube);
+ cubes.add(cube);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ for (Object cube : cubes) {
+ shapes.getShapes().remove(cube);
+ }
+ cubes.clear();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+/**
+ * Holds the results of a single benchmark test.
+ */
+public class TestResult {
+
+ /** The name of the benchmark test. */
+ public final String testName;
+
+ /** Total number of frames rendered during the test. */
+ public final long frameCount;
+
+ /** Duration of the test in seconds. */
+ public final double durationSeconds;
+
+ /** Average frames per second achieved during the test. */
+ public final double averageFps;
+
+ /**
+ * Creates a test result record.
+ * @param testName the name of the test
+ * @param frameCount total frames rendered
+ * @param durationSeconds test duration in seconds
+ */
+ public TestResult(String testName, long frameCount, double durationSeconds) {
+ this.testName = testName;
+ this.frameCount = frameCount;
+ this.durationSeconds = durationSeconds;
+ this.averageFps = frameCount / durationSeconds;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point2D;
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.math.Vertex;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.texturedpolygon.TexturedPolygon;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.base.AbstractCompositeShape;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+/**
+ * A cube composed of textured polygons.
+ * Used by the {@link TexturedCubesTest} benchmark to measure texture rendering performance.
+ */
+public class TexturedCube extends AbstractCompositeShape {
+
+ /**
+ * Creates a textured cube centered at the given position.
+ * @param center the center position of the cube
+ * @param size half the edge length of the cube
+ * @param texture the texture to apply to all faces
+ */
+ public TexturedCube(Point3D center, double size, Texture texture) {
+ double s = size;
+ Point3D p1 = new Point3D(center.x - s, center.y - s, center.z - s);
+ Point3D p7 = new Point3D(center.x + s, center.y + s, center.z + s);
+
+ Point3D p2 = new Point3D(p7.x, p1.y, p1.z);
+ Point3D p3 = new Point3D(p7.x, p1.y, p7.z);
+ Point3D p4 = new Point3D(p1.x, p1.y, p7.z);
+ Point3D p5 = new Point3D(p1.x, p7.y, p1.z);
+ Point3D p6 = new Point3D(p7.x, p7.y, p1.z);
+ Point3D p8 = new Point3D(p1.x, p7.y, p7.z);
+
+ Point2D t00 = new Point2D(0, 0);
+ Point2D t10 = new Point2D(64, 0);
+ Point2D t01 = new Point2D(0, 64);
+ Point2D t11 = new Point2D(64, 64);
+
+ addTexturedFace(p1, p2, p3, p4, t00, t10, t11, t01, texture);
+ addTexturedFace(p5, p8, p7, p6, t00, t01, t11, t10, texture);
+ addTexturedFace(p1, p5, p6, p2, t00, t01, t11, t10, texture);
+ addTexturedFace(p3, p7, p8, p4, t00, t10, t11, t01, texture);
+ addTexturedFace(p1, p4, p8, p5, t00, t10, t11, t01, texture);
+ addTexturedFace(p2, p6, p7, p3, t00, t01, t11, t10, texture);
+
+ setBackfaceCulling(true);
+ }
+
+ private void addTexturedFace(Point3D p1, Point3D p2, Point3D p3, Point3D p4,
+ Point2D t1, Point2D t2, Point2D t3, Point2D t4,
+ Texture texture) {
+ TexturedPolygon tri1 = new TexturedPolygon(
+ new Vertex(p1, t1),
+ new Vertex(p2, t2),
+ new Vertex(p3, t3),
+ texture
+ );
+ tri1.setBackfaceCulling(true);
+
+ TexturedPolygon tri2 = new TexturedPolygon(
+ new Vertex(p1, t1),
+ new Vertex(p3, t3),
+ new Vertex(p4, t4),
+ texture
+ );
+ tri2.setBackfaceCulling(true);
+
+ addShape(tri1);
+ addShape(tri2);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Benchmark test for textured cubes.
+ * Renders a grid of cubes with textures to test textured polygon rendering
+ * and texture sampling performance.
+ */
+public class TexturedCubesTest implements BenchmarkTest {
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+
+ private final List<Object> cubes = new ArrayList<>();
+
+ @Override
+ public String getName() {
+ return "Textured Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+ int idx = 0;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Texture tex = textures[cubeTextureIndices[idx]];
+ TexturedCube cube = new TexturedCube(
+ new Point3D(px, py, pz),
+ CUBE_SIZE,
+ tex
+ );
+ shapes.addShape(cube);
+ cubes.add(cube);
+ idx++;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ for (Object cube : cubes) {
+ shapes.getShapes().remove(cube);
+ }
+ cubes.clear();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
+
+import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
+import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.line.LineAppearance;
+import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.composite.wireframe.WireframeBox;
+import eu.svjatoslav.sixth.e3d.renderer.raster.texture.Texture;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Benchmark test for wireframe cubes.
+ * Renders a grid of wireframe cube outlines to test line rendering performance.
+ */
+public class WireframeCubesTest implements BenchmarkTest {
+
+ private static final int GRID_SIZE = 16;
+ private static final double SPACING = 80;
+ private static final double CUBE_SIZE = 25;
+ private static final long RANDOM_SEED = 42;
+
+ private final Random random = new Random(RANDOM_SEED);
+ private final List<Object> cubes = new ArrayList<>();
+
+ @Override
+ public String getName() {
+ return "Wireframe Cubes";
+ }
+
+ @Override
+ public void setup(ShapeCollection shapes, Texture[] textures, int[] cubeTextureIndices) {
+ random.setSeed(RANDOM_SEED);
+ double offset = -(GRID_SIZE - 1) * SPACING / 2;
+
+ for (int x = 0; x < GRID_SIZE; x++) {
+ for (int y = 0; y < GRID_SIZE; y++) {
+ for (int z = 0; z < GRID_SIZE; z++) {
+ double px = offset + x * SPACING;
+ double py = offset + y * SPACING;
+ double pz = offset + z * SPACING;
+
+ Color color = new Color(
+ 100 + random.nextInt(155),
+ 100 + random.nextInt(155),
+ 100 + random.nextInt(155)
+ );
+ LineAppearance appearance = new LineAppearance(1.5, color);
+
+ Point3D center = new Point3D(px, py, pz);
+ Point3D p1 = new Point3D(center.x - CUBE_SIZE, center.y - CUBE_SIZE, center.z - CUBE_SIZE);
+ Point3D p2 = new Point3D(center.x + CUBE_SIZE, center.y + CUBE_SIZE, center.z + CUBE_SIZE);
+
+ WireframeBox cube = new WireframeBox(p1, p2, appearance);
+ shapes.addShape(cube);
+ cubes.add(cube);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void teardown(ShapeCollection shapes) {
+ for (Object cube : cubes) {
+ shapes.getShapes().remove(cube);
+ }
+ cubes.clear();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Sixth 3D engine demos. Author: Svjatoslav Agejenko.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Graphics benchmark components for measuring the Sixth 3D engine's rendering performance.
+ *
+ * <p>The benchmark suite includes:</p>
+ * <ul>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark} - Main benchmark runner</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.BenchmarkTest} - Interface for test implementations</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.SolidCubesTest} - Solid-color cube rendering test</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.TexturedCubesTest} - Textured cube rendering test</li>
+ * <li>{@link eu.svjatoslav.sixth.e3d.examples.benchmark.WireframeCubesTest} - Wireframe cube rendering test</li>
+ * </ul>
+ */
+
+package eu.svjatoslav.sixth.e3d.examples.benchmark;
\ No newline at end of file
package eu.svjatoslav.sixth.e3d.examples.launcher;
-import eu.svjatoslav.sixth.e3d.examples.*;
-import eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo;
+import eu.svjatoslav.sixth.e3d.examples.GraphDemo;
+import eu.svjatoslav.sixth.e3d.examples.OctreeDemo;
+import eu.svjatoslav.sixth.e3d.examples.RandomPolygonsDemo;
+import eu.svjatoslav.sixth.e3d.examples.RainingNumbersDemo;
import eu.svjatoslav.sixth.e3d.examples.ShadedShapesDemo;
+import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo;
+import eu.svjatoslav.sixth.e3d.examples.TextEditorDemo2;
+import eu.svjatoslav.sixth.e3d.examples.benchmark.GraphicsBenchmark;
+import eu.svjatoslav.sixth.e3d.examples.galaxy_demo.PointCloudDemo;
import javax.swing.*;
import java.awt.event.ActionEvent;