【问题标题】:GLSL 120 texture indirection issueGLSL 120 纹理间接问题
【发布时间】:2015-03-06 10:40:16
【问题描述】:

我在为项目编写片段着色器时遇到了一些问题。我正在创建一个无调色板的终端模拟器,所以我想我会使用以下着色器:

#version 110

uniform sampler2D tileset;
uniform sampler2D indices;
uniform sampler2D colors;
uniform sampler2D bgcolors;

uniform vec2 tileset_size;
uniform vec2 size;

varying vec2 tex_coord;

void main(void)
{
    // Calculated texture coordinate
    vec2 screen_pos = vec2(gl_FragCoord.x / 800.0, 1.0 - gl_FragCoord.y / 500.0);

    // Indirect texture lookup 1
    vec2 index = texture2D(indices, screen_pos.st).rg;
    vec4 color = texture2D(colors, screen_pos.st);
    vec4 bgcolor = texture2D(bgcolors, screen_pos.st);

    // Calculated texture coordinate
    vec2 tileCoord;
    //256.0 because the [0,256) byte value is normalized on [0,1)
    tileCoord.x = mod(screen_pos.x, 1.0/size.x)*(size.x/tileset_size.x) + floor(index.x*256.0)/tileset_size.x;
    tileCoord.y = mod(screen_pos.y, 1.0/size.y)*(size.y/tileset_size.y) + floor(index.y*256.0)/tileset_size.y;

    // Indirect texture lookup 2
    vec4 tile = texture2D(tileset, tileCoord);

    vec4 final = tile*color;

    gl_FragColor = vec4(mix(bgcolor.rgb, final.rgb, final.a), 1.0);
}

为了将它渲染到屏幕上,我绘制了一个大四边形,然后让着色器完成其余的工作。

此代码生成所需的输出。但是,它以每 5 的速度执行此操作。根据我的研究,这可能是由于显示驱动程序在软件而不是硬件中执行我的着色器。我发现通过取消注释texture2D() 调用,事情又顺利进行了。

这导致我得到以下代码:

void main(void)
{
    //vec2 screen_pos = vec2(gl_FragCoord.x / 800.0, 1.0 - gl_FragCoord.y / 500.0);
    vec2 screen_pos = vec2(0.5, 0.5);

    vec2 index = texture2D(indices, screen_pos.st).rg;
    vec4 color = texture2D(colors, screen_pos.st);
    vec4 bgcolor = texture2D(bgcolors, screen_pos.st);
    vec4 tiles = texture2D(tileset, screen_pos.st);

    gl_FragColor = vec4(index.rgg + color.rgb + bgcolor.rgb + tiles.rgb, 1.0);
}

结果同样非常缓慢。注释掉最后一行 vec4 tiles = ...,并将其从输出中删除,再次顺利运行。所以我查看了我的设备支持的 texture2D 调用的数量。我得到了以下结果:

GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB: 8
GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB: 16
GL_MAX_TEXTURE_IMAGE_UNITS_ARB: 8
GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB: 8

所以一定有什么事情发生了。即使我的每个调用都是间接访问(我很确​​定它们不是),我最多应该有 8 个!另外,glGetShaderInfoLog()glGetProgramInfoLog() 无话可说。

我应该列出我的规格:

  • 机器:运行 Linux 3.17.1 的 Intel Atom Duo(特别是 Arch)
  • GPU:英特尔 945GM/GMS/GME、943/940GML 集成图形控制器 Mesa
  • 版本:10.4.5

是的,我在调用标准 glewInit() 过程后检查 GL_ARB_fragment_program。

所以,我想到了两种可能的解决方案。

  1. ARB_fragment_shader 的 spec sheet 声明最小 纹理间接数应该是 4。这可能是我的 程序没有正确初始化 ARB_fragment_program,并且 系统正在回退到默认值。 (我尝试将“ARB”放入尽可能多的 尽可能与着色器相关的地方,但我认为 glewInit() 可以解决这个问题 无论如何。)
  2. Mesa 的编译器在我的特定芯片上存在错误。最后发帖here 提到了这一点,并且有一个类似的声音 GPU。基本上,编译器 错误地将所有纹理读取标记为间接纹理读取,从而 错误地拒绝程序。

