diff --git a/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java index d047d814f..dcc0ed504 100644 --- a/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java +++ b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java @@ -148,6 +148,9 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe public void setCursorVisible(boolean visible){ if (this.visible != visible){ + + lastKnownLocation.x = lastKnownLocation.y = 0; + this.visible = visible; final boolean newVisible = visible; SwingUtilities.invokeLater(new Runnable() { @@ -168,7 +171,9 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe int newWheel = wheelPos; // invert DY - MouseMotionEvent evt = new MouseMotionEvent(newX, newY, + int actualX = lastKnownLocation.x; + int actualY = component.getHeight() - lastKnownLocation.y; + MouseMotionEvent evt = new MouseMotionEvent(actualX, actualY, newX - lastEventX, lastEventY - newY, wheelPos, lastEventWheel - wheelPos); diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanel.java b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java index b25f3f378..e84d4743a 100644 --- a/engine/src/desktop/com/jme3/system/awt/AwtPanel.java +++ b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java @@ -11,9 +11,11 @@ import com.jme3.util.Screenshots; import java.awt.AWTException; import java.awt.BufferCapabilities; import java.awt.Canvas; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.ImageCapabilities; +import java.awt.RenderingHints; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.geom.AffineTransform; @@ -32,14 +34,16 @@ public class AwtPanel extends Canvas implements SceneProcessor { private FrameBuffer fb; private ByteBuffer byteBuf; private IntBuffer intBuf; - private boolean activeUpdates = true; private RenderManager rm; + private PaintMode paintMode; private ArrayList viewPorts = new ArrayList(); // Visibility/drawing vars private BufferStrategy strategy; private AffineTransformOp transformOp; - private AtomicBoolean visible = new AtomicBoolean(false); + private AtomicBoolean hasNativePeer = new AtomicBoolean(false); + private AtomicBoolean showing = new AtomicBoolean(false); + private AtomicBoolean repaintRequest = new AtomicBoolean(false); // Reshape vars private int newWidth = 1; @@ -47,10 +51,13 @@ public class AwtPanel extends Canvas implements SceneProcessor { private AtomicBoolean reshapeNeeded = new AtomicBoolean(false); private final Object lock = new Object(); - public AwtPanel(boolean activeUpdates){ - this.activeUpdates = activeUpdates; + public AwtPanel(PaintMode paintMode){ + this.paintMode = paintMode; + + if (paintMode == PaintMode.Accelerated){ + setIgnoreRepaint(true); + } - setIgnoreRepaint(true); addComponentListener(new ComponentAdapter(){ @Override public void componentResized(ComponentEvent e) { @@ -73,7 +80,7 @@ public class AwtPanel extends Canvas implements SceneProcessor { super.addNotify(); synchronized (lock){ - visible.set(true); + hasNativePeer.set(true); System.out.println("EDT: addNotify"); } @@ -83,58 +90,102 @@ public class AwtPanel extends Canvas implements SceneProcessor { @Override public void removeNotify(){ synchronized (lock){ - visible.set(false); -// strategy.dispose(); -// strategy = null; + hasNativePeer.set(false); System.out.println("EDT: removeNotify"); } super.removeNotify(); } - public void drawFrameInThread(){ - if (!visible.get()){ + @Override + public void paint(Graphics g){ + Graphics2D g2d = (Graphics2D) g; + synchronized (lock){ + g2d.drawImage(img, transformOp, 0, 0); + } + } + + public boolean checkVisibilityState(){ + if (!hasNativePeer.get()){ if (strategy != null){ strategy.dispose(); strategy = null; + System.out.println("OGL: Not visible. Destroy strategy."); } - return; + return false; } - - if (strategy == null || strategy.contentsLost()){ - if (strategy != null){ - strategy.dispose(); - } - try { - createBufferStrategy(1, new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.UNDEFINED)); - } catch (AWTException ex) { - ex.printStackTrace(); + + boolean currentShowing = isShowing(); + if (showing.getAndSet(currentShowing) != currentShowing){ + if (currentShowing){ + System.out.println("OGL: Enter showing state."); + }else{ + System.out.println("OGL: Exit showing state."); } - strategy = getBufferStrategy(); - System.out.println("OGL: BufferStrategy lost!"); } - - Graphics2D g2d; + return currentShowing; + } + + public void repaintInThread(){ + // Convert screenshot. + byteBuf.clear(); + rm.getRenderer().readFrameBuffer(fb, byteBuf); synchronized (lock){ - g2d = (Graphics2D) strategy.getDrawGraphics(); + // All operations on img must be synchronized + // as it is accessed from EDT. + Screenshots.convertScreenShot2(intBuf, img); + repaint(); } - - g2d.drawImage(img, transformOp, 0, 0); - g2d.dispose(); - strategy.show(); } - public boolean isActiveUpdates() { - return activeUpdates; - } - - public void setActiveUpdates(boolean activeUpdates) { + public void drawFrameInThread(){ + // Convert screenshot. + byteBuf.clear(); + rm.getRenderer().readFrameBuffer(fb, byteBuf); + Screenshots.convertScreenShot2(intBuf, img); + synchronized (lock){ - this.activeUpdates = activeUpdates; + // All operations on strategy should be synchronized (?) + if (strategy == null){ + try { + createBufferStrategy(1, + new BufferCapabilities( + new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.UNDEFINED) + ); + } catch (AWTException ex) { + ex.printStackTrace(); + } + strategy = getBufferStrategy(); + System.out.println("OGL: Visible. Create strategy."); + } + + // Draw screenshot. + do { + do { + Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics(); + if (g2d == null){ + System.out.println("OGL: DrawGraphics was null."); + return; + } + + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_SPEED); + + g2d.drawImage(img, transformOp, 0, 0); + g2d.dispose(); + strategy.show(); + } while (strategy.contentsRestored()); + } while (strategy.contentsLost()); } } + public boolean isActiveDrawing(){ + return paintMode != PaintMode.OnRequest && showing.get(); + } + public void attachTo(ViewPort ... vps){ if (viewPorts.size() > 0){ for (ViewPort vp : viewPorts){ @@ -158,14 +209,19 @@ 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 = 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); @@ -173,6 +229,12 @@ public class AwtPanel extends Canvas implements SceneProcessor { for (ViewPort vp : viewPorts){ vp.setOutputFrameBuffer(fb); vp.getCamera().resize(width, height, true); + + // NOTE: Hack alert. This is done ONLY for custom framebuffers. + // Main framebuffer should use RenderManager.notifyReshape(). + for (SceneProcessor sp : vp.getProcessors()){ + sp.reshape(vp, width, height); + } } } @@ -185,6 +247,12 @@ public class AwtPanel extends Canvas implements SceneProcessor { public void postQueue(RenderQueue rq) { } + + @Override + public void invalidate(){ + // For "PaintMode.OnDemand" only. + repaintRequest.set(true); + } public void postFrame(FrameBuffer out) { if (out != fb){ @@ -193,13 +261,25 @@ public class AwtPanel extends Canvas implements SceneProcessor { if (reshapeNeeded.getAndSet(false)){ reshapeInThread(newWidth, newHeight); - }else if (activeUpdates){ - byteBuf.clear(); - rm.getRenderer().readFrameBuffer(fb, byteBuf); - Screenshots.convertScreenShot2(intBuf, img); - drawFrameInThread(); + }else{ + if (!checkVisibilityState()){ + return; + } + + switch (paintMode){ + case Accelerated: + drawFrameInThread(); + break; + case Repaint: + repaintInThread(); + break; + case OnRequest: + if (repaintRequest.getAndSet(false)){ + repaintInThread(); + } + break; + } } - } public void reshape(ViewPort vp, int w, int h) { diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java index 75f91ef65..56c7289f7 100644 --- a/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java +++ b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java @@ -25,6 +25,8 @@ public class AwtPanelsContext implements JmeContext { protected AwtMouseInput mouseInput = new AwtMouseInput(); protected AwtKeyInput keyInput = new AwtKeyInput(); + protected boolean lastThrottleState = false; + private class AwtPanelsListener implements SystemListener { public void initialize() { @@ -119,8 +121,8 @@ public class AwtPanelsContext implements JmeContext { public AwtPanelsContext(){ } - public AwtPanel createPanel(boolean activeUpdates){ - AwtPanel panel = new AwtPanel(activeUpdates); + public AwtPanel createPanel(PaintMode paintMode){ + AwtPanel panel = new AwtPanel(paintMode); panels.add(panel); return panel; } @@ -130,6 +132,32 @@ public class AwtPanelsContext implements JmeContext { } private void updateInThread(){ + // Check if throttle required + boolean needThrottle = true; + + for (AwtPanel panel : panels){ + if (panel.isActiveDrawing()){ + needThrottle = false; + break; + } + } + + if (lastThrottleState != needThrottle){ + lastThrottleState = needThrottle; + if (lastThrottleState){ + System.out.println("OGL: Throttling update loop."); + }else{ + System.out.println("OGL: Ceased throttling update loop."); + } + } + + if (needThrottle){ + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + listener.update(); } @@ -146,8 +174,9 @@ public class AwtPanelsContext implements JmeContext { } public void create(boolean waitFor) { - if (actualContext != null) + if (actualContext != null){ throw new IllegalStateException("Already created"); + } actualContext = JmeSystem.newContext(settings, Type.OffscreenSurface); actualContext.setSystemListener(new AwtPanelsListener()); diff --git a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java index ef2acbfd6..2ceeb55e6 100644 --- a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -1633,15 +1633,16 @@ public class LwjglRenderer implements Renderer { assert fb.getId() >= 0; assert context.boundFBO == fb.getId(); + lastFb = fb; - } - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); - printRealFrameBufferInfo(fb); - throw ex; + + try { + checkFrameBufferError(); + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); + printRealFrameBufferInfo(fb); + throw ex; + } } } diff --git a/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java index 4bec7992b..43095fd74 100644 --- a/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java +++ b/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java @@ -114,17 +114,12 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable { pbuffer.destroy(); try{ pbuffer = new Pbuffer(width, height, pixelFormat, null); + pbuffer.makeCurrent(); }catch (LWJGLException ex){ listener.handleError("Failed to restore pbuffer content", ex); } } - try{ - pbuffer.makeCurrent(); - }catch (LWJGLException ex){ - listener.handleError( "Error occured while making pbuffer current", ex); - } - listener.update(); assert checkGLError(); diff --git a/engine/src/test/jme3test/awt/TestAwtPanels.java b/engine/src/test/jme3test/awt/TestAwtPanels.java index 8eac2073d..a49dcea40 100644 --- a/engine/src/test/jme3test/awt/TestAwtPanels.java +++ b/engine/src/test/jme3test/awt/TestAwtPanels.java @@ -8,6 +8,7 @@ import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; import com.jme3.system.awt.AwtPanel; import com.jme3.system.awt.AwtPanelsContext; +import com.jme3.system.awt.PaintMode; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; @@ -56,11 +57,11 @@ public class TestAwtPanels extends SimpleApplication { SwingUtilities.invokeLater(new Runnable(){ public void run(){ final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext(); - panel = ctx.createPanel(true); + panel = ctx.createPanel(PaintMode.Accelerated); panel.setPreferredSize(new Dimension(400, 300)); ctx.setInputSource(panel); - panel2 = ctx.createPanel(true); + panel2 = ctx.createPanel(PaintMode.Accelerated); panel2.setPreferredSize(new Dimension(400, 300)); createWindowForPanel(panel, 300);