// // 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 #include #include #include #include "common/utilities.h" #include "util/test_utils.h" namespace { GLuint CompileProgramInternal(const char *vsSource, const char *tcsSource, const char *tesSource, const char *gsSource, const char *fsSource, const std::function &preLinkCallback) { GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource); if (vs == 0 || fs == 0) { glDeleteShader(fs); glDeleteShader(vs); glDeleteProgram(program); return 0; } 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(const_cast(userParam)); if (callbackChain) { callbackChain(source, type, id, severity, length, message, gCallbackChainUserParam); } } void GetPerfCounterValue(const CounterNameToIndexMap &counterIndexMap, std::vector &triplets, const char *name, GLuint64 *counterOut) { auto iter = counterIndexMap.find(name); ASSERT(iter != counterIndexMap.end()); GLuint counterIndex = iter->second; for (const angle::PerfMonitorTriplet &triplet : triplets) { ASSERT(triplet.group == 0); if (triplet.counter == counterIndex) { *counterOut = triplet.value; return; } } // Additional logs for b/382094011 std::cerr << "GetPerfCounterValue missing counter: " << name << "; index: " << counterIndex << "; triplets: " << std::endl; for (const angle::PerfMonitorTriplet &triplet : triplets) { std::cerr << triplet.counter << " " << triplet.value << std::endl; } UNREACHABLE(); } } // 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 infoLog(infoLogLength); glGetShaderInfoLog(shader, static_cast(infoLog.size()), nullptr, &infoLog[0]); std::cerr << "shader compilation failed: " << &infoLog[0]; } else { std::cerr << "shader compilation failed. "; } std::cerr << std::endl; glDeleteShader(shader); shader = 0; } return shader; } GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath) { std::string source; if (!angle::ReadEntireFileToString(sourcePath.c_str(), &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 infoLog(infoLogLength); glGetProgramInfoLog(program, static_cast(infoLog.size()), nullptr, &infoLog[0]); std::cerr << "program link failed: " << &infoLog[0]; } else { std::cerr << "program link failed. "; } } 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 &transformFeedbackVaryings, GLenum bufferMode) { auto preLink = [&](GLuint program) { if (transformFeedbackVaryings.size() > 0) { std::vector constCharTFVaryings; for (const std::string &transformFeedbackVarying : transformFeedbackVaryings) { constCharTFVaryings.push_back(transformFeedbackVarying.c_str()); } glTransformFeedbackVaryings(program, static_cast(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 &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 (!angle::ReadEntireFileToString(vsPath.c_str(), &vsSource)) { std::cerr << "Error reading shader: " << vsPath << "\n"; return 0; } std::string fsSource; if (!angle::ReadEntireFileToString(fsPath.c_str(), &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 &binary, GLenum binaryFormat) { GLuint program = glCreateProgram(); glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast(binary.size())); return CheckLinkStatusAndReturnProgram(program, true); } GLuint LoadBinaryProgramES3(const std::vector &binary, GLenum binaryFormat) { GLuint program = glCreateProgram(); glProgramBinary(program, binaryFormat, binary.data(), static_cast(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(callbackChain)); } CounterNameToIndexMap BuildCounterNameToIndexMap() { GLint numCounters = 0; glGetPerfMonitorCountersAMD(0, &numCounters, nullptr, 0, nullptr); if (glGetError() != GL_NO_ERROR) { std::cerr << "glGetPerfMonitorCountersAMD failed (count)" << std::endl; return {}; } std::vector counterIndexes(numCounters, 0); glGetPerfMonitorCountersAMD(0, nullptr, nullptr, numCounters, counterIndexes.data()); if (glGetError() != GL_NO_ERROR) { std::cerr << "glGetPerfMonitorCountersAMD failed (data)" << std::endl; return {}; } CounterNameToIndexMap indexMap; for (GLuint counterIndex : counterIndexes) { static constexpr size_t kBufSize = 1000; char buffer[kBufSize] = {}; glGetPerfMonitorCounterStringAMD(0, counterIndex, kBufSize, nullptr, buffer); if (glGetError() != GL_NO_ERROR) { std::cerr << "glGetPerfMonitorCounterStringAMD failed" << std::endl; return {}; } indexMap[buffer] = counterIndex; } return indexMap; } std::vector GetPerfMonitorTriplets() { GLuint resultSize = 0; glGetPerfMonitorCounterDataAMD(0, GL_PERFMON_RESULT_SIZE_AMD, sizeof(GLuint), &resultSize, nullptr); if (glGetError() != GL_NO_ERROR || resultSize == 0) { std::cerr << "glGetPerfMonitorCounterDataAMD failed (count)" << std::endl; return {}; } std::vector perfResults(resultSize / sizeof(angle::PerfMonitorTriplet)); GLint bytesWritten = 0; glGetPerfMonitorCounterDataAMD( 0, GL_PERFMON_RESULT_AMD, static_cast(perfResults.size() * sizeof(perfResults[0])), &perfResults.data()->group, &bytesWritten); if (glGetError() != GL_NO_ERROR) { std::cerr << "glGetPerfMonitorCounterDataAMD failed (data)" << std::endl; return {}; } ASSERT(static_cast(bytesWritten) == resultSize); return perfResults; } angle::VulkanPerfCounters GetPerfCounters(const CounterNameToIndexMap &indexMap) { std::vector perfResults = GetPerfMonitorTriplets(); angle::VulkanPerfCounters counters; #define ANGLE_UNPACK_PERF_COUNTER(COUNTER) \ GetPerfCounterValue(indexMap, perfResults, #COUNTER, &counters.COUNTER); ANGLE_VK_PERF_COUNTERS_X(ANGLE_UNPACK_PERF_COUNTER) #undef ANGLE_UNPACK_PERF_COUNTER return counters; } CounterNameToValueMap BuildCounterNameToValueMap() { CounterNameToIndexMap indexMap = BuildCounterNameToIndexMap(); std::vector perfResults = GetPerfMonitorTriplets(); CounterNameToValueMap valueMap; for (const auto &iter : indexMap) { const std::string &name = iter.first; GLuint index = iter.second; valueMap[name] = perfResults[index].value; } return valueMap; } 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 sets gl_Position to attribute a_position, and sets gl_PointSize to 1. const char *SimpleForPoints() { return R"(precision highp float; attribute vec4 a_position; void main() { gl_Position = a_position; gl_PointSize = 1.0; })"; } // 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 = a_position; v_texCoord = a_position.xy * 0.5 + vec2(0.5); })"; } const char *Texture2DArray() { return R"(#version 300 es out vec2 v_texCoord; in vec4 a_position; void main() { gl_Position = vec4(a_position.xy, 0.0, 1.0); v_texCoord = (a_position.xy * 0.5) + 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); })"; } const char *Texture2DArray() { return R"(#version 300 es precision highp float; uniform highp sampler2DArray tex2DArray; uniform int slice; in vec2 v_texCoord; out vec4 fragColor; void main() { fragColor = texture(tex2DArray, vec3(v_texCoord, float(slice))); })"; } } // 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 sets gl_Position to attribute a_position, and sets gl_PointSize to 1. const char *SimpleForPoints() { return R"(#version 300 es in vec4 a_position; void main() { gl_Position = a_position; gl_PointSize = 1.0; })"; } // 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