为什么我的 OpenGL ES 应用程序在 glDrawElements 上崩溃?
Why does my OpenGL ES app crash on 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>
我猜它与我的绘图代码有关,或者更糟的是与 phone 的某些驱动程序问题有关。我正在使用以下代码来渲染四边形:
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, false, sizeof(float) * 5, IntPtr.Zero); // 3 + 2 = 5
GL.EnableVertexAttribArray(shader.AttribVertex);
// Update attribute value TexCoord
GL.VertexAttribPointer(shader.AttribTexCoord, 2, VertexAttribPointerType.Float, false, sizeof(float) * 5, 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, 1, 3, 2
};
indexBufferId = bufferManager.Load(faceIndexes);
对于我使用的顶点缓冲区:
float[] vertices =
{
0f, 0f, 0f, 0f, ty,
width, 0f, 0f, tx, ty,
0f, height, 0f, 0f, 0f,
width, height, 0f, tx, 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);
向通用 Load
方法添加一个 target
参数。例如:
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, (IntPtr)(data.Length * Marshal.SizeOf(default(T))), data, BufferUsage.StaticDraw);
GL.BindBuffer(target, 0);
return bufferId;
}
indexBufferId = bufferManager.Load(faceIndexes, BufferTarget.ElementArrayBuffer);
int vboId = bufferManager.Load(vertices, BufferTarget.ArrayBuffer);
我发现了导致我的应用程序在某些设备上崩溃的问题。显然有一个项目滑入了绘图例程,它正在将缓冲区 id 0 传递到 GL.BindBuffer。在某些设备上,这会在调用 Gl.DrawElements 时导致以下错误:emuglGLESv2_enc: sendVertexAttributes: bad offset / len!!!!!
解决方案当然是在缓冲区 id 为 0 或没有创建该 id 的缓冲区时不再执行此代码。
在我的例子中,GL.GenBuffers 没有返回 0。我有一些应该为空(未渲染)的精灵,我的原始解决方案是将缓冲区 ID 设置为 0。
我在 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>
我猜它与我的绘图代码有关,或者更糟的是与 phone 的某些驱动程序问题有关。我正在使用以下代码来渲染四边形:
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, false, sizeof(float) * 5, IntPtr.Zero); // 3 + 2 = 5
GL.EnableVertexAttribArray(shader.AttribVertex);
// Update attribute value TexCoord
GL.VertexAttribPointer(shader.AttribTexCoord, 2, VertexAttribPointerType.Float, false, sizeof(float) * 5, 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, 1, 3, 2
};
indexBufferId = bufferManager.Load(faceIndexes);
对于我使用的顶点缓冲区:
float[] vertices =
{
0f, 0f, 0f, 0f, ty,
width, 0f, 0f, tx, ty,
0f, height, 0f, 0f, 0f,
width, height, 0f, tx, 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);
向通用 Load
方法添加一个 target
参数。例如:
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, (IntPtr)(data.Length * Marshal.SizeOf(default(T))), data, BufferUsage.StaticDraw);
GL.BindBuffer(target, 0);
return bufferId;
}
indexBufferId = bufferManager.Load(faceIndexes, BufferTarget.ElementArrayBuffer);
int vboId = bufferManager.Load(vertices, BufferTarget.ArrayBuffer);
我发现了导致我的应用程序在某些设备上崩溃的问题。显然有一个项目滑入了绘图例程,它正在将缓冲区 id 0 传递到 GL.BindBuffer。在某些设备上,这会在调用 Gl.DrawElements 时导致以下错误:emuglGLESv2_enc: sendVertexAttributes: bad offset / len!!!!! 解决方案当然是在缓冲区 id 为 0 或没有创建该 id 的缓冲区时不再执行此代码。 在我的例子中,GL.GenBuffers 没有返回 0。我有一些应该为空(未渲染)的精灵,我的原始解决方案是将缓冲区 ID 设置为 0。