1 /* 2 * Copyright 2019 JetBrains s.r.o. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package perf.metal; 25 26 import org.junit.Test; 27 28 import javax.swing.*; 29 import java.awt.*; 30 import java.awt.event.WindowAdapter; 31 import java.awt.event.WindowEvent; 32 import java.awt.geom.QuadCurve2D; 33 import java.util.concurrent.CountDownLatch; 34 import java.util.concurrent.atomic.AtomicBoolean; 35 36 public class MetalPerfTest { 37 private final static int N = 500; 38 private final static float WIDTH = 800; 39 private final static float HEIGHT = 800; 40 private final static float R = 25; 41 private final static int BW = 50; 42 private final static int BH = 50; 43 private final static int COUNT = 300; 44 private final static int DELAY = 10; 45 private final static int RESOLUTION = 5; 46 private final static int COLOR_TOLERANCE = 10; 47 48 49 interface Renderable { 50 void render(Graphics2D g2d); 51 void update(); 52 } 53 54 static class Particles { 55 private float[] bx; 56 private float[] by; 57 private float[] vx; 58 private float[] vy; 59 private float r; 60 private int n; 61 62 private float x0; 63 private float y0; 64 private float width; 65 private float height; 66 67 Particles(int n, float r, float x0, float y0, float width, float height) { 68 bx = new float[n]; 69 by = new float[n]; 70 vx = new float[n]; 71 vy = new float[n]; 72 this.n = n; 73 this.r = r; 74 this.x0 = x0; 75 this.y0 = y0; 76 this.width = width; 77 this.height = height; 78 for (int i = 0; i < n; i++) { 79 bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0)); 80 by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0)); 81 vx[i] = 0.1f * (float) (Math.random() * 2 * r - r); 82 vy[i] = 0.1f * (float) (Math.random() * 2 * r - r); 83 } 84 85 } 86 87 void render(Graphics2D g2d, ParticleRenderer renderer) { 88 for (int i = 0; i < n; i++) { 89 renderer.render(g2d, i, bx, by, vx, vy); 90 } 91 } 92 93 void update() { 94 for (int i = 0; i < n; i++) { 95 bx[i] += vx[i]; 96 if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i]; 97 by[i] += vy[i]; 98 if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i]; 99 } 100 101 } 102 } 103 104 interface ParticleRenderer { 105 void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy); 106 107 } 108 109 static class FlatParticleRenderer implements ParticleRenderer { 110 Color[] colors; 111 float r; 112 113 FlatParticleRenderer(int n, float r) { 114 colors = new Color[n]; 115 this.r = r; 116 for (int i = 0; i < n; i++) { 117 colors[i] = new Color((float) Math.random(), 118 (float) Math.random(), (float) Math.random()); 119 } 120 } 121 122 @Override 123 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 124 g2d.setColor(colors[id % colors.length]); 125 g2d.fillOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r)); 126 } 127 128 } 129 static class FlatBoxParticleRenderer extends FlatParticleRenderer { 130 131 132 FlatBoxParticleRenderer(int n, float r) { 133 super(n, r); 134 } 135 @Override 136 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 137 g2d.setColor(colors[id % colors.length]); 138 g2d.fillRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r)); 139 140 } 141 142 } 143 static class WiredParticleRenderer extends FlatParticleRenderer { 144 145 146 WiredParticleRenderer(int n, float r) { 147 super(n, r); 148 } 149 @Override 150 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 151 g2d.setColor(colors[id % colors.length]); 152 g2d.drawOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r)); 153 } 154 155 } 156 static class WiredBoxParticleRenderer extends FlatParticleRenderer { 157 158 WiredBoxParticleRenderer(int n, float r) { 159 super(n, r); 160 } 161 162 @Override 163 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 164 g2d.setColor(colors[id % colors.length]); 165 g2d.drawRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r)); 166 } 167 168 } 169 static class SegParticleRenderer extends FlatParticleRenderer { 170 171 SegParticleRenderer(int n, float r) { 172 super(n, r); 173 } 174 175 @Override 176 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 177 double v = Math.sqrt(vx[id]*vx[id]+vy[id]*vy[id]); 178 float nvx = (float) (vx[id]/v); 179 float nvy = (float) (vy[id]/v); 180 g2d.setColor(colors[id % colors.length]); 181 g2d.drawLine((int)(x[id] - r*nvx), (int)(y[id] - r*nvy), 182 (int)(x[id] + 2*r*nvx), (int)(y[id] + 2*r*nvy)); 183 } 184 185 } 186 187 188 static class WiredQuadParticleRenderer extends FlatParticleRenderer { 189 190 WiredQuadParticleRenderer(int n, float r) { 191 super(n, r); 192 } 193 194 @Override 195 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 196 if (id > 2 && (id % 3) == 0) { 197 g2d.setColor(colors[id % colors.length]); 198 g2d.draw(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1])); 199 } 200 201 } 202 } 203 204 static class FlatQuadParticleRenderer extends FlatParticleRenderer { 205 206 FlatQuadParticleRenderer(int n, float r) { 207 super(n, r); 208 } 209 210 @Override 211 public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) { 212 if (id > 2 && (id % 3) == 0) { 213 g2d.setColor(colors[id % colors.length]); 214 g2d.fill(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1])); 215 } 216 217 } 218 } 219 220 class PerfMeter { 221 222 private int frame = 0; 223 224 private JPanel panel; 225 226 private long time; 227 private double execTime = 0; 228 private Color expColor = Color.RED; 229 AtomicBoolean waiting = new AtomicBoolean(false); 230 231 double exec(final Renderable renderable) throws Exception{ 232 final CountDownLatch latch = new CountDownLatch(COUNT); 233 final CountDownLatch latchFrame = new CountDownLatch(1); 234 235 final JFrame f = new JFrame(); 236 f.addWindowListener(new WindowAdapter() { 237 @Override 238 public void windowClosed(WindowEvent e) { 239 latchFrame.countDown(); 240 } 241 }); 242 243 SwingUtilities.invokeAndWait(new Runnable() { 244 @Override 245 public void run() { 246 247 panel = new JPanel() 248 { 249 @Override 250 protected void paintComponent(Graphics g) { 251 252 super.paintComponent(g); 253 time = System.nanoTime(); 254 Graphics2D g2d = (Graphics2D) g; 255 renderable.render(g2d); 256 g2d.setColor(expColor); 257 g.fillRect(0, 0, BW, BH); 258 } 259 }; 260 261 panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH))); 262 panel.setBackground(Color.BLACK); 263 f.add(panel); 264 f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 265 f.pack(); 266 f.setVisible(true); 267 } 268 }); 269 Robot robot = new Robot(); 270 271 Timer timer = new Timer(DELAY, e -> { 272 273 if (waiting.compareAndSet(false, true)) { 274 Color c = robot.getPixelColor(panel.getTopLevelAncestor().getX() + 25, 275 panel.getTopLevelAncestor().getY() + 25); 276 if (isAlmostEqual(c, Color.BLUE)) { 277 expColor = Color.RED; 278 } else { 279 expColor = Color.BLUE; 280 } 281 renderable.update(); 282 panel.getParent().repaint(); 283 284 } else { 285 while (!isAlmostEqual( 286 robot.getPixelColor( 287 panel.getTopLevelAncestor().getX() + BW / 2, 288 panel.getTopLevelAncestor().getY() + BH / 2), 289 expColor)) 290 { 291 try { 292 Thread.sleep(RESOLUTION); 293 } catch (InterruptedException ex) { 294 ex.printStackTrace(); 295 } 296 } 297 time = System.nanoTime() - time; 298 execTime += time; 299 frame++; 300 waiting.set(false); 301 } 302 303 latch.countDown(); 304 }); 305 timer.start(); 306 latch.await(); 307 SwingUtilities.invokeAndWait(() -> { 308 timer.stop(); 309 f.setVisible(false); 310 f.dispose(); 311 }); 312 313 latchFrame.await(); 314 return 1e9/(execTime / frame); 315 } 316 317 private boolean isAlmostEqual(Color c1, Color c2) { 318 return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE || 319 Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE || 320 Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE; 321 322 } 323 } 324 325 private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT); 326 private static final ParticleRenderer flatRenderer = new FlatParticleRenderer(N, R); 327 private static final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R); 328 private static final ParticleRenderer wiredRenderer = new WiredParticleRenderer(N, R); 329 private static final ParticleRenderer wiredBoxRenderer = new WiredBoxParticleRenderer(N, R); 330 private static final ParticleRenderer segRenderer = new SegParticleRenderer(N, R); 331 private static final ParticleRenderer flatQuadRenderer = new FlatQuadParticleRenderer(N, R); 332 private static final ParticleRenderer wiredQuadRenderer = new WiredQuadParticleRenderer(N, R); 333 334 335 @Test 336 public void testFlatBubbles() throws Exception { 337 338 double fps = (new PerfMeter()).exec(new Renderable() { 339 @Override 340 public void render(Graphics2D g2d) { 341 balls.render(g2d, flatRenderer); 342 } 343 344 @Override 345 public void update() { 346 balls.update(); 347 } 348 }); 349 350 System.out.println(fps); 351 } 352 353 @Test 354 public void testFlatBoxBubbles() throws Exception { 355 356 double fps = (new PerfMeter()).exec(new Renderable() { 357 @Override 358 public void render(Graphics2D g2d) { 359 balls.render(g2d, flatBoxRenderer); 360 } 361 362 @Override 363 public void update() { 364 balls.update(); 365 } 366 }); 367 368 System.out.println(fps); 369 } 370 371 @Test 372 public void testWiredBubbles() throws Exception { 373 374 double fps = (new PerfMeter()).exec(new Renderable() { 375 @Override 376 public void render(Graphics2D g2d) { 377 balls.render(g2d, wiredRenderer); 378 } 379 380 @Override 381 public void update() { 382 balls.update(); 383 } 384 }); 385 386 System.out.println(fps); 387 } 388 389 @Test 390 public void testWiredBoxBubbles() throws Exception { 391 392 double fps = (new PerfMeter()).exec(new Renderable() { 393 @Override 394 public void render(Graphics2D g2d) { 395 balls.render(g2d, wiredBoxRenderer); 396 } 397 398 @Override 399 public void update() { 400 balls.update(); 401 } 402 }); 403 404 System.out.println(fps); 405 } 406 407 @Test 408 public void testLines() throws Exception { 409 410 double fps = (new PerfMeter()).exec(new Renderable() { 411 @Override 412 public void render(Graphics2D g2d) { 413 balls.render(g2d, segRenderer); 414 } 415 416 @Override 417 public void update() { 418 balls.update(); 419 } 420 }); 421 422 System.out.println(fps); 423 } 424 425 @Test 426 public void testFlatQuad() throws Exception { 427 428 double fps = (new PerfMeter()).exec(new Renderable() { 429 @Override 430 public void render(Graphics2D g2d) { 431 balls.render(g2d, flatQuadRenderer); 432 } 433 434 @Override 435 public void update() { 436 balls.update(); 437 } 438 }); 439 440 System.out.println(fps); 441 } 442 443 @Test 444 public void testWiredQuad() throws Exception { 445 446 double fps = (new PerfMeter()).exec(new Renderable() { 447 @Override 448 public void render(Graphics2D g2d) { 449 balls.render(g2d, wiredQuadRenderer); 450 } 451 452 @Override 453 public void update() { 454 balls.update(); 455 } 456 }); 457 458 System.out.println(fps); 459 } 460 461 }