在点上绘制纹理时,glDrawArrays在Android 5中崩溃

时间:2015-05-25 01:15:52

标签: android opengl-es

在Android 5的OpenGL ES 2.0中使用glDrawArrays在点处绘制纹理时,如果绘制调用的数量很高,程序会崩溃。下面包含了重现问题的应用程序的所有源代码。 Android 4中的代码不会崩溃,但是在使用Android 5和Adreno GPU的几款手机上,例如带有Android 5.1的Nexus 4。

测试程序创建一个1000点的列表,并为每个点调用glDrawArrays。几百次调用glDrawArrays后崩溃了。在一个真正的应用程序中,glDrawArrays当然会被调用一次所有的点作为输入,但是这个程序专门用于重现这个错误,这个错误最初是在一个更大的应用程序中观察到的,混合了许多不同的绘制调用。只有在点处绘制纹理时,这个大型应用程序才会崩溃。

我很感激有关如何解决这个问题的任何建议。

这是崩溃后的logcat输出:

05-25 02:31:42.576  24891-24913/? A/libc﹕ Fatal signal 7 (SIGBUS), code 2, fault addr 0xa324c000 in tid 24913 (GLThread 6307)
05-25 02:31:42.633  23118-23838/? I/Icing﹕ Indexing 368CB9F40AF00CE01211CBABA69162BEFBB25180 from com.google.android.googlequicksearchbox
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ Build fingerprint: 'google/occam/mako:5.1/LMY47O/1783956:user/release-keys'
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ Revision: '11'
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ ABI: 'arm'
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ pid: 24891, tid: 24913, name: GLThread 6307  >>> com.example.glpointtexture <<<
05-25 02:31:42.678  10820-10820/? I/DEBUG﹕ signal 7 (SIGBUS), code 2 (BUS_ADRERR), fault addr 0xa324c000
05-25 02:31:42.692  23118-23838/? I/Icing﹕ Indexing done 368CB9F40AF00CE01211CBABA69162BEFBB25180
05-25 02:31:42.695  10820-10820/? I/DEBUG﹕ r0 c0004600  r1 a324c004  r2 00004a81  r3 a324bfec
05-25 02:31:42.695  10820-10820/? I/DEBUG﹕ r4 b73f1748  r5 00000000  r6 00000000  r7 c0022200
05-25 02:31:42.695  10820-10820/? I/DEBUG﹕ r8 b74091e8  r9 04000000  sl 00008000  fp 00000000
05-25 02:31:42.695  10820-10820/? I/DEBUG﹕ ip fc000000  sp a47098b0  lr abbdbc91  pc abbcb908  cpsr 600e0030
05-25 02:31:42.695  10820-10820/? I/DEBUG﹕ backtrace:
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #00 pc 000ab908  /system/vendor/lib/egl/libGLESv2_adreno.so (oxili_write_event_write+75)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #01 pc 000bbc8d  /system/vendor/lib/egl/libGLESv2_adreno.so (oxili_wa_predraw+234)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #02 pc 000bbef1  /system/vendor/lib/egl/libGLESv2_adreno.so (oxili_wa_point_sprite_dummy_draw+204)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #03 pc 000ba47b  /system/vendor/lib/egl/libGLESv2_adreno.so (oxili_primitive_drawarrays+318)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #04 pc 000825cf  /system/vendor/lib/egl/libGLESv2_adreno.so (rb_primitive_drawarrays+298)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #05 pc 0005a51f  /system/vendor/lib/egl/libGLESv2_adreno.so (core_glDrawArraysInstancedXXX+334)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #06 pc 0005a877  /system/vendor/lib/egl/libGLESv2_adreno.so (core_glDrawArrays+6)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #07 pc 00049acb  /system/vendor/lib/egl/libGLESv2_adreno.so (glDrawArrays+24)
05-25 02:31:42.696  10820-10820/? I/DEBUG﹕ #08 pc 00bfb9cb  /data/dalvik-cache/arm/system@framework@boot.oat

MainActivity.java

package com.example.glpointtexture;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;


public class MainActivity extends ActionBarActivity {

    private GLSurfaceView glSurfaceView;
    private boolean rendererSet = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        glSurfaceView = new GLSurfaceView(this);
        final ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
        final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
        if (supportsEs2) {
            glSurfaceView.setEGLContextClientVersion(2);
            glSurfaceView.setRenderer(new PointTextureRenderer(this));
            rendererSet = true;
        } else {
            Toast.makeText(this, "This device does not support OpenGL ES 2.0", Toast.LENGTH_LONG).show();
            return;
        }
        setContentView(glSurfaceView);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

PointList.java

package com.example.glpointtexture;

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Random;

public class PointList {
    private final int BYTES_PER_POINT = 8;
    private final FloatBuffer pointData;
    private final int bufferHandle;

