From d0e18bdb7924c71cdca8dd983711171d87ef28be Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 17 Jan 2022 23:12:32 -0500 Subject: glplanet, an OpenGL-based planetary renderer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glplanet draws Earth like it currently appears from space, putting nighttime areas in shadow and daytime areas in light. It’s modeled after Xplanet (http://xplanet.sourceforge.net/), but whereas Xplanet is entirely a CPU-resident program, glplanet draws using OpenGL. It’s thus much less resource-intensive, particularly when using high-resolution textures. --- src/gl/shader.cc | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/gl/shader.cc (limited to 'src/gl/shader.cc') diff --git a/src/gl/shader.cc b/src/gl/shader.cc new file mode 100644 index 0000000..77887f2 --- /dev/null +++ b/src/gl/shader.cc @@ -0,0 +1,204 @@ +// Copyright 2021 Benjamin Barenblat +// +// 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 +// +// https://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 "src/gl/shader.h" + +#include +#include + +#include "src/gl/error.h" +#include "third_party/abseil/absl/strings/str_cat.h" +#include "third_party/abseil/absl/strings/string_view.h" +#include "third_party/glew/include/GL/glew.h" + +namespace gl { + +namespace { + +using ::gl_internal::CheckThreadSafety; +using ::gl_internal::ErrorCheck; +using ::gl_internal::UnnecessaryErrorCheck; + +std::string GetInfoLog( + void (*gl_get)(GLuint, GLenum, GLint*), + void (*gl_get_info_log)(GLuint, GLsizei, GLsizei*, GLchar*), + GLuint resource) noexcept(!gl_internal::kAggressiveErrorChecking) { + GLint log_length; + gl_get(resource, GL_INFO_LOG_LENGTH, &log_length); + UnnecessaryErrorCheck(); + if (log_length == 0) { + return ""; + } + + std::string log(log_length, '\0'); + gl_get_info_log(resource, log_length, /*length=*/nullptr, log.data()); + UnnecessaryErrorCheck(); + log.pop_back(); // pop the nullptr + return log; +} + +} // namespace + +Shader::Shader(GLenum type) : source_set_(false), compile_attempted_(false) { + CheckThreadSafety(); + + shader_ = glCreateShader(type); + UnnecessaryErrorCheck(); + if (shader_ == 0) { + throw std::runtime_error("GL: failed to create shader"); + } +} + +void Shader::SetSource(absl::string_view source) { + CheckThreadSafety(); + + std::array source_ptrs = {source.data()}; + if (source.size() > std::numeric_limits::max()) { + throw std::invalid_argument( + "GL: shader source is too large for OpenGL to handle"); + } + std::array lengths = { + static_cast(source.size())}; + glShaderSource(shader_, source_ptrs.size(), source_ptrs.data(), + lengths.data()); + UnnecessaryErrorCheck(); + + source_set_ = true; + compile_attempted_ = false; +} + +void Shader::Compile() { + CheckThreadSafety(); + + if (!source_set_) { + throw std::logic_error( + "GL: shader compilation requested, but no source available"); + } + + glCompileShader(shader_); + UnnecessaryErrorCheck(); + + compile_attempted_ = true; + compile_log_ = GetInfoLog(glGetShaderiv, glGetShaderInfoLog, shader_); + + GLint compiled; + glGetShaderiv(shader_, GL_COMPILE_STATUS, &compiled); + UnnecessaryErrorCheck(); + if (!compiled) { + throw std::runtime_error( + absl::StrCat("GL: failed to compile shader: ", compile_log_)); + } +} + +const std::string& Shader::compile_log() const { + if (!compile_attempted_) { + throw std::logic_error( + "GL: compilation log examined before shader was compiled"); + } + return compile_log_; +} + +ShaderProgram::ShaderProgram() + : has_fragment_shader_(false), link_attempted_(false) { + CheckThreadSafety(); + program_ = glCreateProgram(); + if (program_ == 0) { + throw std::runtime_error("GL: failed to create shader program"); + } +} + +void ShaderProgram::SetFragmentDataLocation(const char* name, + int framebuffer_id) { + CheckThreadSafety(); + + if (name == nullptr) { + throw std::invalid_argument( + "GL: requested fragment shader uniform with null name"); + } + if (!has_fragment_shader_) { + throw std::logic_error(absl::StrCat( + "GL: cannot set data location for fragment shader uniform ", name, + ": no fragment shader present")); + } + + glBindFragDataLocation(program_, framebuffer_id, name); + // This error check is be necessary because the name might be reserved. + ErrorCheck(); +} + +void ShaderProgram::Link() { + CheckThreadSafety(); + + glLinkProgram(program_); + UnnecessaryErrorCheck(); + + link_attempted_ = true; + link_log_ = GetInfoLog(glGetProgramiv, glGetProgramInfoLog, program_); + + GLint linked; + glGetProgramiv(program_, GL_LINK_STATUS, &linked); + UnnecessaryErrorCheck(); + if (!linked) { + throw std::runtime_error( + absl::StrCat("GL: failed to link shader program: ", link_log_)); + } +} + +const std::string& ShaderProgram::link_log() const { + if (!link_attempted_) { + throw std::logic_error( + "GL: link log examined before shader program was linked"); + } + return link_log_; +} + +int ShaderProgram::active_vertex_attribute(const char* name) const { + CheckThreadSafety(); + + if (name == nullptr) { + throw std::invalid_argument( + "GL: requested active vertex attribute with null name"); + } + + int index = glGetAttribLocation(program_, name); + // This error check is necessary because the program might not have been + // linked yet. + ErrorCheck(); + + if (index == -1) { + throw std::invalid_argument(absl::StrCat("GL: requested vertex attribute ", + name, " is reserved or inactive")); + } + return index; +} + +int ShaderProgram::active_uniform(const char* name) const { + CheckThreadSafety(); + + if (name == nullptr) { + throw std::invalid_argument("GL: requested active uniform with null name"); + } + + int index = glGetUniformLocation(program_, name); + // This error check is necessary because the program might not have been + // linked yet. + ErrorCheck(); + if (index == -1) { + throw std::invalid_argument(absl::StrCat("GL: requested uniform ", name, + " is reserved or inactive")); + } + return index; +} + +} // namespace gl -- cgit v1.2.3