code: package org.sunflow;
  
  import org.sunflow.core.Display;
  import org.sunflow.core.display.FastDisplay;
  import org.sunflow.core.display.FileDisplay;
  import org.sunflow.math.Matrix4;
  import org.sunflow.math.Point3;
  import org.sunflow.math.Vector3;
  import org.sunflow.system.Timer;
  import org.sunflow.system.UI;
  import org.sunflow.system.UI.Module;
  import org.sunflow.system.ui.ConsoleInterface;
  
  public class RealtimeBenchmark extends SunflowAPI {
      public RealtimeBenchmark(boolean showGUI, int threads) {
          Display display = showGUI ? new FastDisplay() : new FileDisplay(false);
          UI.printInfo(Module.BENCH, "Preparing benchmarking scene ...");
          // settings
          parameter("threads", threads);
          // spawn regular priority threads
          parameter("threads.lowPriority", false);
          parameter("resolutionX", 512);
          parameter("resolutionY", 512);
          parameter("aa.min", -3);
          parameter("aa.max", 0);
          parameter("depths.diffuse", 1);
          parameter("depths.reflection", 1);
          parameter("depths.refraction", 0);
          parameter("bucket.order", "hilbert");
          parameter("bucket.size", 32);
          options(SunflowAPI.DEFAULT_OPTIONS);
          // camera
          Point3 eye = new Point3(30, 0, 10.967f);
          Point3 target = new Point3(0, 0, 5.4f);
          Vector3 up = new Vector3(0, 0, 1);
          parameter("transform", Matrix4.lookAt(eye, target, up));
          parameter("fov", 45.0f);
          camera("camera", "pinhole");
          parameter("camera", "camera");
          options(SunflowAPI.DEFAULT_OPTIONS);
          // geometry
          createGeometry();
          // this first render is not timed, it caches the acceleration data
          // structures and tesselations so they won't be
          // included in the main timing
          UI.printInfo(Module.BENCH, "Rendering warmup frame ...");
          render(SunflowAPI.DEFAULT_OPTIONS, display);
          // now disable all output - and run the benchmark
          UI.set(null);
          Timer t = new Timer();
          t.start();
          float phi = 0;
          int frames = 0;
          while (phi < 4 * Math.PI) {
              eye.x = 30 * (float) Math.cos(phi);
              eye.y = 30 * (float) Math.sin(phi);
              phi += Math.PI / 30;
              frames++;
              // update camera
              parameter("transform", Matrix4.lookAt(eye, target, up));
              camera("camera", null);
              render(SunflowAPI.DEFAULT_OPTIONS, display);
          }
          t.end();
          UI.set(new ConsoleInterface());
          UI.printInfo(Module.BENCH, "Benchmark results:");
          UI.printInfo(Module.BENCH, "  * Average FPS:         %.2f", frames / t.seconds());
          UI.printInfo(Module.BENCH, "  * Total time:          %s", t);
      }
  
      private void createGeometry() {
          // light source
          parameter("source", new Point3(-15.5945f, -30.0581f, 45.967f));
          parameter("dir", new Vector3(15.5945f, 30.0581f, -45.967f));
          parameter("radius", 60.0f);
          parameter("radiance", null, 3, 3, 3);
          light("light", "directional");
  
          // gi-engine
          parameter("gi.engine", "fake");
          parameter("gi.fake.sky", null, 0.25f, 0.25f, 0.25f);
          parameter("gi.fake.ground", null, 0.01f, 0.01f, 0.5f);
          parameter("gi.fake.up", new Vector3(0, 0, 1));
          options(DEFAULT_OPTIONS);
  
          // shaders
          parameter("diffuse", null, 0.5f, 0.5f, 0.5f);
          shader("default", "diffuse");
          parameter("diffuse", null, 0.5f, 0.5f, 0.5f);
          parameter("shiny", 0.2f);
          shader("refl", "shiny_diffuse");
          // objects
  
          // teapot
          parameter("subdivs", 10);
          geometry("teapot", "teapot");
          parameter("shaders", "default");
          Matrix4 m = Matrix4.IDENTITY;
          m = Matrix4.scale(0.075f).multiply(m);
          m = Matrix4.rotateZ((float) Math.toRadians(-45f)).multiply(m);
          m = Matrix4.translation(-7, 0, 0).multiply(m);
          parameter("transform", m);
          instance("teapot.instance", "teapot");
  
          // gumbo
          parameter("subdivs", 10);
          geometry("gumbo", "gumbo");
          m = Matrix4.IDENTITY;
          m = Matrix4.scale(0.5f).multiply(m);
          m = Matrix4.rotateZ((float) Math.toRadians(25f)).multiply(m);
          m = Matrix4.translation(3, -7, 0).multiply(m);
          parameter("shaders", "default");
          parameter("transform", m);
          instance("gumbo.instance", "gumbo");
  
          // ground plane
          parameter("center", new Point3(0, 0, 0));
          parameter("normal", new Vector3(0, 0, 1));
          geometry("ground", "plane");
          parameter("shaders", "refl");
          instance("ground.instance", "ground");
      }
  }