* 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
This commit is contained in:
parent
dcb71def31
commit
fa92c00a20
@ -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;
|
||||
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);
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void drawFrame(){
|
||||
checkVisibility();
|
||||
if (!glVisible)
|
||||
return;
|
||||
|
||||
if (strategy.contentsLost()){
|
||||
strategy.dispose();
|
||||
createBufferStrategy(2);
|
||||
strategy = getBufferStrategy();
|
||||
System.out.println("BufferStrategy lost!");
|
||||
System.out.println("OGL: BufferStrategy lost!");
|
||||
}
|
||||
|
||||
Graphics2D g2d;
|
||||
|
||||
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…
x
Reference in New Issue
Block a user