summaryrefslogtreecommitdiff
path: root/src/gl/shader.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/gl/shader.cc')
-rw-r--r--src/gl/shader.cc204
1 files changed, 204 insertions, 0 deletions
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 <stdexcept>
+#include <string>
+
+#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<const GLchar*, 1> source_ptrs = {source.data()};
+ if (source.size() > std::numeric_limits<GLint>::max()) {
+ throw std::invalid_argument(
+ "GL: shader source is too large for OpenGL to handle");
+ }
+ std::array<GLint, source_ptrs.size()> lengths = {
+ static_cast<GLint>(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