在Android上OpenGL ES 2.0中快速动态顶点

9

我正在尝试使用OpenGL ES 2.0在Android上批量绘制一堆线条,我需要知道最佳的方法。

现在我创建了一个名为LineEngine的类,该类构建了一个包含所有要绘制顶点的FloatBuffer,然后一次性绘制所有线条。问题是,显然FloatBuffer.put()非常慢,会消耗大量CPU时间。

这是我的类:

public class LineEngine {
    private static final float[] IDENTIY = new float[16];
    private FloatBuffer mLinePoints;
    private FloatBuffer mLineColors;
    private int mCount;

    public LineEngine(int maxLines) {
        Matrix.setIdentityM(IDENTIY, 0);

        ByteBuffer byteBuf = ByteBuffer.allocateDirect(maxLines * 2 * 4 * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        mLinePoints = byteBuf.asFloatBuffer();

        byteBuf = ByteBuffer.allocateDirect(maxLines * 2 * 4 * 4);
        byteBuf.order(ByteOrder.nativeOrder());
        mLineColors = byteBuf.asFloatBuffer();

        reset();
    }

    public void addLine(float[] position, float[] color){
        mLinePoints.put(position, 0, 8); //These lines
        mLineColors.put(color, 0, 4); // are taking
        mLineColors.put(color, 0, 4); // the longest!
        mCount++;
    }

    public void reset(){
        mLinePoints.position(0);
        mLineColors.position(0);
        mCount = 0;
    }

    public void draw(){
        mLinePoints.position(0);
        mLineColors.position(0);
        GraphicsEngine.setMMatrix(IDENTIY);
        GraphicsEngine.setColors(mLineColors);
        GraphicsEngine.setVertices4d(mLinePoints);
        GraphicsEngine.disableTexture();
        GLES20.glDrawArrays(GLES20.GL_LINES, 0, mCount * 2);
        GraphicsEngine.disableColors();
        reset();
    }
}

有没有更好的方法将所有这些行批处理在一起?

设备正在运行哪个Android版本?FloatBuffer存在问题。GLES20需要API级别8,因此至少是Froyo。你能在运行Honeycomb的设备上测试吗? - Stefan Hanke
我正在我的Galaxy Nexus上的ICS上运行它。我会再次确认它是否确实需要很长时间。 - Lit Climbing
我刚刚在我的Galaxy Nexus和EVO 4G上测试了一下批处理行和不批处理行。EVO的FPS从28.8提高到了30,而Galaxy Nexus保持不变,为58.4。然而,在方法分析器中,它显示绘图函数(包括FloatBuffer.put和实际的OpenGL绘制调用)花费了更多的时间。它从31.5%增加到了53.1%的CPU时间。这只是分析器搞错了吗? - Lit Climbing
请注意您的分析 - 如果您正在使用 GLSurfaceView,那么它很可能会自行决定帧率。 - Michael Slade
3个回答

1
您想要做的是SpriteBatching。如果您希望在openGL es中使程序更加稳定,您需要检查以下内容(这是导致程序变慢的原因列表):
- 每帧更改太多opengl ES状态。 - 再次启用/禁用(纹理等)。一旦启用某些内容,您就不必再次启用它,它会自动应用到该帧。 - 使用太多资源而不是spriteSheets。是的,这会对性能产生巨大影响。例如,如果您有10个图像,您必须为每个图像加载不同的纹理,这很慢。更好的方法是创建一个spriteSheet。(您可以在谷歌上查找免费的spriteSheet创建工具)。 - 您的图像质量很高。是的!检查您的分辨率和文件扩展名。优先使用.png文件。 - 关注api 8浮点缓冲区错误(您必须使用int缓冲区来修复错误)。

-使用Java容器。这是最大的陷阱,如果您使用字符串连接(它不是一个容器,但每次都返回一个新的字符串)或List或任何其他返回新类实例的容器,则您的程序由于垃圾收集而变慢的可能性很大。对于输入处理,我建议您搜索一种称为Pool Class的技术。它的用途是回收对象而不是创建新对象。垃圾收集器是巨大的敌人,尽量让它满意,并避免可能调用它的任何编程技术。

-永远不要即时加载东西,而是制作一个加载器类并在应用程序开始时加载所有必要的资源。

如果您实现了此列表,则您的程序将具有强大的韧性。

最后一个补充。什么是精灵批处理器?SpriteBatcher是一个使用单个纹理来渲染多个对象的类。它将自动创建顶点、索引、颜色、u-v坐标,并将它们作为批处理呈现。这种模式将节省GPU和CPU功率。从您发布的代码中,我无法确定导致CPU变慢的原因,但从我的经验来看,是由于我之前提到的列表中的一项或多项原因。检查列表,遵循它,搜索spriteBatching,并且我确定您的程序将运行快速。希望我能帮到您!

编辑:我认为我找到了导致你的程序变慢的原因,你没有翻转缓冲区!你只是重置了位置。你不断添加更多对象,导致缓冲区超载。在reset方法中,只需翻转缓冲区。mLineColors.flip和mLinePaints.flip就可以完成任务。如果每帧发送新的顶点,请确保每帧都调用它们。


我已经很久没有在这段代码上工作了,但是你的答案有很多好的信息,如果翻转缓冲区会有很大的改进,我不会感到惊讶。旧点将被覆盖,所以不会有溢出吗?或者缓冲区不是这样工作的吗? - Lit Climbing
嗨,抱歉回复晚了,我不在电脑旁。首先祝你圣诞快乐!现在回答你的问题,缓冲区是一个临时存储大量需要处理的信息的数组,而不是逐个处理相同的信息直到结束。当你翻转缓冲区时,它会自动调整大小,因此你不必担心过载(除非你给出非常非常大的尺寸)。过载意味着如果缓冲区容量为10个int,则只要不超过10个就可以,如果你给11个,那么就会发生缓冲区溢出。 - KostasRim
覆盖内存位置并不是重载。只需记住更改内存值,而不是要求程序保留相同的内存加上新点。您只需呈现当前帧点(旧点不再存在!)。希望我有所帮助,祝您新年快乐 :)! - KostasRim

0

使用相对较大的浮点数组进行FloatBuffer.put(float[])操作应该会更快。单个put(float)调用存在很多开销。


0

只需使用一个非常简单的本地函数,该函数将从您的类中调用。 您可以直接将float []放入OpenGL中,无需通过愚蠢的缓冲区接口浪费CPU时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接