* AwtKeyInput now synchronized properly between OGL and EDT threads

* AwtMouseInput will no longer generate events with no mouse movement
 * Fixed issue where settings specified on AwtPanelsContext were not copied properly to the offscreen context
 * Significant performance improvements in AwtPanel system
  - Use of proper locking to prevent deadlocks
  - Optimized BufferStrategy by using a single, accelerated front buffer
  - Removed pbuffer.makeCurrent() which was not necessary and used many CPU cycles

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8337 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 13 years ago
parent dcb71def31
commit fa92c00a20
  1. 47
      engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java
  2. 63
      engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java
  3. 124
      engine/src/desktop/com/jme3/system/awt/AwtPanel.java
  4. 3
      engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java
  5. 5
      engine/src/desktop/com/jme3/util/Screenshots.java
  6. 1
      engine/src/test/jme3test/awt/TestAwtPanels.java

@ -38,8 +38,7 @@ import com.jme3.input.event.KeyInputEvent;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
@ -52,8 +51,9 @@ import java.util.logging.Logger;
public class AwtKeyInput implements KeyInput, KeyListener {
private static final Logger logger = Logger.getLogger(AwtKeyInput.class.getName());
private final ArrayList<KeyInputEvent> eventQueue = new ArrayList<KeyInputEvent>();
private RawInputListener listener;
private List<KeyInputEvent> eventQueue = new LinkedList<KeyInputEvent>();
private Component component;
public AwtKeyInput(){
@ -66,12 +66,14 @@ public class AwtKeyInput implements KeyInput, KeyListener {
}
public void setInputSource(Component comp){
if (component != null){
component.removeKeyListener(this);
eventQueue.clear();
synchronized (eventQueue){
if (component != null){
component.removeKeyListener(this);
eventQueue.clear();
}
component = comp;
component.addKeyListener(this);
}
component = comp;
component.addKeyListener(this);
}
public long getInputTimeNanos() {
@ -83,11 +85,13 @@ public class AwtKeyInput implements KeyInput, KeyListener {
}
public void update() {
// flush events to listener
for (int i = 0; i < eventQueue.size(); i++){
listener.onKeyEvent(eventQueue.get(i));
synchronized (eventQueue){
// flush events to listener
for (int i = 0; i < eventQueue.size(); i++){
listener.onKeyEvent(eventQueue.get(i));
}
eventQueue.clear();
}
eventQueue.clear();
}
public boolean isInitialized() {
@ -100,25 +104,30 @@ public class AwtKeyInput implements KeyInput, KeyListener {
public void keyTyped(KeyEvent evt) {
// key code is zero for typed events
int code = 0;
//int code = convertAwtKey(evt.getKeyCode());
KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), false, true);
keyEvent.setTime(evt.getWhen());
eventQueue.add(keyEvent);
// int code = 0;
// KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), false, true);
// keyEvent.setTime(evt.getWhen());
// synchronized (eventQueue){
// eventQueue.add(keyEvent);
// }
}
public void keyPressed(KeyEvent evt) {
int code = convertAwtKey(evt.getKeyCode());
KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), true, false);
keyEvent.setTime(evt.getWhen());
eventQueue.add(keyEvent);
synchronized (eventQueue){
eventQueue.add(keyEvent);
}
}
public void keyReleased(KeyEvent evt) {
int code = convertAwtKey(evt.getKeyCode());
KeyInputEvent keyEvent = new KeyInputEvent(code, evt.getKeyChar(), false, false);
keyEvent.setTime(evt.getWhen());
eventQueue.add(keyEvent);
synchronized (eventQueue){
eventQueue.add(keyEvent);
}
}
/**

@ -88,6 +88,7 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
private Point centerLocationOnScreen;
private Point lastKnownLocation;
private boolean isRecentering;
private boolean cursorMoved;
private int eventsSinceRecenter;
public AwtMouseInput() {
@ -161,29 +162,33 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
}
public void update() {
int newX = location.x;
int newY = location.y;
int newWheel = wheelPos;
// invert DY
MouseMotionEvent evt = new MouseMotionEvent(newX, newY,
newX - lastEventX,
lastEventY - newY,
wheelPos, lastEventWheel - wheelPos);
listener.onMouseMotionEvent(evt);
if (cursorMoved){
int newX = location.x;
int newY = location.y;
int newWheel = wheelPos;
// invert DY
MouseMotionEvent evt = new MouseMotionEvent(newX, newY,
newX - lastEventX,
lastEventY - newY,
wheelPos, lastEventWheel - wheelPos);
listener.onMouseMotionEvent(evt);
lastEventX = newX;
lastEventY = newY;
lastEventWheel = newWheel;
cursorMoved = false;
}
int size = eventQueue.size();
for (int i = 0; i < size; i++){
listener.onMouseButtonEvent(eventQueue.get(i));
}
eventQueue.clear();
lastEventX = newX;
lastEventY = newY;
lastEventWheel = newWheel;
}
private final Cursor getTransparentCursor() {
private Cursor getTransparentCursor() {
if (transparentCursor == null){
BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
cursorImage.setRGB(0, 0, 0);
@ -191,10 +196,7 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
}
return transparentCursor;
}
//
// public boolean isCursorVisible() {
// return( isCursorVisible );
// }
// public void setHardwareCursor(URL file, int xHotspot, int yHotspot) {
// //Create the image from the provided url
// java.awt.Image cursorImage = new ImageIcon( file ).getImage( );
@ -239,6 +241,7 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
public void mouseWheelMoved(MouseWheelEvent arg0) {
int dwheel = arg0.getUnitsToScroll();
wheelPos -= dwheel;
cursorMoved = true;
}
public void mouseDragged(MouseEvent arg0) {
@ -267,16 +270,8 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
}
lastKnownLocation.x = arg0.getX();
lastKnownLocation.y = arg0.getY();
// MHenze (cylab) Fix Issue 35: all accesses to the swingEvents list have to be synchronized for StandardGame to work...
// TODO MHenze (cylab): Cleanup. this members seem obsolete by the newly introduced above.
// absPoint.setLocation(location);
// if (lastPoint.x == Integer.MIN_VALUE) {
// lastPoint.setLocation(absPoint.x, absPoint.y);
// }
// lastPoint.setLocation(arg0.getPoint());
// currentDeltaPoint.x = absPoint.x - lastPoint.x;
// currentDeltaPoint.y = -(absPoint.y - lastPoint.y);
// lastPoint.setLocation(location);
cursorMoved = true;
}
}
@ -285,14 +280,10 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
if (robot != null) {
eventsSinceRecenter = 0;
isRecentering = true;
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
centerLocation.setLocation(component.getWidth()/2, component.getHeight()/2);
centerLocationOnScreen.setLocation(centerLocation);
SwingUtilities.convertPointToScreen(centerLocationOnScreen, component);
robot.mouseMove(centerLocationOnScreen.x, centerLocationOnScreen.y);
// }
// });
}
}
@ -301,13 +292,13 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
switch (arg0.getButton()) {
default:
case MouseEvent.BUTTON1: //left
index = 0;
index = MouseInput.BUTTON_LEFT;
break;
case MouseEvent.BUTTON2: //middle
index = 2;
index = MouseInput.BUTTON_MIDDLE;
break;
case MouseEvent.BUTTON3: //right
index = 1;
index = MouseInput.BUTTON_RIGHT;
break;
}
return index;

@ -8,9 +8,12 @@ import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
import com.jme3.util.Screenshots;
import java.awt.AWTException;
import java.awt.BufferCapabilities;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.ImageCapabilities;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
@ -18,10 +21,9 @@ import java.awt.image.AffineTransformOp;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
public class AwtPanel extends Canvas implements SceneProcessor {
@ -29,6 +31,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
private BufferedImage img;
private FrameBuffer fb;
private ByteBuffer byteBuf;
private IntBuffer intBuf;
private boolean activeUpdates = true;
private RenderManager rm;
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
@ -36,15 +39,13 @@ public class AwtPanel extends Canvas implements SceneProcessor {
// Visibility/drawing vars
private BufferStrategy strategy;
private AffineTransformOp transformOp;
private CyclicBarrier visibleBarrier = new CyclicBarrier(2);
private AtomicBoolean visible = new AtomicBoolean(false);
private boolean glVisible = false;
// Reshape vars
private int newWidth = 0;
private int newHeight = 0;
private CyclicBarrier reshapeBarrier = new CyclicBarrier(2);
private int newWidth = 1;
private int newHeight = 1;
private AtomicBoolean reshapeNeeded = new AtomicBoolean(false);
private final Object lock = new Object();
public AwtPanel(boolean activeUpdates){
this.activeUpdates = activeUpdates;
@ -53,16 +54,15 @@ public class AwtPanel extends Canvas implements SceneProcessor {
addComponentListener(new ComponentAdapter(){
@Override
public void componentResized(ComponentEvent e) {
newWidth = Math.max(getWidth(), 1);
newHeight = Math.max(getHeight(), 1);
reshapeNeeded.set(true);
try {
reshapeBarrier.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BrokenBarrierException ex) {
ex.printStackTrace();
synchronized (lock){
int newWidth2 = Math.max(getWidth(), 1);
int newHeight2 = Math.max(getHeight(), 1);
if (newWidth != newWidth2 || newHeight != newHeight2){
newWidth = newWidth2;
newHeight = newHeight2;
reshapeNeeded.set(true);
System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
}
}
}
});
@ -72,15 +72,9 @@ public class AwtPanel extends Canvas implements SceneProcessor {
public void addNotify(){
super.addNotify();
try {
createBufferStrategy(2);
strategy = getBufferStrategy();
synchronized (lock){
visible.set(true);
visibleBarrier.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BrokenBarrierException ex) {
ex.printStackTrace();
System.out.println("EDT: addNotify");
}
requestFocusInWindow();
@ -88,48 +82,47 @@ public class AwtPanel extends Canvas implements SceneProcessor {
@Override
public void removeNotify(){
try {
synchronized (lock){
visible.set(false);
visibleBarrier.await();
strategy = null;
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BrokenBarrierException ex) {
ex.printStackTrace();
// strategy.dispose();
// strategy = null;
System.out.println("EDT: removeNotify");
}
super.removeNotify();
}
private void checkVisibility(){
if (visible.get() != glVisible){
public void drawFrameInThread(){
if (!visible.get()){
if (strategy != null){
strategy.dispose();
strategy = null;
}
return;
}
if (strategy == null || strategy.contentsLost()){
if (strategy != null){
strategy.dispose();
}
try {
glVisible = visible.get();
visibleBarrier.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BrokenBarrierException ex) {
createBufferStrategy(1, new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.UNDEFINED));
} catch (AWTException ex) {
ex.printStackTrace();
}
strategy = getBufferStrategy();
System.out.println("OGL: BufferStrategy lost!");
}
}
public void drawFrame(){
checkVisibility();
if (!glVisible)
return;
Graphics2D g2d;
if (strategy.contentsLost()){
strategy.dispose();
createBufferStrategy(2);
strategy = getBufferStrategy();
System.out.println("BufferStrategy lost!");
synchronized (lock){
g2d = (Graphics2D) strategy.getDrawGraphics();
}
Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
g2d.drawImage(img, transformOp, 0, 0);
g2d.dispose();
strategy.show();
strategy.show();
}
public boolean isActiveUpdates() {
@ -137,7 +130,9 @@ public class AwtPanel extends Canvas implements SceneProcessor {
}
public void setActiveUpdates(boolean activeUpdates) {
this.activeUpdates = activeUpdates;
synchronized (lock){
this.activeUpdates = activeUpdates;
}
}
public void attachTo(ViewPort ... vps){
@ -162,11 +157,15 @@ public class AwtPanel extends Canvas implements SceneProcessor {
private void reshapeInThread(int width, int height) {
byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
intBuf = byteBuf.asIntBuffer();
fb = new FrameBuffer(width, height, 1);
fb.setDepthBuffer(Format.Depth);
fb.setColorBuffer(Format.RGB8);
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// synchronized (lock){
// img = (BufferedImage) getGraphicsConfiguration().createCompatibleImage(width, height);
// }
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
tx.translate(0, -img.getHeight());
transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
@ -188,26 +187,19 @@ public class AwtPanel extends Canvas implements SceneProcessor {
}
public void postFrame(FrameBuffer out) {
if (out != fb)
if (out != fb){
throw new IllegalStateException("Why did you change the output framebuffer?");
}
if (reshapeNeeded.getAndSet(false)){
reshapeInThread(newWidth, newHeight);
try {
reshapeBarrier.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BrokenBarrierException ex) {
ex.printStackTrace();
}
}else if (activeUpdates){
byteBuf.clear();
rm.getRenderer().readFrameBuffer(fb, byteBuf);
Screenshots.convertScreenShot2(byteBuf, img);
drawFrame();
}else{
checkVisibility();
Screenshots.convertScreenShot2(intBuf, img);
drawFrameInThread();
}
}
public void reshape(ViewPort vp, int w, int h) {

@ -138,7 +138,8 @@ public class AwtPanelsContext implements JmeContext {
}
public void setSettings(AppSettings settings) {
settings.copyFrom(settings);
this.settings.copyFrom(settings);
this.settings.setRenderer(AppSettings.LWJGL_OPENGL2);
if (actualContext != null){
actualContext.setSettings(settings);
}

@ -5,17 +5,18 @@ import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public final class Screenshots {
public static void convertScreenShot2(ByteBuffer bgraBuf, BufferedImage out){
public static void convertScreenShot2(IntBuffer bgraBuf, BufferedImage out){
WritableRaster wr = out.getRaster();
DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
int[] cpuArray = db.getData();
bgraBuf.clear();
bgraBuf.asIntBuffer().get(cpuArray);
bgraBuf.get(cpuArray);
// int width = wr.getWidth();
// int height = wr.getHeight();

@ -49,6 +49,7 @@ public class TestAwtPanels extends SimpleApplication {
app.setShowSettings(false);
AppSettings settings = new AppSettings(true);
settings.setCustomRenderer(AwtPanelsContext.class);
settings.setFrameRate(60);
app.setSettings(settings);
app.start();

Loading…
Cancel
Save