1 /* 2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. 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 * @test 25 * @bug 6678218 6681745 6691737 26 * @summary Tests that v-synced BufferStrategies works (if vsync is supported) 27 * @author Dmitri.Trembovetski@sun.com: area=Graphics 28 * @modules java.desktop/sun.java2d.pipe.hw 29 * @compile -XDignore.symbol.file=true VSyncedBufferStrategyTest.java 30 * @run main/manual/othervm VSyncedBufferStrategyTest 31 * @run main/manual/othervm -Dsun.java2d.opengl=True VSyncedBufferStrategyTest 32 */ 33 34 import java.awt.AWTException; 35 import java.awt.BufferCapabilities; 36 import java.awt.BufferCapabilities.FlipContents; 37 import java.awt.Button; 38 import java.awt.Canvas; 39 import java.awt.Color; 40 import java.awt.Dimension; 41 import java.awt.EventQueue; 42 import java.awt.FlowLayout; 43 import java.awt.Font; 44 import java.awt.Frame; 45 import java.awt.Graphics; 46 import java.awt.HeadlessException; 47 import java.awt.ImageCapabilities; 48 import java.awt.Panel; 49 import java.awt.event.ActionEvent; 50 import java.awt.event.ActionListener; 51 import java.awt.event.WindowAdapter; 52 import java.awt.event.WindowEvent; 53 import java.awt.image.BufferStrategy; 54 import java.util.concurrent.CountDownLatch; 55 import javax.swing.JButton; 56 import javax.swing.JFrame; 57 import javax.swing.JPanel; 58 import javax.swing.JScrollPane; 59 import javax.swing.JTextArea; 60 61 public class VSyncedBufferStrategyTest extends Canvas implements Runnable { 62 63 private static final int BLOCK_W = 50; 64 private static final int BLOCK_H = 200; 65 66 BufferStrategy bs; 67 Thread renderThread; 68 69 int blockX = 10; 70 int blockY = 10; 71 72 private volatile boolean done = false; 73 private volatile boolean requestVSync; 74 private boolean currentBSVSynced; 75 76 public VSyncedBufferStrategyTest(boolean requestVSync) { 77 this.requestVSync = requestVSync; 78 this.currentBSVSynced = !requestVSync; 79 renderThread = new Thread(this); 80 renderThread.start(); 81 } 82 83 private static final BufferCapabilities defaultBC = 84 new BufferCapabilities( 85 new ImageCapabilities(true), 86 new ImageCapabilities(true), 87 null); 88 89 private void createBS(boolean requestVSync) { 90 if (bs != null && requestVSync == currentBSVSynced) { 91 return; 92 } 93 94 BufferCapabilities bc = defaultBC; 95 if (requestVSync) { 96 bc = new sun.java2d.pipe.hw.ExtendedBufferCapabilities( 97 new ImageCapabilities(true), 98 new ImageCapabilities(true), 99 FlipContents.COPIED, 100 sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.VSYNC_ON); 101 } 102 try { 103 createBufferStrategy(2, bc); 104 } catch (AWTException e) { 105 System.err.println("Warning: cap is not supported: "+bc); 106 e.printStackTrace(); 107 createBufferStrategy(2); 108 } 109 currentBSVSynced = requestVSync; 110 bs = getBufferStrategy(); 111 String s = 112 getParent() instanceof Frame ? 113 ((Frame)getParent()).getTitle() : "parent"; 114 System.out.println("Created BS for \"" + s + "\" frame, bs="+bs); 115 } 116 117 @Override 118 public void paint(Graphics g) { 119 } 120 @Override 121 public void update(Graphics g) { 122 } 123 124 @Override 125 public void run() { 126 while (!isShowing()) { 127 try { Thread.sleep(5); } catch (InterruptedException e) {} 128 } 129 try { Thread.sleep(2000); } catch (InterruptedException e) {} 130 131 try { 132 while (!done && isShowing()) { 133 createBS(requestVSync); 134 do { 135 step(); 136 Graphics g = bs.getDrawGraphics(); 137 render(g); 138 if (!bs.contentsRestored()) { 139 bs.show(); 140 } 141 } while (bs.contentsLost()); 142 Thread.yield(); 143 } 144 } catch (Throwable e) { 145 // since we're not bothering with proper synchronization, exceptions 146 // may be thrown when the frame is closed 147 if (isShowing()) { 148 throw new RuntimeException(e); 149 } 150 } 151 } 152 153 int inc = 5; 154 private void step() { 155 blockX += inc; 156 if (blockX > getWidth() - BLOCK_W - 10) { 157 inc = -inc; 158 blockX += inc; 159 } 160 if (blockX < 10) { 161 inc = -inc; 162 blockX += inc; 163 } 164 } 165 166 private void render(Graphics g) { 167 g.setColor(Color.white); 168 g.fillRect(0, 0, getWidth(), getHeight()); 169 170 g.setColor(Color.black); 171 g.fillRect(blockX, blockY, BLOCK_W, BLOCK_H); 172 } 173 174 private void setRequestVSync(boolean reqVSync) { 175 requestVSync = reqVSync; 176 } 177 178 @Override 179 public Dimension getPreferredSize() { 180 return new Dimension(BLOCK_W*10+20, BLOCK_H+20); 181 } 182 183 private static int frameNum = 0; 184 private static Frame createAndShowBSFrame() { 185 final Frame f = new Frame("Not V-Synced"); 186 187 int myNum; 188 synchronized (VSyncedBufferStrategyTest.class) { 189 myNum = frameNum++; 190 } 191 192 final VSyncedBufferStrategyTest component = 193 new VSyncedBufferStrategyTest(false); 194 f.setIgnoreRepaint(true); 195 f.add("Center", component); 196 197 Panel p = new Panel(); 198 199 Button b = new Button("Request VSync"); 200 b.addActionListener(new ActionListener() { 201 public void actionPerformed(ActionEvent e) { 202 f.setTitle("Possibly V-Synced"); 203 component.setRequestVSync(true); 204 } 205 }); 206 p.add(b); 207 208 b = new Button("Relinquish VSync"); 209 b.addActionListener(new ActionListener() { 210 int inc = 1; 211 public void actionPerformed(ActionEvent e) { 212 f.setTitle("Not V-Synced"); 213 component.setRequestVSync(false); 214 f.setSize(f.getWidth()+inc, f.getHeight()); 215 inc = -inc; 216 } 217 }); 218 p.add(b); 219 220 f.add("South", p); 221 222 f.pack(); 223 f.setLocation(10, myNum * f.getHeight()); 224 f.setVisible(true); 225 f.addWindowListener(new WindowAdapter() { 226 @Override 227 public void windowClosing(WindowEvent e) { 228 component.done = true; 229 f.dispose(); 230 } 231 @Override 232 public void windowClosed(WindowEvent e) { 233 component.done = true; 234 } 235 }); 236 237 return f; 238 } 239 240 private static final String description = 241 "Tests that v-synced BufferStrategy works. Note that it in some\n" + 242 "cases the v-sync can not be enabled, and it is accepted.\n" + 243 "The following however is true: only one buffer strategy at a time can\n"+ 244 "be created v-synced. In order for other BS to become v-synced, the one\n"+ 245 "that currently is v-synched (or its window) needs to be disposed.\n" + 246 "Try the following scenarios:\n" + 247 " - click the \"Request VSync\" button in one of the frames. If the\n"+ 248 " behavior of the animation changes - the animation becomes smooth\n" + 249 " it had successfully created a v-synced BS. Note that the animation\n" + 250 " in other frames may also become smoother - this is a side-effect\n"+ 251 " of one of the BS-es becoming v-synched\n" + 252 " - click the \"Relinquish VSync\" button on the same frame. If the\n"+ 253 " behavior changes to the original (tearing)- it had successfully\n" + 254 " created a non-vsynced strategy.\n" + 255 " - next, try making another one v-synced. It should succeed.\n" + 256 " - next, try making another one v-synced - while there's already\n" + 257 " a v-synced frame. It should not succeed - meaning, it shouldn't\n" + 258 " appear to become smoother, and the behavior of the current v-synced\n" + 259 " frame shouldn't change.\n" + 260 "\n" + 261 "If there aren't any BufferStrategy-related exceptions or other\n" + 262 "issues, and the scenarios worked, the test passed, otherwise it\n"+ 263 "failed.\n"; 264 265 private static void createAndShowDescGUI(final Frame f3, final Frame f1, 266 final Frame f2) 267 throws HeadlessException, RuntimeException 268 { 269 final JFrame desc = 270 new JFrame("VSyncedBufferStrategyTest - Description"); 271 desc.addWindowListener(new WindowAdapter() { 272 273 @Override 274 public void windowClosing(WindowEvent e) { 275 f1.dispose(); 276 f2.dispose(); 277 f3.dispose(); 278 l.countDown(); 279 } 280 }); 281 JPanel p = new JPanel(); 282 JButton bPassed = new JButton("Passed"); 283 bPassed.addActionListener(new ActionListener() { 284 public void actionPerformed(ActionEvent e) { 285 desc.dispose(); 286 f1.dispose(); 287 f2.dispose(); 288 f3.dispose(); 289 l.countDown(); 290 } 291 }); 292 JButton bFailed = new JButton("Failed"); 293 bFailed.addActionListener(new ActionListener() { 294 public void actionPerformed(ActionEvent e) { 295 failed = true; 296 desc.dispose(); 297 f1.dispose(); 298 f2.dispose(); 299 f3.dispose(); 300 l.countDown(); 301 } 302 }); 303 p.setLayout(new FlowLayout()); 304 p.add(bPassed); 305 p.add(bFailed); 306 JTextArea ta = new JTextArea(24, 75); 307 ta.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); 308 ta.setEditable(false); 309 ta.setText(description); 310 desc.add("Center", new JScrollPane(ta)); 311 desc.add("South", p); 312 desc.pack(); 313 desc.setLocation(BLOCK_W*10+50, 0); 314 desc.setVisible(true); 315 } 316 317 private static void createTestFrames() { 318 Frame f1 = createAndShowBSFrame(); 319 Frame f2 = createAndShowBSFrame(); 320 Frame f3 = createAndShowBSFrame(); 321 createAndShowDescGUI(f1, f2, f3); 322 } 323 324 static boolean failed = false; 325 static CountDownLatch l = new CountDownLatch(1); 326 public static void main(String[] args) throws Exception { 327 EventQueue.invokeLater(new Runnable() { 328 public void run() { 329 createTestFrames(); 330 } 331 }); 332 l.await(); 333 if (failed) { 334 throw new RuntimeException("Test FAILED"); 335 } 336 System.out.println("Test PASSED"); 337 } 338 339 }