Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.badlogic.gdx.backends.android.AndroidGraphics Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.backends.android;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.EGLConfigChooser;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.LifecycleListener;
import com.badlogic.gdx.backends.android.surfaceview.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.Cursor.SystemCursor;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.GLVersion;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.WindowedMean;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.SnapshotArray;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
/** An implementation of {@link Graphics} for Android.
*
* @author mzechner */
public class AndroidGraphics implements Graphics, Renderer {
private static final String LOG_TAG = "AndroidGraphics";
/** When {@link AndroidFragmentApplication#onPause()} or {@link AndroidApplication#onPause()} call
* {@link AndroidGraphics#pause()} they MUST enforce continuous rendering. If not, {@link #onDrawFrame(GL10)} will not
* be called in the GLThread while {@link #pause()} is sleeping in the Android UI Thread which will cause the
* {@link AndroidGraphics#pause} variable never be set to false. As a result, the {@link AndroidGraphics#pause()} method will
* kill the current process to avoid ANR */
static volatile boolean enforceContinuousRendering = false;
final GLSurfaceView20 view;
int width;
int height;
int safeInsetLeft, safeInsetTop, safeInsetBottom, safeInsetRight;
AndroidApplicationBase app;
GL20 gl20;
GL30 gl30;
EGLContext eglContext;
GLVersion glVersion;
String extensions;
protected long lastFrameTime = System.nanoTime();
protected float deltaTime = 0;
protected long frameStart = System.nanoTime();
protected long frameId = -1;
protected int frames = 0;
protected int fps;
volatile boolean created = false;
volatile boolean running = false;
volatile boolean pause = false;
volatile boolean resume = false;
volatile boolean destroy = false;
private float ppiX = 0;
private float ppiY = 0;
private float ppcX = 0;
private float ppcY = 0;
private float density = 1;
protected final AndroidApplicationConfiguration config;
private BufferFormat bufferFormat = new BufferFormat(8, 8, 8, 0, 16, 0, 0, false);
private boolean isContinuous = true;
public AndroidGraphics (AndroidApplicationBase application, AndroidApplicationConfiguration config,
ResolutionStrategy resolutionStrategy) {
this(application, config, resolutionStrategy, true);
}
public AndroidGraphics (AndroidApplicationBase application, AndroidApplicationConfiguration config,
ResolutionStrategy resolutionStrategy, boolean focusableView) {
this.config = config;
this.app = application;
view = createGLSurfaceView(application, resolutionStrategy);
preserveEGLContextOnPause();
if (focusableView) {
view.setFocusable(true);
view.setFocusableInTouchMode(true);
}
}
protected void preserveEGLContextOnPause () {
view.setPreserveEGLContextOnPause(true);
}
protected GLSurfaceView20 createGLSurfaceView (AndroidApplicationBase application, final ResolutionStrategy resolutionStrategy) {
if (!checkGL20()) throw new GdxRuntimeException("Libgdx requires OpenGL ES 2.0");
EGLConfigChooser configChooser = getEglConfigChooser();
GLSurfaceView20 view = new GLSurfaceView20(application.getContext(), resolutionStrategy, config.useGL30 ? 3 : 2);
if (configChooser != null)
view.setEGLConfigChooser(configChooser);
else
view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);
view.setRenderer(this);
return view;
}
public void onPauseGLSurfaceView () {
if (view != null) {
view.onPause();
}
}
public void onResumeGLSurfaceView () {
if (view != null) {
view.onResume();
}
}
protected EGLConfigChooser getEglConfigChooser () {
return new GdxEglConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil, config.numSamples);
}
protected void updatePpi () {
DisplayMetrics metrics = new DisplayMetrics();
app.getWindowManager().getDefaultDisplay().getMetrics(metrics);
ppiX = metrics.xdpi;
ppiY = metrics.ydpi;
ppcX = metrics.xdpi / 2.54f;
ppcY = metrics.ydpi / 2.54f;
density = metrics.density;
}
protected boolean checkGL20 () {
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
egl.eglInitialize(display, version);
int EGL_OPENGL_ES2_BIT = 4;
int[] configAttribs = {EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4, EGL10.EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE};
EGLConfig[] configs = new EGLConfig[10];
int[] num_config = new int[1];
egl.eglChooseConfig(display, configAttribs, configs, 10, num_config);
egl.eglTerminate(display);
return num_config[0] > 0;
}
/** {@inheritDoc} */
@Override
public GL20 getGL20 () {
return gl20;
}
/** {@inheritDoc} */
@Override
public void setGL20 (GL20 gl20) {
this.gl20 = gl20;
if (gl30 == null) {
Gdx.gl = gl20;
Gdx.gl20 = gl20;
}
}
/** {@inheritDoc} */
@Override
public boolean isGL30Available () {
return gl30 != null;
}
/** {@inheritDoc} */
@Override
public GL30 getGL30 () {
return gl30;
}
/** {@inheritDoc} */
@Override
public void setGL30 (GL30 gl30) {
this.gl30 = gl30;
if (gl30 != null) {
this.gl20 = gl30;
Gdx.gl = gl20;
Gdx.gl20 = gl20;
Gdx.gl30 = gl30;
}
}
/** {@inheritDoc} */
@Override
public int getHeight () {
return height;
}
/** {@inheritDoc} */
@Override
public int getWidth () {
return width;
}
@Override
public int getBackBufferWidth () {
return width;
}
@Override
public int getBackBufferHeight () {
return height;
}
/** This instantiates the GL10, GL11 and GL20 instances. Includes the check for certain devices that pretend to support GL11 but
* fuck up vertex buffer objects. This includes the pixelflinger which segfaults when buffers are deleted as well as the
* Motorola CLIQ and the Samsung Behold II.
*
* @param gl */
protected void setupGL (javax.microedition.khronos.opengles.GL10 gl) {
String versionString = gl.glGetString(GL10.GL_VERSION);
String vendorString = gl.glGetString(GL10.GL_VENDOR);
String rendererString = gl.glGetString(GL10.GL_RENDERER);
glVersion = new GLVersion(Application.ApplicationType.Android, versionString, vendorString, rendererString);
if (config.useGL30 && glVersion.getMajorVersion() > 2) {
if (gl30 != null) return;
gl20 = gl30 = new AndroidGL30();
Gdx.gl = gl30;
Gdx.gl20 = gl30;
Gdx.gl30 = gl30;
} else {
if (gl20 != null) return;
gl20 = new AndroidGL20();
Gdx.gl = gl20;
Gdx.gl20 = gl20;
}
Gdx.app.log(LOG_TAG, "OGL renderer: " + gl.glGetString(GL10.GL_RENDERER));
Gdx.app.log(LOG_TAG, "OGL vendor: " + gl.glGetString(GL10.GL_VENDOR));
Gdx.app.log(LOG_TAG, "OGL version: " + gl.glGetString(GL10.GL_VERSION));
Gdx.app.log(LOG_TAG, "OGL extensions: " + gl.glGetString(GL10.GL_EXTENSIONS));
}
@Override
public void onSurfaceChanged (javax.microedition.khronos.opengles.GL10 gl, int width, int height) {
this.width = width;
this.height = height;
updatePpi();
updateSafeAreaInsets();
gl.glViewport(0, 0, this.width, this.height);
if (created == false) {
app.getApplicationListener().create();
created = true;
synchronized (this) {
running = true;
}
}
app.getApplicationListener().resize(width, height);
}
@Override
public void onSurfaceCreated (javax.microedition.khronos.opengles.GL10 gl, EGLConfig config) {
eglContext = ((EGL10)EGLContext.getEGL()).eglGetCurrentContext();
setupGL(gl);
logConfig(config);
updatePpi();
updateSafeAreaInsets();
Mesh.invalidateAllMeshes(app);
Texture.invalidateAllTextures(app);
Cubemap.invalidateAllCubemaps(app);
TextureArray.invalidateAllTextureArrays(app);
ShaderProgram.invalidateAllShaderPrograms(app);
FrameBuffer.invalidateAllFrameBuffers(app);
logManagedCachesStatus();
Display display = app.getWindowManager().getDefaultDisplay();
this.width = display.getWidth();
this.height = display.getHeight();
this.lastFrameTime = System.nanoTime();
gl.glViewport(0, 0, this.width, this.height);
}
protected void logConfig (EGLConfig config) {
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int r = getAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
int g = getAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = getAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = getAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
int d = getAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
int s = getAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
int samples = Math.max(getAttrib(egl, display, config, EGL10.EGL_SAMPLES, 0),
getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0));
boolean coverageSample = getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0) != 0;
Gdx.app.log(LOG_TAG, "framebuffer: (" + r + ", " + g + ", " + b + ", " + a + ")");
Gdx.app.log(LOG_TAG, "depthbuffer: (" + d + ")");
Gdx.app.log(LOG_TAG, "stencilbuffer: (" + s + ")");
Gdx.app.log(LOG_TAG, "samples: (" + samples + ")");
Gdx.app.log(LOG_TAG, "coverage sampling: (" + coverageSample + ")");
bufferFormat = new BufferFormat(r, g, b, a, d, s, samples, coverageSample);
}
int[] value = new int[1];
private int getAttrib (EGL10 egl, EGLDisplay display, EGLConfig config, int attrib, int defValue) {
if (egl.eglGetConfigAttrib(display, config, attrib, value)) {
return value[0];
}
return defValue;
}
Object synch = new Object();
void resume () {
synchronized (synch) {
running = true;
resume = true;
}
}
void pause () {
synchronized (synch) {
if (!running) return;
running = false;
pause = true;
view.queueEvent(new Runnable() {
@Override
public void run() {
if (!pause) {
// pause event already picked up by onDrawFrame
return;
}
// it's ok to call ApplicationListener's events
// from onDrawFrame because it's executing in GL thread
onDrawFrame(null);
}
});
while (pause) {
try {
// Android ANR time is 5 seconds, so wait up to 4 seconds before assuming
// deadlock and killing process.
synch.wait(4000);
if (pause) {
// pause will never go false if onDrawFrame is never called by the GLThread
// when entering this method, we MUST enforce continuous rendering
Gdx.app.error(LOG_TAG, "waiting for pause synchronization took too long; assuming deadlock and killing");
android.os.Process.killProcess(android.os.Process.myPid());
}
} catch (InterruptedException ignored) {
Gdx.app.log(LOG_TAG, "waiting for pause synchronization failed!");
}
}
}
}
void destroy () {
synchronized (synch) {
running = false;
destroy = true;
while (destroy) {
try {
synch.wait();
} catch (InterruptedException ex) {
Gdx.app.log(LOG_TAG, "waiting for destroy synchronization failed!");
}
}
}
}
@Override
public void onDrawFrame (javax.microedition.khronos.opengles.GL10 gl) {
long time = System.nanoTime();
// After pause deltaTime can have somewhat huge value that destabilizes the mean, so let's cut it off
if (!resume) {
deltaTime = (time - lastFrameTime) / 1000000000.0f;
} else {
deltaTime = 0;
}
lastFrameTime = time;
boolean lrunning = false;
boolean lpause = false;
boolean ldestroy = false;
boolean lresume = false;
synchronized (synch) {
lrunning = running;
lpause = pause;
ldestroy = destroy;
lresume = resume;
if (resume) {
resume = false;
}
if (pause) {
pause = false;
synch.notifyAll();
}
if (destroy) {
destroy = false;
synch.notifyAll();
}
}
if (lresume) {
SnapshotArray lifecycleListeners = app.getLifecycleListeners();
synchronized (lifecycleListeners) {
LifecycleListener[] listeners = lifecycleListeners.begin();
for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
listeners[i].resume();
}
lifecycleListeners.end();
}
app.getApplicationListener().resume();
Gdx.app.log(LOG_TAG, "resumed");
}
if (lrunning) {
synchronized (app.getRunnables()) {
app.getExecutedRunnables().clear();
app.getExecutedRunnables().addAll(app.getRunnables());
app.getRunnables().clear();
}
for (int i = 0; i < app.getExecutedRunnables().size; i++) {
try {
app.getExecutedRunnables().get(i).run();
} catch (Throwable t) {
t.printStackTrace();
}
}
app.getInput().processEvents();
frameId++;
app.getApplicationListener().render();
}
if (lpause) {
SnapshotArray lifecycleListeners = app.getLifecycleListeners();
synchronized (lifecycleListeners) {
LifecycleListener[] listeners = lifecycleListeners.begin();
for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
listeners[i].pause();
}
}
app.getApplicationListener().pause();
Gdx.app.log(LOG_TAG, "paused");
}
if (ldestroy) {
SnapshotArray lifecycleListeners = app.getLifecycleListeners();
synchronized (lifecycleListeners) {
LifecycleListener[] listeners = lifecycleListeners.begin();
for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
listeners[i].dispose();
}
}
app.getApplicationListener().dispose();
Gdx.app.log(LOG_TAG, "destroyed");
}
if (time - frameStart > 1000000000) {
fps = frames;
frames = 0;
frameStart = time;
}
frames++;
}
@Override
public long getFrameId () {
return frameId;
}
/** {@inheritDoc} */
@Override
public float getDeltaTime () {
return deltaTime;
}
@Override
public float getRawDeltaTime () {
return deltaTime;
}
/** {@inheritDoc} */
@Override
public GraphicsType getType () {
return GraphicsType.AndroidGL;
}
/** {@inheritDoc} */
@Override
public GLVersion getGLVersion () {
return glVersion;
}
/** {@inheritDoc} */
@Override
public int getFramesPerSecond () {
return fps;
}
public void clearManagedCaches () {
Mesh.clearAllMeshes(app);
Texture.clearAllTextures(app);
Cubemap.clearAllCubemaps(app);
TextureArray.clearAllTextureArrays(app);
ShaderProgram.clearAllShaderPrograms(app);
FrameBuffer.clearAllFrameBuffers(app);
logManagedCachesStatus();
}
protected void logManagedCachesStatus () {
Gdx.app.log(LOG_TAG, Mesh.getManagedStatus());
Gdx.app.log(LOG_TAG, Texture.getManagedStatus());
Gdx.app.log(LOG_TAG, Cubemap.getManagedStatus());
Gdx.app.log(LOG_TAG, ShaderProgram.getManagedStatus());
Gdx.app.log(LOG_TAG, FrameBuffer.getManagedStatus());
}
public View getView () {
return view;
}
@Override
public float getPpiX () {
return ppiX;
}
@Override
public float getPpiY () {
return ppiY;
}
@Override
public float getPpcX () {
return ppcX;
}
@Override
public float getPpcY () {
return ppcY;
}
@Override
public float getDensity () {
return density;
}
@Override
public boolean supportsDisplayModeChange () {
return false;
}
@Override
public boolean setFullscreenMode (DisplayMode displayMode) {
return false;
}
@Override
public Monitor getPrimaryMonitor () {
return new AndroidMonitor(0, 0, "Primary Monitor");
}
@Override
public Monitor getMonitor () {
return getPrimaryMonitor();
}
@Override
public Monitor[] getMonitors () {
return new Monitor[] { getPrimaryMonitor() };
}
@Override
public DisplayMode[] getDisplayModes (Monitor monitor) {
return getDisplayModes();
}
@Override
public DisplayMode getDisplayMode (Monitor monitor) {
return getDisplayMode();
}
@Override
public DisplayMode[] getDisplayModes () {
return new DisplayMode[] {getDisplayMode()};
}
protected void updateSafeAreaInsets() {
safeInsetLeft = 0;
safeInsetTop = 0;
safeInsetRight = 0;
safeInsetBottom = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
DisplayCutout displayCutout = app.getApplicationWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
safeInsetRight = displayCutout.getSafeInsetRight();
safeInsetBottom = displayCutout.getSafeInsetBottom();
safeInsetTop = displayCutout.getSafeInsetTop();
safeInsetLeft = displayCutout.getSafeInsetLeft();
}
} // Some Application implementations (such as Live Wallpapers) do not implement Application#getApplicationWindow()
catch (UnsupportedOperationException e) {
Gdx.app.log("AndroidGraphics", "Unable to get safe area insets");
}
}
}
@Override
public int getSafeInsetLeft() {
return safeInsetLeft;
}
@Override
public int getSafeInsetTop() {
return safeInsetTop;
}
@Override
public int getSafeInsetBottom() {
return safeInsetBottom;
}
@Override
public int getSafeInsetRight() {
return safeInsetRight;
}
@Override
public boolean setWindowedMode (int width, int height) {
return false;
}
@Override
public void setTitle (String title) {
}
@Override
public void setUndecorated (boolean undecorated) {
final int mask = (undecorated) ? 1 : 0;
app.getApplicationWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, mask);
}
@Override
public void setResizable (boolean resizable) {
}
@Override
public DisplayMode getDisplayMode () {
DisplayMetrics metrics = new DisplayMetrics();
app.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return new AndroidDisplayMode(metrics.widthPixels, metrics.heightPixels, 0, 0);
}
@Override
public BufferFormat getBufferFormat () {
return bufferFormat;
}
@Override
public void setVSync (boolean vsync) {
}
@Override
public boolean supportsExtension (String extension) {
if (extensions == null) extensions = Gdx.gl.glGetString(GL10.GL_EXTENSIONS);
return extensions.contains(extension);
}
@Override
public void setContinuousRendering (boolean isContinuous) {
if (view != null) {
// ignore setContinuousRendering(false) while pausing
this.isContinuous = enforceContinuousRendering || isContinuous;
int renderMode = this.isContinuous ? GLSurfaceView.RENDERMODE_CONTINUOUSLY : GLSurfaceView.RENDERMODE_WHEN_DIRTY;
view.setRenderMode(renderMode);
}
}
@Override
public boolean isContinuousRendering () {
return isContinuous;
}
@Override
public void requestRendering () {
if (view != null) {
view.requestRender();
}
}
@Override
public boolean isFullscreen () {
return true;
}
@Override
public Cursor newCursor (Pixmap pixmap, int xHotspot, int yHotspot) {
return null;
}
@Override
public void setCursor (Cursor cursor) {
}
@Override
public void setSystemCursor (SystemCursor systemCursor) {
}
private class AndroidDisplayMode extends DisplayMode {
protected AndroidDisplayMode (int width, int height, int refreshRate, int bitsPerPixel) {
super(width, height, refreshRate, bitsPerPixel);
}
}
private class AndroidMonitor extends Monitor {
public AndroidMonitor (int virtualX, int virtualY, String name) {
super(virtualX, virtualY, name);
}
}
}