| /* | |
| * Copyright (c) 2007, 2018, 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 8198613 | |
| * @summary Tests that v-synced BufferStrategies works (if vsync is supported) | |
| * @author Dmitri.Trembovetski@sun.com: area=Graphics | |
| * @modules java.desktop/sun.java2d.pipe.hw | |
| * @compile -XDignore.symbol.file=true VSyncedBufferStrategyTest.java | |
| * @run main/manual/othervm 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"); | |
| } | |
| } |