Parcourir la source

Lwjgl3 canvas (#1868)

* Fix offscreen rendering by not triggering unnecessary resize

* Not title

* Better javadoc

* Modernize the code a bit

* Modernize the code a bit

* Don't init GLWF controllers (same as LWJGL 2)

* Finals and interfaces

* Java2D graphics based offscreen rendering Canvas solution

* Import desktop
Toni Helenius il y a 1 an
Parent
commit
ec9c8c5efc

+ 6 - 3
jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java

@@ -363,7 +363,8 @@ public class FrameBuffer extends NativeObject {
      *
      * @param format The format to use for the depth buffer.
      * @throws IllegalArgumentException If <code>format</code> is not a depth format.
-     * @deprecated Use setDepthTarget
+     * @deprecated Use
+     * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferBufferTarget)}
      */
     @Deprecated
     public void setDepthBuffer(Image.Format format) {
@@ -656,7 +657,8 @@ public class FrameBuffer extends NativeObject {
      * Set the depth texture to use for this framebuffer.
      *
      * @param tex The color texture to set.
-     * @deprecated Use setDepthTarget
+     * @deprecated Use
+     * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)}
      */
     @Deprecated
     public void setDepthTexture(Texture2D tex) {
@@ -677,7 +679,8 @@ public class FrameBuffer extends NativeObject {
      * 
      * @param tex the TextureArray to apply
      * @param layer (default=-1)
-     * @deprecated Use setDepthTarget
+     * @deprecated Use
+     * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)}
      */
     @Deprecated
     public void setDepthTexture(TextureArray tex, int layer) {

+ 4 - 5
jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java

@@ -31,18 +31,17 @@
  */
 package com.jme3.input;
 
+import com.jme3.input.event.KeyInputEvent;
 import java.awt.Component;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 
-import com.jme3.input.KeyInput;
-import com.jme3.input.event.KeyInputEvent;
 import com.jme3.system.AWTContext;
 
-
 /**
  * The implementation of the {@link KeyInput} dedicated to AWT {@link Component component}.
  * <p>
@@ -166,11 +165,11 @@ public class AWTKeyInput extends AWTInput implements KeyInput, KeyListener{
         KEY_CODE_TO_JME.put(KeyEvent.VK_META, KEY_RCONTROL);
     }
 
-    private final LinkedList<KeyInputEvent> keyInputEvents;
+    private final Deque<KeyInputEvent> keyInputEvents;
 
     public AWTKeyInput(AWTContext context) {
         super(context);
-        keyInputEvents = new LinkedList<KeyInputEvent>();
+        keyInputEvents = new LinkedList<>();
     }
 
     @Override

+ 8 - 9
jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java

@@ -31,22 +31,21 @@
  */
 package com.jme3.input;
 
+import com.jme3.cursors.plugins.JmeCursor;
+import com.jme3.input.event.MouseButtonEvent;
+import com.jme3.input.event.MouseMotionEvent;
 import java.awt.Component;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 
-import com.jme3.cursors.plugins.JmeCursor;
-import com.jme3.input.MouseInput;
-import com.jme3.input.event.MouseButtonEvent;
-import com.jme3.input.event.MouseMotionEvent;
 import com.jme3.system.AWTContext;
-
 /**
  * The implementation of the {@link MouseInput} dedicated to AWT {@link Component component}.
  * <p>
@@ -70,9 +69,9 @@ public class AWTMouseInput extends AWTInput implements MouseInput, MouseListener
      */
     private static final int WHEEL_SCALE = 10;
 
-    private final LinkedList<MouseMotionEvent> mouseMotionEvents;
+    private final Deque<MouseMotionEvent> mouseMotionEvents;
 
-    private final LinkedList<MouseButtonEvent> mouseButtonEvents;
+    private final Deque<MouseButtonEvent> mouseButtonEvents;
 
     private int mouseX;
     private int mouseY;
@@ -80,8 +79,8 @@ public class AWTMouseInput extends AWTInput implements MouseInput, MouseListener
 
     public AWTMouseInput(AWTContext context) {
         super(context);
-        mouseMotionEvents = new LinkedList<MouseMotionEvent>();
-        mouseButtonEvents = new LinkedList<MouseButtonEvent>();
+        mouseMotionEvents = new LinkedList<>();
+        mouseButtonEvents = new LinkedList<>();
     }
 
     @Override

+ 3 - 2
jme3-desktop/src/main/java/com/jme3/input/awt/AwtKeyInput.java

@@ -39,6 +39,7 @@ import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -53,10 +54,10 @@ public class AwtKeyInput implements KeyInput, KeyListener {
 
     private static final Logger logger = Logger.getLogger(AwtKeyInput.class.getName());
     
-    private final ArrayList<KeyInputEvent> eventQueue = new ArrayList<>();
+    private final List<KeyInputEvent> eventQueue = new ArrayList<>();
     private RawInputListener listener;
     private Component component;
-    private BitSet keyStateSet = new BitSet(0xFF);
+    private final BitSet keyStateSet = new BitSet(0xFF);
     
     public AwtKeyInput(){
     }

+ 2 - 2
jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java

@@ -64,8 +64,8 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe
 
     private Component component;
 
-    private final ArrayList<MouseButtonEvent> eventQueue = new ArrayList<>();
-    private final ArrayList<MouseButtonEvent> eventQueueCopy = new ArrayList<>();
+    private final java.util.List<MouseButtonEvent> eventQueue = new ArrayList<>();
+    private final java.util.List<MouseButtonEvent> eventQueueCopy = new ArrayList<>();
 
     private int lastEventX;
     private int lastEventY;

+ 12 - 11
jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java

@@ -37,7 +37,6 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.texture.FrameBuffer;
-import com.jme3.texture.Image.Format;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.Screenshots;
 import java.awt.*;
@@ -57,6 +56,8 @@ import java.util.logging.Logger;
 
 public class AwtPanel extends Canvas implements SceneProcessor {
 
+    private static final Logger logger = Logger.getLogger(AwtPanel.class.getName());
+
     private boolean attachAsMain = false;
 
     private BufferedImage img;
@@ -66,19 +67,19 @@ public class AwtPanel extends Canvas implements SceneProcessor {
     private IntBuffer intBuf;
     private RenderManager rm;
     private PaintMode paintMode;
-    private ArrayList<ViewPort> viewPorts = new ArrayList<>();
+    private final java.util.List<ViewPort> viewPorts = new ArrayList<>();
 
     // Visibility/drawing vars
     private BufferStrategy strategy;
     private AffineTransformOp transformOp;
-    private AtomicBoolean hasNativePeer = new AtomicBoolean(false);
-    private AtomicBoolean showing = new AtomicBoolean(false);
-    private AtomicBoolean repaintRequest = new AtomicBoolean(false);
+    private final AtomicBoolean hasNativePeer = new AtomicBoolean(false);
+    private final AtomicBoolean showing = new AtomicBoolean(false);
+    private final AtomicBoolean repaintRequest = new AtomicBoolean(false);
 
     // Reshape vars
     private int newWidth = 1;
     private int newHeight = 1;
-    private AtomicBoolean reshapeNeeded = new AtomicBoolean(false);
+    private final AtomicBoolean reshapeNeeded = new AtomicBoolean(false);
     private final Object lock = new Object();
 
     public AwtPanel(PaintMode paintMode) {
@@ -180,7 +181,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
                                     BufferCapabilities.FlipContents.UNDEFINED)
                     );
                 } catch (AWTException ex) {
-                    ex.printStackTrace();
+                    logger.log(Level.WARNING, "Failed to create buffer strategy!", ex);
                 }
                 strategy = getBufferStrategy();
             }
@@ -190,7 +191,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
                 do {
                     Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
                     if (g2d == null) {
-                        Logger.getLogger(AwtPanel.class.getName()).log(Level.WARNING, "OGL: DrawGraphics was null.");
+                        logger.log(Level.WARNING, "OGL: DrawGraphics was null.");
                         return;
                     }
 
@@ -210,7 +211,7 @@ public class AwtPanel extends Canvas implements SceneProcessor {
     }
 
     public void attachTo(boolean overrideMainFramebuffer, ViewPort... vps) {
-        if (viewPorts.size() > 0) {
+        if (!viewPorts.isEmpty()) {
             for (ViewPort vp : viewPorts) {
                 vp.setOutputFrameBuffer(null);
             }
@@ -242,8 +243,8 @@ public class AwtPanel extends Canvas implements SceneProcessor {
         }
 
         fb = new FrameBuffer(width, height, 1);
-        fb.setDepthBuffer(Format.Depth);
-        fb.setColorBuffer(Format.RGB8);
+        fb.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.Depth));
+        fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.RGB8));
         fb.setSrgb(srgb);
 
         if (attachAsMain) {

+ 1 - 0
jme3-lwjgl3/build.gradle

@@ -1,5 +1,6 @@
 dependencies {
     api project(':jme3-core')
+    api project(':jme3-desktop')
 
     api "org.lwjgl:lwjgl:${lwjgl3Version}"
     api "org.lwjgl:lwjgl-glfw:${lwjgl3Version}"

+ 301 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2009-2022 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.system.lwjgl;
+
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.awt.AwtKeyInput;
+import com.jme3.input.awt.AwtMouseInput;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.FrameBufferTarget;
+import com.jme3.texture.Image;
+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.Graphics;
+import java.awt.Graphics2D;
+import java.awt.ImageCapabilities;
+import java.awt.RenderingHints;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.geom.AffineTransform;
+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.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class LwjglCanvas extends LwjglWindow implements JmeCanvasContext, Runnable {
+
+    private static final Logger logger = Logger.getLogger(LwjglCanvas.class.getName());
+
+    private final Canvas canvas;
+
+    private BufferedImage img;
+    private FrameBuffer fb;
+
+    private ByteBuffer byteBuf;
+    private IntBuffer intBuf;
+
+    private BufferStrategy strategy;
+    private AffineTransformOp transformOp;
+    private final AtomicBoolean hasNativePeer = new AtomicBoolean(false);
+    private final AtomicBoolean showing = new AtomicBoolean(false);
+
+    private int width = 1;
+    private int height = 1;
+    private AtomicBoolean needResize = new AtomicBoolean(false);
+    private final Object lock = new Object();
+
+    private AwtKeyInput keyInput;
+    private AwtMouseInput mouseInput;
+
+    public LwjglCanvas() {
+        super(Type.Canvas);
+
+        canvas = new Canvas() {
+            @Override
+            public void paint(Graphics g) {
+                Graphics2D g2d = (Graphics2D) g;
+                synchronized (lock) {
+                    g2d.drawImage(img, transformOp, 0, 0);
+                }
+            }
+
+            @Override
+            public void addNotify() {
+                super.addNotify();
+
+                synchronized (lock) {
+                    hasNativePeer.set(true);
+                }
+
+                requestFocusInWindow();
+            }
+
+            @Override
+            public void removeNotify() {
+                synchronized (lock) {
+                    hasNativePeer.set(false);
+                }
+
+                super.removeNotify();
+            }
+        };
+        canvas.addComponentListener(new ComponentAdapter() {
+
+            @Override
+            public void componentResized(ComponentEvent e) {
+                synchronized (lock) {
+                    int newWidth = Math.max(canvas.getWidth(), 1);
+                    int newHeight = Math.max(canvas.getHeight(), 1);
+                    if (width != newWidth || height != newHeight) {
+                        width = newWidth;
+                        height = newHeight;
+                        needResize.set(true);
+                    }
+                }
+            }
+        });
+        canvas.setFocusable(true);
+        canvas.setIgnoreRepaint(true);
+    }
+
+    @Override
+    public Canvas getCanvas() {
+        return canvas;
+    }
+
+    @Override
+    protected void showWindow() {
+    }
+
+    @Override
+    protected void setWindowIcon(final AppSettings settings) {
+    }
+
+    @Override
+    public void setTitle(String title) {
+    }
+
+    @Override
+    public KeyInput getKeyInput() {
+        if (keyInput == null) {
+            keyInput = new AwtKeyInput();
+            keyInput.setInputSource(canvas);
+        }
+
+        return keyInput;
+    }
+
+    @Override
+    public MouseInput getMouseInput() {
+        if (mouseInput == null) {
+            mouseInput = new AwtMouseInput();
+            mouseInput.setInputSource(canvas);
+        }
+
+        return mouseInput;
+    }
+
+    public boolean checkVisibilityState() {
+        if (!hasNativePeer.get()) {
+            synchronized (lock) {
+                if (strategy != null) {
+                    strategy.dispose();
+                    strategy = null;
+                }
+            }
+            return false;
+        }
+
+        boolean currentShowing = canvas.isShowing();
+        showing.set(currentShowing);
+        return currentShowing;
+    }
+
+    @Override
+    protected void destroyContext() {
+        synchronized (lock) {
+            destroyFrameBuffer();
+            img = null;
+            byteBuf = null;
+            intBuf = null;
+        }
+
+        super.destroyContext();
+    }
+
+    @Override
+    protected void createContext(AppSettings settings) {
+        super.createContext(settings);
+
+        if (renderer != null) {
+            createFrameBuffer(width, height);
+        }
+    }
+
+    @Override
+    protected void runLoop() {
+        if (needResize.get()) {
+            needResize.set(false);
+            listener.reshape(width, height);
+            createFrameBuffer(width, height);
+        }
+
+        if (!checkVisibilityState()) {
+            return;
+        }
+
+        super.runLoop();
+
+        drawFrameInThread();
+    }
+
+    public void drawFrameInThread() {
+
+        // Convert screenshot
+        byteBuf.clear();
+        renderer.readFrameBuffer(fb, byteBuf);
+        Screenshots.convertScreenShot2(intBuf, img);
+
+        synchronized (lock) {
+            // All operations on strategy should be synchronized (?)
+            if (strategy == null) {
+                try {
+                    canvas.createBufferStrategy(1,
+                            new BufferCapabilities(
+                                    new ImageCapabilities(true),
+                                    new ImageCapabilities(true),
+                                    BufferCapabilities.FlipContents.UNDEFINED)
+                    );
+                } catch (AWTException ex) {
+                    logger.log(Level.SEVERE, "Failed to create buffer strategy!", ex);
+                }
+                strategy = canvas.getBufferStrategy();
+            }
+
+            // Draw screenshot
+            do {
+                do {
+                    Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
+                    if (g2d == null) {
+                        logger.log(Level.WARNING, "OGL: DrawGraphics was null.");
+                        return;
+                    }
+
+                    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
+                            RenderingHints.VALUE_RENDER_QUALITY);
+
+                    g2d.drawImage(img, transformOp, 0, 0);
+                    g2d.dispose();
+                    strategy.show();
+                } while (strategy.contentsRestored());
+            } while (strategy.contentsLost());
+        }
+    }
+
+    private void createFrameBuffer(int width, int height) {
+        byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
+        intBuf = byteBuf.asIntBuffer();
+
+        destroyFrameBuffer();
+
+        fb = new FrameBuffer(width, height, settings.getSamples());
+        fb.setDepthTarget(FrameBufferTarget.newTarget(Image.Format.Depth));
+        fb.addColorTarget(FrameBufferTarget.newTarget(Image.Format.RGB8));
+        fb.setSrgb(settings.isGammaCorrection());
+
+        renderer.setMainFrameBufferOverride(fb);
+
+        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
+
+        AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
+        tx.translate(0, -img.getHeight());
+        transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+    }
+
+    public void destroyFrameBuffer() {
+        if (fb != null) {
+            fb.dispose();
+            fb = null;
+        }
+    }
+}

+ 41 - 0
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java

@@ -31,6 +31,12 @@
  */
 package com.jme3.system.lwjgl;
 
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeContext;
 
@@ -39,6 +45,9 @@ import com.jme3.system.JmeContext;
  */
 public class LwjglOffscreenBuffer extends LwjglWindow {
 
+    private KeyInput keyInput;
+    private MouseInput mouseInput;
+
     public LwjglOffscreenBuffer() {
         super(JmeContext.Type.OffscreenSurface);
     }
@@ -50,4 +59,36 @@ public class LwjglOffscreenBuffer extends LwjglWindow {
     @Override
     protected void setWindowIcon(final AppSettings settings) {
     }
+
+    @Override
+    public void setTitle(String title) {
+    }
+
+    @Override
+    public MouseInput getMouseInput() {
+        if (mouseInput == null) {
+            mouseInput = new DummyMouseInput();
+        }
+
+        return mouseInput;
+    }
+
+    @Override
+    public KeyInput getKeyInput() {
+        if (keyInput == null) {
+            keyInput = new DummyKeyInput();
+        }
+
+        return keyInput;
+    }
+
+    @Override
+    public JoyInput getJoyInput() {
+        return null;
+    }
+
+    @Override
+    public TouchInput getTouchInput() {
+        return null;
+    }
 }

+ 12 - 12
jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java

@@ -46,15 +46,6 @@ import com.jme3.system.JmeSystem;
 import com.jme3.system.NanoTimer;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.SafeArrayList;
-import org.lwjgl.Version;
-import org.lwjgl.glfw.GLFWErrorCallback;
-import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
-import org.lwjgl.glfw.GLFWImage;
-import org.lwjgl.glfw.GLFWVidMode;
-import org.lwjgl.glfw.GLFWWindowFocusCallback;
-import org.lwjgl.glfw.GLFWWindowSizeCallback;
-import org.lwjgl.system.Platform;
-
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.nio.ByteBuffer;
@@ -64,10 +55,17 @@ import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
+import org.lwjgl.Version;
 import static org.lwjgl.glfw.GLFW.*;
+import org.lwjgl.glfw.GLFWErrorCallback;
+import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
+import org.lwjgl.glfw.GLFWImage;
+import org.lwjgl.glfw.GLFWVidMode;
+import org.lwjgl.glfw.GLFWWindowFocusCallback;
+import org.lwjgl.glfw.GLFWWindowSizeCallback;
 import static org.lwjgl.opengl.GL11.GL_FALSE;
 import static org.lwjgl.system.MemoryUtil.NULL;
+import org.lwjgl.system.Platform;
 
 /**
  * A wrapper class over the GLFW framework in LWJGL 3.
@@ -155,8 +153,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
     protected boolean allowSwapBuffers = false;
 
     // temp variables used for glfw calls
-    private int width[] = new int[1];
-    private int height[] = new int[1];
+    private final int width[] = new int[1];
+    private final int height[] = new int[1];
 
     // state maintained by updateSizes()
     private int oldFramebufferWidth;
@@ -297,6 +295,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
             requestWidth = videoMode.width();
             requestHeight = videoMode.height();
         }
+        oldFramebufferHeight = requestHeight;
+        oldFramebufferWidth = requestWidth;
         window = glfwCreateWindow(requestWidth, requestHeight, settings.getTitle(), monitor, NULL);
         if (window == NULL) {
             throw new RuntimeException("Failed to create the GLFW window");