Ответ 1
Обновление: Я нашел способ обойти эту проблему, и на самом деле это довольно просто.
Прежде всего: Android по умолчанию EGLConfigChooser
реализация принимает плохие решения по некоторым
устройства. Особенно старые устройства Android, похоже, страдают от этой проблемы EGL_BAD_MATCH
. Во время сеансов моей отладки я также обнаружил, что у этих более старых устройств с нарушениями правил был довольно ограниченный набор доступных конфигураций OpenGL ES.
Причиной этой проблемы "плохого совпадения" является не только несоответствие между пиксельным форматом GLSurfaceView и настройками глубины цветовой разрядности OpenGL ES. В целом мы имеем дело со следующими проблемами:
- Несоответствие версии OpenGL ES API
- Несоответствие запрошенного типа целевой поверхности
- Запрошенная глубина цвета изображения не может быть отображена на вид поверхности
Документация разработчика Android сильно отсутствует, когда дело доходит до объяснения API OpenGL ES. Поэтому важно прочитать оригинальную документацию на Khronos.org. Особенно полезен документ doc о eglChooseConfig.
Чтобы устранить перечисленные выше проблемы, вы должны указать следующую минимальную конфигурацию:
-
EGL_RENDERABLE_TYPE
должен соответствовать используемой версии OpenGL ES API. В вероятном случае OpenGL ES 2.x вы должны установить этот атрибут в4
(см.egl.h
) -
EGL_SURFACE_TYPE
должен иметьEGL_WINDOW_BIT
установить
И, конечно же, вы также хотите настроить контекст OpenGL ES, который предоставляет вам правильные настройки буфера цвета, глубины и трафарета.
К сожалению, невозможно использовать эти параметры конфигурации в прямом режиме. Мы должны выбирать из того, что доступно на любом устройстве. Для этого необходимо реализовать пользовательский EGLConfigChooser
, который проходит через список доступных наборов конфигураций и выбирает наиболее подходящий, который соответствует лучшим данные критерии.
В любом случае, я выбрал примерную реализацию для такого конфигурационного выбора:
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 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;
}
}