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 }