如果有人在这方面有任何令人难以置信的知识,我真的很想听听。通常我会说“搞砸了,买一台更好的电脑”,但拥有高端显卡只是为了运行终端仿真器的纯粹讽刺是......好吧......讽刺。

如果我忘记在这里写一些信息,请告诉我。

编辑

glxinfo -l:pastebin

ARB assembly(部分由cgc生成)

禁用任何 TEX 指令使其进入硬件模式,所有 4 条指令都将返回到软件模式。

【问题讨论】:

  • 不能对阅读表示感谢?我认为这是一篇很长的帖子。
  • 你能添加glxinfo -l的输出吗(片段程序和片段着色器部分)?您也可以尝试就地计算结果颜色,例如vec4 result = vec4(0); vec4 texresult = texture2D(...); result += texresult; texresult = texture2D(...); ... - 可能是您受到临时寄存器数量的限制。
  • 作为辅助解决方案,您可以使用 ARB_fragment_program 代替片段着色器,这可以让您更好地控制代码。这将需要在 ARB 程序集中编写着色器,或者使用可以输出它的编译器(例如 nvidia cgc)。
  • 我安装了cgc编译器。我是否以同样的方式将其发送到 OpenGL? (自动检测 ARB 程序集)我也找到了 glProgramBinary(),也许用这个?
  • 对于一个理智的 GLSL 编译器,第二个着色器中的这些纹理提取都不是间接的。它们都使用恒定坐标。因此,如果您仍然遇到性能问题,那么您可以将间接纹理提取作为问题扔掉。也许您的实际问题是tileset 使用的图像格式/过滤器?那个特定的采样器似乎是两个着色器中的问题。

标签: opengl glsl fragment-shader mesa


【解决方案1】:

片段程序

好吧,看起来下面的 ARB 片段程序汇编成功了。由cgc 生成,但绝大多数都是手工废弃和编写的。

!!ARBfp1.0
# cgc version 3.1.0013, build date Apr 18 2012
# command line args: -oglsl -profile arbfp1
# source file: tilemap.frag
#vendor NVIDIA Corporation
#version 3.1.0.13
#profile arbfp1
#program main
#semantic tileset
#semantic indices
#semantic colors
#semantic bgcolors
#semantic tileset_size
#semantic size
#var float4 gl_FragCoord : $vin.WPOS : WPOS : -1 : 1
#var float4 gl_FragColor : $vout.COLOR : COL : -1 : 1
#var sampler2D tileset :  : texunit 3 : -1 : 1
#var sampler2D indices :  : texunit 0 : -1 : 1
#var sampler2D colors :  : texunit 1 : -1 : 1
#var sampler2D bgcolors :  : texunit 2 : -1 : 1
#var float2 tileset_size :  : c[0] : -1 : 1
#var float2 size :  : c[1] : -1 : 1
#var float2 tex_coord :  :  : -1 : 0
#const c[2] = 0.0020000001 1 0.00125 256
PARAM c[3] = {
        program.local[0..1],
        { 0.0020000001, 1, 0.00125, 256 }
};
TEMP R0;
TEMP R1;
TEMP R2;
TEMP R3;

# R2 := normalized screen coords

MAD R2.z, -fragment.position.y, c[2].x, c[2].y;
MUL R2.x, fragment.position, c[2].z;
MOV R2.y, R2.z;

TEX R3, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
TEX R1, R2, texture[0], 2D;

# multiply by screen size
MUL R2.x, R2.x, c[0].x;
MUL R2.y, R2.y, c[0].y;
# backup original
MOV R2.z, R2.x;
MOV R2.w, R2.y;

