@ -1,7 +1,5 @@
package com.jme3.system.android ;
package com.jme3.system.android ;
import android.graphics.PixelFormat ;
import android.opengl.GLSurfaceView ;
import android.opengl.GLSurfaceView.EGLConfigChooser ;
import android.opengl.GLSurfaceView.EGLConfigChooser ;
import com.jme3.renderer.android.RendererUtil ;
import com.jme3.renderer.android.RendererUtil ;
import com.jme3.system.AppSettings ;
import com.jme3.system.AppSettings ;
@ -14,20 +12,16 @@ import javax.microedition.khronos.egl.EGLDisplay;
/ * *
/ * *
* AndroidConfigChooser is used to determine the best suited EGL Config
* AndroidConfigChooser is used to determine the best suited EGL Config
*
*
* @author larynx
* @author iwgeric
* /
* /
public class AndroidConfigChooser implements EGLConfigChooser {
public class AndroidConfigChooser implements EGLConfigChooser {
private static final Logger logger = Logger . getLogger ( AndroidConfigChooser . class . getName ( ) ) ;
private static final Logger logger = Logger . getLogger ( AndroidConfigChooser . class . getName ( ) ) ;
public final static String SETTINGS_CONFIG_TYPE = "configType" ;
protected EGLConfig choosenConfig = null ;
protected EGLDisplay configForDisplay = null ;
protected AppSettings settings ;
protected AppSettings settings ;
protected GLSurfaceView view ;
protected int pixelFormat ;
protected boolean verbose = false ;
private final static int EGL_OPENGL_ES2_BIT = 4 ;
private final static int EGL_OPENGL_ES2_BIT = 4 ;
@Deprecated
public enum ConfigType {
public enum ConfigType {
/ * *
/ * *
@ -75,161 +69,123 @@ public class AndroidConfigChooser implements EGLConfigChooser {
}
}
}
}
/ * *
*
* @param type
* @deprecated use AndroidConfigChooser ( AppSettings settings )
* /
@Deprecated
public AndroidConfigChooser ( ConfigType type ) {
this . settings = new AppSettings ( true ) ;
settings . put ( SETTINGS_CONFIG_TYPE , type ) ;
}
public AndroidConfigChooser ( AppSettings settings ) {
public AndroidConfigChooser ( AppSettings settings ) {
this . settings = settings ;
this . settings = settings ;
}
}
public AndroidConfigChooser ( AppSettings settings , GLSurfaceView view ) {
this ( settings ) ;
this . view = view ;
}
private static int eglGetConfigAttribSafe ( EGL10 egl , EGLDisplay display , EGLConfig config , int attribute ) {
int [ ] value = new int [ 1 ] ;
if ( ! egl . eglGetConfigAttrib ( display , config , attribute , value ) ) {
RendererUtil . checkEGLError ( egl ) ;
throw new AssertionError ( ) ;
}
return value [ 0 ] ;
}
/ * *
/ * *
* Gets called by the GLSurfaceView class to return the best config
* Gets called by the GLSurfaceView class to return the best config
* /
* /
@Override
@Override
public EGLConfig chooseConfig ( EGL10 egl , EGLDisplay display ) {
public EGLConfig chooseConfig ( EGL10 egl , EGLDisplay display ) {
logger . fine ( "GLSurfaceView asking for egl config" ) ;
logger . fine ( "GLSurfaceView asking for egl config" ) ;
boolean configFound = findConfig ( egl , display ) ;
Config requestedConfig = getRequestedConfig ( ) ;
if ( configFound ) {
EGLConfig [ ] configs = getConfigs ( egl , display ) ;
logger . fine ( "GLSurfaceView asks for egl config, returning: " ) ;
logEGLConfig ( choosenConfig , display , egl , Level . FINE ) ;
// First try to find an exact match, but allowing a higher stencil
view . getHolder ( ) . setFormat ( pixelFormat ) ;
EGLConfig choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , false , false , false , true ) ;
return choosenConfig ;
if ( choosenConfig = = null & & requestedConfig . d > 16 ) {
} else {
logger . log ( Level . INFO , "EGL configuration not found, reducing depth" ) ;
logger . fine ( "GLSurfaceView asks for egl config, No config found" ) ;
requestedConfig . d = 16 ;
return null ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , false , false , false , true ) ;
}
}
if ( choosenConfig = = null ) {
logger . log ( Level . INFO , "EGL configuration not found, allowing higher RGB" ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , false , false , true ) ;
}
}
/ * *
if ( choosenConfig = = null & & requestedConfig . a > 0 ) {
* findConfig is used to locate the best config and init the chooser with
logger . log ( Level . INFO , "EGL configuration not found, allowing higher alpha" ) ;
*
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , true , false , true ) ;
* @param egl
}
* @param display
* @return true if successfull , false if no config was found
* /
private boolean findConfig ( EGL10 egl , EGLDisplay display ) {
ConfigType type = ( ConfigType ) settings . get ( SETTINGS_CONFIG_TYPE ) ;
ComponentSizeChooser compChooser = new ComponentSizeChooser ( type , settings . getSamples ( ) ) ;
if ( choosenConfig = = null & & requestedConfig . s > 0 ) {
choosenConfig = compChooser . chooseConfig ( egl , display ) ;
logger . log ( Level . INFO , "EGL configuration not found, allowing higher samples" ) ;
logger . log ( Level . FINE , "JME3 using {0} EGL configuration available here: " , type . name ( ) ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , true , true , true ) ;
}
if ( choosenConfig ! = null ) {
if ( choosenConfig = = null & & requestedConfig . a > 0 ) {
// Remember the display for which we have the EGLConfig for
logger . log ( Level . INFO , "EGL configuration not found, reducing alpha" ) ;
configForDisplay = display ;
requestedConfig . a = 1 ;
logger . info ( "JME3 using choosen config: " ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , true , false , true ) ;
logEGLConfig ( choosenConfig , display , egl , Level . INFO ) ;
}
pixelFormat = getPixelFormat ( choosenConfig , display , egl ) ;
if ( pixelFormat = = PixelFormat . TRANSLUCENT ) {
if ( choosenConfig = = null & & requestedConfig . s > 0 ) {
view . setZOrderOnTop ( true ) ;
logger . log ( Level . INFO , "EGL configuration not found, reducing samples" ) ;
}
requestedConfig . s = 1 ;
return true ;
if ( requestedConfig . a > 0 ) {
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , true , true , true ) ;
} else {
} else {
logger . severe ( "ERROR: Unable to get a valid OpenGL ES 2.0 config, neither Fastest nor Best found! Bug. Please report this." ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , false , true , true ) ;
pixelFormat = PixelFormat . UNKNOWN ;
return false ;
}
}
}
}
private int getPixelFormat ( EGLConfig conf , EGLDisplay display , EGL10 egl ) {
if ( choosenConfig = = null & & requestedConfig . getBitsPerPixel ( ) > 16 ) {
//Android Pixel format is not very well documented.
logger . log ( Level . INFO , "EGL configuration not found, setting to RGB565" ) ;
//From what i gathered, the format is chosen automatically except for the alpha channel
requestedConfig . r = 5 ;
//if the alpha channel has 8 bit or more, e set the pixel format to Transluscent, as it allow transparent view background
requestedConfig . g = 6 ;
//if it's 0 bit, the format is OPAQUE otherwise it's TRANSPARENT
requestedConfig . b = 5 ;
int result = eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_ALPHA_SIZE ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , false , false , true ) ;
if ( result > = 8 ) {
if ( choosenConfig = = null ) {
logger . log ( Level . FINE , "Pixel Format: TRANSLUCENT ") ;
logger . log ( Level . INFO , "EGL configuration not found, allowing higher alpha ") ;
return PixelFormat . TRANSLUCENT ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , true , false , true ) ;
}
}
if ( result > = 1 ) {
logger . log ( Level . FINE , "Pixel Format: TRANSPARENT" ) ;
return PixelFormat . TRANSPARENT ;
}
}
logger . log ( Level . FINE , "Pixel Format: OPAQUE" ) ;
if ( choosenConfig = = null ) {
return PixelFormat . OPAQUE ;
logger . log ( Level . INFO , "EGL configuration not found, looking for best config with >= 16 bit Depth" ) ;
//failsafe, should pick best config with at least 16 depth
requestedConfig = new Config ( 0 , 0 , 0 , 0 , 16 , 0 , 0 ) ;
choosenConfig = chooseConfig ( egl , display , configs , requestedConfig , true , false , false , true ) ;
}
}
private int getOpenGLVersion ( EGLConfig conf , EGLDisplay display , EGL10 egl ) {
if ( choosenConfig ! = null ) {
int val = eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_RENDERABLE_TYPE ) ;
logger . fine ( "GLSurfaceView asks for egl config, returning: " ) ;
// Check if conf is OpenGL ES 2.0
logEGLConfig ( choosenConfig , display , egl , Level . FINE ) ;
if ( ( val & EGL_OPENGL_ES2_BIT ) ! = 0 ) {
return 2 ;
storeSelectedConfig ( egl , display , choosenConfig ) ;
return choosenConfig ;
} else {
} else {
return 1 ;
logger . severe ( "No EGL Config found" ) ;
return null ;
}
}
}
}
private Config getRequestedConfig ( ) {
int r , g , b ;
if ( settings . getBitsPerPixel ( ) = = 24 ) {
r = g = b = 8 ;
} else {
if ( settings . getBitsPerPixel ( ) ! = 16 ) {
logger . log ( Level . SEVERE , "Invalid bitsPerPixel setting: {0}, setting to RGB565 (16)" , settings . getBitsPerPixel ( ) ) ;
settings . setBitsPerPixel ( 16 ) ;
}
r = 5 ;
g = 6 ;
b = 5 ;
}
logger . log ( Level . FINE , "Requested Display Config:" ) ;
logger . log ( Level . FINE , "RGB: {0}, alpha: {1}, depth: {2}, samples: {3}, stencil: {4}" ,
new Object [ ] { settings . getBitsPerPixel ( ) ,
settings . getAlphaBits ( ) , settings . getDepthBits ( ) ,
settings . getSamples ( ) , settings . getStencilBits ( ) } ) ;
return new Config (
r , g , b ,
settings . getAlphaBits ( ) ,
settings . getDepthBits ( ) ,
settings . getSamples ( ) ,
settings . getStencilBits ( ) ) ;
}
/ * *
/ * *
* log output with egl config details
* Query egl for the available configs
*
* @param conf
* @param display
* @param egl
* @param egl
* @param display
* @return
* /
* /
private void logEGLConfig ( EGLConfig conf , EGLDisplay display , EGL10 egl , Level level ) {
private EGLConfig [ ] getConfigs ( EGL10 egl , EGLDisplay display ) {
logger . log ( level , "EGL_RED_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_RED_SIZE ) ) ;
logger . log ( level , "EGL_GREEN_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_GREEN_SIZE ) ) ;
logger . log ( level , "EGL_BLUE_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_BLUE_SIZE ) ) ;
logger . log ( level , "EGL_ALPHA_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_ALPHA_SIZE ) ) ;
logger . log ( level , "EGL_DEPTH_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_DEPTH_SIZE ) ) ;
logger . log ( level , "EGL_STENCIL_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_STENCIL_SIZE ) ) ;
logger . log ( level , "EGL_RENDERABLE_TYPE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_RENDERABLE_TYPE ) ) ;
logger . log ( level , "EGL_SURFACE_TYPE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SURFACE_TYPE ) ) ;
logger . log ( level , "EGL_SAMPLE_BUFFERS = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SAMPLE_BUFFERS ) ) ;
logger . log ( level , "EGL_SAMPLES = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SAMPLES ) ) ;
}
private abstract class BaseConfigChooser implements EGLConfigChooser {
public BaseConfigChooser ( ) {
}
@Override
public EGLConfig chooseConfig ( EGL10 egl , EGLDisplay display ) {
int [ ] num_config = new int [ 1 ] ;
int [ ] num_config = new int [ 1 ] ;
int [ ] configSpec = new int [ ] {
int [ ] configSpec = new int [ ] {
@ -254,34 +210,22 @@ public class AndroidConfigChooser implements EGLConfigChooser {
logger . fine ( "----------------------------------------" ) ;
logger . fine ( "----------------------------------------" ) ;
}
}
EGLConfig config = chooseConfig ( egl , display , configs ) ;
return configs ;
return config ;
}
abstract EGLConfig chooseConfig ( EGL10 egl , EGLDisplay display ,
EGLConfig [ ] configs ) ;
}
/ * *
* Choose a configuration with exactly the specified r , g , b , a sizes , and at
* least the specified depth and stencil sizes .
* /
private class ComponentSizeChooser extends BaseConfigChooser {
private ConfigType configType ;
protected int mSamples ;
public ComponentSizeChooser ( ConfigType configType , int samples ) {
mSamples = samples ;
this . configType = configType ;
}
}
@Override
private EGLConfig chooseConfig (
public EGLConfig chooseConfig ( EGL10 egl , EGLDisplay display , EGLConfig [ ] configs ) {
EGL10 egl , EGLDisplay display , EGLConfig [ ] configs , Config requestedConfig ,
boolean higherRGB , boolean higherAlpha ,
boolean higherSamples , boolean higherStencil ) {
EGLConfig keptConfig = null ;
EGLConfig keptConfig = null ;
int kr = 0 ;
int kg = 0 ;
int kb = 0 ;
int ka = 0 ;
int kd = 0 ;
int kd = 0 ;
int knbMs = 0 ;
int ks = 0 ;
int kst = 0 ;
// first pass through config list. Try to find an exact match.
// first pass through config list. Try to find an exact match.
@ -297,87 +241,144 @@ public class AndroidConfigChooser implements EGLConfigChooser {
int d = eglGetConfigAttribSafe ( egl , display , config ,
int d = eglGetConfigAttribSafe ( egl , display , config ,
EGL10 . EGL_DEPTH_SIZE ) ;
EGL10 . EGL_DEPTH_SIZE ) ;
int s = eglGetConfigAttribSafe ( egl , display , config ,
int s = eglGetConfigAttribSafe ( egl , display , config ,
EGL10 . EGL_STENCIL_SIZE ) ;
int isMs = eglGetConfigAttribSafe ( egl , display , config ,
EGL10 . EGL_SAMPLE_BUFFERS ) ;
int nbMs = eglGetConfigAttribSafe ( egl , display , config ,
EGL10 . EGL_SAMPLES ) ;
EGL10 . EGL_SAMPLES ) ;
int st = eglGetConfigAttribSafe ( egl , display , config ,
EGL10 . EGL_STENCIL_SIZE ) ;
if ( inRange ( r , configType . mr , configType . r )
logger . log ( Level . FINE , "Checking Config r: {0}, g: {1}, b: {2}, alpha: {3}, depth: {4}, samples: {5}, stencil: {6}" ,
& & inRange ( g , configType . mg , configType . g )
new Object [ ] { r , g , b , a , d , s , st } ) ;
& & inRange ( b , configType . mb , configType . b )
& & inRange ( a , configType . ma , configType . a )
& & inRange ( d , configType . md , configType . d )
& & inRange ( s , configType . ms , configType . s ) ) {
if ( mSamples = = 0 & & isMs ! = 0 ) {
continue ;
}
boolean keep ;
//we keep the config if the depth is better or if the AA setting is better
if ( d > = kd ) {
kd = d ;
keep = true ;
} else {
keep = false ;
}
if ( mSamples ! = 0 ) {
if ( higherRGB & & r < requestedConfig . r ) { continue ; }
if ( nbMs > = knbMs & & nbMs < = mSamples ) {
if ( ! higherRGB & & r ! = requestedConfig . r ) { continue ; }
knbMs = nbMs ;
keep = true ;
if ( higherRGB & & g < requestedConfig . g ) { continue ; }
} else {
if ( ! higherRGB & & g ! = requestedConfig . g ) { continue ; }
keep = false ;
}
if ( higherRGB & & b < requestedConfig . b ) { continue ; }
}
if ( ! higherRGB & & b ! = requestedConfig . b ) { continue ; }
if ( higherAlpha & & a < requestedConfig . a ) { continue ; }
if ( ! higherAlpha & & a ! = requestedConfig . a ) { continue ; }
if ( d < requestedConfig . d ) { continue ; } // always allow higher depth
if ( higherSamples & & s < requestedConfig . s ) { continue ; }
if ( ! higherSamples & & s ! = requestedConfig . s ) { continue ; }
if ( keep ) {
if ( higherStencil & & st < requestedConfig . st ) { continue ; }
if ( ! higherStencil & & ! inRange ( st , 0 , requestedConfig . st ) ) { continue ; }
//we keep the config if it is better
if ( r > = kr | | g > = kg | | b > = kb | | a > = ka | |
d > = kd | | s > = ks | | st > = kst ) {
kr = r ; kg = g ; kb = b ; ka = a ;
kd = d ; ks = s ; kst = st ;
keptConfig = config ;
keptConfig = config ;
}
logger . log ( Level . FINE , "Keeping Config r: {0 }, g: {1}, b: {2}, alpha: {3}, depth: {4}, samples: {5}, stencil: {6}" ,
}
new Object [ ] { r , g , b , a , d , s , st } ) ;
}
}
if ( keptConfig ! = null ) {
return keptConfig ;
}
}
if ( configType = = ConfigType . BEST ) {
logger . log ( Level . WARNING , "Failed to find a suitable display configuration for BEST, attempting BEST_TRANSLUCENT" ) ;
configType = ConfigType . BEST_TRANSLUCENT ;
keptConfig = chooseConfig ( egl , display , configs ) ;
if ( keptConfig ! = null ) {
if ( keptConfig ! = null ) {
return keptConfig ;
return keptConfig ;
}
}
}
if ( configType = = ConfigType . BEST_TRANSLUCENT ) {
//no match found
logger . log ( Level . WARNING , "Failed to find a suitable display configuration for BEST_TRANSLUCENT, attempting FASTEST ") ;
logger . log ( Level . SEVERE , "No egl config match found ") ;
configType = ConfigType . FASTEST ;
return null ;
keptConfig = chooseConfig ( egl , display , configs ) ;
}
if ( keptConfig ! = null ) {
private static int eglGetConfigAttribSafe ( EGL10 egl , EGLDisplay display , EGLConfig config , int attribute ) {
return keptConfig ;
int [ ] value = new int [ 1 ] ;
if ( ! egl . eglGetConfigAttrib ( display , config , attribute , value ) ) {
RendererUtil . checkEGLError ( egl ) ;
throw new AssertionError ( ) ;
}
}
return value [ 0 ] ;
}
}
logger . log ( Level . WARNING , "Failed to find a suitable display configuration for FASTEST, hoping for the best..." ) ;
private void storeSelectedConfig ( EGL10 egl , EGLDisplay display , EGLConfig eglConfig ) {
int r = eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_RED_SIZE ) ;
int g = eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_GREEN_SIZE ) ;
int b = eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_BLUE_SIZE ) ;
settings . setBitsPerPixel ( r + g + b ) ;
// failsafe. pick the 1st config with a 16 bit depth buffer.
settings . setAlphaBits (
for ( EGLConfig config : configs ) {
eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_ALPHA_SIZE ) ) ;
int d = eglGetConfigAttribSafe ( egl , display , config ,
settings . setDepthBits (
EGL10 . EGL_DEPTH_SIZE ) ;
eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_DEPTH_SIZE ) ) ;
if ( d > = 16 ) {
settings . setSamples (
return config ;
eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_SAMPLES ) ) ;
}
settings . setStencilBits (
eglGetConfigAttribSafe ( egl , display , eglConfig , EGL10 . EGL_STENCIL_SIZE ) ) ;
}
}
//nothing much we can do...
/ * *
return null ;
* log output with egl config details
*
* @param conf
* @param display
* @param egl
* /
private void logEGLConfig ( EGLConfig conf , EGLDisplay display , EGL10 egl , Level level ) {
logger . log ( level , "EGL_RED_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_RED_SIZE ) ) ;
logger . log ( level , "EGL_GREEN_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_GREEN_SIZE ) ) ;
logger . log ( level , "EGL_BLUE_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_BLUE_SIZE ) ) ;
logger . log ( level , "EGL_ALPHA_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_ALPHA_SIZE ) ) ;
logger . log ( level , "EGL_DEPTH_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_DEPTH_SIZE ) ) ;
logger . log ( level , "EGL_STENCIL_SIZE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_STENCIL_SIZE ) ) ;
logger . log ( level , "EGL_RENDERABLE_TYPE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_RENDERABLE_TYPE ) ) ;
logger . log ( level , "EGL_SURFACE_TYPE = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SURFACE_TYPE ) ) ;
logger . log ( level , "EGL_SAMPLE_BUFFERS = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SAMPLE_BUFFERS ) ) ;
logger . log ( level , "EGL_SAMPLES = {0}" ,
eglGetConfigAttribSafe ( egl , display , conf , EGL10 . EGL_SAMPLES ) ) ;
}
}
private boolean inRange ( int val , int min , int max ) {
private boolean inRange ( int val , int min , int max ) {
return min < = val & & val < = max ;
return min < = val & & val < = max ;
}
}
private class Config {
/ * *
* red , green , blue , alpha , depth , samples , stencil
* /
int r , g , b , a , d , s , st ;
private Config ( int r , int g , int b , int a , int d , int s , int st ) {
this . r = r ;
this . g = g ;
this . b = b ;
this . a = a ;
this . d = d ;
this . s = s ;
this . st = st ;
}
private int getBitsPerPixel ( ) {
return r + g + b ;
}
}
}
//DON'T REMOVE THIS, USED FOR UNIT TESTING FAILING CONFIGURATION LISTS.
//DON'T REMOVE THIS, USED FOR UNIT TESTING FAILING CONFIGURATION LISTS.
// private static class Config {
// private static class Config {
//
//