GLSL 120 纹理间接问题
GLSL 120 texture indirection issue
我 运行 在为项目编写片段着色器时遇到了一些问题。我正在创建一个无调色板的终端模拟器,所以我想我会用以下着色器来做到这一点:
#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 运行ning Linux 3.17.1(特别是 Arch)
- GPU:英特尔 945GM/GMS/GME、943/940GML 集成图形控制器 Mesa
- 版本:10.4.5
是的,我在调用标准 glewInit()
过程后检查 GL_ARB_fragment_program。
所以,我想到了两个可能的解决方案。
- ARB_fragment_shader 的 spec sheet 声明最小值
纹理间接数应该是 4。这可能是我的
程序没有正确初始化 ARB_fragment_program,并且
系统正在回退到默认值。 (我尝试将 "ARB" 放入尽可能多的
尽可能与着色器相关的地方,但我认为 glewInit() 会处理这个
无论如何。)
- Mesa 的编译器在我的特定芯片上存在错误。最后的posthere
提到了这一点,并且有一个类似的声音 GPU。基本上,编译器
错误地将所有纹理读取标记为间接纹理读取,从而
错误地拒绝了程序。
如果有人在这方面有任何令人难以置信的知识,我真的很想听听。通常我会说 "screw it, get a better computer," 但是拥有高端显卡只是为了 运行 终端仿真器的纯粹讽刺是..好吧..讽刺.
如果我忘记在这里写一些信息,请告诉我。
编辑
glxinfo -l: pastebin
ARB assembly(部分由cgc生成)
禁用任何 TEX 指令将其置于硬件模式,所有 4 个都会 return 到软件。
片段程序
嗯,看起来像下面的 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
中阅读的内容
A texture indirection can be considered a node in the texture
dependency chain. Each node contains a set of texture
instructions which execute in parallel, followed by a sequence of
ALU instructions. A dependent texture instruction is one that
uses a temporary as an input coordinate rather than an attribute
or a parameter. A program with no dependent texture instructions
(or no texture instructions at all) will have a single node in
its texture dependency chain, and thus a single indirection.
所以,至少我删除了一个纹理间接。 cgc
版本(可能还有 glsl
编译器)似乎正在尝试最小化 temporaries,而不是 texture accesses .毕竟最终可以使用 4 个临时对象;我仍然不确定为什么需要这种优化。
ARB 总帐代码
这个 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 为我指明了正确的方向。
我 运行 在为项目编写片段着色器时遇到了一些问题。我正在创建一个无调色板的终端模拟器,所以我想我会用以下着色器来做到这一点:
#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 运行ning Linux 3.17.1(特别是 Arch)
- GPU:英特尔 945GM/GMS/GME、943/940GML 集成图形控制器 Mesa
- 版本:10.4.5
是的,我在调用标准 glewInit()
过程后检查 GL_ARB_fragment_program。
所以,我想到了两个可能的解决方案。
- ARB_fragment_shader 的 spec sheet 声明最小值 纹理间接数应该是 4。这可能是我的 程序没有正确初始化 ARB_fragment_program,并且 系统正在回退到默认值。 (我尝试将 "ARB" 放入尽可能多的 尽可能与着色器相关的地方,但我认为 glewInit() 会处理这个 无论如何。)
- Mesa 的编译器在我的特定芯片上存在错误。最后的posthere 提到了这一点,并且有一个类似的声音 GPU。基本上,编译器 错误地将所有纹理读取标记为间接纹理读取,从而 错误地拒绝了程序。
如果有人在这方面有任何令人难以置信的知识,我真的很想听听。通常我会说 "screw it, get a better computer," 但是拥有高端显卡只是为了 运行 终端仿真器的纯粹讽刺是..好吧..讽刺.
如果我忘记在这里写一些信息,请告诉我。
编辑
glxinfo -l: pastebin
ARB assembly(部分由cgc生成)
禁用任何 TEX 指令将其置于硬件模式,所有 4 个都会 return 到软件。
片段程序
嗯,看起来像下面的 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
中阅读的内容A texture indirection can be considered a node in the texture dependency chain. Each node contains a set of texture instructions which execute in parallel, followed by a sequence of ALU instructions. A dependent texture instruction is one that uses a temporary as an input coordinate rather than an attribute or a parameter. A program with no dependent texture instructions (or no texture instructions at all) will have a single node in its texture dependency chain, and thus a single indirection.
所以,至少我删除了一个纹理间接。 cgc
版本(可能还有 glsl
编译器)似乎正在尝试最小化 temporaries,而不是 texture accesses .毕竟最终可以使用 4 个临时对象;我仍然不确定为什么需要这种优化。
ARB 总帐代码
这个 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 为我指明了正确的方向。