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,11 @@ 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 );
102106 private Application application ;
103107 private Material blitMaterial ;
104108 private Picture blitGeometry ;
@@ -452,17 +456,45 @@ public void onSurfaceChanged(GL10 gl, int width, int height) {
452456 new Object [] { width , height }
453457 );
454458 }
455- // update the application settings with the new resolution
456- settings .setResolution (width , height );
459+ framebufferWidth = Math .max (width , 1 );
460+ framebufferHeight = Math .max (height , 1 );
461+ float density = getAndroidDisplayDensity ();
462+ displayScale .set (density , density );
463+ float mode = settings .getDisplayScaleMode ();
464+ if (mode == AppSettings .DISPLAY_SCALE_DPI_AWARE || mode > 0f ) {
465+ logicalWidth = Math .max (Math .round (framebufferWidth / density ), 1 );
466+ logicalHeight = Math .max (Math .round (framebufferHeight / density ), 1 );
467+ } else {
468+ logicalWidth = framebufferWidth ;
469+ logicalHeight = framebufferHeight ;
470+ }
471+ settings .setResolution (logicalWidth , logicalHeight );
457472 // Reload settings in androidInput so the correct touch event scaling can be
458473 // calculated in case the surface resolution is different than the view.
459474 androidInput .loadSettings (settings );
460475 // if the application has already been initialized (ie renderable is set)
461476 // then call reshape so the app can adjust to the new resolution.
462477 if (renderable .get ()) {
463478 logger .log (Level .FINE , "App already initialized, calling reshape" );
464- listener .reshape (width , height );
479+ listener .reshape (logicalWidth , logicalHeight , getRenderFramebufferWidth (), getRenderFramebufferHeight ());
480+ listener .rescale (displayScale .x , displayScale .y );
481+ }
482+ }
483+
484+ private float getAndroidDisplayDensity () {
485+ if (androidInput != null && androidInput .getView () != null
486+ && androidInput .getView ().getResources () != null ) {
487+ android .util .DisplayMetrics metrics = androidInput .getView ().getResources ().getDisplayMetrics ();
488+ if (metrics != null ) {
489+ if (metrics .density > 0f ) {
490+ return metrics .density ;
491+ }
492+ if (metrics .densityDpi > 0 ) {
493+ return metrics .densityDpi / 160f ;
494+ }
495+ }
465496 }
497+ return 1f ;
466498 }
467499
468500 // SystemListener:update
@@ -477,6 +509,10 @@ public void onDrawFrame(GL10 gl) {
477509 if (created .get ()) {
478510 logger .fine ("GL Surface is setup, initializing application" );
479511 listener .initialize ();
512+ if (framebufferWidth > 0 && framebufferHeight > 0 ) {
513+ listener .reshape (logicalWidth , logicalHeight , getRenderFramebufferWidth (), getRenderFramebufferHeight ());
514+ listener .rescale (displayScale .x , displayScale .y );
515+ }
480516 renderable .set (true );
481517 }
482518 } else {
@@ -549,12 +585,35 @@ private boolean useBlitSrgbConversion() {
549585 return settings .isGammaCorrection () && application != null ;
550586 }
551587
588+ private boolean useBlitFrameBuffer () {
589+ return application != null && (useBlitSrgbConversion () || settings .getDisplayScaleMode () > 0f );
590+ }
591+
592+ private int getRenderFramebufferWidth () {
593+ float mode = settings .getDisplayScaleMode ();
594+ if (mode > 0f ) {
595+ return Math .max (Math .round (framebufferWidth * mode ), 1 );
596+ }
597+ return Math .max (framebufferWidth , 1 );
598+ }
599+
600+ private int getRenderFramebufferHeight () {
601+ float mode = settings .getDisplayScaleMode ();
602+ if (mode > 0f ) {
603+ return Math .max (Math .round (framebufferHeight * mode ), 1 );
604+ }
605+ return Math .max (framebufferHeight , 1 );
606+ }
607+
552608 private int getLinearFrameBufferSampleCount () {
553609 int samples = Math .max (settings .getSamples (), 1 );
554- if (samples > 1 && renderer != null && !renderer .getCaps ().contains (Caps .TextureMultisample )) {
610+ if (samples > 1 && renderer != null
611+ && (!renderer .getCaps ().contains (Caps .TextureMultisample )
612+ || !renderer .getCaps ().contains (Caps .OpenGL32 ))) {
555613 if (!multisampleTextureWarningIssued ) {
556- logger .warning ("sRGB blit conversion requires multisampled textures for MSAA. "
557- + "Falling back to a single-sample linear framebuffer." );
614+ logger .log (Level .WARNING ,
615+ "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." ,
616+ samples );
558617 multisampleTextureWarningIssued = true ;
559618 }
560619 return 1 ;
@@ -563,13 +622,13 @@ private int getLinearFrameBufferSampleCount() {
563622 }
564623
565624 private void rebuildLinearFrameBufferIfNeeded () {
566- if (!useBlitSrgbConversion ()) {
625+ if (!useBlitFrameBuffer ()) {
567626 destroyLinearFrameBufferResources ();
568627 return ;
569628 }
570629
571- int width = Math . max ( settings . getWidth (), 1 );
572- int height = Math . max ( settings . getHeight (), 1 );
630+ int width = getRenderFramebufferWidth ( );
631+ int height = getRenderFramebufferHeight ( );
573632 int samples = getLinearFrameBufferSampleCount ();
574633
575634 if (linearFrameBuffer != null && linearFrameBuffer .getWidth () == width
@@ -585,6 +644,8 @@ private void rebuildLinearFrameBufferIfNeeded() {
585644
586645 Texture2D colorTexture = new Texture2D (
587646 new Image (getLinearFrameBufferColorFormat (), width , height , null , ColorSpace .Linear ));
647+ colorTexture .setMagFilter (Texture .MagFilter .Bilinear );
648+ colorTexture .setMinFilter (Texture .MinFilter .BilinearNoMipMaps );
588649 if (samples > 1 ) {
589650 colorTexture .getImage ().setMultiSamples (samples );
590651 }
@@ -610,7 +671,7 @@ private Format getLinearFrameBufferColorFormat() {
610671 }
611672
612673 private boolean ensureBlitResources () {
613- if (!useBlitSrgbConversion ()) {
674+ if (!useBlitFrameBuffer ()) {
614675 return false ;
615676 }
616677
@@ -622,10 +683,10 @@ private boolean ensureBlitResources() {
622683
623684 if (blitMaterial == null ) {
624685 blitMaterial = new Material (assetManager , BLIT_MATERIAL );
625- blitMaterial .setBoolean ("Srgb" , true );
626686 blitMaterial .getAdditionalRenderState ().setDepthTest (false );
627687 blitMaterial .getAdditionalRenderState ().setDepthWrite (false );
628688 }
689+ blitMaterial .setBoolean ("Srgb" , useBlitSrgbConversion ());
629690
630691 if (blitGeometry == null ) {
631692 blitGeometry = new Picture ("Linear to sRGB Blit" );
@@ -667,7 +728,7 @@ private void destroyLinearFrameBufferResources() {
667728 }
668729
669730 private boolean renderFrameWithBlitSrgbConversion () {
670- if (!useBlitSrgbConversion ()) {
731+ if (!useBlitFrameBuffer ()) {
671732 return false ;
672733 }
673734
@@ -699,8 +760,8 @@ private boolean renderFrameWithBlitSrgbConversion() {
699760 Camera previousCamera = renderManager .getCurrentCamera ();
700761 try {
701762 renderer .setFrameBuffer (null );
702- int blitWidth = linearFrameBuffer . getWidth ( );
703- int blitHeight = linearFrameBuffer . getHeight ( );
763+ int blitWidth = Math . max ( getFramebufferWidth (), 1 );
764+ int blitHeight = Math . max ( getFramebufferHeight (), 1 );
704765 if (blitCamera .getWidth () != blitWidth || blitCamera .getHeight () != blitHeight ) {
705766 blitCamera .resize (blitWidth , blitHeight , true );
706767 }
@@ -816,11 +877,19 @@ public void onClick(DialogInterface dialog, int whichButton) {
816877 *
817878 * @return the height (in pixels)
818879 */
880+ @ Override
881+ public int getLogicalHeight () {
882+ return logicalHeight ;
883+ }
884+
885+ @ Override
886+ public int getLogicalWidth () {
887+ return logicalWidth ;
888+ }
889+
819890 @ Override
820891 public int getFramebufferHeight () {
821- Rect rect = getSurfaceFrame ();
822- int result = rect .height ();
823- return result ;
892+ return framebufferHeight ;
824893 }
825894
826895 /**
@@ -830,9 +899,33 @@ public int getFramebufferHeight() {
830899 */
831900 @ Override
832901 public int getFramebufferWidth () {
833- Rect rect = getSurfaceFrame ();
834- int result = rect .width ();
835- return result ;
902+ return framebufferWidth ;
903+ }
904+
905+ @ Override
906+ public Vector2f getDisplayScale (Vector2f store ) {
907+ if (store == null ) {
908+ store = new Vector2f ();
909+ }
910+ return store .set (displayScale );
911+ }
912+
913+ @ Override
914+ public Vector2f getPixelDensity (Vector2f store ) {
915+ if (store == null ) {
916+ store = new Vector2f ();
917+ }
918+ return store .set ((float ) framebufferWidth / Math .max (logicalWidth , 1 ),
919+ (float ) framebufferHeight / Math .max (logicalHeight , 1 ));
920+ }
921+
922+ @ Override
923+ public Vector2f getInputScale (Vector2f store ) {
924+ if (store == null ) {
925+ store = new Vector2f ();
926+ }
927+ return store .set ((float ) logicalWidth / Math .max (framebufferWidth , 1 ),
928+ (float ) logicalHeight / Math .max (framebufferHeight , 1 ));
836929 }
837930
838931 /**
@@ -855,19 +948,6 @@ public int getWindowYPosition() {
855948 throw new UnsupportedOperationException ("not implemented yet" );
856949 }
857950
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-
871951 @ Override
872952 public Displays getDisplays () {
873953 // TODO Auto-generated method stub
0 commit comments