GLSL 120纹理间接问题

时间:2015-03-04 03:17:07

标签: opengl glsl fragment-shader mesa

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

#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()无话可说。

我应该列出我的规格:

  • 机器:Intel Atom Duo运行Linux 3.17.1(特别是Arch)
  • GPU:Intel 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。基本上是编译器 错误地将所有纹理读取标记为间接纹理读取,从而 不正确地拒绝该程序。
  3. 如果有人对这方面有任何不可思议的知识,我真的很想听。通常情况下,我会说“搞砸它,买一台更好的电脑”,但是为了运行一个终端模拟器而拥有一个高端显卡的纯粹讽刺是......好吧......具有讽刺意味。

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

    编辑

    glxinfo -l:pastebin

    ARB assembly(部分由cgc生成)

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

1 个答案:

答案 0 :(得分: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编译器)试图最小化 temporaries ,而不是纹理访问。毕竟最终可以使用4个临时工具;我仍然不确定为什么需要这种优化。


ARB GL-Code

此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指出我正确的方向。