| // |
| // Copyright (c) 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 "shader_utils.h" |
| |
| #include <cstring> |
| #include <fstream> |
| #include <iostream> |
| #include <vector> |
| |
| namespace |
| { |
| std::string ReadFileToString(const std::string &source) |
| { |
| std::ifstream stream(source.c_str()); |
| if (!stream) |
| { |
| std::cerr << "Failed to load shader file: " << source; |
| return ""; |
| } |
| |
| std::string result; |
| |
| stream.seekg(0, std::ios::end); |
| result.reserve(static_cast<unsigned int>(stream.tellg())); |
| stream.seekg(0, std::ios::beg); |
| |
| result.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()); |
| |
| return result; |
| } |
| |
| GLuint CompileProgramInternal(const char *vsSource, |
| 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 gs = 0; |
| |
| if (strlen(gsSource) > 0) |
| { |
| gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource); |
| if (gs == 0) |
| { |
| glDeleteShader(vs); |
| glDeleteShader(fs); |
| glDeleteProgram(program); |
| return 0; |
| } |
| |
| glAttachShader(program, gs); |
| glDeleteShader(gs); |
| } |
| |
| if (preLinkCallback) |
| { |
| preLinkCallback(program); |
| } |
| |
| glLinkProgram(program); |
| |
| return CheckLinkStatusAndReturnProgram(program, true); |
| } |
| } // 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 = ReadFileToString(sourcePath); |
| if (source.empty()) |
| { |
| 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 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 CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath) |
| { |
| std::string vsSource = ReadFileToString(vsPath); |
| std::string fsSource = ReadFileToString(fsPath); |
| if (vsSource.empty() || fsSource.empty()) |
| { |
| 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); |
| } |
| |
| namespace angle |
| { |
| |
| namespace essl1_shaders |
| { |
| |
| const char *PositionAttrib() |
| { |
| return "a_position"; |
| } |
| const char *ColorUniform() |
| { |
| return "u_color"; |
| } |
| |
| 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; |
| })"; |
| } |
| |
| } // 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() |
| { |
| if (v_position.x * v_position.y > 0.0) |
| { |
| gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); |
| } |
| else |
| { |
| gl_FragColor = vec4(0.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); |
| })"; |
| } |
| |
| } // namespace fs |
| } // namespace essl1_shaders |
| |
| namespace essl3_shaders |
| { |
| |
| const char *PositionAttrib() |
| { |
| return "a_position"; |
| } |
| |
| 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; |
| })"; |
| } |
| |
| } // 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); |
| })"; |
| } |
| |
| } // 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); |
| })"; |
| } |
| |
| } // namespace fs |
| } // namespace essl31_shaders |
| } // namespace angle |