Windows下ANGLE的GL ES 3.1计算Shader中imageStore引发崩溃
Compute Shader生成亮度波形时崩溃问题排查与解决
目标
使用Compute Shader生成视频亮度波形图。
开发环境
- C++窗口应用程序
- EGL/egl.h
- GLES3/gl31.h(明确请求ES 3.1上下文)
- ANGLE 2024年1月4日构建版本的libGLESv2.dll
问题现象
执行Compute Shader后调用glMemoryBarrier时触发访问违例异常:
Exception thrown at 0x00007FFA387FBE36 (libGLESv2.dll) in angle_test.exe: 0xC0000005: Access violation reading location 0x00000000000000C8.
移除Compute Shader中的imageStore语句后,崩溃消失。
相关代码
Compute Shader代码
#version 310 es layout(local_size_x = 16, local_size_y = 16) in; layout(binding = 0, rgba8ui) uniform highp readonly uimage2D u_sourceTexture; layout(binding = 1, rgba8ui) uniform highp writeonly uimage2D u_waveformTexture; void main() { ivec2 sourceSize = imageSize(u_sourceTexture); ivec2 waveformSize = imageSize(u_waveformTexture); ivec2 gid = ivec2(gl_GlobalInvocationID.xy); if (gid.x < sourceSize.x && gid.y < sourceSize.y) { uvec4 color = imageLoad(u_sourceTexture, gid); float luminance = dot(vec3(color.rgb) / 255.0, vec3(0.299, 0.587, 0.114)); int waveformY = int(clamp(luminance * float(waveformSize.y-1), 0.0, float(waveformSize.y - 1))); int waveformX = int(clamp(float(gid.x) * float(waveformSize.x) / float(sourceSize.x), 0.0, float(waveformSize.x - 1))); ivec2 waveformPos = ivec2(waveformX, waveformY); imageStore(u_waveformTexture, waveformPos, color); // 此行导致崩溃 } }
渲染循环代码
glUseProgram(m_shaderprog_compute_luma_waveform); glBindImageTexture( 0, // 图像单元 m_tex_source, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8UI ); glBindImageTexture( 1, // 图像单元 m_tex_luma_waveform, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8UI ); const int groupSize = 16; unsigned int sourceWidth = m_source_width(); unsigned int sourceHeight = m_source_height(); GLuint numGroupsX = (sourceWidth + groupSize - 1) / groupSize; GLuint numGroupsY = (sourceHeight + groupSize - 1) / groupSize; glDispatchCompute(numGroupsX, numGroupsY, 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glUseProgram(0);
输出纹理创建代码
glActiveTexture(GL_TEXTURE0); glGenTextures(1, &m_tex_luma_waveform); if (m_tex_luma_waveform == 0) { throw std::runtime_error("Failed to create luma waveform texture"); } glBindTexture(GL_TEXTURE_2D, m_tex_luma_waveform); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8UI, m_luma_waveform_width, m_luma_waveform_height, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL ); glBindTexture(GL_TEXTURE_2D, 0);
输入纹理创建代码
... // 生成并绑定纹理,设置过滤和环绕方式 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels );
核心原因与解决方法
1. 纹理格式不匹配(崩溃的直接原因)
输入纹理创建时使用的是GL_RGBA(归一化浮点格式),但Compute Shader中声明的是uimage2D(无符号整数图像),两者格式不兼容,导致访问纹理时触发内存错误。
修复步骤:
修改输入纹理的创建代码,将内部格式改为GL_RGBA8UI,同时调整外部格式参数:
... // 生成并绑定纹理,设置过滤和环绕方式 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8UI, // 改为与着色器匹配的整数格式 image->width, image->height, 0, GL_RGBA_INTEGER, // 对应整数格式的外部格式 GL_UNSIGNED_BYTE, image->pixels );
如果需要保留原纹理用于片段着色器显示,可以创建一个副本纹理专门用于Compute Shader访问,避免影响原有渲染流程。
2. 多线程写入冲突(波形逻辑优化)
当前代码中多个源像素会映射到波形纹理的同一个位置,直接用imageStore会导致写入覆盖,最终波形不符合预期。建议改用原子操作统计亮度分布:
- 将输出纹理改为
r32ui格式存储计数 - 在着色器中使用
atomicCounterIncrement累加每个位置的像素数量 - 后续用片段着色器将计数转换为可视化的亮度波形
修改后的Compute Shader示例:
#version 310 es layout(local_size_x = 16, local_size_y = 16) in; layout(binding = 0, rgba8ui) uniform highp readonly uimage2D u_sourceTexture; layout(binding = 1, r32ui) uniform highp writeonly uimage2D u_waveformTexture; void main() { ivec2 sourceSize = imageSize(u_sourceTexture); ivec2 waveformSize = imageSize(u_waveformTexture); ivec2 gid = ivec2(gl_GlobalInvocationID.xy); if (gid.x < sourceSize.x && gid.y < sourceSize.y) { uvec4 color = imageLoad(u_sourceTexture, gid); float luminance = dot(vec3(color.rgb) / 255.0, vec3(0.299, 0.587, 0.114)); int waveformY = int(clamp(luminance * float(waveformSize.y-1), 0.0, float(waveformSize.y - 1))); int waveformX = int(clamp(float(gid.x) * float(waveformSize.x) / float(sourceSize.x), 0.0, float(waveformSize.x - 1))); ivec2 waveformPos = ivec2(waveformX, waveformY); atomicCounterIncrement(imageAtomic(u_waveformTexture, waveformPos)); } }
3. 额外验证步骤
- 检查
glBindImageTexture的格式参数是否与纹理内部格式完全一致 - 确认ANGLE支持Compute Shader:调用
glGetString(GL_EXTENSIONS),检查是否包含GL_EXT_compute_shader - 确保输出纹理的尺寸合法,避免坐标越界(虽然代码中用了
clamp,但可额外添加判断)
内容的提问来源于stack exchange,提问作者MindStudio
相关产品推荐
相关产品推荐

