3737import android .content .DialogInterface ;
3838import android .content .pm .ConfigurationInfo ;
3939import android .graphics .PixelFormat ;
40- import android .graphics .Rect ;
4140import android .opengl .GLSurfaceView ;
4241import android .os .Build ;
4342import android .text .InputType ;
4443import android .view .Gravity ;
45- import android .view .SurfaceHolder ;
46- import android .view .SurfaceView ;
4744import android .view .View ;
4845import android .view .ViewGroup .LayoutParams ;
4946import android .widget .EditText ;
5855import com .jme3 .input .controls .SoftTextDialogInputListener ;
5956import com .jme3 .input .dummy .DummyKeyInput ;
6057import com .jme3 .material .Material ;
58+ import com .jme3 .math .Vector2f ;
6159import com .jme3 .renderer .Caps ;
6260import com .jme3 .renderer .Camera ;
6361import com .jme3 .renderer .RenderManager ;
6866import com .jme3 .texture .FrameBuffer .FrameBufferTarget ;
6967import com .jme3 .texture .Image ;
7068import com .jme3 .texture .Image .Format ;
69+ import com .jme3 .texture .Texture ;
7170import com .jme3 .texture .Texture2D ;
7271import com .jme3 .texture .image .ColorSpace ;
7372import com .jme3 .ui .Picture ;
@@ -99,6 +98,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
9998 protected AndroidInputHandler androidInput ;
10099 protected long minFrameDuration = 0 ; // No FPS cap
101100 protected long lastUpdateTime = 0 ;
101+ private int logicalWidth = 1 ;
102+ private int logicalHeight = 1 ;
103+ private int framebufferWidth = 1 ;
104+ private int framebufferHeight = 1 ;
105+ private final Vector2f displayScale = new Vector2f (1f , 1f );
106+ private float appliedDisplayScaleMode = Float .NaN ;
102107 private Application application ;
103108 private Material blitMaterial ;
104109 private Picture blitGeometry ;
@@ -452,16 +457,62 @@ public void onSurfaceChanged(GL10 gl, int width, int height) {
452457 new Object [] { width , height }
453458 );
454459 }
455- // update the application settings with the new resolution
456- settings .setResolution (width , height );
457- // Reload settings in androidInput so the correct touch event scaling can be
458- // calculated in case the surface resolution is different than the view.
459- androidInput .loadSettings (settings );
460+ framebufferWidth = Math .max (width , 1 );
461+ framebufferHeight = Math .max (height , 1 );
462+ updateDisplayScaleMetrics ();
460463 // if the application has already been initialized (ie renderable is set)
461464 // then call reshape so the app can adjust to the new resolution.
462465 if (renderable .get ()) {
463466 logger .log (Level .FINE , "App already initialized, calling reshape" );
464- listener .reshape (width , height );
467+ listener .reshape (logicalWidth , logicalHeight , getRenderFramebufferWidth (), getRenderFramebufferHeight ());
468+ listener .rescale (displayScale .x , displayScale .y );
469+ }
470+ }
471+
472+ private float getAndroidDisplayDensity () {
473+ if (androidInput != null && androidInput .getView () != null
474+ && androidInput .getView ().getResources () != null ) {
475+ android .util .DisplayMetrics metrics = androidInput .getView ().getResources ().getDisplayMetrics ();
476+ if (metrics != null ) {
477+ if (metrics .density > 0f ) {
478+ return metrics .density ;
479+ }
480+ if (metrics .densityDpi > 0 ) {
481+ return metrics .densityDpi / 160f ;
482+ }
483+ }
484+ }
485+ return 1f ;
486+ }
487+
488+ private void updateDisplayScaleMetrics () {
489+ float density = DisplayScaleUtils .sanitizeScale (getAndroidDisplayDensity ());
490+ displayScale .set (density , density );
491+ appliedDisplayScaleMode = settings .getDisplayScaleMode ();
492+ if (DisplayScaleUtils .isNativePixelsMode (appliedDisplayScaleMode )) {
493+ logicalWidth = framebufferWidth ;
494+ logicalHeight = framebufferHeight ;
495+ } else {
496+ logicalWidth = Math .max (Math .round (framebufferWidth / density ), 1 );
497+ logicalHeight = Math .max (Math .round (framebufferHeight / density ), 1 );
498+ }
499+ settings .setResolution (logicalWidth , logicalHeight );
500+ // Reload settings in androidInput so the correct touch event scaling can be
501+ // calculated in case the surface resolution is different than the view.
502+ if (androidInput != null ) {
503+ androidInput .loadSettings (settings );
504+ }
505+ }
506+
507+ private void applyDisplayScaleModeIfNeeded () {
508+ if (Float .compare (settings .getDisplayScaleMode (), appliedDisplayScaleMode ) == 0 ) {
509+ return ;
510+ }
511+
512+ updateDisplayScaleMetrics ();
513+ if (renderable .get ()) {
514+ listener .reshape (logicalWidth , logicalHeight , getRenderFramebufferWidth (), getRenderFramebufferHeight ());
515+ listener .rescale (displayScale .x , displayScale .y );
465516 }
466517 }
467518
@@ -475,15 +526,21 @@ public void onDrawFrame(GL10 gl) {
475526
476527 if (!renderable .get ()) {
477528 if (created .get ()) {
529+ applyDisplayScaleModeIfNeeded ();
478530 logger .fine ("GL Surface is setup, initializing application" );
479531 listener .initialize ();
532+ if (framebufferWidth > 0 && framebufferHeight > 0 ) {
533+ listener .reshape (logicalWidth , logicalHeight , getRenderFramebufferWidth (), getRenderFramebufferHeight ());
534+ listener .rescale (displayScale .x , displayScale .y );
535+ }
480536 renderable .set (true );
481537 }
482538 } else {
483539 if (!created .get ()) {
484540 throw new IllegalStateException ("onDrawFrame without create" );
485541 }
486542
543+ applyDisplayScaleModeIfNeeded ();
487544 if (!renderFrameWithBlitSrgbConversion ()) {
488545 listener .update ();
489546 }
@@ -549,12 +606,43 @@ private boolean useBlitSrgbConversion() {
549606 return settings .isGammaCorrection () && application != null ;
550607 }
551608
609+ private boolean useBlitFrameBuffer () {
610+ float mode = settings .getDisplayScaleMode ();
611+ return application != null && (useBlitSrgbConversion ()
612+ || DisplayScaleUtils .isDisabledMode (mode ) || DisplayScaleUtils .isEmulatedScaleMode (mode ));
613+ }
614+
615+ private int getRenderFramebufferWidth () {
616+ float mode = settings .getDisplayScaleMode ();
617+ if (DisplayScaleUtils .isDisabledMode (mode )) {
618+ return Math .max (logicalWidth , 1 );
619+ }
620+ if (DisplayScaleUtils .isEmulatedScaleMode (mode )) {
621+ return Math .max (Math .round (framebufferWidth * mode ), 1 );
622+ }
623+ return Math .max (framebufferWidth , 1 );
624+ }
625+
626+ private int getRenderFramebufferHeight () {
627+ float mode = settings .getDisplayScaleMode ();
628+ if (DisplayScaleUtils .isDisabledMode (mode )) {
629+ return Math .max (logicalHeight , 1 );
630+ }
631+ if (DisplayScaleUtils .isEmulatedScaleMode (mode )) {
632+ return Math .max (Math .round (framebufferHeight * mode ), 1 );
633+ }
634+ return Math .max (framebufferHeight , 1 );
635+ }
636+
552637 private int getLinearFrameBufferSampleCount () {
553638 int samples = Math .max (settings .getSamples (), 1 );
554- if (samples > 1 && renderer != null && !renderer .getCaps ().contains (Caps .TextureMultisample )) {
639+ if (samples > 1 && renderer != null
640+ && (!renderer .getCaps ().contains (Caps .TextureMultisample )
641+ || !renderer .getCaps ().contains (Caps .OpenGL32 ))) {
555642 if (!multisampleTextureWarningIssued ) {
556- logger .warning ("sRGB blit conversion requires multisampled textures for MSAA. "
557- + "Falling back to a single-sample linear framebuffer." );
643+ logger .log (Level .WARNING ,
644+ "Display scale blit requested {0}x MSAA, but this backend cannot sample multisample textures for the blit path. Falling back to a single-sample linear framebuffer." ,
645+ samples );
558646 multisampleTextureWarningIssued = true ;
559647 }
560648 return 1 ;
@@ -563,13 +651,13 @@ private int getLinearFrameBufferSampleCount() {
563651 }
564652
565653 private void rebuildLinearFrameBufferIfNeeded () {
566- if (!useBlitSrgbConversion ()) {
654+ if (!useBlitFrameBuffer ()) {
567655 destroyLinearFrameBufferResources ();
568656 return ;
569657 }
570658
571- int width = Math . max ( settings . getWidth (), 1 );
572- int height = Math . max ( settings . getHeight (), 1 );
659+ int width = getRenderFramebufferWidth ( );
660+ int height = getRenderFramebufferHeight ( );
573661 int samples = getLinearFrameBufferSampleCount ();
574662
575663 if (linearFrameBuffer != null && linearFrameBuffer .getWidth () == width
@@ -585,6 +673,8 @@ private void rebuildLinearFrameBufferIfNeeded() {
585673
586674 Texture2D colorTexture = new Texture2D (
587675 new Image (getLinearFrameBufferColorFormat (), width , height , null , ColorSpace .Linear ));
676+ colorTexture .setMagFilter (Texture .MagFilter .Bilinear );
677+ colorTexture .setMinFilter (Texture .MinFilter .BilinearNoMipMaps );
588678 if (samples > 1 ) {
589679 colorTexture .getImage ().setMultiSamples (samples );
590680 }
@@ -610,7 +700,7 @@ private Format getLinearFrameBufferColorFormat() {
610700 }
611701
612702 private boolean ensureBlitResources () {
613- if (!useBlitSrgbConversion ()) {
703+ if (!useBlitFrameBuffer ()) {
614704 return false ;
615705 }
616706
@@ -622,10 +712,10 @@ private boolean ensureBlitResources() {
622712
623713 if (blitMaterial == null ) {
624714 blitMaterial = new Material (assetManager , BLIT_MATERIAL );
625- blitMaterial .setBoolean ("Srgb" , true );
626715 blitMaterial .getAdditionalRenderState ().setDepthTest (false );
627716 blitMaterial .getAdditionalRenderState ().setDepthWrite (false );
628717 }
718+ blitMaterial .setBoolean ("Srgb" , useBlitSrgbConversion ());
629719
630720 if (blitGeometry == null ) {
631721 blitGeometry = new Picture ("Linear to sRGB Blit" );
@@ -667,7 +757,7 @@ private void destroyLinearFrameBufferResources() {
667757 }
668758
669759 private boolean renderFrameWithBlitSrgbConversion () {
670- if (!useBlitSrgbConversion ()) {
760+ if (!useBlitFrameBuffer ()) {
671761 return false ;
672762 }
673763
@@ -699,8 +789,8 @@ private boolean renderFrameWithBlitSrgbConversion() {
699789 Camera previousCamera = renderManager .getCurrentCamera ();
700790 try {
701791 renderer .setFrameBuffer (null );
702- int blitWidth = linearFrameBuffer . getWidth ( );
703- int blitHeight = linearFrameBuffer . getHeight ( );
792+ int blitWidth = Math . max ( getFramebufferWidth (), 1 );
793+ int blitHeight = Math . max ( getFramebufferHeight (), 1 );
704794 if (blitCamera .getWidth () != blitWidth || blitCamera .getHeight () != blitHeight ) {
705795 blitCamera .resize (blitWidth , blitHeight , true );
706796 }
@@ -811,16 +901,9 @@ public void onClick(DialogInterface dialog, int whichButton) {
811901 );
812902 }
813903
814- /**
815- * Returns the height of the input surface.
816- *
817- * @return the height (in pixels)
818- */
819904 @ Override
820905 public int getFramebufferHeight () {
821- Rect rect = getSurfaceFrame ();
822- int result = rect .height ();
823- return result ;
906+ return framebufferHeight ;
824907 }
825908
826909 /**
@@ -830,9 +913,7 @@ public int getFramebufferHeight() {
830913 */
831914 @ Override
832915 public int getFramebufferWidth () {
833- Rect rect = getSurfaceFrame ();
834- int result = rect .width ();
835- return result ;
916+ return framebufferWidth ;
836917 }
837918
838919 /**
@@ -855,19 +936,6 @@ public int getWindowYPosition() {
855936 throw new UnsupportedOperationException ("not implemented yet" );
856937 }
857938
858- /**
859- * Retrieves the dimensions of the input surface. Note: do not modify the
860- * returned object.
861- *
862- * @return the dimensions (in pixels, left and top are 0)
863- */
864- private Rect getSurfaceFrame () {
865- SurfaceView view = (SurfaceView ) androidInput .getView ();
866- SurfaceHolder holder = view .getHolder ();
867- Rect result = holder .getSurfaceFrame ();
868- return result ;
869- }
870-
871939 @ Override
872940 public Displays getDisplays () {
873941 // TODO Auto-generated method stub
0 commit comments