# multiply by inverse of font size
MUL R2.x, R2.x, c[1].z;
MUL R2.y, R2.y, c[1].w;
FLR R2.x, R2.x;
FLR R2.y, R2.y;
MUL R2.x, R2.x, c[1].x;
MUL R2.y, R2.y, c[1].y;
# now we have a bit of a staircase, take the original minus staircase
ADD R2.x, R2.z, -R2.x;
ADD R2.y, R2.w, -R2.y;
# modulo is complete

# normalize per unit (inv font size)
MUL R2.x, R2.x, c[1].z;
MUL R2.y, R2.y, c[1].w;
# divide by 16 for proper texture offset
MUL R2.x, R2.x, .0625;
MUL R2.y, R2.y, .0625;
# add to given texture offset
ADD R2.x, R2.x, R1.x;
ADD R2.y, R2.y, R1.y;

# ... and sample!
TEX R2, R2, texture[3], 2D;

#R2 is tile color
#R3 is background color
#R0 is color color
MUL R0, R0, R2;
#R0 is result color
SUB R3, R3, R0;
#R3 is bgcolor - rescolor

# lerp R3 (multiply by 1 - r)
MAD R3, R3, -R0.a, R3;

#R3 is (bgcolor - rescolor) * rescolor.a - (bgcolor - rescolor)
ADD result.color, R3, R0;
END

无论出于何种原因,写出简化案例的程序集,例如

TEX ...
TEX ...
TEX ...
TEX ...

像以前一样将着色器置于软件模式。在使用cgc 编译了几个不同的版本后,我发现有些仍然可以使用 4 个纹理访问。此外,我交换了原来的内容:

TEX R1, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
ADD R0, R0, R1
TEX R1, R2, texture[0], 2D;

进入

TEX R3, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
TEX R1, R2, texture[0], 2D;

# ... addition done later

基于我在the ARB_fragment_program spec中读到的内容

纹理间接可以被认为是纹理中的一个节点 依赖链。每个节点包含一组纹理 并行执行的指令,后跟一系列 ALU 指令。依赖纹理指令是 使用临时作为输入坐标而不是属性 或参数。没有依赖纹理指令的程序 (或根本没有纹理指令)将有一个节点 它的纹理依赖链,因此是一个间接的。

所以,至少我删除了一个纹理间接。似乎cgc 版本(可能还有glsl 编译器)试图最小化临时文件,而不是纹理访问。毕竟使用 4 个临时对象是可以的;我仍然不确定为什么需要这种优化。


ARB GL-代码

很难获得有关此 API 的文档。我想它是 2002 年的新产品?不管怎样,我成功了。

    if(!GLEW_ARB_fragment_program)
    {
            printf("GLEW_ARB_fragment_program is unavailable.\n");
            return false;
    }

    glClear(GL_COLOR_BUFFER_BIT);
    SDL_GL_SwapWindow(window);


    glEnable(GL_FRAGMENT_PROGRAM_ARB);

    glGenProgramsARB(1, &tilemap_prog);

    if(!tilemap_prog)
    {
            printf("Failed to generate fragment program\n");
            return false;
    }

    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, tilemap_prog);

    glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(tilemap_frag_asm), tilemap_frag_asm);

    GLuint error = glGetError();

    if(error == GL_INVALID_OPERATION)
    {
            printf("GL_INVALID_OPERATION!\n");

            printf("glGetString(GL_PROGRAM_ERROR_STRING_ARB): %s\n", glGetString(GL_PROGRAM_ERROR_STRING_ARB));

            GLint texture_units;
            glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
            printf("GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
            glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
            printf("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
            glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
            printf("GL_MAX_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
            glGetIntegerv(GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB, &texture_units);
            printf("GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB: %d\n", texture_units);

            return false;
    }

    // Window size
    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, width, height, 00.0, 00.0);
    // Font output size and inverse font output size
    glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 10.0, 10.0, 1/10.0, 1/10.0);

有点挑剔,但它最终奏效了。特别感谢 keltar 为我指明了正确的方向。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-28
    • 2021-01-05
    相关资源
    最近更新 更多