Related
So I am updating my app to use OpenGLES 3.0 to take advantage of transform feedback, but the shader isn't compiling.
error:
06-27 17:29:43.299 18593-18627/com.harmonicprocesses.penelopefree E/MyGLRenderer﹕ Could not compile shader 35633:
06-27 17:29:43.299 18593-18627/com.harmonicprocesses.penelopefree E/MyGLRenderer﹕ ERROR: 0:1: 'in' : Syntax error: syntax error
INTERNAL ERROR: no main() function!
ERROR: 1 compilation errors. No code generated.
Here is the vertex shader code:
private final String vertexShaderSrc =
"in float inValue;" +
"out float outValue;" +
"void main() {" +
" outValue = sqrt(inValue);" +
"}";
Here is the compilation code:
int vertexShader = MyGLRenderer.loadShader(GLES30.GL_VERTEX_SHADER,
vertexShaderSrc);
...
public static int loadShader(int shaderType, String source) {
int shader = GLES30.glCreateShader(shaderType);
if (shader != 0) {
GLES30.glShaderSource(shader, source);
GLES30.glCompileShader(shader);
int[] compiled = new int[1];
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
Does anyone see anything wrong with this? If I change in and out to attribute varying respectively it doesn't throw an error, but glUseProgram throw gl error 1282 "Invalid Operation".
This is how I set up GL for es version 3.0:
public MyGLSurfaceViewLegacy(Context context) {
super(context);
setListenForTouchOnTouchListener();
NoteSpectrum = DSPEngine.staticCalcNoteBins(AudioConstants.defaultBufferSize*2,
AudioConstants.sampleRate);
// Create an OpenGL ES 2.0 context.
setEGLContextClientVersion(3);
// Set the Renderer for drawing on the GLSurfaceView
mRenderer = new MyGLRenderer(context,this);
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
mContextFactory = new MyContextFactory();
setEGLContextFactory(mContextFactory);
//mEGLWindowSurfaceFactory = new MyEGLWindowSurfaceFactory();
//setEGLWindowSurfaceFactory(mEGLWindowSurfaceFactory);
setRenderer(mRenderer);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
mContext = context;
drawOverlay = PreferenceManager.getDefaultSharedPreferences(mContext)
.getBoolean("turn_on_visualization_key",true);
}
class MyContextFactory implements EGLContextFactory {
private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
private EGLContext mEGLContext;
private EGLDisplay mEGLDisplay;
private MyGLSurfaceView mMyGLSurfaceView;
private EGL10 mEGL;
#Override
public EGLContext createContext(EGL10 egl, EGLDisplay display,
EGLConfig eglConfig) {
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3,
EGL10.EGL_NONE };
mEGL = egl;
mEGLContext = egl.eglGetCurrentContext();
mEGLDisplay = display;
mEGLContext = egl.eglCreateContext(display, eglConfig,
egl.eglGetCurrentContext(), attrib_list);
return mEGLContext;
}
Add the OpenGLES 3.0 version specifier to the start of your shader:
#version 300 es
Also make sure you are calling setEGLContextClientVersion(3) in your GLSurfaceView setup code.
This piece of code used to work in my Nexus 7 2012 KitKat:
int[] maxSize = new int[1];
GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
In KitKat I can obtain the max pixel value correctly, but after the upgrade to factory image Lollipop this snippet of code causes problem as it only returns 0. The logcat showed this output when it reached this method:
E/libEGL﹕ call to OpenGL ES API with no current context (logged once per thread)
I already have android:hardwareAccelerated="true" in my Manifest.xml. Is there any API changes that I am not aware of, that causes the above code unusable? Please advise.
The error log points out the basic problem very clearly:
call to OpenGL ES API with no current context (logged once per thread)
You need a current OpenGL context in your thread before you can make any OpenGL calls, which includes your glGetIntegerv() call. This was always true. But it seems like in pre-Lollipop, there was an OpenGL context that was created in the frameworks, and that was sometimes (always?) current when app code was called.
I don't believe this was ever documented or intended behavior. Apps were always supposed to explicitly create a context, and make it current, if they wanted to make OpenGL calls. And it appears like this is more strictly enforced in Lollipop.
There are two main approaches to create an OpenGL context:
Create a GLSurfaceView (documentation). This is the easiest and most convenient approach, but only really makes sense if you plan to do OpenGL rendering to the display.
Use EGL14 (documentation). This provides a lower level interface that allows you to complete the necessary setup for OpenGL calls without creating a view or rendering to the display.
The GLSurfaceView approach is extensively documented with examples and tutorials all over the place. So I will focus on the EGL approach.
Using EGL14 (API level 17)
The following code assumes that you care about ES 2.0, some attribute values would have to be adjusted for other ES versions.
At the start of the file, import the EGL14 class, and a few related classes:
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
Then get a hold of the default display, and initialize. This could get more complex if you have to deal with devices that could have multiple displays, but will be sufficient for a typical phone/tablet:
EGLDisplay dpy = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
EGL14.eglInitialize(dpy, vers, 0, vers, 1);
Next, we need to find a config. Since we won't use this context for rendering, the exact attributes aren't very critical:
int[] configAttr = {
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
EGL14.eglChooseConfig(dpy, configAttr, 0,
configs, 0, 1, numConfig, 0);
if (numConfig[0] == 0) {
// TROUBLE! No config found.
}
EGLConfig config = configs[0];
To make a context current, which we will need later, you need a rendering surface, even if you don't actually plan to render. To satisfy this requirement, create a small offscreen (Pbuffer) surface:
int[] surfAttr = {
EGL14.EGL_WIDTH, 64,
EGL14.EGL_HEIGHT, 64,
EGL14.EGL_NONE
};
EGLSurface surf = EGL14.eglCreatePbufferSurface(dpy, config, surfAttr, 0);
Next, create the context:
int[] ctxAttrib = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
EGLContext ctx = EGL14.eglCreateContext(dpy, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0);
Ready to make the context current now:
EGL14.eglMakeCurrent(dpy, surf, surf, ctx);
If all of the above succeeded (error checking was omitted), you can make your OpenGL calls now:
int[] maxSize = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0);
Once you're all done, you can tear down everything:
EGL14.eglMakeCurrent(dpy, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroySurface(dpy, surf);
EGL14.eglDestroyContext(dpy, ctx);
EGL14.eglTerminate(dpy);
Using EGL10 (API level 1)
If you need something that works for earlier levels, you can use EGL10 (documentation) instead of EGL14, which has been available since API level 1. The code above adopted for 1.0 looks like this:
import android.opengl.GLES10;
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.egl.EGLSurface;
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
egl.eglInitialize(dpy, vers);
int[] configAttr = {
EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER,
EGL10.EGL_LEVEL, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
egl.eglChooseConfig(dpy, configAttr, configs, 1, numConfig);
if (numConfig[0] == 0) {
// TROUBLE! No config found.
}
EGLConfig config = configs[0];
int[] surfAttr = {
EGL10.EGL_WIDTH, 64,
EGL10.EGL_HEIGHT, 64,
EGL10.EGL_NONE
};
EGLSurface surf = egl.eglCreatePbufferSurface(dpy, config, surfAttr);
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; // missing in EGL10
int[] ctxAttrib = {
EGL_CONTEXT_CLIENT_VERSION, 1,
EGL10.EGL_NONE
};
EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, ctxAttrib);
egl.eglMakeCurrent(dpy, surf, surf, ctx);
int[] maxSize = new int[1];
GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(dpy, surf);
egl.eglDestroyContext(dpy, ctx);
egl.eglTerminate(dpy);
Note that this version of the code uses an ES 1.x context. The reported maximum texture size can be different for ES 1.x and ES 2.0.
The error message is saying that you are calling the GLES function before the OpenGL ES context exists. I have found that KitKat is stricter about correctness in several areas so that may be the reason for the problem appearing now, or there may be some difference in the order in which you app is starting up that is causing it. If you posted more of your initialisation code, the reason may be clearer.
Typically you have a class that implements GLSurfaceView.Renderer that has a function:
public void onSurfaceCreated(GL10 gl, EGLConfig config)
In this function, you should be able to call gl.glGetIntegerv safely as at this point you know that the OpenGL ES context has been created. If you are calling it earlier than this, then that would explain the error you are seeing.
Is there a way to implement Antialiasing technique in OpenGL ES 2.0? I have goggled and found few methods but there was no change in the output.
In the worst case, I've planned to implement multiple pass rendering, to smooth the edges in fragment shader, by displaying average colour of the pixels around every pixel, but it costs more GPU performance.
Any suggestions?
A lot of devices support MSAA (Multi-Sample Anti-Aliasing). To take advantage of this feature, you have to choose a EGLConfig that has multisampling.
On Android, if you use GLSurfaceView, you will have to implement your own EGLConfigChooser. You can then use EGL functions, particularly eglChooseConfig() to find a config you like.
The following code is untested, but it should at least sketch how this can be implemented. In the constructor of your GLSurfaceView derived class, before calling setRenderer(), add:
setEGLConfigChooser(new MyConfigChooser());
Then implement MyConfigChooser. You can make this a nested class inside your GLSurfaceView:
class MyConfigChooser implements GLSurfaceView.EGLConfigChooser {
#Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int attribs[] = {
EGL10.EGL_LEVEL, 0,
EGL10.EGL_RENDERABLE_TYPE, 4, // EGL_OPENGL_ES2_BIT
EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_SAMPLE_BUFFERS, 1,
EGL10.EGL_SAMPLES, 4, // This is for 4x MSAA.
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] configCounts = new int[1];
egl.eglChooseConfig(display, attribs, configs, 1, configCounts);
if (configCounts[0] == 0) {
// Failed! Error handling.
return null;
} else {
return configs[0];
}
}
}
You will obviously want to substitute the specific values you need for your configuration. In reality, it's much more robust to call eglChooseConfig() with a small set of strictly necessary attributes, let it enumerate all configs that match those attributes, and then implement your own logic to choose the best among them. The defined behavior of eglChooseConfig() is kind of odd already (see documentation), and there's no telling how GPU vendors implement it.
On iOS, you can set this property on your GLKView to enable 4x MSAA:
[view setDrawableMultisample: GLKViewDrawableMultisample4X];
There are other antialiasing approaches you can consider:
Supersampling: Render to a texture that is a multiple (typically twice) the size of your final render surface in each direction, and then downsample it. This uses a lot of memory, and the overhead is substantial. But if it meets your performance requirements, the quality will be excellent.
Old school: Render the frame multiple times with slight offsets, and average the frames. This was commonly done with the accumulation buffer in the early days of OpenGL. The accumulation buffer is obsolete, but you can do the same thing with FBOs. See the section "Scene Antialiasing" under "The Framebuffer" in the original Red Book for a description of the method.
On Android platform, you can download this OpenGL demo apps source code from GDC 2011: it contains lots of best practices and show you how to do multisampling, including coverage antialiasing.
What you need to do is just customize GLSurfaceView.EGLConfigChooser and set this chooser:
// Set this chooser before calling setRenderer()
setEGLConfigChooser(new MultisampleConfigChooser());
setRenderer(mRenderer);
MultisampleConfigChooser.java sample code below:
package com.example.gdc11;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.opengl.GLSurfaceView;
import android.util.Log;
// This class shows how to use multisampling. To use this, call
// myGLSurfaceView.setEGLConfigChooser(new MultisampleConfigChooser());
// before calling setRenderer(). Multisampling will probably slow down
// your app -- measure performance carefully and decide if the vastly
// improved visual quality is worth the cost.
public class MultisampleConfigChooser implements GLSurfaceView.EGLConfigChooser {
static private final String kTag = "GDC11";
#Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
mValue = new int[1];
// Try to find a normal multisample configuration first.
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
// Requires that setEGLContextClientVersion(2) is called on the view.
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL10.EGL_SAMPLE_BUFFERS, 1 /* true */,
EGL10.EGL_SAMPLES, 2,
EGL10.EGL_NONE
};
if (!egl.eglChooseConfig(display, configSpec, null, 0,
mValue)) {
throw new IllegalArgumentException("eglChooseConfig failed");
}
int numConfigs = mValue[0];
if (numConfigs <= 0) {
// No normal multisampling config was found. Try to create a
// converage multisampling configuration, for the nVidia Tegra2.
// See the EGL_NV_coverage_sample documentation.
final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
configSpec = new int[]{
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL_COVERAGE_BUFFERS_NV, 1 /* true */,
EGL_COVERAGE_SAMPLES_NV, 2, // always 5 in practice on tegra 2
EGL10.EGL_NONE
};
if (!egl.eglChooseConfig(display, configSpec, null, 0,
mValue)) {
throw new IllegalArgumentException("2nd eglChooseConfig failed");
}
numConfigs = mValue[0];
if (numConfigs <= 0) {
// Give up, try without multisampling.
configSpec = new int[]{
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL10.EGL_NONE
};
if (!egl.eglChooseConfig(display, configSpec, null, 0,
mValue)) {
throw new IllegalArgumentException("3rd eglChooseConfig failed");
}
numConfigs = mValue[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
} else {
mUsesCoverageAa = true;
}
}
// Get all matching configurations.
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs,
mValue)) {
throw new IllegalArgumentException("data eglChooseConfig failed");
}
// CAUTION! eglChooseConfigs returns configs with higher bit depth
// first: Even though we asked for rgb565 configurations, rgb888
// configurations are considered to be "better" and returned first.
// You need to explicitly filter the data returned by eglChooseConfig!
int index = -1;
for (int i = 0; i < configs.length; ++i) {
if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 5) {
index = i;
break;
}
}
if (index == -1) {
Log.w(kTag, "Did not find sane config, using first");
}
EGLConfig config = configs.length > 0 ? configs[index] : null;
if (config == null) {
throw new IllegalArgumentException("No config chosen");
}
return config;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
public boolean usesCoverageAa() {
return mUsesCoverageAa;
}
private int[] mValue;
private boolean mUsesCoverageAa;
}
Before using this feature, you should known this will affect rendering efficiency, and may need to do a fully performance test.
I'm facing a problem with some opengl ES drivers, when calling glReadPixels for a Pbuffer some devices will just kill the app with no message at all. Others will give me the next trace and then freeze for around 10 seconds before killing the app.
Unable to Find Phys Addr for 0
So far the affected devices where the problem is reproducible are:
Galaxy Y, Galaxy Ace, Galaxy Mini, Galaxy Young
I've also tested the code in the next devices where it works correctly as expected, no problems at all:
Nexy 4, Nexus 7, Nexus Galaxy, SGI, SGII, SGIII, Motorola Mini-Defy, and some others more.
I've put together a quick test function which reproduces the problem. Maybe someone can spot the issue. Please this is only a test method, no reviews about it are necessary as I just put it together to allow testing the bug, if I missed something let me know.
private static void bugTest()
{
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// Initialize
int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);
// Query total number of configurations
int[] totalConfigurations = new int[1];
egl.eglGetConfigs(eglDisplay, null, 0, totalConfigurations);
EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
int attribs[] = { EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */, EGL10.EGL_RED_SIZE, 1, EGL10.EGL_GREEN_SIZE, 1, EGL10.EGL_BLUE_SIZE, 1, EGL10.EGL_NONE };
if (egl.eglChooseConfig(eglDisplay, attribs, configurationsList, 1, totalConfigurations) == false)
{
Log.e(TAG, "Could not find config for GLES2");
egl.eglTerminate(eglDisplay);
return;
}
// Create the PBuffer
EGLSurface eglSurface = null;
final int surfaceWidth = 512;
final int surfaceHeight = 512;
try
{
int[] attribList = new int[] { EGL10.EGL_WIDTH, surfaceWidth, EGL10.EGL_HEIGHT, surfaceHeight, EGL10.EGL_NONE };
eglSurface = egl.eglCreatePbufferSurface(eglDisplay, configurationsList[0], attribList);
}
catch (Exception ex)
{
Log.e(TAG, "Failed to create surface");
egl.eglTerminate(eglDisplay);
return;
}
// BUG Test for glReadPixels
if (eglSurface != null)
{
// Create context
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
final int GLES_VERSION = 2;
int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL10.EGL_NONE };
EGLContext eglContext = egl.eglCreateContext(eglDisplay, configurationsList[0], EGL10.EGL_NO_CONTEXT, attribList);
if (eglContext != null)
{
// Attach context to surface
if (egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) == true)
{
// Perform the actual bug test
GL10 gl = (GL10)eglContext.getGL();
int buffer[] = new int[surfaceWidth * surfaceHeight];
IntBuffer wrappedBuffer = IntBuffer.wrap(buffer);
wrappedBuffer.position(0);
// BUG: Line of failure
gl.glReadPixels(0, 0, surfaceWidth, surfaceHeight, GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, wrappedBuffer);
// Also fails when using RGBA
//gl.glReadPixels(0, 0, surfaceWidth, surfaceHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, wrappedBuffer);
}
egl.eglDestroyContext(eglDisplay, eglContext);
}
egl.eglDestroySurface(display, eglSurface);
}
egl.eglTerminate(eglDisplay);
}
Pbuffers are not supported on devices with Nvidia Tegra GPUs. The problem is their EGL driver, not your code. But there is really no good reason to use pbuffers anyway. They are obsolete. You should use FBOs instead, especially on Android. This article explains in detail why:
http://processors.wiki.ti.com/index.php/Render_to_Texture_with_OpenGL_ES
The best way to create an off-screen surface on Android is to construct a new SurfaceTexture() and pass that to eglCreateWindowSurface().
the version android is 2.2.1 the device is a samsung galaxy II the full crash log is:
java.lang.RuntimeException: createWindowSurface failed: EGL_BAD_MATCH
at android.opengl.GLSurfaceView$EglHelper.throwEglException(GLSurfaceView.java:1077)
at android.opengl.GLSurfaceView$EglHelper.createSurface(GLSurfaceView.java:981)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1304)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1116)
this is the relevant code to the crash:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
glView = new GLSurfaceView(this);
glView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);
glView.setRenderer(this);
setContentView(glView);
\\etc..............}
i used setEGLConfigChooser() because the app would crash on API-17 if it wasnt in there so for this specific device that it is crashing on i been looking around and it has something to do with the PixelFormat for the device.
What im wondering is how can i use some code so this will not crash on the samsung galaxy II android version 2.2.1, i cant test this in an emulator and i dont have the device to test it in, i just need for sure code and im not sure how to change it up?
Update: I found a way to work around this issue and actually it is fairly straightforward.
First of all: Android's default EGLConfigChooser implementation makes bad decisions on some
devices. Especially the older Android devices seem to suffer this EGL_BAD_MATCH issue. During my debugging sessions I also discovered that those older troublemaker devices had quite a limited set of available OpenGL ES configurations.
The cause of this "bad match" problem is more than just a mismatch between the GLSurfaceView's pixel format and the color bit depth settings of OpenGL ES. Overall we have to deal with the following issues:
A mismatch of the OpenGL ES API version
A mismatch of the requested target surface type
The requested color bit depth cannot be rendered on the surface view
The Android developer documentation is severely lacking when it comes to explaining the OpenGL ES API. It is therefore important to read the original documentation over at Khronos.org. Especially the doc page about eglChooseConfig is helpful here.
In order to remedy above listed problems you have to make sure to specify the following minimum configuration:
EGL_RENDERABLE_TYPE must match the OpenGL ES API version you are using. In the likely case of OpenGL ES 2.x you must set that attribute to 4(see egl.h)
EGL_SURFACE_TYPE should have the EGL_WINDOW_BIT set
And of course you also want to set up an OpenGL ES context that provides you with the correct color, depth and stencil buffer settings.
Unfortunately it is not possible to cherry-pick these configuration options in a straightforward way. We have to choose from whatever is available on any given device. That's why it is necessary to implement a custom EGLConfigChooser, that goes through the list of available configuration sets and picks the most suitable one that matches best the given criteria.
Anyway, I whipped up a sample implementation for such a config chooser:
public class MyConfigChooser implements EGLConfigChooser {
final private static String TAG = "MyConfigChooser";
// This constant is not defined in the Android API, so we need to do that here:
final private static int EGL_OPENGL_ES2_BIT = 4;
// Our minimum requirements for the graphics context
private static int[] mMinimumSpec = {
// We want OpenGL ES 2 (or set it to any other version you wish)
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
// We want to render to a window
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
// We do not want a translucent window, otherwise the
// home screen or activity in the background may shine through
EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE,
// indicate that this list ends:
EGL10.EGL_NONE
};
private int[] mValue = new int[1];
protected int mAlphaSize;
protected int mBlueSize;
protected int mDepthSize;
protected int mGreenSize;
protected int mRedSize;
protected int mStencilSize;
/**
* The constructor lets you specify your minimum pixel format,
* depth and stencil buffer requirements.
*/
public MyConfigChooser(int r, int g, int b, int a, int depth, int
stencil) {
mRedSize = r;
mGreenSize = g;
mBlueSize = b;
mAlphaSize = a;
mDepthSize = depth;
mStencilSize = stencil;
}
#Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] arg = new int[1];
egl.eglChooseConfig(display, mMinimumSpec, null, 0, arg);
int numConfigs = arg[0];
Log.i(TAG, "%d configurations available", numConfigs);
if(numConfigs <= 0) {
// Ooops... even the minimum spec is not available here
return null;
}
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, mMinimumSpec, configs,
numConfigs, arg);
// Let's do the hard work now (see next method below)
EGLConfig chosen = chooseConfig(egl, display, configs);
if(chosen == null) {
throw new RuntimeException(
"Could not find a matching configuration out of "
+ configs.length + " available.",
configs);
}
// Success
return chosen;
}
/**
* This method iterates through the list of configurations that
* fulfill our minimum requirements and tries to pick one that matches best
* our requested color, depth and stencil buffer requirements that were set using
* the constructor of this class.
*/
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
EGLConfig bestMatch = null;
int bestR = Integer.MAX_VALUE, bestG = Integer.MAX_VALUE,
bestB = Integer.MAX_VALUE, bestA = Integer.MAX_VALUE,
bestD = Integer.MAX_VALUE, bestS = Integer.MAX_VALUE;
for(EGLConfig config : configs) {
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config,
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if(r <= bestR && g <= bestG && b <= bestB && a <= bestA
&& d <= bestD && s <= bestS && r >= mRedSize
&& g >= mGreenSize && b >= mBlueSize
&& a >= mAlphaSize && d >= mDepthSize
&& s >= mStencilSize) {
bestR = r;
bestG = g;
bestB = b;
bestA = a;
bestD = d;
bestS = s;
bestMatch = config;
}
}
return bestMatch;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if(egl.eglGetConfigAttrib(display, config, attribute,
mValue)) {
return mValue[0];
}
return defaultValue;
}
}
I don't have the reputation score to add a comment yet, or else I would have put a brief comment on Nobu Games' answer. I encountered this same EGL_BAD_MATCH error and their answer helped put me on the right path. Instead, I have to create a separate answer.
As Nobu Games mentions, there appears to be a mismatch between the GLSurfaceView's PixelFormat and the pixel format parameters passed to setEGLConfigChooser(). In my case, I was asking for RGBA8888 but my GLSurfaceView was RGB565. This caused the EGL_BAD_MATCH error later on within my initialization.
The enhancement to their answer is that you can get the desired PixelFormat for the window and use it to dynamically choose an EGL context.
To make my code as generic as possible, I changed the GLSurfaceView to take in an additional parameter -- the pixel format of the display. I get this from my activity by calling:
getWindowManager().getDefaultDisplay().getPixelFormat();
I pass this value down to the GLSurfaceView and then extract the optimal bit depths for each of RGBA like this:
if (pixelFormatVal > 0) {
PixelFormat info = new PixelFormat();
PixelFormat.getPixelFormatInfo(pixelFormatVal, info);
if (PixelFormat.formatHasAlpha(pixelFormatVal)) {
if (info.bitsPerPixel >= 24) {
m_desiredABits = 8;
} else {
m_desiredABits = 6; // total guess
}
} else {
m_desiredABits = 0;
}
if (info.bitsPerPixel >= 24) {
m_desiredRBits = 8;
m_desiredGBits = 8;
m_desiredBBits = 8;
} else if (info.bitsPerPixel >= 16) {
m_desiredRBits = 5;
m_desiredGBits = 6;
m_desiredRBits = 5;
} else {
m_desiredRBits = 4;
m_desiredGBits = 4;
m_desiredBBits = 4;
}
} else {
m_desiredRBits = 8;
m_desiredGBits = 8;
m_desiredBBits = 8;
}
I then pass these values down to my config chooser. This code works for me on a RGB565 device as well as a RGBA8888 device.
My assumption is that the vendor has chosen the default for a reason and that it will give the most performant results. Of course I have nothing to back that statement up, but it is the strategy I'm going with.