java-topology/test/jdk/performance/client/RenderPerfTest/src/renderperf/RenderPerfTest.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

2414 lines
91 KiB
Java

/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package renderperf;
import java.awt.AWTException;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LinearGradientPaint;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.VolatileImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntBinaryOperator;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public final class RenderPerfTest {
private final static String VERSION = "RenderPerfTest 2024.02";
private static final HashSet<String> ignoredTests = new HashSet<>();
static {
// add ignored tests here
// ignoredTests.add("testMyIgnoredTest");
ignoredTests.add("testCalibration"); // not from command line
}
private final static String EXEC_MODE_ROBOT = "robot";
private final static String EXEC_MODE_BUFFER = "buffer";
private final static String EXEC_MODE_VOLATILE = "volatile";
private final static String EXEC_MODE_DEFAULT = EXEC_MODE_ROBOT;
public final static List<String> EXEC_MODES = Arrays.asList(EXEC_MODE_ROBOT, EXEC_MODE_BUFFER, EXEC_MODE_VOLATILE);
private static String EXEC_MODE = EXEC_MODE_DEFAULT;
private final static String GC_MODE_DEF = "def";
private final static String GC_MODE_ALL = "all";
private static String GC_MODE = GC_MODE_DEF;
// System properties:
private final static boolean CALIBRATION = "true".equalsIgnoreCase(System.getProperty("CALIBRATION", "false"));
private final static boolean REPORT_OVERALL_FPS = "true".equalsIgnoreCase(System.getProperty("REPORT_OVERALL_FPS", "false"));
private final static boolean DUMP_SAMPLES = "true".equalsIgnoreCase(System.getProperty("DUMP_SAMPLES", "false"));
private final static boolean TRACE = "true".equalsIgnoreCase(System.getProperty("TRACE", "false"));
private final static boolean TRACE_CONFIGURE = "true".equalsIgnoreCase(System.getProperty("TRACE_CONFIGURE", "false"));
private final static boolean TRACE_SYNC = "true".equalsIgnoreCase(System.getProperty("TRACE_SYNC", "false"));
private final static boolean DELAY_START = "true".equalsIgnoreCase(System.getProperty("DelayStart", "false"));
private final static boolean DELAY_TEST = "true".equalsIgnoreCase(System.getProperty("DelayTest", "false"));
private final static boolean ROBOT_TIME_DELAY = "true".equalsIgnoreCase(System.getProperty("ROBOT_TIME_DELAY", "true"));
private final static boolean ROBOT_TIME_ROUND = "true".equalsIgnoreCase(System.getProperty("ROBOT_TIME_ROUND", "false"));
private final static boolean TEXT_VERSION = "true".equalsIgnoreCase(System.getProperty("TEXT_VERSION", "true"));
// time scale multiplier to get more samples so refined metrics:
private final static int TIME_SCALE = Integer.getInteger("TIME_SCALE", 1);
// default settings:
private static boolean VERBOSE = false;
private static boolean VERBOSE_FONT_CONFIG = false;
private static boolean VERBOSE_GRAPHICS_CONFIG = false;
private static int REPEATS = 1;
private static boolean USE_FPS = true;
private static int NW = 1;
private final static int N_DEFAULT = 1000;
private static int N = N_DEFAULT;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int IMAGE_W = (int) (WIDTH + BW);
private final static int IMAGE_H = (int) (HEIGHT + BH);
// Test attributes:
private static String TEXT_STR = TEXT_VERSION ? VERSION : "The quick brown fox jumps over the lazy dog";
private static String TEXT_FONT = Font.DIALOG;
private static int TEXT_SIZE_DEFAULT = 12;
private static int TEXT_SIZE_LARGE = 32;
private final static int COUNT = 600 * TIME_SCALE;
private final static int MIN_COUNT = 20;
private final static int MAX_SAMPLE_COUNT = 2 * COUNT;
private static int WARMUP_COUNT = MIN_COUNT;
private final static int DELAY = 1;
private final static int CYCLE_DELAY = DELAY;
private final static long MIN_MEASURE_TIME_NS = 1000L * 1000 * 1000 * TIME_SCALE; // 1s min
private final static long MAX_MEASURE_TIME_NS = 6000L * 1000 * 1000 * TIME_SCALE; // 6s max
private final static int MAX_FRAME_CYCLES = 1000 * TIME_SCALE / CYCLE_DELAY;
private final static int COLOR_TOLERANCE = 10;
private final static Color[] MARKER = {Color.RED, Color.BLUE, Color.GREEN};
private final static Toolkit TOOLKIT = Toolkit.getDefaultToolkit();
private final static long FRAME_MAX = 60;
private final static long FRAME_PREC_IN_NANOS = (1000L * 1000 * 1000) / (2L * FRAME_MAX);
interface Configurable {
void configure(Graphics2D g2d, boolean enabled);
}
final static class ConfigurableAA implements Configurable {
@Override
public void configure(final Graphics2D g2d, final boolean enabled) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
enabled ? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF);
}
}
final static class ConfigurableTextAA implements Configurable {
@Override
public void configure(final Graphics2D g2d, final boolean enabled) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
enabled ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
}
final static class ConfigurableTextLCD implements Configurable {
@Override
public void configure(final Graphics2D g2d, final boolean enabled) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
enabled ? RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB
: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
}
}
final static class ConfigurableXORMode implements Configurable {
@Override
public void configure(final Graphics2D g2d, final boolean enabled) {
if (enabled) {
g2d.setXORMode(Color.WHITE);
} else {
g2d.setPaintMode();
}
}
}
final static class ConfigurableXORModeTextLCD implements Configurable {
@Override
public void configure(final Graphics2D g2d, final boolean enabled) {
if (enabled) {
g2d.setXORMode(Color.WHITE);
} else {
g2d.setPaintMode();
}
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
enabled ? RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB
: RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
}
}
final static class Particles {
private final float[] bx;
private final float[] by;
private final float[] vx;
private final float[] vy;
private final float r;
private final int n;
private final float x0;
private final float y0;
private final float width;
private final float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
interface Renderable {
void setup(Graphics2D g2d, boolean enabled);
void render(Graphics2D g2d);
void update();
}
final static class ParticleRenderable implements Renderable {
final Particles balls;
final ParticleRenderer renderer;
Configurable configure = null;
ParticleRenderable(final Particles balls, final ParticleRenderer renderer) {
this.balls = balls;
this.renderer = renderer;
}
@Override
public void setup(final Graphics2D g2d, final boolean enabled) {
if (configure != null) {
if (TRACE_CONFIGURE) {
System.out.println("configure(" + configure.getClass().getSimpleName() + "): " + enabled);
}
configure.configure(g2d, enabled);
}
}
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, renderer);
}
@Override
public void update() {
balls.update();
}
public ParticleRenderable configure(final Configurable configure) {
this.configure = configure;
return this;
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
final static class CalibrationParticleRenderer implements ParticleRenderer {
CalibrationParticleRenderer() {
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
// no-op
}
}
final static class MixedParticleRenderer implements ParticleRenderer {
private final ParticleRenderer[] renderers;
MixedParticleRenderer(ParticleRenderer... renderers) {
this.renderers = renderers;
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
renderers[id % renderers.length].render(g2d, id, x, y, vx, vy);
}
}
final static class BatchedParticleRenderer implements ParticleRenderer {
private final ParticleRenderer[] renderers;
BatchedParticleRenderer(ParticleRenderer... renderers) {
this.renderers = renderers;
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
final int step = N / renderers.length;
renderers[(id / step) % renderers.length].render(g2d, id, x, y, vx, vy);
}
}
static class FlatParticleRenderer implements ParticleRenderer {
Color[] colors;
float r;
FlatParticleRenderer(int n, float r) {
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillOval((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
}
}
static class ClipFlatParticleRenderer extends FlatParticleRenderer {
ClipFlatParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if ((id % 10) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.setClip(new Ellipse2D.Double((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r)));
g2d.fillRect((int) (x[id] - 2 * r), (int) (y[id] - 2 * r), (int) (4 * r), (int) (4 * r));
}
}
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
final float r;
final Font font;
WhiteTextParticleRenderer(float r) {
this(r, TEXT_SIZE_DEFAULT);
}
WhiteTextParticleRenderer(float r, int fontSize) {
this.r = r;
font = new Font(TEXT_FONT, Font.PLAIN, fontSize);
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
g2d.setFont(font);
g2d.drawString(TEXT_STR, (int) (x[id] - r), (int) (y[id] - r));
g2d.drawString(TEXT_STR, (int) (x[id] - r), (int) y[id]);
g2d.drawString(TEXT_STR, (int) (x[id] - r), (int) (y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r) {
this(n,r, TEXT_SIZE_DEFAULT);
}
TextParticleRenderer(int n, float r, int fontSize) {
super(r, fontSize);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class LargeTextParticleRenderer extends TextParticleRenderer {
LargeTextParticleRenderer(int n, float r) {
super(n, r, TEXT_SIZE_LARGE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id % 100 != 0) return;
super.render(g2d, id, x, y, vx, vy);
}
}
static class FlatOvalRotParticleRenderer extends FlatParticleRenderer {
FlatOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillOval(-(int) r, (int) (-0.5 * r), (int) (2 * r), (int) r);
g2d.setTransform(t);
} else {
g2d.fillOval((int) (x[id] - r), (int) (y[id] - 0.5 * r),
(int) (2 * r), (int) r);
}
}
}
static class LinGradOvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGradOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(-r, -0.5 * r);
Point2D end = new Point2D.Double(2 * r, r);
float[] dist = {0.0f, 1.0f};
Color[] cls = {colors[id % colors.length], colors[(colors.length - id) % colors.length]};
LinearGradientPaint p = new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class LinGrad3OvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGrad3OvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(-r, -0.5 * r);
Point2D end = new Point2D.Double(2 * r, r);
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] cls = {
colors[id % colors.length],
colors[(colors.length - id) % colors.length],
colors[(id * 5) % colors.length]};
LinearGradientPaint p = new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class RadGrad3OvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
RadGrad3OvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double();
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] cls = {
colors[id % colors.length],
colors[(colors.length - id) % colors.length],
colors[(id * 5) % colors.length]};
RadialGradientPaint p = new RadialGradientPaint(start, r, dist, cls);
g2d.setPaint(p);
}
}
static class FlatBoxParticleRenderer extends FlatParticleRenderer {
FlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillRect((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
}
}
static class ClipFlatBoxParticleRenderer extends FlatParticleRenderer {
ClipFlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if ((id % 10) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.setClip((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
g2d.fillRect((int) (x[id] - 2 * r), (int) (y[id] - 2 * r), (int) (4 * r), (int) (4 * r));
}
}
}
static class ImgParticleRenderer extends FlatParticleRenderer {
BufferedImage dukeImg;
ImgParticleRenderer(int n, float r) {
super(n, r);
try {
dukeImg = ImageIO.read(
Objects.requireNonNull(
RenderPerfTest.class.getClassLoader().getResourceAsStream(
"renderperf/images/duke.png")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawImage(dukeImg, (int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r), null);
}
}
static class VolImgParticleRenderer extends ImgParticleRenderer {
VolatileImage volImg;
VolImgParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
GraphicsConfiguration config = g2d.getDeviceConfiguration();
if (volImg == null) {
volImg = config.createCompatibleVolatileImage(dukeImg.getWidth(), dukeImg.getHeight(),
Transparency.TRANSLUCENT);
Graphics2D g = volImg.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(dukeImg, null, null);
g.dispose();
} else {
int status = volImg.validate(config);
if (status == VolatileImage.IMAGE_INCOMPATIBLE) {
volImg = config.createCompatibleVolatileImage(dukeImg.getWidth(), dukeImg.getHeight(),
Transparency.TRANSLUCENT);
}
if (status != VolatileImage.IMAGE_OK) {
Graphics2D g = volImg.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(dukeImg, null, null);
g.dispose();
}
}
Composite savedComposite = g2d.getComposite();
g2d.setComposite(AlphaComposite.SrcOver);
g2d.drawImage(volImg, (int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r), null);
g2d.setComposite(savedComposite);
}
}
static class FlatBoxRotParticleRenderer extends FlatParticleRenderer {
FlatBoxRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillRect(-(int) r, -(int) r, (int) (2 * r), (int) (2 * r));
g2d.setTransform(t);
} else {
g2d.fillRect((int) (x[id] - r), (int) (y[id] - r),
(int) (2 * r), (int) (2 * r));
}
}
}
static class WiredParticleRenderer extends FlatParticleRenderer {
WiredParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawOval((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
}
}
static class WiredBoxParticleRenderer extends FlatParticleRenderer {
WiredBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawRect((int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r));
}
}
static class SegParticleRenderer extends FlatParticleRenderer {
SegParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
double v = Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
float nvx = (float) (vx[id] / v);
float nvy = (float) (vy[id] / v);
g2d.setColor(colors[id % colors.length]);
g2d.drawLine((int) (x[id] - r * nvx), (int) (y[id] - r * nvy),
(int) (x[id] + 2 * r * nvx), (int) (y[id] + 2 * r * nvy));
}
}
static class WiredQuadParticleRenderer extends FlatParticleRenderer {
WiredQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.draw(new QuadCurve2D.Float(x[id - 3], y[id - 3], x[id - 2], y[id - 2], x[id - 1], y[id - 1]));
}
}
}
static class FlatQuadParticleRenderer extends FlatParticleRenderer {
FlatQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.fill(new QuadCurve2D.Float(x[id - 3], y[id - 3], x[id - 2], y[id - 2], x[id - 1], y[id - 1]));
}
}
}
static class BlitImageParticleRenderer extends FlatParticleRenderer {
BufferedImage image;
BlitImageParticleRenderer(int n, float r, BufferedImage img) {
super(n, r);
image = img;
fill(image);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.drawImage(image, (int) (x[id] - r), (int) (y[id] - r), (int) (2 * r), (int) (2 * r), null);
}
private static void fill(final Image image) {
final Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setComposite(AlphaComposite.Src);
for (int i = 0; i < image.getHeight(null); ++i) {
graphics.setColor(new Color(i, 0, 0));
graphics.fillRect(0, i, image.getWidth(null), 1);
}
graphics.dispose();
}
}
static class SwBlitImageParticleRenderer extends BlitImageParticleRenderer {
SwBlitImageParticleRenderer(int n, float r, final int type) {
super(n, r, makeUnmanagedBI(type));
}
private static BufferedImage makeUnmanagedBI(final int type) {
final BufferedImage bi = new BufferedImage(17, 33, type);
final DataBuffer db = bi.getRaster().getDataBuffer();
if (db instanceof DataBufferInt) {
((DataBufferInt) db).getData();
} else if (db instanceof DataBufferShort) {
((DataBufferShort) db).getData();
} else if (db instanceof DataBufferByte) {
((DataBufferByte) db).getData();
}
bi.setAccelerationPriority(0.0f);
return bi;
}
}
static class SurfaceBlitImageParticleRenderer extends BlitImageParticleRenderer {
SurfaceBlitImageParticleRenderer(int n, float r, final int type) {
super(n, r, makeManagedBI(type));
}
private static BufferedImage makeManagedBI(final int type) {
final BufferedImage bi = new BufferedImage(17, 33, type);
bi.setAccelerationPriority(1.0f);
return bi;
}
}
final static class PerfMeter {
private final FrameHandler fh;
private final String name;
private final PerfMeterExecutor executor;
PerfMeter(final FrameHandler fh, String name) {
this.fh = fh;
this.name = name;
executor = getExecutor();
}
void exec(final Renderable renderable) throws Exception {
executor.exec(name, renderable);
}
private PerfMeterExecutor getExecutor() {
switch (EXEC_MODE) {
default:
case EXEC_MODE_ROBOT:
return new PerfMeterRobot(fh);
case EXEC_MODE_BUFFER:
fh.prepareImageProvider(false);
return new PerfMeterImageProvider(fh);
case EXEC_MODE_VOLATILE:
fh.prepareImageProvider(true);
return new PerfMeterImageProvider(fh);
}
}
}
static void paintTest(final Renderable renderable, final Graphics2D g2d,
final Color markerColor, final boolean doSync) {
// clip to frame:
g2d.setClip(0, 0, IMAGE_W, IMAGE_H);
// clear background:
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, IMAGE_W, IMAGE_H);
// render test:
renderable.setup(g2d, true);
renderable.render(g2d);
renderable.setup(g2d, false);
// draw marker at end:
g2d.setClip(0, 0, BW, BH);
g2d.setColor(markerColor);
g2d.fillRect(0, 0, BW, BH);
if (doSync) {
// synchronize toolkit:
TOOLKIT.sync();
}
}
final static class FrameHandler {
private boolean calibrate = VERBOSE;
private int threadId = -1;
private int frameId = -1;
private final GraphicsConfiguration gc;
private JFrame frame = null;
private final CountDownLatch latchShownFrame = new CountDownLatch(1);
private final CountDownLatch latchClosedFrame = new CountDownLatch(1);
private ImageProvider imageProvider = null;
FrameHandler(GraphicsConfiguration gc) {
this.gc = gc;
}
void setIds(int threadId, int frameId) {
this.threadId = threadId;
this.frameId = frameId;
}
void prepareFrameEDT(final String title) {
if (frame == null) {
frame = new JFrame(gc);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
latchShownFrame.countDown();
}
});
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchClosedFrame.countDown();
}
});
}
frame.setTitle(title);
}
void showFrameEDT(final JPanel panel) {
if (frame != null) {
panel.setPreferredSize(new Dimension(IMAGE_W, IMAGE_H));
panel.setBackground(Color.BLACK);
frame.getContentPane().removeAll();
frame.getContentPane().add(panel);
frame.getContentPane().revalidate();
if (!frame.isVisible()) {
if (frameId != -1) {
final int off = (frameId - 1) * 100;
final Rectangle gcBounds = gc.getBounds();
final int xoff = gcBounds.x + off;
final int yoff = gcBounds.y + off;
if ((xoff != 0) || (yoff != 0)) {
frame.setLocation(xoff, yoff);
}
}
frame.pack();
frame.setVisible(true);
}
}
}
void waitFrameShown() throws Exception {
latchShownFrame.await();
}
void resetFrame() throws Exception {
if (frame != null) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
frame.getContentPane().removeAll();
frame.getContentPane().revalidate();
}
});
}
}
void repaintFrame() throws Exception {
if (frame != null) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
frame.repaint();
}
});
}
}
private void waitFrameHidden() throws Exception {
latchClosedFrame.await();
}
void hideFrameAndWait() throws Exception {
if (frame != null) {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
frame.setVisible(false);
frame.dispose();
frame = null;
}
});
waitFrameHidden();
}
}
void prepareImageProvider(final boolean useVolatile) {
if (this.imageProvider == null) {
this.imageProvider = new ImageProvider(useVolatile);
}
}
}
static abstract class PerfMeterExecutor {
protected final static int SCORE_MAIN = 0;
protected final static int SCORE_ERROR = 1;
protected final static int SCORE_OTHER = 2;
protected final static int PCT_00 = 0;
protected final static int PCT_10 = 1;
protected final static int PCT_25 = 2;
protected final static int PCT_50 = 3;
protected final static int PCT_75 = 4;
protected final static int PCT_90 = 5;
protected final static int PCT_100 = 6;
private final static IntBinaryOperator INC_MOD_FUNC = new IntBinaryOperator() {
public int applyAsInt(int x, int y) {
return (x + 1) % y;
}
};
private static final AtomicInteger headerMark = new AtomicInteger(1);
/* members */
protected final FrameHandler fh;
protected final boolean skipWait;
protected final AtomicInteger paintIdx = new AtomicInteger(0);
protected final AtomicInteger markerIdx = new AtomicInteger(0);
protected final AtomicLong markerStartTime = new AtomicLong(0);
protected final AtomicLong markerPaintTime = new AtomicLong(0);
protected String name = null;
protected int skippedFrames = 0;
protected int frames = 0;
// test timestamp data:
protected long[] testTimestamp = new long[MAX_SAMPLE_COUNT];
// test duration data (ns):
protected long[] testTime = new long[MAX_SAMPLE_COUNT];
protected final double[] scores = new double[SCORE_OTHER + 1];
protected final double[] results = new double[PCT_100 + 1];
protected PerfMeterExecutor(final boolean skipWait, final FrameHandler fh) {
this.skipWait = skipWait;
this.fh = fh;
}
protected void beforeExec() {
}
protected void afterExec() {
}
protected void reset() {
paintIdx.set(0);
markerIdx.set(0);
markerStartTime.set(0);
markerPaintTime.set(0);
}
protected void updateMarkerIdx() {
markerIdx.accumulateAndGet(MARKER.length, INC_MOD_FUNC);
}
protected final void exec(final String testName, final Renderable renderable) throws Exception {
if (TRACE) System.out.print("\n!");
this.name = testName + (isMultiThreads() ? ("-" + fh.threadId) : "");
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
fh.prepareFrameEDT(name);
// call beforeExec() after frame is created:
beforeExec();
final JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
if (TRACE) System.out.print("P");
paintPanel(renderable, g);
if (TRACE) System.out.print("Q");
}
};
fh.showFrameEDT(panel);
if (TRACE) System.out.print(">>");
}
});
// Wait frame to be shown:
fh.waitFrameShown();
if (TRACE) System.out.print(":");
// Reset before warmup:
reset();
if (WARMUP_COUNT > 0) {
// Warmup to prepare frame synchronization:
for (int i = 0; i < WARMUP_COUNT; i++) {
updateMarkerIdx();
renderable.update();
fh.repaintFrame();
sleep(10);
while (markerStartTime.get() == 0) {
if (TRACE) System.out.print("-");
sleep(1);
}
markerStartTime.set(0);
}
// Reset before measurements:
reset();
}
if (TRACE) System.out.print(":>>");
// signal thread is ready for test
readyCount.countDown();
if (TRACE_SYNC) traceSync(name + " ready => waiting start signal...");
// wait start signal:
triggerStart.await();
// Run Benchmark (all threads):
if (TRACE_SYNC) traceSync(name + " benchmark started");
int cycles = 0;
frames = 0;
long paintStartTime = 0L;
long paintElapsedTime = 0L;
long lastFrameTime = 0L;
final long startTime = System.nanoTime();
final long minTime = startTime + MIN_MEASURE_TIME_NS;
final long endTime = startTime + MAX_MEASURE_TIME_NS;
// Start 1st measurement:
fh.repaintFrame();
for (; ; ) {
long t;
if ((t = markerStartTime.getAndSet(0L)) > 0L) {
paintStartTime = t;
if (TRACE) System.out.print("|");
}
boolean wait = true;
if (paintStartTime > 0L) {
// get optional elapsed time:
paintElapsedTime = markerPaintTime.get();
if (TRACE) System.out.print(".");
wait = !skipWait;
final Color c = getMarkerColor();
if (isAlmostEqual(c, MARKER[markerIdx.get()])) {
final long durationNs = getElapsedTime((paintElapsedTime != 0L) ? paintElapsedTime : paintStartTime);
if ((durationNs > 0L) && (frames < MAX_SAMPLE_COUNT)) {
testTimestamp[frames] = paintStartTime - startTime;
testTime[frames] = durationNs;
}
if (REPORT_OVERALL_FPS) {
lastFrameTime = System.nanoTime();
}
if (TRACE) System.out.print("R");
frames++;
paintStartTime = 0L;
paintElapsedTime = 0L;
cycles = 0;
updateMarkerIdx();
renderable.update();
fh.repaintFrame();
} else if (cycles >= MAX_FRAME_CYCLES) {
if (TRACE) System.out.print("M");
skippedFrames++;
paintStartTime = 0L;
paintElapsedTime = 0L;
cycles = 0;
updateMarkerIdx();
fh.repaintFrame();
} else {
if (TRACE) System.out.print("-");
}
}
final long currentTime = System.nanoTime();
if ((frames >= MIN_COUNT) && (currentTime >= endTime)) {
break;
}
if ((frames >= COUNT) && (currentTime >= minTime)) {
break;
}
if (wait) {
sleep(CYCLE_DELAY);
}
cycles++;
} // end measurements
// signal test completed:
completedCount.countDown();
if (TRACE_SYNC) traceSync(name + " completed => waiting stop signal...");
// wait stop signal:
triggerStop.await();
// Stop Benchmark (all threads):
if (TRACE_SYNC) traceSync(name + " stopped");
if (DELAY_TEST) {
sleep(1000);
}
fh.resetFrame();
// Process results:
if (REPORT_OVERALL_FPS && (lastFrameTime != 0)) {
final double elapsedTime = (lastFrameTime - startTime);
final double elapsedFPS = 1000000000.0 * frames / elapsedTime;
System.err.println(frames + " in " + (elapsedTime / 1000000) + " ms: ~ " + elapsedFPS + " FPS");
}
processTimes();
if (TRACE) System.out.print("<<\n");
afterExec();
// Log header once:
if (headerMark.getAndDecrement() == 1) {
System.err.println(getHeader());
}
// Log report:
System.err.println(getResults());
// signal test done:
doneCount.countDown();
if (TRACE_SYNC) traceSync(name + " done => waiting exit signal...");
// wait exit signal:
triggerExit.await();
// Stop Benchmark (all threads):
if (TRACE_SYNC) traceSync(name + " exited");
}
protected abstract void paintPanel(final Renderable renderable, final Graphics g);
protected abstract long getElapsedTime(long paintTime);
protected abstract Color getMarkerColor() throws Exception;
protected boolean isAlmostEqual(Color c1, Color c2) {
return (Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE) &&
(Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE) &&
(Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE);
}
protected void processTimes() {
if (frames != 0) {
frames = Math.min(frames, MAX_SAMPLE_COUNT);
if (DUMP_SAMPLES) {
// Dump all results:
final File file = new File("./rp-" + replaceNonFileNameChars(name) + "-samples.csv");
System.out.println("Writing samples to : " + file.getAbsolutePath());
try (final PrintWriter w = new PrintWriter(file, Charset.forName("UTF-8"))) {
w.write("# ");
w.write(VERSION);
w.write(" - ");
w.write("Test: ");
w.write(name);
w.write('\n');
for (int i = 0; i < frames; i++) {
w.write(Double.toString(millis(testTimestamp[i])));
w.write(',');
w.write(Double.toString(millis(testTime[i])));
w.write('\n');
}
} catch (IOException ioe) {
System.err.println("IO exception:");
ioe.printStackTrace();
}
}
// Ignore first 10% (warmup at the beginning):
final int first = (int) Math.floor(frames * 0.10);
final int last = frames - 1;
// free testTimestamp to avoid any future usage:
testTimestamp = null;
// note: testTime array is modified below:
// Sort values to get percentiles:
Arrays.sort(testTime, first, frames);
final long[] pcts = getPercentiles(testTime, first, last);
final long median = pcts[PCT_50];
if (USE_FPS) {
scores[SCORE_MAIN] = fps(median);
results[PCT_100] = fps(pcts[PCT_00]);
results[PCT_90] = fps(pcts[PCT_10]);
results[PCT_75] = fps(pcts[PCT_25]);
results[PCT_50] = fps(pcts[PCT_50]);
results[PCT_25] = fps(pcts[PCT_75]);
results[PCT_10] = fps(pcts[PCT_90]);
results[PCT_00] = fps(pcts[PCT_100]);
// STDDEV = IQR / 1.35 = (Q3 - Q1) * 20 / 27
scores[SCORE_ERROR] = (results[PCT_75] - results[PCT_25]) * 20L / 27L;
scores[SCORE_OTHER] = millis(median);
} else {
scores[SCORE_MAIN] = millis(median);
results[PCT_00] = millis(pcts[PCT_00]);
results[PCT_10] = millis(pcts[PCT_10]);
results[PCT_25] = millis(pcts[PCT_25]);
results[PCT_50] = millis(pcts[PCT_50]);
results[PCT_75] = millis(pcts[PCT_75]);
results[PCT_90] = millis(pcts[PCT_90]);
results[PCT_100] = millis(pcts[PCT_100]);
// STDDEV = IQR / 1.35 = (Q3 - Q1) * 20 / 27 (normal distribution ?)
scores[SCORE_ERROR] = (results[PCT_75] - results[PCT_25]) * 20L / 27L;
scores[SCORE_OTHER] = fps(median);
// System.out.println("stddev(IQR) = " + scores[SCORE_ERROR]);
// MAD = Median Absolute Deviation:
for (int i = first; i <= last; i++) {
testTime[i] = Math.abs(testTime[i] - median);
}
// Sort values to get percentiles:
Arrays.sort(testTime, first, frames);
// STDDEV = 1.4826 * MAD (normal distribution ?)
scores[SCORE_ERROR] = 1.4826 * millis(testTime[pctIndex(first, last, 0.50)]); // 50% (median)
// System.out.println("stddev(MAD) = " + scores[SCORE_ERROR]);
}
// free testTime to avoid any future usage:
testTime = null;
}
}
protected static String getHeader() {
if (VERBOSE) {
return String.format("%-25s : %s ± %s %s [%s] (p00: ... p10: ... p25: ... p50: ... p75: ... p90: ... p100: ... %s) (... frames)",
"Test Name", (USE_FPS ? "Median(FPS)" : "Median(TimeMs)"), (USE_FPS ? "Stddev(FPS)" : "Stddev(TimeMs)"), "Unit",
(!USE_FPS ? "Median(FPS)" : "Median(TimeMs)"), "Unit");
}
return String.format("%-25s : %s ± %s %s",
"Test Name", (USE_FPS ? "Median(FPS)" : "Median(TimeMs)"), (USE_FPS ? "Stddev(FPS)" : "Stddev(TimeMs)"), "Unit");
}
protected String getResults() {
if (skippedFrames > 0) {
System.err.println(name + " : " + skippedFrames + " frame(s) skipped");
}
if (VERBOSE) {
return String.format("%-25s : %.3f ± %.3f %s [%.3f %s] (p00: %.3f p10: %.3f p25: %.3f p50: %.3f p75: %.3f p90: %.3f p100: %.3f %s) (%d frames)",
name, scores[SCORE_MAIN], scores[SCORE_ERROR], (USE_FPS ? "FPS" : "ms"),
scores[SCORE_OTHER], (USE_FPS ? "ms" : "FPS"),
results[PCT_00], results[PCT_10], results[PCT_25], results[PCT_50], results[PCT_75], results[PCT_90], results[PCT_100],
(USE_FPS ? "FPS" : "ms"),
frames);
}
return String.format("%-25s : %.3f ± %.3f %s",
name, scores[SCORE_MAIN], scores[SCORE_ERROR], (USE_FPS ? "FPS" : "ms"));
}
protected double fps(long timeNs) {
return 1e9 / timeNs;
}
protected double millis(long timeNs) {
return 1e-6 * timeNs;
}
protected static long[] getPercentiles(final long[] data, final int first, final int last) {
final long[] pcts = new long[PCT_100 + 1];
pcts[PCT_00] = data[first]; // 0% (min)
pcts[PCT_10] = data[pctIndex(first, last, 0.10)]; // 10%
pcts[PCT_25] = data[pctIndex(first, last, 0.25)]; // 25% (Q1)
pcts[PCT_50] = data[pctIndex(first, last, 0.50)]; // 50% (Median)
pcts[PCT_75] = data[pctIndex(first, last, 0.75)]; // 75% (Q3)
pcts[PCT_90] = data[pctIndex(first, last, 0.90)]; // 90%
pcts[PCT_100] = data[pctIndex(first, last, 1.00)]; // 100% (max)
return pcts;
}
protected static int pctIndex(final int min, final int last, final double pct) {
return min + (int) Math.round((last - min) * pct);
}
}
final static class PerfMeterRobot extends PerfMeterExecutor {
private int nRobotTimes = 0;
private long[] robotTime = (fh.calibrate) ? new long[COUNT] : null;
private int nDelayTimes = 0;
private long[] delayTime = (ROBOT_TIME_DELAY) ? null : new long[COUNT];
private long lastPaintTime = 0L;
private int renderedMarkerIdx = -1;
private Robot robot = null;
PerfMeterRobot(final FrameHandler fh) {
super(true, fh);
}
protected void beforeExec() {
try {
robot = new Robot();
} catch (AWTException ae) {
throw new RuntimeException(ae);
}
}
protected void reset() {
super.reset();
nRobotTimes = 0;
nDelayTimes = 0;
lastPaintTime = 0L;
renderedMarkerIdx = -1;
}
protected void paintPanel(final Renderable renderable, final Graphics g) {
final int idx = markerIdx.get();
final long start = System.nanoTime();
final Graphics2D g2d = (Graphics2D) g.create();
try {
paintTest(renderable, g2d, MARKER[idx], false);
} finally {
g2d.dispose();
}
// Update paintIdx:
paintIdx.incrementAndGet();
// publish start time:
if (idx != renderedMarkerIdx) {
renderedMarkerIdx = idx;
markerStartTime.set(start);
}
}
protected long getElapsedTime(final long paintTime) {
final long now = System.nanoTime();
long duration = (!ROBOT_TIME_DELAY) ? roundDuration(now - paintTime) : 0L;
if (lastPaintTime != 0L) {
final long delay = roundDuration(now - lastPaintTime);
if (ROBOT_TIME_DELAY) {
duration = delay;
} else if (nDelayTimes < COUNT) {
delayTime[nDelayTimes++] = delay;
}
}
lastPaintTime = now;
return duration;
}
private static long roundDuration(final long durationNs) {
return (durationNs <= 0L) ? 0L : (
(ROBOT_TIME_ROUND) ?
FRAME_PREC_IN_NANOS * (long) Math.rint(((double) durationNs) / FRAME_PREC_IN_NANOS) : durationNs
);
}
protected Color getMarkerColor() {
final Point frameOffset = fh.frame.getLocationOnScreen();
final Insets insets = fh.frame.getInsets();
final int px = frameOffset.x + insets.left + BW / 2;
final int py = frameOffset.y + insets.top + BH / 2;
final long beforeRobot = (fh.calibrate) ? System.nanoTime() : 0L;
final Color c = robot.getPixelColor(px, py);
if ((fh.calibrate) && (nRobotTimes < COUNT)) {
robotTime[nRobotTimes++] = System.nanoTime() - beforeRobot;
}
return c;
}
protected String getResults() {
if (fh.calibrate && (nRobotTimes != 0)) {
fh.calibrate = false; // only first time
Arrays.sort(robotTime);
final long[] pcts = getPercentiles(robotTime, 0, nRobotTimes - 1);
// free testTime to avoid any future usage:
testTime = null;
System.err.printf("%-25s : %.3f ms (p00: %.3f p10: %.3f p25: %.3f p50: %.3f p75: %.3f p90: %.3f p100: %.3f ms) (%d times)%n",
"Robot" + (isMultiThreads() ? ("-" + fh.threadId) : ""), millis(pcts[PCT_50]),
millis(pcts[PCT_00]), millis(pcts[PCT_10]), millis(pcts[PCT_25]), millis(pcts[PCT_50]),
millis(pcts[PCT_75]), millis(pcts[PCT_90]), millis(pcts[PCT_100]), nRobotTimes);
}
if (nDelayTimes != 0) {
Arrays.sort(delayTime);
final long[] pcts = getPercentiles(robotTime, 0, nDelayTimes - 1);
// free delayTime to avoid any future usage:
delayTime = null;
System.err.printf("%-25s : %.3f ms [%.3f FPS] (p00: %.3f p10: %.3f p25: %.3f p50: %.3f p75: %.3f p90: %.3f p100: %.3f ms) (%d times)%n",
"DelayTime-" + name + (isMultiThreads() ? ("-" + fh.threadId) : ""), millis(pcts[PCT_50]), fps(pcts[PCT_50]),
millis(pcts[PCT_00]), millis(pcts[PCT_10]), millis(pcts[PCT_25]), millis(pcts[PCT_50]),
millis(pcts[PCT_75]), millis(pcts[PCT_90]), millis(pcts[PCT_100]), nDelayTimes);
}
return super.getResults();
}
}
final static class PerfMeterImageProvider extends PerfMeterExecutor {
private final ImageProvider imageProvider;
PerfMeterImageProvider(final FrameHandler fh) {
super(false, fh);
this.imageProvider = fh.imageProvider;
}
protected void beforeExec() {
imageProvider.create(fh.frame.getGraphicsConfiguration(), IMAGE_W, IMAGE_H);
}
protected void afterExec() {
imageProvider.reset();
}
protected void paintPanel(final Renderable renderable, final Graphics g) {
// suppose image provider is ready yet
final int idx = markerIdx.get();
long start = System.nanoTime();
// Get Graphics from image provider:
final Graphics2D g2d = imageProvider.createGraphics();
try {
paintTest(renderable, g2d, MARKER[idx], true);
} finally {
g2d.dispose();
}
final long now = System.nanoTime();
// Update paintIdx:
paintIdx.incrementAndGet();
// publish start time:
markerStartTime.set(start);
// publish elapsed time:
markerPaintTime.set(now - start);
// Draw image on screen:
g.drawImage(imageProvider.getImage(), 0, 0, null);
}
protected long getElapsedTime(long paintTime) {
return paintTime;
}
protected Color getMarkerColor() {
final int px = BW / 2;
final int py = BH / 2;
return new Color(imageProvider.getSnapshot().getRGB(px, py));
}
}
private final static class ImageProvider {
private final static int TRANSPARENCY = Transparency.TRANSLUCENT;
private final boolean useVolatile;
private Image image = null;
private ImageProvider(boolean useVolatile) {
this.useVolatile = useVolatile;
}
void create(GraphicsConfiguration gc, int width, int height) {
this.image = (useVolatile) ? gc.createCompatibleVolatileImage(width, height, TRANSPARENCY)
: gc.createCompatibleImage(width, height, TRANSPARENCY);
}
public void reset() {
image = null;
}
public Image getImage() {
return image;
}
public Graphics2D createGraphics() {
return (useVolatile) ? ((VolatileImage) image).createGraphics()
: ((BufferedImage) image).createGraphics();
}
public BufferedImage getSnapshot() {
return (useVolatile) ? ((VolatileImage) image).getSnapshot()
: (BufferedImage) image;
}
}
private final FrameHandler fh;
private final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private final ParticleRenderer calibRenderer = new CalibrationParticleRenderer();
private final ParticleRenderer flatRenderer = new FlatParticleRenderer(N, R);
private final ParticleRenderer clipFlatRenderer = new ClipFlatParticleRenderer(N, R);
private final ParticleRenderer flatOvalRotRenderer = new FlatOvalRotParticleRenderer(N, R);
private final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R);
private final ParticleRenderer clipFlatBoxParticleRenderer = new ClipFlatBoxParticleRenderer(N, R);
private final ParticleRenderer flatBoxRotRenderer = new FlatBoxRotParticleRenderer(N, R);
private final ParticleRenderer linGradOvalRotRenderer = new LinGradOvalRotParticleRenderer(N, R);
private final ParticleRenderer linGrad3OvalRotRenderer = new LinGrad3OvalRotParticleRenderer(N, R);
private final ParticleRenderer radGrad3OvalRotRenderer = new RadGrad3OvalRotParticleRenderer(N, R);
private final ParticleRenderer wiredRenderer = new WiredParticleRenderer(N, R);
private final ParticleRenderer wiredBoxRenderer = new WiredBoxParticleRenderer(N, R);
private final ParticleRenderer segRenderer = new SegParticleRenderer(N, R);
private final ParticleRenderer flatQuadRenderer = new FlatQuadParticleRenderer(N, R);
private final ParticleRenderer wiredQuadRenderer = new WiredQuadParticleRenderer(N, R);
private final ParticleRenderer imgRenderer = new ImgParticleRenderer(N, R);
private final ParticleRenderer volImgRenderer = new VolImgParticleRenderer(N, R);
private final ParticleRenderer textRenderer = new TextParticleRenderer(N, R);
private final ParticleRenderer largeTextRenderer = new LargeTextParticleRenderer(N, R);
private final ParticleRenderer whiteTextRenderer = new WhiteTextParticleRenderer(R);
private final ParticleRenderer argbSwBlitImageRenderer = new SwBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_ARGB);
private final ParticleRenderer bgrSwBlitImageRenderer = new SwBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_BGR);
private final ParticleRenderer argbSurfaceBlitImageRenderer = new SurfaceBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_ARGB);
private final ParticleRenderer bgrSurfaceBlitImageRenderer = new SurfaceBlitImageParticleRenderer(N, R, BufferedImage.TYPE_INT_BGR);
private final ParticleRenderer textWiredQuadBatchedRenderer = new BatchedParticleRenderer(textRenderer, wiredQuadRenderer);
private final ParticleRenderer textWiredQuadMixedRenderer = new MixedParticleRenderer(textRenderer, wiredQuadRenderer);
private final ParticleRenderer volImgFlatBoxBatchedRenderer = new BatchedParticleRenderer(volImgRenderer, flatBoxRenderer);
private final ParticleRenderer volImgFlatBoxMixedRenderer = new MixedParticleRenderer(volImgRenderer, flatBoxRenderer);
private final ParticleRenderer volImgWiredQuadBatchedRenderer = new BatchedParticleRenderer(volImgRenderer, wiredQuadRenderer);
private final ParticleRenderer volImgWiredQuadMixedRenderer = new MixedParticleRenderer(volImgRenderer, wiredQuadRenderer);
private final ParticleRenderer volImgTextBatchedRenderer = new BatchedParticleRenderer(volImgRenderer, textRenderer);
private final ParticleRenderer volImgTextMixedRenderer = new MixedParticleRenderer(volImgRenderer, textRenderer);
private final static Configurable AA = new ConfigurableAA();
private final static Configurable TextAA = new ConfigurableTextAA();
private final static Configurable TextLCD = new ConfigurableTextLCD();
private final static Configurable XORMode = new ConfigurableXORMode();
private final static Configurable XORModeLCDText = new ConfigurableXORModeTextLCD();
RenderPerfTest(final GraphicsConfiguration gc) {
fh = new FrameHandler(gc);
}
ParticleRenderable createPR(final ParticleRenderer renderer) {
return new ParticleRenderable(balls, renderer);
}
PerfMeter createPerfMeter(final String name) {
return new PerfMeter(fh, name);
}
public void testCalibration() throws Exception {
createPerfMeter(testName).exec(createPR(calibRenderer));
}
public void testFlatOval() throws Exception {
createPerfMeter(testName).exec(createPR(flatRenderer));
}
public void testFlatOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(flatRenderer).configure(AA));
}
public void testClipFlatOval() throws Exception {
createPerfMeter(testName).exec(createPR(clipFlatRenderer));
}
public void testClipFlatOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(clipFlatRenderer).configure(AA));
}
public void testFlatBox() throws Exception {
createPerfMeter(testName).exec(createPR(flatBoxRenderer));
}
public void testFlatBoxAA() throws Exception {
createPerfMeter(testName).exec(createPR(flatBoxRenderer).configure(AA));
}
public void testClipFlatBox() throws Exception {
createPerfMeter(testName).exec(createPR(clipFlatBoxParticleRenderer));
}
public void testClipFlatBoxAA() throws Exception {
createPerfMeter(testName).exec(createPR(clipFlatBoxParticleRenderer).configure(AA));
}
public void testImage() throws Exception {
createPerfMeter(testName).exec(createPR(imgRenderer));
}
public void testImageAA() throws Exception {
createPerfMeter(testName).exec(createPR(imgRenderer).configure(AA));
}
public void testVolImage() throws Exception {
createPerfMeter(testName).exec(createPR(volImgRenderer));
}
public void testVolImageAA() throws Exception {
createPerfMeter(testName).exec(createPR(volImgRenderer).configure(AA));
}
public void testRotatedBox() throws Exception {
createPerfMeter(testName).exec(createPR(flatBoxRotRenderer));
}
public void testRotatedBoxAA() throws Exception {
createPerfMeter(testName).exec(createPR(flatBoxRotRenderer).configure(AA));
}
public void testRotatedOval() throws Exception {
createPerfMeter(testName).exec(createPR(flatOvalRotRenderer));
}
public void testRotatedOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(flatOvalRotRenderer).configure(AA));
}
public void testLinGrad3RotatedOval() throws Exception {
createPerfMeter(testName).exec(createPR(linGrad3OvalRotRenderer));
}
public void testLinGrad3RotatedOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(linGrad3OvalRotRenderer).configure(AA));
}
public void testRadGrad3RotatedOval() throws Exception {
createPerfMeter(testName).exec(createPR(radGrad3OvalRotRenderer));
}
public void testRadGrad3RotatedOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(radGrad3OvalRotRenderer).configure(AA));
}
public void testLinGradRotatedOval() throws Exception {
createPerfMeter(testName).exec(createPR(linGradOvalRotRenderer));
}
public void testLinGradRotatedOvalAA() throws Exception {
createPerfMeter(testName).exec(createPR(linGradOvalRotRenderer).configure(AA));
}
public void testWiredBubbles() throws Exception {
createPerfMeter(testName).exec(createPR(wiredRenderer));
}
public void testWiredBubblesAA() throws Exception {
createPerfMeter(testName).exec(createPR(wiredRenderer).configure(AA));
}
public void testWiredBox() throws Exception {
createPerfMeter(testName).exec(createPR(wiredBoxRenderer));
}
public void testWiredBoxAA() throws Exception {
createPerfMeter(testName).exec(createPR(wiredBoxRenderer).configure(AA));
}
public void testLines() throws Exception {
createPerfMeter(testName).exec(createPR(segRenderer));
}
public void testLinesAA() throws Exception {
createPerfMeter(testName).exec(createPR(segRenderer).configure(AA));
}
public void testFlatQuad() throws Exception {
createPerfMeter(testName).exec(createPR(flatQuadRenderer));
}
public void testFlatQuadAA() throws Exception {
createPerfMeter(testName).exec(createPR(flatQuadRenderer).configure(AA));
}
public void testWiredQuad() throws Exception {
createPerfMeter(testName).exec(createPR(wiredQuadRenderer));
}
public void testWiredQuadAA() throws Exception {
createPerfMeter(testName).exec(createPR(wiredQuadRenderer).configure(AA));
}
public void testTextNoAA() throws Exception {
createPerfMeter(testName).exec(createPR(textRenderer));
}
public void testTextLCD() throws Exception {
createPerfMeter(testName).exec(createPR(textRenderer).configure(TextLCD));
}
public void testTextGray() throws Exception {
createPerfMeter(testName).exec(createPR(textRenderer).configure(TextAA));
}
public void testLargeTextNoAA() throws Exception {
createPerfMeter(testName).exec(createPR(largeTextRenderer));
}
public void testLargeTextLCD() throws Exception {
createPerfMeter(testName).exec(createPR(largeTextRenderer).configure(TextLCD));
}
public void testLargeTextGray() throws Exception {
createPerfMeter(testName).exec(createPR(largeTextRenderer).configure(TextAA));
}
public void testWhiteTextNoAA() throws Exception {
createPerfMeter(testName).exec(createPR(whiteTextRenderer));
}
public void testWhiteTextLCD() throws Exception {
createPerfMeter(testName).exec(createPR(whiteTextRenderer).configure(TextLCD));
}
public void testWhiteTextGray() throws Exception {
createPerfMeter(testName).exec(createPR(whiteTextRenderer).configure(TextAA));
}
public void testArgbSwBlitImage() throws Exception {
createPerfMeter(testName).exec(createPR(argbSwBlitImageRenderer));
}
public void testBgrSwBlitImage() throws Exception {
createPerfMeter(testName).exec(createPR(bgrSwBlitImageRenderer));
}
public void testArgbSurfaceBlitImage() throws Exception {
createPerfMeter(testName).exec(createPR(argbSurfaceBlitImageRenderer));
}
public void testBgrSurfaceBlitImage() throws Exception {
createPerfMeter(testName).exec(createPR(bgrSurfaceBlitImageRenderer));
}
// XOR mode:
public void testFlatOval_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(flatRenderer).configure(XORMode));
}
public void testRotatedBox_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(flatBoxRotRenderer).configure(XORMode));
}
public void testLines_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(segRenderer).configure(XORMode));
}
public void testImage_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(imgRenderer).configure(XORMode));
}
public void testTextNoAA_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(textRenderer).configure(XORMode));
}
public void testTextLCD_XOR() throws Exception {
createPerfMeter(testName).exec(createPR(textRenderer).configure(XORModeLCDText));
}
// Mixed/Batched mode:
public void testTextWiredQuadBat() throws Exception {
createPerfMeter(testName).exec(createPR(textWiredQuadBatchedRenderer));
}
public void testTextWiredQuadMix() throws Exception {
createPerfMeter(testName).exec(createPR(textWiredQuadMixedRenderer));
}
public void testTextWiredQuadAABat() throws Exception {
createPerfMeter(testName).exec(createPR(textWiredQuadBatchedRenderer).configure(AA));
}
public void testTextWiredQuadAAMix() throws Exception {
createPerfMeter(testName).exec(createPR(textWiredQuadMixedRenderer).configure(AA));
}
public void testVolImageFlatBoxBat() throws Exception {
createPerfMeter(testName).exec(createPR(volImgFlatBoxBatchedRenderer));
}
public void testVolImageFlatBoxMix() throws Exception {
createPerfMeter(testName).exec(createPR(volImgFlatBoxMixedRenderer));
}
public void testVolImageFlatBoxAABat() throws Exception {
createPerfMeter(testName).exec(createPR(volImgFlatBoxBatchedRenderer).configure(AA));
}
public void testVolImageFlatBoxAAMix() throws Exception {
createPerfMeter(testName).exec(createPR(volImgFlatBoxMixedRenderer).configure(AA));
}
public void testVolImageWiredQuadBat() throws Exception {
createPerfMeter(testName).exec(createPR(volImgWiredQuadBatchedRenderer));
}
public void testVolImageWiredQuadMix() throws Exception {
createPerfMeter(testName).exec(createPR(volImgWiredQuadMixedRenderer));
}
public void testVolImageWiredQuadAABat() throws Exception {
createPerfMeter(testName).exec(createPR(volImgWiredQuadBatchedRenderer).configure(AA));
}
public void testVolImageWiredQuadAAMix() throws Exception {
createPerfMeter(testName).exec(createPR(volImgWiredQuadMixedRenderer).configure(AA));
}
public void testVolImageTextNoAABat() throws Exception {
createPerfMeter(testName).exec(createPR(volImgTextBatchedRenderer));
}
public void testVolImageTextNoAAMix() throws Exception {
createPerfMeter(testName).exec(createPR(volImgTextMixedRenderer));
}
private static void help() {
System.out.print("##############################################################\n");
System.out.printf("# %s\n", VERSION);
System.out.print("##############################################################\n");
System.out.println("# java ... RenderPerfTest <args> <tests>");
System.out.println("#");
System.out.println("# Supported arguments <args>:");
System.out.println("# -h : display this help");
System.out.println("# -v : set verbose output");
System.out.println("#");
System.out.println("# -e=<mode> : set the execution mode (default: " + EXEC_MODE_DEFAULT
+ ") among " + EXEC_MODES);
System.out.println("#");
System.out.println("# -f : use FPS unit (default)");
System.out.println("# -t : use TIME(ms) unit");
System.out.println("#");
System.out.println("# -g=all|\"0-0,0-1...\" : use 'all' or specific graphics configurations");
System.out.println("#");
System.out.println("# -font=\"<name>\" : set the font name used by all text renderers");
System.out.println("# -fontSize=<number> : set the font size used by Text renderers");
System.out.println("# -fontSizeLarge=<number> : set the font size used by LargeText renderers");
System.out.println("# -text=\"<string>\" : set the text drawn by all text renderers");
System.out.println("#");
System.out.println("# -lf : list available font names");
System.out.println("# -lg : list available graphics configurations");
System.out.println("#");
System.out.println("# -n=<number> : set number of primitives (default: " + N_DEFAULT + ")");
System.out.println("# -r=<number> : set number of test repeats (default: 1)");
System.out.println("#");
System.out.println("#");
System.out.println("# -w=<number> : use number of test frames (default: 1) per screen");
System.out.println("#");
System.out.println("# -u=<number> : set number of warmup iterations (default: " + MIN_COUNT + ")");
System.out.println("#");
System.out.print("# Supported test arguments <tests>:");
final ArrayList<Method> testCases = new ArrayList<>();
for (Method m : RenderPerfTest.class.getDeclaredMethods()) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
testCases.add(m);
}
}
testCases.sort(Comparator.comparing(Method::getName));
for (Method m : testCases) {
System.out.print(extractTestName(m));
System.out.print(" ");
}
System.out.println();
}
private static String extractTestName(final Method m) {
return m.getName().substring("test".length());
}
public static void main(String[] args)
throws NoSuchMethodException, NumberFormatException {
// Set the default locale to en-US locale (for Numerical Fields "." ",")
Locale.setDefault(Locale.US);
boolean help = false;
final ArrayList<Method> testCases = new ArrayList<>();
for (String arg : args) {
if (arg.length() >= 2) {
if (arg.startsWith("-")) {
switch (arg.substring(1, 2)) {
case "e":
if (arg.length() >= 3) {
EXEC_MODE = arg.substring(3).toLowerCase();
}
break;
case "f":
if (arg.length() == 2) {
USE_FPS = true;
} else {
if ((arg.length() > 6) && "font=".equalsIgnoreCase(arg.substring(1, 6))) {
TEXT_FONT = arg.substring(6);
break;
}
if ((arg.length() > 10) && "fontSize=".equalsIgnoreCase(arg.substring(1, 10))) {
TEXT_SIZE_DEFAULT = Integer.parseInt(arg.substring(10));
break;
}
if ((arg.length() > 15) && "fontSizeLarge=".equalsIgnoreCase(arg.substring(1, 15))) {
TEXT_SIZE_LARGE = Integer.parseInt(arg.substring(15));
break;
}
}
break;
case "g":
if (arg.length() >= 3) {
GC_MODE = arg.substring(3).toLowerCase();
}
break;
case "h":
help = true;
break;
case "l":
if (arg.length() == 3) {
if ("f".equalsIgnoreCase(arg.substring(2, 3))) {
VERBOSE_FONT_CONFIG = true;
} else if ("g".equalsIgnoreCase(arg.substring(2, 3))) {
VERBOSE_GRAPHICS_CONFIG = true;
}
}
break;
case "t":
if (arg.length() == 2) {
USE_FPS = false;
} else {
if ((arg.length() > 6) && "text=".equalsIgnoreCase(arg.substring(1, 6))) {
TEXT_STR = arg.substring(6);
}
}
break;
case "n":
if (arg.length() >= 3) {
N = Integer.parseInt(arg.substring(3));
}
break;
case "r":
if (arg.length() >= 3) {
REPEATS = Integer.parseInt(arg.substring(3));
}
break;
case "v":
VERBOSE = true;
break;
case "w":
if (arg.length() >= 3) {
NW = Integer.parseInt(arg.substring(3));
}
break;
case "u":
if (arg.length() >= 3) {
WARMUP_COUNT = Integer.parseInt(arg.substring(3));
}
break;
default:
System.err.println("Unsupported argument '" + arg + "' !");
help = true;
}
} else {
Method m = RenderPerfTest.class.getDeclaredMethod("test" + arg);
testCases.add(m);
}
}
}
if (testCases.isEmpty()) {
for (Method m : RenderPerfTest.class.getDeclaredMethods()) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
testCases.add(m);
}
}
testCases.sort(Comparator.comparing(Method::getName));
}
if (help) {
help();
}
if (CALIBRATION) {
Method m = RenderPerfTest.class.getDeclaredMethod("testCalibration");
testCases.add(0, m); // first
}
if (VERBOSE) {
System.out.print("##############################################################\n");
System.out.printf("# %s\n", VERSION);
System.out.print("##############################################################\n");
System.out.printf("# Java: %s\n", System.getProperty("java.runtime.version"));
System.out.printf("# VM: %s %s (%s)\n", System.getProperty("java.vm.name"), System.getProperty("java.vm.version"), System.getProperty("java.vm.info"));
System.out.printf("# OS: %s %s (%s)\n", System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch"));
System.out.printf("# CPUs: %d (virtual)\n", Runtime.getRuntime().availableProcessors());
System.out.print("##############################################################\n");
System.out.printf("# AWT Toolkit: %s \n", TOOLKIT.getClass().getSimpleName());
System.out.printf("# Execution mode: %s\n", EXEC_MODE);
System.out.printf("# GraphicsConfiguration mode: %s\n", GC_MODE);
System.out.print("##############################################################\n");
System.out.printf("# Repeats: %d\n", REPEATS);
System.out.printf("# NW: %d\n", NW);
System.out.printf("# N: %d\n", N);
System.out.printf("# WARMUP_COUNT: %d\n", WARMUP_COUNT);
System.out.printf("# Unit: %s\n", USE_FPS ? "FPS" : "TIME(ms)");
System.out.print("##############################################################\n");
System.out.printf("# Font: '%s'\n", TEXT_FONT);
System.out.printf("# Text: '%s'\n", TEXT_STR);
System.out.printf("# FontSize: %s\n", TEXT_SIZE_DEFAULT);
System.out.printf("# FontSizeLarge: %s\n", TEXT_SIZE_LARGE);
System.out.print("##############################################################\n");
}
// Graphics Configuration handling:
final Set<String> fontNames = new LinkedHashSet<>();
final Map<String, GraphicsConfiguration> gcByID = new LinkedHashMap<>();
final Map<GraphicsConfiguration, String> idByGC = new HashMap<>();
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (String name : ge.getAvailableFontFamilyNames()) {
fontNames.add(name);
}
// Check font:
if (!fontNames.contains(TEXT_FONT)) {
System.err.println("Bad font name: [" + TEXT_FONT + "] !");
VERBOSE_FONT_CONFIG = true;
}
if (VERBOSE_FONT_CONFIG) {
System.out.print("# Available font names: ");
for (String name : fontNames) {
System.out.print("'");
System.out.print(name);
System.out.print("' ");
}
System.out.println();
}
final GraphicsDevice[] gds = ge.getScreenDevices();
if (VERBOSE_GRAPHICS_CONFIG) {
System.out.println("# Available GraphicsDevice(s) and their GraphicsConfiguration(s):");
}
for (int gdIdx = 0; gdIdx < gds.length; gdIdx++) {
final GraphicsDevice gd = gds[gdIdx];
if (VERBOSE_GRAPHICS_CONFIG) {
System.out.println("# [" + gdIdx + "] = GraphicsDevice[" + gd.getIDstring() + "]");
}
final GraphicsConfiguration[] gcs = gd.getConfigurations();
for (int gcIdx = 0; gcIdx < gcs.length; gcIdx++) {
final GraphicsConfiguration gc = gcs[gcIdx];
final String gcId = gdIdx + "-" + gcIdx;
gcByID.put(gcId, gc);
idByGC.put(gc, gcId);
if (VERBOSE_GRAPHICS_CONFIG) {
System.out.println("# - [" + gcId + "] = GraphicsConfiguration[" + gc + "] bounds:" + gc.getBounds());
}
}
}
final Set<GraphicsConfiguration> gcSet = new LinkedHashSet<>();
if (GC_MODE != null) {
if (!GC_MODE_DEF.equals(GC_MODE)) {
if (GC_MODE_ALL.equals(GC_MODE)) {
gcSet.addAll(gcByID.values());
} else {
for (String gcKey : GC_MODE.split(",")) {
final GraphicsConfiguration gc = gcByID.get(gcKey);
if (gc != null) {
gcSet.add(gc);
} else {
System.err.println("Bad GraphicsConfiguration identifier 'x-y' where x is GraphicsDevice ID " +
"and y GraphicsConfiguration ID : [" + gcKey + "] ! (available values: " + gcByID.keySet() + ")");
}
}
}
}
}
if (gcSet.isEmpty()) {
final GraphicsDevice gdDef = ge.getDefaultScreenDevice();
final GraphicsConfiguration gcDef = gdDef.getDefaultConfiguration();
final String gcId = idByGC.get(gcDef);
if (VERBOSE_GRAPHICS_CONFIG) {
System.out.println("# Using default [" + gcId + "] = GraphicsConfiguration[" + gcDef + "] bounds:" + gcDef.getBounds());
}
gcSet.add(gcDef);
}
final List<GraphicsConfiguration> gcList = new ArrayList<>(gcSet);
final int NGC = gcList.size();
System.out.print("# Using GraphicsConfiguration(s): ");
for (GraphicsConfiguration gc : gcList) {
final String gcId = idByGC.get(gc);
System.out.print("[" + gcId + "][" + gc + "]");
System.out.print(" ");
}
System.out.println();
final List<RenderPerfTest> instances = new ArrayList<>();
int retCode = 0;
try {
if (!help) {
final List<Thread> threads = new ArrayList<>();
for (int i = 0; i < NGC; i++) {
final GraphicsConfiguration gc = gcList.get(i);
for (int j = 0; j < NW; j++) {
final RenderPerfTest rp = new RenderPerfTest(gc);
instances.add(rp);
threads.add(rp.createThreadTests(threads.size() + 1, j + 1, testCases));
}
}
if (TRACE_SYNC) traceSync("testCount: " + testCount);
initThreads(threads.size());
initBarrierStart();
for (Thread thread : threads) {
if (TRACE_SYNC) traceSync(thread.getName() + " starting...");
thread.start();
}
for (int n = 0; n < testCount; n++) {
if (VERBOSE) {
final int k = n / REPEATS;
final String methodName = extractTestName(testCases.get(k));
System.out.println("# --- Test [" + (n + 1) + " / " + testCount + "] = " + methodName + " ---");
}
// reset stop barrier (to be ready):
initBarrierStop();
if (TRACE_SYNC) traceSync("Waiting " + threadCount + " threads to be ready...");
readyCount.await();
if (TRACE_SYNC) traceSync("Threads are ready => starting benchmark on " + threadCount + " threads now");
triggerStart.countDown();
// reset done barrier (to be ready):
initBarrierDone();
if (TRACE_SYNC) traceSync("Waiting " + threadCount + " threads to complete benchmark...");
completedCount.await();
if (TRACE_SYNC) traceSync("Test completed on " + threadCount + " threads => stopping benchmark on all threads now");
triggerStop.countDown();
// reset start barrier (to be ready):
initBarrierStart();
if (TRACE_SYNC) traceSync("Waiting " + threadCount + " threads to exit test...");
doneCount.await();
if (TRACE_SYNC) traceSync("Test exited on " + threadCount + " threads => finalize benchmark on all threads now");
triggerExit.countDown();
}
for (Thread thread : threads) {
thread.join();
if (TRACE_SYNC) traceSync(thread.getName() + " terminated");
}
}
} catch (Throwable th) {
System.err.println("Exception occurred during :");
th.printStackTrace(System.err);
retCode = 1;
} finally {
for (RenderPerfTest rp : instances) {
try {
rp.fh.hideFrameAndWait();
} catch (Throwable th) {
System.err.println("Exception occurred in hideFrameAndWait():");
th.printStackTrace(System.err);
retCode = 1;
}
}
// ensure jvm immediate shutdown:
System.exit(retCode);
}
}
// thread synchronization
private static int threadCount = 0;
private static int testCount = 0;
private static volatile String testName = null;
private static volatile CountDownLatch readyCount = null;
private static volatile CountDownLatch triggerStart = null;
private static volatile CountDownLatch completedCount = null;
private static volatile CountDownLatch triggerStop = null;
private static volatile CountDownLatch doneCount = null;
private static volatile CountDownLatch triggerExit = null;
static void traceSync(final String msg) {
System.out.println("[" + System.nanoTime() + "] " + msg);
}
private static void initThreads(int count) {
threadCount = count;
if (TRACE_SYNC) traceSync("initThreads(): threadCount: " + threadCount);
}
static boolean isMultiThreads() {
return threadCount > 1;
}
private static void initBarrierStart() {
readyCount = new CountDownLatch(threadCount);
triggerStart = new CountDownLatch(1);
}
private static void initBarrierStop() {
completedCount = new CountDownLatch(threadCount);
triggerStop = new CountDownLatch(1);
}
private static void initBarrierDone() {
doneCount = new CountDownLatch(threadCount);
triggerExit = new CountDownLatch(1);
}
public Thread createThreadTests(final int threadId, final int frameId,
final ArrayList<Method> testCases) throws Exception {
fh.setIds(threadId, frameId);
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
fh.prepareFrameEDT(VERSION + " [" + fh.threadId + "]");
final JLabel label = new JLabel((DELAY_START) ? "Waiting 3s before starting benchmark..." : "Starting benchmark...");
label.setForeground(Color.WHITE);
final JPanel panel = new JPanel();
panel.add(label);
fh.showFrameEDT(panel);
}
});
// Wait frame to be shown:
fh.waitFrameShown();
// Set test count per thread:
testCount = testCases.size() * REPEATS;
final RenderPerfTest rp = this;
return new Thread("RenderPerfThread[" + threadId + "]") {
@Override
public void run() {
if (DELAY_START) {
RenderPerfTest.sleep(3000);
}
try {
for (Method m : testCases) {
for (int i = 0; i < REPEATS; i++) {
testName = extractTestName(m);
m.invoke(rp);
}
}
} catch (Throwable th) {
System.err.println("Exception occurred in RenderPerfThread[" + threadId + "]:");
th.printStackTrace(System.err);
}
}
};
}
private static void sleep(final long millis) {
if (millis > 0) {
try {
Thread.sleep(millis);
} catch (InterruptedException ie) {
ie.printStackTrace(System.err);
}
}
}
/** regular expression used to match characters different than alpha/numeric/_/-/. (1..n) */
private final static Pattern PATTERN_NON_FILE_NAME = Pattern.compile("[^a-zA-Z0-9\\-_\\.]");
private static String replaceNonFileNameChars(final String value) {
return PATTERN_NON_FILE_NAME.matcher(value).replaceAll("_");
}
}