    public PointList(int numPoints) {
        Random rnd = new Random();
        // Create and populate buffer.
        pointData = ByteBuffer.allocateDirect(numPoints * BYTES_PER_POINT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        pointData.position(0);
        for (int i=0; i<numPoints; i++) {
            pointData.put(rnd.nextFloat() - 0.5f);
            pointData.put(rnd.nextFloat() - 0.5f);
        }
        // Send buffer to GPU.
        final int buffers[] = new int[1];
        GLES20.glGenBuffers(1, buffers, 0);
        bufferHandle = buffers[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandle);
        pointData.position(0);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, numPoints * BYTES_PER_POINT, pointData, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    }
    public int getHandle() {
        return bufferHandle;
    }
    public int getSize() {
        return pointData.capacity() / 2;
    }
}

PointShader.java

package com.example.glpointtexture;

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class PointShader {

    private final int programHandle;
    private final int uSizeLocation;
    private final int uColorLocation;
    private final int aPositionLocation;
    private final int textureId;

    private String vertexShader =
        "uniform float u_Size;" +
        "attribute vec4 a_Position;" +
        "void main() {" +
        "  gl_PointSize = u_Size;" +
        "  gl_Position = a_Position;" +
        "}";

    private String fragmentShader =
        "precision mediump float;" +
        "uniform sampler2D u_Texture;" +
        "uniform vec4 u_Color;" +
        "void main() {" +
        "  float alpha = texture2D(u_Texture, gl_PointCoord).a;" +
        "  vec4 rgba = u_Color;" +
        "  rgba.a = alpha;" +
        "  gl_FragColor = rgba;" +
        "}";

    public PointShader() {
        final int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        GLES20.glShaderSource(vertexShaderHandle, vertexShader);
        GLES20.glCompileShader(vertexShaderHandle);
        final int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);
        GLES20.glCompileShader(fragmentShaderHandle);
        programHandle = GLES20.glCreateProgram();

        GLES20.glAttachShader(programHandle, vertexShaderHandle);
        GLES20.glAttachShader(programHandle, fragmentShaderHandle);
        GLES20.glLinkProgram(programHandle);
        aPositionLocation = GLES20.glGetAttribLocation(programHandle, "a_Position");
        uSizeLocation = GLES20.glGetUniformLocation(programHandle,"u_Size");
        uColorLocation = GLES20.glGetUniformLocation(programHandle, "u_Color");
        textureId = createTexture(128);
    }

    public boolean enable() {
        if (programHandle == 0)
            return false;
        GLES20.glUseProgram(programHandle);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        return true;
    }

    public int getPositionHandle() {
        return aPositionLocation;
    }

    public void setSize(float size) {
        GLES20.glUniform1f(uSizeLocation, size);
    }

    public void setColor(float red, float green, float blue, float alpha) {
        GLES20.glUniform4f(uColorLocation, red, green, blue, alpha);
    }
    private static int createTexture(int size) {
        ByteBuffer alphaMask = ByteBuffer.allocateDirect(size * size);
        alphaMask.order(ByteOrder.BIG_ENDIAN);
        for (int y = size - 1; y >= 0; y--) {
            for (int x = 0; x < size; x++) {
                int offset = y * size + x;
                int alpha = (x <= y) ? 255 : 0;
                alphaMask.put(offset, (byte)(alpha));
            }
        }
        return getAlphaMaskTexture(size, size, alphaMask);
    }

    private static int getAlphaMaskTexture(int width, int height, ByteBuffer alphaMask) {
        alphaMask.position(0);
        int[] texIds = new int[1];
        GLES20.glGenTextures(1, texIds, 0);
        int textureId = texIds[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_ALPHA, width, height, 0, GLES20.GL_ALPHA, GLES20.GL_UNSIGNED_BYTE, alphaMask);
        return textureId;
    }
}

PointTextureRenderer.java

package com.example.glpointtexture;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class PointTextureRenderer implements GLSurfaceView.Renderer {

    private final Context context;
    private PointList pointList;
    private PointShader pointShader;

    public PointTextureRenderer(Context context) {
        this.context = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        GLES20.glClearColor(0.0f, 0.0f, 0.5f, 0.0f);
        pointList = new PointList(1000);
        pointShader = new PointShader();
        pointShader.enable();
        pointShader.setColor(1.0f, 0.0f, 0.0f, 1.0f);
        pointShader.setSize(80);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        drawPoints();
    }

    private void drawPoints() {
        final int coordinatesPerPoint = 2;
        final int numPoints = pointList.getSize();

        pointShader.enable();

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, pointList.getHandle());
        GLES20.glVertexAttribPointer(pointShader.getPositionHandle(), coordinatesPerPoint, GLES20.GL_FLOAT, false, 0, 0);
        GLES20.glEnableVertexAttribArray(pointShader.getPositionHandle());

        // The points would normally be drawn with this call:
        //GLES20.glDrawArrays(GLES20.GL_POINTS, 0, numPoints);
        // Instead, we draw one point at a time in order to reproduce a bug.
        for (int i=0; i<numPoints; i++) {
            GLES20.glDrawArrays(GLES20.GL_POINTS, i, 1);
        }
        GLES20.glDisableVertexAttribArray(pointShader.getPositionHandle());

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    }
}

1 个答案:

答案 0 :(得分:2)

Open GL ES是异步API。在某些GL实现中,每次调用glDraw *时,驱动程序都不会立即执行渲染,而是创建“任务”并将其放入每个上下文软件命令队列。稍后,调度程序从队列中弹出任务,执行一些优化(任务合并,状态更改减少等)并执行它。软件队列的大小是有限的,如果你推送任务的速度比调度程序更快,那么你的线程可能会被冻结(并等待调度程序),或者你可能会遇到一些驱动程序崩溃(mali,一些旧的MESA版本),如你所见。我在Samsung Galaxy S3和Galaxy Note上看到了确切的行为。可以使用绘制100000个相同三角形的简单样本重现该问题。解决方案是将小型绘制调用合并为一个大型调用。