2

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

#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的规格表指出纹理间接的最小数量应该是 4。这可能是我的程序没有正确初始化 ARB_fragment_program,系统正在回退到默认值。(我尝试将“ARB”放在尽可能多的与着色器相关的地方,但我认为 glewInit() 无论如何都会处理这个问题。)
  2. Mesa 的编译器对我的特定芯片有一个错误。这里的最后一篇文章 提到了这一点,并且有一个类似的 GPU。基本上,编译器错误地将所有纹理读取标记为间接纹理读取,从而错误地拒绝程序。

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

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

编辑

glxinfo -l:粘贴

ARB 程序集(部分由 cgc 生成)

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

4

1 回答 1

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

基于我在ARB_fragment_program 规范中读到的内容

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

所以,至少我删除了一个纹理间接。似乎cgc版本(并且可能是glsl编译器)正在尝试最小化temporaries,而不是texture accesses。毕竟使用 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 为我指明了正确的方向。

于 2015-03-06T02:29:45.450 回答