You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:25:56