代码之家  ›  专栏  ›  技术社区  ›  BPL

为什么等效着色器输出不同的结果?

  •  0
  • BPL  · 技术社区  · 5 年前

    让我们考虑一下这个MCVE:

    from OpenGL.GL import *
    from OpenGL.GLU import *
    from OpenGL.GLUT import *
    import textwrap
    from string import Template
    
    
    def compile(shader_type, source):
        identifier = glCreateShader(shader_type)
        glShaderSource(identifier, source)
        glCompileShader(identifier)
    
        if not glGetShaderiv(identifier, GL_COMPILE_STATUS):
            for i, l in enumerate(source.splitlines()):
                print(f"{i+1}: {l}")
            raise Exception(glGetShaderInfoLog(identifier).decode("utf-8"))
    
        return identifier
    
    
    def create_program(vs, fs):
        vs_identifier = compile(GL_VERTEX_SHADER, vs)
        fs_identifier = compile(GL_FRAGMENT_SHADER, fs)
    
        program = glCreateProgram()
        glAttachShader(program, vs_identifier)
        glAttachShader(program, fs_identifier)
        glLinkProgram(program)
        if not glGetProgramiv(program, GL_LINK_STATUS):
            raise RuntimeError(glGetProgramInfoLog(program))
    
        return program
    
    
    def set_uniform1f(prog, name, v0):
        # print("set_uniform1f", name, glGetUniformLocation(prog, name))
        glUniform1f(glGetUniformLocation(prog, name), v0)
    
    
    def set_uniform1i(prog, name, v0):
        # print("set_uniform1i", name, glGetUniformLocation(prog, name))
        glUniform1i(glGetUniformLocation(prog, name), v0)
    
    
    def set_uniform2f(prog, name, v0, v1):
        # print("set_uniform2f", name, glGetUniformLocation(prog, name))
        glUniform2f(glGetUniformLocation(prog, name), v0, v1)
    
    
    class Window:
    
        def __init__(self, w, h):
            glutInit()
            # glutInitContextVersion(3,2) # at least 3.2 is required, you can use a higer version when needed
            # glutInitContextProfile(GLUT_CORE_PROFILE)
            # glutInitContextFlags(GLUT_FORWARD_COMPATIBLE)
            glutSetOption(GLUT_MULTISAMPLE, 16)
            glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE)
            glutInitWindowSize(w, h)
            glutCreateWindow('Mcve uniforms')
            glutReshapeFunc(self.reshape)
            glutKeyboardFunc(self.keyboard_func)
            glutKeyboardUpFunc(self.keyboard_up_func)
            glutDisplayFunc(self.display)
            glutIdleFunc(self.idle_func)
            self.keys = {chr(i): False for i in range(256)}
    
            # -------- Conflicting shader exposing bug --------
            self.MAGIC_CONSTANT = 635
            vs_code = textwrap.dedent("""\
                 void main()
                 {
                     gl_Position = ftransform();
                 }
            """)
            text = """\
                #pragma optimize (off)
    
                uniform vec2 resolution;
                $block_declaration
    
                vec3 f(vec2 x) {
                    x = sin(abs(x * 0.5));
                    float cs = cos(float($var_name) * 10037.5);
                    float ss = sin(float($var_name) * 12.5) * 0.09;
                    float t = sin(float($var_name)) * 0.5 + 0.5;
                    float d = sin(10. * length(x - vec2(cs, ss)) + mix(8., 10., t));
                    vec3 color = mix(vec3(0.8,0.86,0.85), vec3(0.52,0.72,0.79), sin(t) * 0.5);
                    return color*pow(d, 2.) / 5.;
                }
    
                void main() {
                    vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
                    uv.x *= (resolution.x / resolution.y);
                    vec3 col = f(uv);
                    gl_FragColor = vec4(col*5.0, 1.0);
                }
            """
            VAR_NAME = "my_time"
            fs_code0 = textwrap.dedent(Template(text).substitute(
                block_declaration=f"int {VAR_NAME} = {self.MAGIC_CONSTANT};",
                var_name=VAR_NAME
            ))
            fs_code1 = textwrap.dedent(Template(text).substitute(
                block_declaration=f"uniform int {VAR_NAME};",
                var_name=VAR_NAME
            ))
            print("SHADER0".center(80, '-'))
            print(fs_code0)
            print("SHADER1".center(80, '-'))
            print(fs_code1)
    
            # -------- Shader using time uniform --------
            self.program0 = create_program(vs_code, fs_code0)
            self.program1 = create_program(vs_code, fs_code1)
    
            # -------- Setup --------
            s = 1.0
            glClearColor(1, 1, 1, 1)
            glEnable(GL_DEPTH_TEST)
            glMatrixMode(GL_PROJECTION)
            glOrtho(-s, s, -s, s, -s, s)
            glMatrixMode(GL_MODELVIEW)
            glLoadIdentity()
    
        def keyboard_func(self, *args):
            self.keys[args[0].decode("utf8")] = True
    
        def keyboard_up_func(self, *args):
            self.keys[args[0].decode("utf8")] = False
    
        def display(self):
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    
            if self.keys['w']:
                # Case a) Using uniform
                glUseProgram(self.program0)
                set_uniform2f(self.program0, "resolution", self.width, self.height)
            else:
                # Case b) Using constant with same value than uniform
                glUseProgram(self.program1)
                set_uniform1i(self.program1, "my_time", self.MAGIC_CONSTANT)
                set_uniform2f(self.program1, "resolution", self.width, self.height)
    
            s = 0.5
            glBegin(GL_QUADS)
            glVertex3f(-s, -s, 0)
            glVertex3f(s, -s, 0)
            glVertex3f(s, s, 0)
            glVertex3f(-s, s, 0)
            glEnd()
    
            glutSwapBuffers()
    
        def run(self):
            glutMainLoop()
    
        def idle_func(self):
            glutPostRedisplay()
    
        def reshape(self, w, h):
            glViewport(0, 0, w, h)
            self.width = w
            self.height = h
    
    
    if __name__ == '__main__':
        Window(800, 600).run()
    

    运行后,您将看到生成了这两个片段着色器(您可以通过按“w”键在它们之间切换):

    ------------------------------------SHADER0-------------------------------------
    #pragma optimize (off)
    
    uniform vec2 resolution;
    int my_time = 635;
    
    vec3 f(vec2 x) {
    
        x = sin(abs(x * 0.5));
        float cs = cos(float(my_time) * 10037.5);
        float ss = sin(float(my_time) * 12.5) * 0.09;
        float t = sin(float(my_time)) * 0.5 + 0.5;
        float d = sin(10. * length(x - vec2(cs, ss)) + mix(8., 10., t));
        vec3 color = mix(vec3(0.8,0.86,0.85), vec3(0.52,0.72,0.79), sin(t) * 0.5);
        return color*pow(d, 2.) / 5.;
    }
    
    void main() {
        vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
        uv.x *= (resolution.x / resolution.y);
        vec3 col = f(uv);
        gl_FragColor = vec4(col*5.0, 1.0);
    }
    
    ------------------------------------SHADER1-------------------------------------
    #pragma optimize (off)
    
    uniform vec2 resolution;
    uniform int my_time;
    
    vec3 f(vec2 x) {
    
        x = sin(abs(x * 0.5));
        float cs = cos(float(my_time) * 10037.5);
        float ss = sin(float(my_time) * 12.5) * 0.09;
        float t = sin(float(my_time)) * 0.5 + 0.5;
        float d = sin(10. * length(x - vec2(cs, ss)) + mix(8., 10., t));
        vec3 color = mix(vec3(0.8,0.86,0.85), vec3(0.52,0.72,0.79), sin(t) * 0.5);
        return color*pow(d, 2.) / 5.;
    }
    
    void main() {
        vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
        uv.x *= (resolution.x / resolution.y);
        vec3 col = f(uv);
        gl_FragColor = vec4(col*5.0, 1.0);
    }
    

    人们会期望两个着色器提供完全相同的输出。不幸的是,事实并非如此,如果您运行此代码,您将看到这两种代码看起来有多么不同:

    enter image description here enter image description here

    问题 :

    • 如果你能在那里复制它,你认为产生这些差异的原因是什么?这可能是司机的错误吗?
    • 但最重要的是。。。即使这是一个驱动程序错误,我如何防止当前驱动程序的这种不当行为?

    主要原因是我希望在着色器中使用统一变量作为硬编码值,而不会在调整着色器后得到任何不同的结果。

    可以复制错误的规范

    • GeForce GTX 970M/PCIe/SSE2&4.4.0英伟达344.42和;win7终极版
    • GeForce GTX 1060 6GB/PCIe/SSE2&英伟达435.21&Debian 9.11
    • GeForce RTX 2070&driver 26.21.14.3160 2019年7月16日及;窗户

    该漏洞不会出现的规格

    • Radeon Pro 560&苹果的烘焙驱动程序&马科斯·莫哈韦(10.14.4)
    0 回复  |  直到 5 年前