我正在制作一个生成模型的图形程序。当用户执行某些操作时,着色器的行为需要更改。这些操作不仅会影响数字常量,也不会影响输入数据,它们会影响一系列计算步骤的数量,顺序和类型。
为了解决这个问题,我想到了两个解决方案:
我为两种方法开发了原型,结果比我预期的更加极端。
编译时间在很大程度上取决于着色器的其余部分(我猜有很多函数内嵌),我想我可以重构着色器来减少每个线程的工作量并缩短编译时间。但是,我现在不知道这是否足够,而且我不太喜欢运行时重新编译的想法(非常依赖平台,更难调试,更复杂)。
另一方面,字节码方法运行(不考虑第一种方法的编译时间)慢25倍。
我知道字节码方法会变慢,但我没想到这一点,特别是在优化之后。
解释器通过从统一缓冲区对象读取字节码来工作。这是对它的简化,我在有用(非簿记)代码中放置了一个“...”,该部分与其他方法相同(显然,这不是在一个带有大if / else的循环内部)选择正确的指令):
layout (std140, binding=7) uniform shader_data{
uvec4 code[256];
};
float interpreter(vec3 init){
float d[4];
vec3 positions[3];
int dDepth=0;
positions[0]=init;
for (int i=0; i<code[128].x; i+=3){
const uint instruction=code[i].x;
const uint ldi=code[i].y;
const uint sti=code[i].z;
if (instruction==MIX){
...
}else{
if (instruction<=BOX){
if (instruction<=TRANSLATION){
if(instruction==PARA){
...
}else{//TRANSLATION;
...
}
}else{
if (instruction==EZROT){
...
}else{//BOX
...
}
}
}else{
if (instruction<=ELLI){
if (instruction==CYL){
...
}else{//ELLI
...
}
}else{
if (instruction==REPETITION){
...
}else{//MIRRORING
...
}
}
}
}
}
return d[0];
}
我的问题是:你知道为什么这么慢(因为我在翻译中没有看到这么多的簿记)?你能猜出这个翻译的主要性能问题是什么?
答案 0 :(得分:3)
GPU在最好的时候不喜欢条件分支。因此,字节代码解释是您可以在GPU上执行的最差事项之一。
当然,在您的情况下,分支的主要问题并不是那么糟糕,因为您的&#34;字节代码&#34;一切都在统一的记忆中。即便如此,由于所有分支机构,它的运行速度也会过慢。
在高级别上更好地处理着色器的可能性会更好,然后使用非常小的数量的分支来决定整个着色器的行为将会。这些不会处于字节代码的级别。他们更喜欢用矩阵蒙皮计算位置&#34;或者&#34;使用此BRDF计算照明&#34;或&#34;使用阴影贴&#34;。
这就是所谓的&#34; ubershader&#34;方法:一个着色器,具有许多大而独特的代码路径,由几个统一的设置决定。
如果你不能这样做,那么在需要时你可以在重新编译之外做些什么。这会对CPU造成伤害;您不能指望使用着色器开始重新编译它的帧(或者之后的几帧)。 SPIR-V着色器可能有助于重新编译性能,但可能没那么多。
尽管有一个小的延迟(~100ms)并不是那么糟糕,因为它不是游戏
我说测量着色器编译所需的时间。如果它不到100毫秒(或者你认为足够的互动),那就去吧。
但是,请注意,许多OpenGL实现在单独的线程上重新编译着色器。因此,当CREATE INDEX idx_metadata_btree_direction ON analytics.incidents ((metadata #>> '{fakeWeather, windDirection}'));
完成时,着色器可能无法完成。要准确地描述此过程,您需要强制重新编译。获得glLinkProgram
应该可以解决问题。
还有一个表现技巧:不要使用GL_LINK_STATUS
和glCompileShader
。相反,请改用glLinkProgram
。它创建了一个可分离的程序(只包含一个着色器阶段),但该过程可能比必须编译和链接为单独的操作更快。