Skip to content

Commit 2b86611

Browse files
authored
Separate concept of window size from default framebuffer size (#1750)
* Separate concept of window size from default framebuffer size that are not always interchangeable in modern platforms. Add methods to query both sizes from AppSettings * Add rescaling support * Use different workaround that doesn't meddle with the initialization flow, for wrong FB size bug on first frames. * Ensure listener.rescale/resize is called only after the listener is initialized. Call rescale only when scale changes. Update scale and size when window size changes (if needed). * Fix documentation. Make SystemListener.rescale a default method * Fix typo and formatting
1 parent 6f66077 commit 2b86611

File tree

10 files changed

+168
-25
lines changed

10 files changed

+168
-25
lines changed

jme3-android/src/main/java/com/jme3/app/AndroidHarness.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,11 @@ public void reshape(int width, int height) {
494494
app.reshape(width, height);
495495
}
496496

497+
@Override
498+
public void rescale(float x, float y) {
499+
app.rescale(x, y);
500+
}
501+
497502
@Override
498503
public void update() {
499504
app.update();

jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,11 @@ public void reshape(int width, int height) {
571571
app.reshape(width, height);
572572
}
573573

574+
@Override
575+
public void rescale(float x, float y) {
576+
app.rescale(x, y);
577+
}
578+
574579
@Override
575580
public void update() {
576581
app.update();

jme3-android/src/main/java/com/jme3/app/jmeSurfaceView/JmeSurfaceView.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,17 @@ public void reshape(int width, int height) {
395395
jmeSurfaceViewLogger.log(Level.INFO, "Requested reshaping from the system listener");
396396
}
397397

398+
399+
@Override
400+
public void rescale(float x, float y) {
401+
if (legacyApplication == null) {
402+
return;
403+
}
404+
legacyApplication.rescale(x, y);
405+
jmeSurfaceViewLogger.log(Level.INFO, "Requested rescaling from the system listener");
406+
}
407+
408+
398409
@Override
399410
public void update() {
400411
/*Invoking can be delayed by delaying the draw of GlSurfaceView component on the screen*/

jme3-core/src/main/java/com/jme3/app/LegacyApplication.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,14 @@ public void reshape(int w, int h) {
578578
}
579579
}
580580

581+
582+
@Override
583+
public void rescale(float x, float y){
584+
if (renderManager != null) {
585+
renderManager.notifyRescale(x, y);
586+
}
587+
}
588+
581589
/**
582590
* Restarts the context, applying any changed settings.
583591
* <p>

jme3-core/src/main/java/com/jme3/post/SceneProcessor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ public interface SceneProcessor {
6262
*/
6363
public void reshape(ViewPort vp, int w, int h);
6464

65+
/**
66+
* Called when the scale of the viewport has been changed.
67+
*
68+
* @param vp the affected ViewPort
69+
* @param x the new horizontal scale
70+
* @param y the new vertical scale
71+
*/
72+
public default void rescale(ViewPort vp, float x, float y) {
73+
74+
}
75+
6576
/**
6677
* @return True if initialize() has been called on this SceneProcessor,
6778
* false if otherwise.

jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,17 @@ private void notifyReshape(ViewPort vp, int w, int h) {
342342
}
343343
}
344344

345+
private void notifyRescale(ViewPort vp, float x, float y) {
346+
List<SceneProcessor> processors = vp.getProcessors();
347+
for (SceneProcessor proc : processors) {
348+
if (!proc.isInitialized()) {
349+
proc.initialize(this, vp);
350+
} else {
351+
proc.rescale(vp, x, y);
352+
}
353+
}
354+
}
355+
345356
/**
346357
* Internal use only.
347358
* Updates the resolution of all on-screen cameras to match
@@ -374,6 +385,25 @@ public void notifyReshape(int w, int h) {
374385
}
375386
}
376387

388+
/**
389+
* Internal use only.
390+
* Updates the scale of all on-screen ViewPorts
391+
*
392+
* @param x the new horizontal scale
393+
* @param y the new vertical scale
394+
*/
395+
public void notifyRescale(float x, float y) {
396+
for (ViewPort vp : preViewPorts) {
397+
notifyRescale(vp, x, y);
398+
}
399+
for (ViewPort vp : viewPorts) {
400+
notifyRescale(vp, x, y);
401+
}
402+
for (ViewPort vp : postViewPorts) {
403+
notifyRescale(vp, x, y);
404+
}
405+
}
406+
377407
/**
378408
* Sets the material to use to render all future objects.
379409
* This overrides the material set on the geometry and renders

jme3-core/src/main/java/com/jme3/system/AppSettings.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ public final class AppSettings extends HashMap<String, Object> {
269269
defaults.put("CenterWindow", true);
270270
defaults.put("Width", 640);
271271
defaults.put("Height", 480);
272+
defaults.put("WindowWidth", Integer.MIN_VALUE);
273+
defaults.put("WindowHeight", Integer.MIN_VALUE);
272274
defaults.put("BitsPerPixel", 24);
273275
defaults.put("Frequency", 60);
274276
defaults.put("DepthBits", 24);
@@ -735,23 +737,24 @@ public void setAudioRenderer(String audioRenderer) {
735737
}
736738

737739
/**
738-
* @param value the width for the rendering display.
740+
* @param value the width for the default framebuffer.
739741
* (Default: 640)
740742
*/
741743
public void setWidth(int value) {
742744
putInteger("Width", value);
743745
}
744746

745747
/**
746-
* @param value the height for the rendering display.
748+
* @param value the height for the default framebuffer.
747749
* (Default: 480)
748750
*/
749751
public void setHeight(int value) {
750752
putInteger("Height", value);
751753
}
752754

753755
/**
754-
* Set the resolution for the rendering display
756+
* Set the resolution for the default framebuffer
757+
* Use {@link #setWindowSize(int, int)} instead, for HiDPI display support.
755758
* @param width The width
756759
* @param height The height
757760
* (Default: 640x480)
@@ -761,6 +764,16 @@ public void setResolution(int width, int height) {
761764
setHeight(height);
762765
}
763766

767+
/**
768+
* Set the size of the window
769+
*
770+
* @param width The width in pixels (default = width of the default framebuffer)
771+
* @param height The height in pixels (default = height of the default framebuffer)
772+
*/
773+
public void setWindowSize(int width, int height) {
774+
putInteger("WindowWidth", width);
775+
putInteger("WindowHeight", height);
776+
}
764777

765778
/**
766779
* @param value the minimum width the settings window will allow for the rendering display.
@@ -991,7 +1004,7 @@ public String getRenderer() {
9911004
/**
9921005
* Get the width
9931006
*
994-
* @return the width of the rendering display (in pixels)
1007+
* @return the width of the default framebuffer (in pixels)
9951008
* @see #setWidth(int)
9961009
*/
9971010
public int getWidth() {
@@ -1001,13 +1014,35 @@ public int getWidth() {
10011014
/**
10021015
* Get the height
10031016
*
1004-
* @return the height of the rendering display (in pixels)
1017+
* @return the height of the default framebuffer (in pixels)
10051018
* @see #setHeight(int)
10061019
*/
10071020
public int getHeight() {
10081021
return getInteger("Height");
10091022
}
10101023

1024+
/**
1025+
* Get the width of the window
1026+
*
1027+
* @return the width of the window (in pixels)
1028+
* @see #setWindowWidth(int)
1029+
*/
1030+
public int getWindowWidth() {
1031+
int w = getInteger("WindowWidth");
1032+
return w != Integer.MIN_VALUE ? w : getWidth();
1033+
}
1034+
1035+
/**
1036+
* Get the height of the window
1037+
*
1038+
* @return the height of the window (in pixels)
1039+
* @see #setWindowHeight(int)
1040+
*/
1041+
public int getWindowHeight() {
1042+
int h = getInteger("WindowHeight");
1043+
return h != Integer.MIN_VALUE ? h : getHeight();
1044+
}
1045+
10111046
/**
10121047
* Get the width
10131048
*

jme3-core/src/main/java/com/jme3/system/SystemListener.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ public interface SystemListener {
5151
*/
5252
public void reshape(int width, int height);
5353

54+
/**
55+
* Called to notify the application that the scale has changed.
56+
* @param x the new horizontal scale of the display
57+
* @param y the new vertical scale of the display
58+
*/
59+
public default void rescale(float x, float y){
60+
61+
}
62+
63+
5464
/**
5565
* Callback to update the application state, and render the scene
5666
* to the back buffer.

jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanelsContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ public void reshape(int width, int height) {
6767
throw new IllegalStateException();
6868
}
6969

70+
@Override
71+
public void rescale(float x, float y) {
72+
throw new IllegalStateException();
73+
}
74+
7075
@Override
7176
public void update() {
7277
updateInThread();

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

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable {
151151
// temp variables used for glfw calls
152152
private int width[] = new int[1];
153153
private int height[] = new int[1];
154-
154+
private final Vector2f currentScale = new Vector2f(1, 1);
155+
155156
public LwjglWindow(final JmeContext.Type type) {
156157

157158
if (!SUPPORTED_TYPES.contains(type)) {
@@ -281,11 +282,16 @@ public void invoke(int error, long description) {
281282

282283
final GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
283284

284-
if (settings.getWidth() <= 0 || settings.getHeight() <= 0) {
285-
settings.setResolution(videoMode.width(), videoMode.height());
285+
if (settings.getWindowWidth() <= 0 || settings.getWindowHeight() <= 0) {
286+
settings.setWindowSize(videoMode.width(), videoMode.height());
287+
} else {
288+
settings.setWindowSize(settings.getWindowWidth(), settings.getWindowHeight());
286289
}
287290

288-
window = glfwCreateWindow(settings.getWidth(), settings.getHeight(), settings.getTitle(), monitor, NULL);
291+
// Assume default framebuffer size == window size
292+
settings.setResolution(settings.getWindowWidth(), settings.getWindowHeight());
293+
294+
window = glfwCreateWindow(settings.getWindowWidth(), settings.getWindowHeight(), settings.getTitle(), monitor, NULL);
289295

290296
if (window == NULL) {
291297
throw new RuntimeException("Failed to create the GLFW window");
@@ -333,6 +339,10 @@ public void invoke(final long window, final boolean focus) {
333339
setWindowIcon(settings);
334340
showWindow();
335341

342+
// HACK: the framebuffer seems to be initialized with the wrong size
343+
// on some HiDPI platforms until glfwPollEvents is called 2 or 3 times
344+
for (int i = 0; i < 4; i++) glfwPollEvents();
345+
336346
// Windows resize callback
337347
glfwSetWindowSizeCallback(window, windowSizeCallback = new GLFWWindowSizeCallback() {
338348

@@ -347,6 +357,7 @@ public void invoke(final long window, final int width, final int height) {
347357
for (WindowSizeListener listener : windowSizeListeners.getArray()) {
348358
listener.onWindowSizeChanged(width, height);
349359
}
360+
updateDefaultFramebufferSize(true);
350361
}
351362
});
352363

@@ -355,8 +366,7 @@ public void invoke(final long window, final int width, final int height) {
355366

356367
@Override
357368
public void invoke(final long window, final int width, final int height) {
358-
// https://www.glfw.org/docs/latest/window_guide.html#window_fbsize
359-
listener.reshape(width, height);
369+
updateDefaultFramebufferSize(true);
360370
}
361371
});
362372

@@ -367,7 +377,31 @@ public void invoke(final long window, final int width, final int height) {
367377
initOpenCL(window);
368378
}
369379

370-
framesAfterContextStarted = 0;
380+
381+
updateDefaultFramebufferSize(false);
382+
383+
}
384+
385+
386+
private void updateDefaultFramebufferSize(boolean updateListener) {
387+
// If default framebuffer size is different than window size (e.g. HiDPI)
388+
int[] width = new int[1];
389+
int[] height = new int[1];
390+
glfwGetFramebufferSize(window, width, height);
391+
392+
Vector2f scale = getWindowContentScale(null);
393+
394+
if (updateListener) {
395+
if (settings.getWidth() != width[0] || settings.getHeight() != height[0]) {
396+
listener.reshape(width[0], height[0]);
397+
}
398+
if(!scale.equals(currentScale)){
399+
listener.rescale(scale.x, scale.y);
400+
currentScale.set(scale);
401+
}
402+
403+
}
404+
settings.setResolution(width[0], height[0]);
371405
}
372406

373407
private void onWindowSizeChanged(final int width, final int height) {
@@ -568,10 +602,11 @@ protected boolean initInThread() {
568602
}
569603

570604
listener.initialize();
605+
updateDefaultFramebufferSize(true);
606+
571607
return true;
572608
}
573609

574-
private int framesAfterContextStarted = 0;
575610

576611
/**
577612
* execute one iteration of the render loop in the OpenGL thread
@@ -586,18 +621,6 @@ protected void runLoop() {
586621
throw new IllegalStateException();
587622
}
588623

589-
// Update the frame buffer size from 2nd frame since the initial value
590-
// of frame buffer size from glfw maybe incorrect when HiDPI display is in use
591-
if (framesAfterContextStarted < 2) {
592-
framesAfterContextStarted++;
593-
if (framesAfterContextStarted == 2) {
594-
glfwGetFramebufferSize(window, width, height);
595-
596-
if (settings.getWidth() != width[0] || settings.getHeight() != height[0]) {
597-
listener.reshape(width[0], height[0]);
598-
}
599-
}
600-
}
601624

602625
listener.update();
603626

0 commit comments

Comments
 (0)