为什么我的OpenGL ES应用程序在glDrawElements上崩溃?

问题描述

我在Xamarin(使用OpenTK)上开发了带有OpenGL ES 3的手机游戏。在大多数设备上运行正常,但在某些设备上崩溃(HUAWEI Y5 lite)。不幸的是,我没有该错误的详细日志:

  #00  pc 0000000000093d2a  /vendor/lib/egl/libGLESv2_mtk.so
  #01  pc 000000000001c137  /vendor/lib/egl/libGLESv2_mtk.so
  #02  pc 000000000001eddf  /vendor/lib/egl/libGLESv2_mtk.so
  #03  pc 000000000001af75  /vendor/lib/egl/libGLESv2_mtk.so
  #04  pc 000000000001aabf  /vendor/lib/egl/libGLESv2_mtk.so (glDrawElements+54)
  #05  pc 000000000000ca0c  <anonymous>

我猜想这与我的绘画代码有关,或更糟糕的是与手机的某些驱动程序有关。我正在使用以下代码来渲染四边形:

public void BeforeRender()
{
    // Use shader program.
    GL.UseProgram(shader.Program);

    // Enable transparency
    GL.Enable(EnableCap.Blend);
    GL.BlendFunc(BlendingFactorSrc.SrcAlpha,BlendingFactorDest.OneMinusSrcAlpha);

    // Use texture
    GL.ActiveTexture(TextureUnit.Texture0);
    GL.Uniform1(shader.UniformTexture,0);

    // Only bind once for all quads
    GL.BindBuffer(BufferTarget.ElementArrayBuffer,indexBufferId);
}

public void Render(Sprite sprite,Vector4 color,Matrix4 modelViewProjection)
{            
    // Set model view projection
    GL.UniformMatrix4(shader.UniformModelViewProjection,false,ref modelViewProjection);

    // Set color
    GL.Uniform4(shader.UniformColor,color);

    // Set texture
    GL.BindTexture(TextureTarget.Texture2D,sprite.TextureId);

    // Update attribute value Position
    GL.BindBuffer(BufferTarget.ArrayBuffer,sprite.Vbo);
    GL.VertexAttribPointer(shader.AttribVertex,3,VertexAttribPointerType.Float,sizeof(float) * 5,IntPtr.Zero); // 3 + 2 = 5
    GL.EnableVertexAttribArray(shader.AttribVertex);

    // Update attribute value TexCoord
    GL.VertexAttribPointer(shader.AttribTexCoord,2,new IntPtr(sizeof(float) * 3));
    GL.EnableVertexAttribArray(shader.AttribTexCoord);

    // Draw quad
    GL.DrawElements(BeginMode.Triangles,faceIndexes.Length,DrawElementsType.UnsignedShort,IntPtr.Zero); 
}

public void AfterRender()
{
    // Unbind / Disable
    GL.BindBuffer(BufferTarget.ArrayBuffer,0);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer,0);
    GL.Disable(EnableCap.Blend);
}

要绘制多个四边形,我只需调用如下方法:

BeforeRender();

foreach(var sprite in sprites)
{
    Render(sprite);
}

AfterRender();

我的代码总体上是否存在问题,可能会导致某些其他设备“容忍”的设备出现问题?

谢谢!

更新:

这是我创建缓冲区的方式:

public int Load<T>(T[] data) 
        where T : struct
    {
        int bufferId;

        GL.GenBuffers(1,out bufferId);

        bufferIds.Add(bufferId);

        GL.BindBuffer(BufferTarget.ArrayBuffer,bufferId);
        GL.BufferData(BufferTarget.ArrayBuffer,(IntPtr)(data.Length * Marshal.SizeOf(default(T))),data,BufferUsage.StaticDraw);
        GL.BindBuffer(BufferTarget.ArrayBuffer,0);

        return bufferId;
    }

对于索引缓冲区,我使用:

ushort[] faceIndexes =
        {
            0,1,2
        };

indexBufferId = bufferManager.Load(faceIndexes);

对于顶点缓冲区,我使用:

float[] vertices =
            {
                0f,0f,ty,width,tx,height,0f
            };

int vboId = bufferManager.Load(vertices);

解决方法

索引必须存储到ElementArrayBuffer而不是ArrayBuffer

GL.BindBuffer(BufferTarget.ArrayBuffer,bufferId); ...

GL.BindBuffer(BufferTarget.ElementArrayBuffer,bufferId);
GL.BufferData(BufferTarget.ElementArrayBuffer,(IntPtr)(data.Length * Marshal.SizeOf(default(T))),data,BufferUsage.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer,0);

向通用的target方法添加一个Load参数。例如:

public int Load<T>(T[] data,BufferTarget target) 
    where T : struct
{
    int bufferId;
    GL.GenBuffers(1,out bufferId);
    bufferIds.Add(bufferId);

    GL.BindBuffer(target,bufferId);
    GL.BufferData(target,BufferUsage.StaticDraw);
    GL.BindBuffer(target,0);

    return bufferId;
}
indexBufferId = bufferManager.Load(faceIndexes,BufferTarget.ElementArrayBuffer);
int vboId = bufferManager.Load(vertices,BufferTarget.ArrayBuffer);

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...