Edit

kc3-lang/angle/util/shader_utils.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-03-23 16:38:50
    Hash : 68f06888
    Message : Perf Tests: Trigger test failure on API errors. A prior refactor had broken the method we used to cause test steps to fail on API errors. This CL restores the path. We detected this when analyzing a particular GLES trace that contained invalid GLES calls. Bug: angleproject:5788 Change-Id: I26940e49cf73ce6050ea6ee274d5c5748835a167 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2782008 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Cody Northrop <cnorthrop@google.com> Reviewed-by: Tim Van Patten <timvp@google.com>

  • util/shader_utils.cpp
  • //
    // Copyright 2014 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "util/shader_utils.h"
    
    #include <cstring>
    #include <fstream>
    #include <iostream>
    #include <vector>
    
    #include "common/utilities.h"
    #include "util/test_utils.h"
    
    namespace
    {
    bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
    {
        constexpr uint32_t kMaxBufferSize = 2000;
        char buffer[kMaxBufferSize]       = {};
        if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
            strlen(buffer) == 0)
            return false;
        *contentsOut = buffer;
        return true;
    }
    
    GLuint CompileProgramInternal(const char *vsSource,
                                  const char *tcsSource,
                                  const char *tesSource,
                                  const char *gsSource,
                                  const char *fsSource,
                                  const std::function<void(GLuint)> &preLinkCallback)
    {
        GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
        GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
    
        if (vs == 0 || fs == 0)
        {
            glDeleteShader(fs);
            glDeleteShader(vs);
            return 0;
        }
    
        GLuint program = glCreateProgram();
    
        glAttachShader(program, vs);
        glDeleteShader(vs);
    
        glAttachShader(program, fs);
        glDeleteShader(fs);
    
        GLuint tcs = 0;
        GLuint tes = 0;
        GLuint gs  = 0;
    
        if (strlen(tcsSource) > 0)
        {
            tcs = CompileShader(GL_TESS_CONTROL_SHADER_EXT, tcsSource);
            if (tcs == 0)
            {
                glDeleteShader(vs);
                glDeleteShader(fs);
                glDeleteProgram(program);
                return 0;
            }
    
            glAttachShader(program, tcs);
            glDeleteShader(tcs);
        }
    
        if (strlen(tesSource) > 0)
        {
            tes = CompileShader(GL_TESS_EVALUATION_SHADER_EXT, tesSource);
            if (tes == 0)
            {
                glDeleteShader(vs);
                glDeleteShader(fs);
                glDeleteShader(tcs);
                glDeleteProgram(program);
                return 0;
            }
    
            glAttachShader(program, tes);
            glDeleteShader(tes);
        }
    
        if (strlen(gsSource) > 0)
        {
            gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
            if (gs == 0)
            {
                glDeleteShader(vs);
                glDeleteShader(fs);
                glDeleteShader(tcs);
                glDeleteShader(tes);
                glDeleteProgram(program);
                return 0;
            }
    
            glAttachShader(program, gs);
            glDeleteShader(gs);
        }
    
        if (preLinkCallback)
        {
            preLinkCallback(program);
        }
    
        glLinkProgram(program);
    
        return CheckLinkStatusAndReturnProgram(program, true);
    }
    
    const void *gCallbackChainUserParam;
    
    void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
                                               GLenum type,
                                               GLuint id,
                                               GLenum severity,
                                               GLsizei length,
                                               const GLchar *message,
                                               const void *userParam)
    {
        std::string sourceText   = gl::GetDebugMessageSourceString(source);
        std::string typeText     = gl::GetDebugMessageTypeString(type);
        std::string severityText = gl::GetDebugMessageSeverityString(severity);
        std::cerr << sourceText << ", " << typeText << ", " << severityText << ": " << message << "\n";
    
        GLDEBUGPROC callbackChain = reinterpret_cast<GLDEBUGPROC>(const_cast<void *>(userParam));
        if (callbackChain)
        {
            callbackChain(source, type, id, severity, length, message, gCallbackChainUserParam);
        }
    }
    }  // namespace
    
    GLuint CompileShader(GLenum type, const char *source)
    {
        GLuint shader = glCreateShader(type);
    
        const char *sourceArray[1] = {source};
        glShaderSource(shader, 1, sourceArray, nullptr);
        glCompileShader(shader);
    
        GLint compileResult;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
    
        if (compileResult == 0)
        {
            GLint infoLogLength;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
    
            // Info log length includes the null terminator, so 1 means that the info log is an empty
            // string.
            if (infoLogLength > 1)
            {
                std::vector<GLchar> infoLog(infoLogLength);
                glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
                std::cerr << "shader compilation failed: " << &infoLog[0];
            }
            else
            {
                std::cerr << "shader compilation failed. <Empty log message>";
            }
    
            std::cerr << std::endl;
    
            glDeleteShader(shader);
            shader = 0;
        }
    
        return shader;
    }
    
    GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
    {
        std::string source;
        if (!ReadEntireFile(sourcePath, &source))
        {
            std::cerr << "Error reading shader file: " << sourcePath << "\n";
            return 0;
        }
    
        return CompileShader(type, source.c_str());
    }
    
    GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
    {
        if (glGetError() != GL_NO_ERROR)
            return 0;
    
        GLint linkStatus;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus == 0)
        {
            if (outputErrorMessages)
            {
                GLint infoLogLength;
                glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
    
                // Info log length includes the null terminator, so 1 means that the info log is an
                // empty string.
                if (infoLogLength > 1)
                {
                    std::vector<GLchar> infoLog(infoLogLength);
                    glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
                                        &infoLog[0]);
    
                    std::cerr << "program link failed: " << &infoLog[0];
                }
                else
                {
                    std::cerr << "program link failed. <Empty log message>";
                }
            }
    
            glDeleteProgram(program);
            return 0;
        }
    
        return program;
    }
    
    GLuint GetProgramShader(GLuint program, GLint requestedType)
    {
        static constexpr GLsizei kMaxShaderCount = 16;
        GLuint attachedShaders[kMaxShaderCount]  = {0u};
        GLsizei count                            = 0;
        glGetAttachedShaders(program, kMaxShaderCount, &count, attachedShaders);
        for (int i = 0; i < count; ++i)
        {
            GLint type = 0;
            glGetShaderiv(attachedShaders[i], GL_SHADER_TYPE, &type);
            if (type == requestedType)
            {
                return attachedShaders[i];
            }
        }
    
        return 0;
    }
    
    GLuint CompileProgramWithTransformFeedback(
        const char *vsSource,
        const char *fsSource,
        const std::vector<std::string> &transformFeedbackVaryings,
        GLenum bufferMode)
    {
        auto preLink = [&](GLuint program) {
            if (transformFeedbackVaryings.size() > 0)
            {
                std::vector<const char *> constCharTFVaryings;
    
                for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
                {
                    constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
                }
    
                glTransformFeedbackVaryings(program,
                                            static_cast<GLsizei>(transformFeedbackVaryings.size()),
                                            &constCharTFVaryings[0], bufferMode);
            }
        };
    
        return CompileProgramInternal(vsSource, "", "", "", fsSource, preLink);
    }
    
    GLuint CompileProgram(const char *vsSource, const char *fsSource)
    {
        return CompileProgramInternal(vsSource, "", "", "", fsSource, nullptr);
    }
    
    GLuint CompileProgram(const char *vsSource,
                          const char *fsSource,
                          const std::function<void(GLuint)> &preLinkCallback)
    {
        return CompileProgramInternal(vsSource, "", "", "", fsSource, preLinkCallback);
    }
    
    GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const char *fsSource)
    {
        return CompileProgramInternal(vsSource, "", "", gsSource, fsSource, nullptr);
    }
    
    GLuint CompileProgramWithTESS(const char *vsSource,
                                  const char *tcsSource,
                                  const char *tesSource,
                                  const char *fsSource)
    {
        return CompileProgramInternal(vsSource, tcsSource, tesSource, "", fsSource, nullptr);
    }
    
    GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
    {
        std::string vsSource;
        if (!ReadEntireFile(vsPath, &vsSource))
        {
            std::cerr << "Error reading shader: " << vsPath << "\n";
            return 0;
        }
    
        std::string fsSource;
        if (!ReadEntireFile(fsPath, &fsSource))
        {
            std::cerr << "Error reading shader: " << fsPath << "\n";
            return 0;
        }
    
        return CompileProgram(vsSource.c_str(), fsSource.c_str());
    }
    
    GLuint CompileComputeProgram(const char *csSource, bool outputErrorMessages)
    {
        GLuint program = glCreateProgram();
    
        GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
        if (cs == 0)
        {
            glDeleteProgram(program);
            return 0;
        }
    
        glAttachShader(program, cs);
    
        glLinkProgram(program);
    
        return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
    }
    
    GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
    {
        GLuint program = glCreateProgram();
        glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
        return CheckLinkStatusAndReturnProgram(program, true);
    }
    
    GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
    {
        GLuint program = glCreateProgram();
        glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
        return CheckLinkStatusAndReturnProgram(program, true);
    }
    
    bool LinkAttachedProgram(GLuint program)
    {
        glLinkProgram(program);
        return (CheckLinkStatusAndReturnProgram(program, true) != 0);
    }
    
    void EnableDebugCallback(GLDEBUGPROC callbackChain, const void *userParam)
    {
        gCallbackChainUserParam = userParam;
    
        glEnable(GL_DEBUG_OUTPUT);
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
        // Enable medium and high priority messages.
        glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr,
                                 GL_TRUE);
        glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr,
                                 GL_TRUE);
        // Disable low and notification priority messages.
        glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr,
                                 GL_FALSE);
        glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr,
                                 GL_FALSE);
        // Disable performance messages to reduce spam.
        glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, 0, nullptr,
                                 GL_FALSE);
        glDebugMessageCallbackKHR(DebugMessageCallback, reinterpret_cast<const void *>(callbackChain));
    }
    
    namespace angle
    {
    
    namespace essl1_shaders
    {
    
    const char *PositionAttrib()
    {
        return "a_position";
    }
    const char *ColorUniform()
    {
        return "u_color";
    }
    
    const char *Texture2DUniform()
    {
        return "u_tex2D";
    }
    
    namespace vs
    {
    
    // A shader that sets gl_Position to zero.
    const char *Zero()
    {
        return R"(void main()
    {
        gl_Position = vec4(0);
    })";
    }
    
    // A shader that sets gl_Position to attribute a_position.
    const char *Simple()
    {
        return R"(precision highp float;
    attribute vec4 a_position;
    
    void main()
    {
        gl_Position = a_position;
    })";
    }
    
    // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
    // v_position.
    const char *Passthrough()
    {
        return R"(precision highp float;
    attribute vec4 a_position;
    varying vec4 v_position;
    
    void main()
    {
        gl_Position = a_position;
        v_position = a_position;
    })";
    }
    
    // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
    // texcoord.
    const char *Texture2D()
    {
        return R"(precision highp float;
    attribute vec4 a_position;
    varying vec2 v_texCoord;
    
    void main()
    {
        gl_Position = vec4(a_position.xy, 0.0, 1.0);
        v_texCoord = a_position.xy * 0.5 + vec2(0.5);
    })";
    }
    
    }  // namespace vs
    
    namespace fs
    {
    
    // A shader that renders a simple checker pattern of red and green. X axis and y axis separate the
    // different colors. Needs varying v_position.
    const char *Checkered()
    {
        return R"(precision highp float;
    varying vec4 v_position;
    
    void main()
    {
        bool isLeft = v_position.x < 0.0;
        bool isTop = v_position.y < 0.0;
        if (isLeft)
        {
            if (isTop)
            {
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
            }
            else
            {
                gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
            }
        }
        else
        {
            if (isTop)
            {
                gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
            }
            else
            {
                gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
            }
        }
    })";
    }
    
    // A shader that fills with color taken from uniform named "color".
    const char *UniformColor()
    {
        return R"(uniform mediump vec4 u_color;
    void main(void)
    {
        gl_FragColor = u_color;
    })";
    }
    
    // A shader that fills with 100% opaque red.
    const char *Red()
    {
        return R"(precision mediump float;
    
    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    })";
    }
    
    // A shader that fills with 100% opaque green.
    const char *Green()
    {
        return R"(precision mediump float;
    
    void main()
    {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    })";
    }
    
    // A shader that fills with 100% opaque blue.
    const char *Blue()
    {
        return R"(precision mediump float;
    
    void main()
    {
        gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
    })";
    }
    
    // A shader that samples the texture.
    const char *Texture2D()
    {
        return R"(precision mediump float;
    uniform sampler2D u_tex2D;
    varying vec2 v_texCoord;
    
    void main()
    {
        gl_FragColor = texture2D(u_tex2D, v_texCoord);
    })";
    }
    
    }  // namespace fs
    }  // namespace essl1_shaders
    
    namespace essl3_shaders
    {
    
    const char *PositionAttrib()
    {
        return "a_position";
    }
    const char *Texture2DUniform()
    {
        return "u_tex2D";
    }
    const char *LodUniform()
    {
        return "u_lod";
    }
    
    namespace vs
    {
    
    // A shader that sets gl_Position to zero.
    const char *Zero()
    {
        return R"(#version 300 es
    void main()
    {
        gl_Position = vec4(0);
    })";
    }
    
    // A shader that sets gl_Position to attribute a_position.
    const char *Simple()
    {
        return R"(#version 300 es
    in vec4 a_position;
    void main()
    {
        gl_Position = a_position;
    })";
    }
    
    // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
    // v_position.
    const char *Passthrough()
    {
        return R"(#version 300 es
    in vec4 a_position;
    out vec4 v_position;
    void main()
    {
        gl_Position = a_position;
        v_position = a_position;
    })";
    }
    
    // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
    // texcoord.
    const char *Texture2DLod()
    {
        return R"(#version 300 es
    in vec4 a_position;
    out vec2 v_texCoord;
    
    void main()
    {
        gl_Position = vec4(a_position.xy, 0.0, 1.0);
        v_texCoord = a_position.xy * 0.5 + vec2(0.5);
    })";
    }
    
    }  // namespace vs
    
    namespace fs
    {
    
    // A shader that fills with 100% opaque red.
    const char *Red()
    {
        return R"(#version 300 es
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    })";
    }
    
    // A shader that fills with 100% opaque green.
    const char *Green()
    {
        return R"(#version 300 es
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    })";
    }
    
    // A shader that fills with 100% opaque blue.
    const char *Blue()
    {
        return R"(#version 300 es
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
    })";
    }
    
    // A shader that samples the texture at a given lod.
    const char *Texture2DLod()
    {
        return R"(#version 300 es
    precision mediump float;
    uniform sampler2D u_tex2D;
    uniform float u_lod;
    in vec2 v_texCoord;
    out vec4 my_FragColor;
    
    void main()
    {
        my_FragColor = textureLod(u_tex2D, v_texCoord, u_lod);
    })";
    }
    
    }  // namespace fs
    }  // namespace essl3_shaders
    
    namespace essl31_shaders
    {
    
    const char *PositionAttrib()
    {
        return "a_position";
    }
    
    namespace vs
    {
    
    // A shader that sets gl_Position to zero.
    const char *Zero()
    {
        return R"(#version 310 es
    void main()
    {
        gl_Position = vec4(0);
    })";
    }
    
    // A shader that sets gl_Position to attribute a_position.
    const char *Simple()
    {
        return R"(#version 310 es
    in vec4 a_position;
    void main()
    {
        gl_Position = a_position;
    })";
    }
    
    // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
    // v_position.
    const char *Passthrough()
    {
        return R"(#version 310 es
    in vec4 a_position;
    out vec4 v_position;
    void main()
    {
        gl_Position = a_position;
        v_position = a_position;
    })";
    }
    
    }  // namespace vs
    
    namespace fs
    {
    
    // A shader that fills with 100% opaque red.
    const char *Red()
    {
        return R"(#version 310 es
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    })";
    }
    
    // A shader that fills with 100% opaque green.
    const char *Green()
    {
        return R"(#version 310 es
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    })";
    }
    
    // A shader that renders a simple gradient of red to green. Needs varying v_position.
    const char *RedGreenGradient()
    {
        return R"(#version 310 es
    precision highp float;
    in vec4 v_position;
    out vec4 my_FragColor;
    
    void main()
    {
        my_FragColor = vec4(v_position.xy * 0.5 + vec2(0.5), 0.0, 1.0);
    })";
    }
    
    }  // namespace fs
    }  // namespace essl31_shaders
    }  // namespace angle