/*
* Copyright (c) 2007, 2008, Oracle and/or its affiliates. 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.
*/
/*
* @test
* @bug 6678218 6681745 6691737
* @summary Tests that v-synced BufferStrategies works (if vsync is supported)
* @author Dmitri.Trembovetski@sun.com: area=Graphics
* @compile -XDignore.symbol.file=true VSyncedBufferStrategyTest.java
* @run main/manual/othervm VSyncedBufferStrategyTest
* @run main/manual/othervm -Dsun.java2d.opengl=True VSyncedBufferStrategyTest
*/
import java.awt.AWTException;
import java.awt.BufferCapabilities;
import java.awt.BufferCapabilities.FlipContents;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.ImageCapabilities;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.concurrent.CountDownLatch;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class VSyncedBufferStrategyTest extends Canvas implements Runnable {
private static final int BLOCK_W = 50;
private static final int BLOCK_H = 200;
BufferStrategy bs;
Thread renderThread;
int blockX = 10;
int blockY = 10;
private volatile boolean done = false;
private volatile boolean requestVSync;
private boolean currentBSVSynced;
public VSyncedBufferStrategyTest(boolean requestVSync) {
this.requestVSync = requestVSync;
this.currentBSVSynced = !requestVSync;
renderThread = new Thread(this);
renderThread.start();
}
private static final BufferCapabilities defaultBC =
new BufferCapabilities(
new ImageCapabilities(true),
new ImageCapabilities(true),
null);
private void createBS(boolean requestVSync) {
if (bs != null && requestVSync == currentBSVSynced) {
return;
}
BufferCapabilities bc = defaultBC;
if (requestVSync) {
bc = new sun.java2d.pipe.hw.ExtendedBufferCapabilities(
new ImageCapabilities(true),
new ImageCapabilities(true),
FlipContents.COPIED,
sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.VSYNC_ON);
}
try {
createBufferStrategy(2, bc);
} catch (AWTException e) {
System.err.println("Warning: cap is not supported: "+bc);
e.printStackTrace();
createBufferStrategy(2);
}
currentBSVSynced = requestVSync;
bs = getBufferStrategy();
String s =
getParent() instanceof Frame ?
((Frame)getParent()).getTitle() : "parent";
System.out.println("Created BS for \"" + s + "\" frame, bs="+bs);
}
@Override
public void paint(Graphics g) {
}
@Override
public void update(Graphics g) {
}
@Override
public void run() {
while (!isShowing()) {
try { Thread.sleep(5); } catch (InterruptedException e) {}
}
try { Thread.sleep(2000); } catch (InterruptedException e) {}
try {
while (!done && isShowing()) {
createBS(requestVSync);
do {
step();
Graphics g = bs.getDrawGraphics();
render(g);
if (!bs.contentsRestored()) {
bs.show();
}
} while (bs.contentsLost());
Thread.yield();
}
} catch (Throwable e) {
// since we're not bothering with proper synchronization, exceptions
// may be thrown when the frame is closed
if (isShowing()) {
throw new RuntimeException(e);
}
}
}
int inc = 5;
private void step() {
blockX += inc;
if (blockX > getWidth() - BLOCK_W - 10) {
inc = -inc;
blockX += inc;
}
if (blockX < 10) {
inc = -inc;
blockX += inc;
}
}
private void render(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
g.fillRect(blockX, blockY, BLOCK_W, BLOCK_H);
}
private void setRequestVSync(boolean reqVSync) {
requestVSync = reqVSync;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(BLOCK_W*10+20, BLOCK_H+20);
}
private static int frameNum = 0;
private static Frame createAndShowBSFrame() {
final Frame f = new Frame("Not V-Synced");
int myNum;
synchronized (VSyncedBufferStrategyTest.class) {
myNum = frameNum++;
}
final VSyncedBufferStrategyTest component =
new VSyncedBufferStrategyTest(false);
f.setIgnoreRepaint(true);
f.add("Center", component);
Panel p = new Panel();
Button b = new Button("Request VSync");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
f.setTitle("Possibly V-Synced");
component.setRequestVSync(true);
}
});
p.add(b);
b = new Button("Relinquish VSync");
b.addActionListener(new ActionListener() {
int inc = 1;
public void actionPerformed(ActionEvent e) {
f.setTitle("Not V-Synced");
component.setRequestVSync(false);
f.setSize(f.getWidth()+inc, f.getHeight());
inc = -inc;
}
});
p.add(b);
f.add("South", p);
f.pack();
f.setLocation(10, myNum * f.getHeight());
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
component.done = true;
f.dispose();
}
@Override
public void windowClosed(WindowEvent e) {
component.done = true;
}
});
return f;
}
private static final String description =
"Tests that v-synced BufferStrategy works. Note that it in some\n" +
"cases the v-sync can not be enabled, and it is accepted.\n" +
"The following however is true: only one buffer strategy at a time can\n"+
"be created v-synced. In order for other BS to become v-synced, the one\n"+
"that currently is v-synched (or its window) needs to be disposed.\n" +
"Try the following scenarios:\n" +
" - click the \"Request VSync\" button in one of the frames. If the\n"+
" behavior of the animation changes - the animation becomes smooth\n" +
" it had successfully created a v-synced BS. Note that the animation\n" +
" in other frames may also become smoother - this is a side-effect\n"+
" of one of the BS-es becoming v-synched\n" +
" - click the \"Relinquish VSync\" button on the same frame. If the\n"+
" behavior changes to the original (tearing)- it had successfully\n" +
" created a non-vsynced strategy.\n" +
" - next, try making another one v-synced. It should succeed.\n" +
" - next, try making another one v-synced - while there's already\n" +
" a v-synced frame. It should not succeed - meaning, it shouldn't\n" +
" appear to become smoother, and the behavior of the current v-synced\n" +
" frame shouldn't change.\n" +
"\n" +
"If there aren't any BufferStrategy-related exceptions or other\n" +
"issues, and the scenarios worked, the test passed, otherwise it\n"+
"failed.\n";
private static void createAndShowDescGUI(final Frame f3, final Frame f1,
final Frame f2)
throws HeadlessException, RuntimeException
{
final JFrame desc =
new JFrame("VSyncedBufferStrategyTest - Description");
desc.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
f1.dispose();
f2.dispose();
f3.dispose();
l.countDown();
}
});
JPanel p = new JPanel();
JButton bPassed = new JButton("Passed");
bPassed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
desc.dispose();
f1.dispose();
f2.dispose();
f3.dispose();
l.countDown();
}
});
JButton bFailed = new JButton("Failed");
bFailed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
failed = true;
desc.dispose();
f1.dispose();
f2.dispose();
f3.dispose();
l.countDown();
}
});
p.setLayout(new FlowLayout());
p.add(bPassed);
p.add(bFailed);
JTextArea ta = new JTextArea(24, 75);
ta.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
ta.setEditable(false);
ta.setText(description);
desc.add("Center", new JScrollPane(ta));
desc.add("South", p);
desc.pack();
desc.setLocation(BLOCK_W*10+50, 0);
desc.setVisible(true);
}
private static void createTestFrames() {
Frame f1 = createAndShowBSFrame();
Frame f2 = createAndShowBSFrame();
Frame f3 = createAndShowBSFrame();
createAndShowDescGUI(f1, f2, f3);
}
static boolean failed = false;
static CountDownLatch l = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
EventQueue.invokeLater(new Runnable() {
public void run() {
createTestFrames();
}
});
l.await();
if (failed) {
throw new RuntimeException("Test FAILED");
}
System.out.println("Test PASSED");
}
}