// Copyright 2019 The MediaPipe Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "mediapipe/gpu/shader_util.h" #include #include "mediapipe/framework/port/logging.h" #if DEBUG #define GL_DEBUG_LOG(type, object, action) \ do { \ GLint log_length = 0; \ glGet##type##iv(object, GL_INFO_LOG_LENGTH, &log_length); \ if (log_length > 0) { \ GLchar* log = static_cast(malloc(log_length)); \ glGet##type##InfoLog(object, log_length, &log_length, log); \ LOG(INFO) << #type " " action " log:\n" << log; \ free(log); \ } \ } while (0) #else #define GL_DEBUG_LOG(type, object, action) #endif #define GL_ERROR_LOG(type, object, action) \ do { \ GLint log_length = 0; \ glGet##type##iv(object, GL_INFO_LOG_LENGTH, &log_length); \ if (log_length > 0) { \ GLchar* log = static_cast(malloc(log_length)); \ glGet##type##InfoLog(object, log_length, &log_length, log); \ LOG(ERROR) << #type " " action " log:\n" << log; \ free(log); \ } \ } while (0) namespace mediapipe { constexpr int kMaxShaderInfoLength = 1024; GLint GlhCompileShader(GLenum target, const GLchar* source, GLuint* shader, bool force_log_errors) { *shader = glCreateShader(target); if (*shader == 0) { return GL_FALSE; } glShaderSource(*shader, 1, &source, NULL); glCompileShader(*shader); GL_DEBUG_LOG(Shader, *shader, "compile"); #if UNSAFE_EMSCRIPTEN_SKIP_GL_ERROR_HANDLING if (!force_log_errors) { return GL_TRUE; } #endif // UNSAFE_EMSCRIPTEN_SKIP_GL_ERROR_HANDLING GLint status; glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); LOG_IF(ERROR, status == GL_FALSE) << "Failed to compile shader:\n" << source; if (status == GL_FALSE) { int length = 0; GLchar cmessage[kMaxShaderInfoLength]; glGetShaderInfoLog(*shader, kMaxShaderInfoLength, &length, cmessage); LOG(ERROR) << "Error message: " << std::string(cmessage, length); } return status; } GLint GlhLinkProgram(GLuint program, bool force_log_errors) { glLinkProgram(program); #if UNSAFE_EMSCRIPTEN_SKIP_GL_ERROR_HANDLING if (!force_log_errors) { return GL_TRUE; } #endif // UNSAFE_EMSCRIPTEN_SKIP_GL_ERROR_HANDLING GLint status; GL_DEBUG_LOG(Program, program, "link"); glGetProgramiv(program, GL_LINK_STATUS, &status); LOG_IF(ERROR, status == GL_FALSE) << "Failed to link program " << program; return status; } GLint GlhValidateProgram(GLuint program) { GLint status; glValidateProgram(program); GL_DEBUG_LOG(Program, program, "validate"); glGetProgramiv(program, GL_VALIDATE_STATUS, &status); LOG_IF(ERROR, status == GL_FALSE) << "Failed to validate program " << program; return status; } GLint GlhCreateProgram(const GLchar* vert_src, const GLchar* frag_src, GLsizei attr_count, const GLchar* const* attr_names, const GLint* attr_locations, GLuint* program, bool force_log_errors) { GLuint vert_shader = 0; GLuint frag_shader = 0; GLint ok = GL_TRUE; *program = glCreateProgram(); if (*program == 0) { return GL_FALSE; } ok = ok && GlhCompileShader(GL_VERTEX_SHADER, vert_src, &vert_shader, force_log_errors); ok = ok && GlhCompileShader(GL_FRAGMENT_SHADER, frag_src, &frag_shader, force_log_errors); if (ok) { glAttachShader(*program, vert_shader); glAttachShader(*program, frag_shader); // Attribute location binding must be set before linking. for (int i = 0; i < attr_count; i++) { glBindAttribLocation(*program, attr_locations[i], attr_names[i]); } ok = GlhLinkProgram(*program, force_log_errors); } if (vert_shader) glDeleteShader(vert_shader); if (frag_shader) glDeleteShader(frag_shader); if (!ok) { glDeleteProgram(*program); *program = 0; } return ok; } bool CompileShader(GLenum shader_type, const std::string& shader_source, GLuint* shader) { *shader = glCreateShader(shader_type); if (*shader == 0) { VLOG(2) << "Unable to create shader of type: " << shader_type; return false; } const char* shader_source_cstr = shader_source.c_str(); glShaderSource(*shader, 1, &shader_source_cstr, NULL); glCompileShader(*shader); GLint compiled; glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { VLOG(2) << "Unable to compile shader:\n" << shader_source; GL_ERROR_LOG(Shader, *shader, "compile"); glDeleteShader(*shader); *shader = 0; return false; } return true; } bool CreateShaderProgram( GLuint vertex_shader, GLuint fragment_shader, const std::unordered_map& attributes, GLuint* shader_program) { *shader_program = glCreateProgram(); if (*shader_program == 0) { VLOG(2) << "Unable to create shader program"; return false; } glAttachShader(*shader_program, vertex_shader); glAttachShader(*shader_program, fragment_shader); for (const auto& it : attributes) { glBindAttribLocation(*shader_program, it.first, it.second.c_str()); } glLinkProgram(*shader_program); GLint is_linked = 0; glGetProgramiv(*shader_program, GL_LINK_STATUS, &is_linked); if (!is_linked) { glDeleteProgram(*shader_program); *shader_program = 0; return false; } return true; } } // namespace mediapipe