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