summaryrefslogtreecommitdiff
path: root/src/gl/shader.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/gl/shader.h')
-rw-r--r--src/gl/shader.h211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/gl/shader.h b/src/gl/shader.h
new file mode 100644
index 0000000..b4cfb63
--- /dev/null
+++ b/src/gl/shader.h
@@ -0,0 +1,211 @@
+// 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.
+
+// Shaders and shader programs.
+
+#ifndef GLPLANET_SRC_GL_SHADER_H_
+#define GLPLANET_SRC_GL_SHADER_H_
+
+#include <array>
+#include <string>
+
+#include "src/gl/error.h"
+#include "third_party/abseil/absl/strings/string_view.h"
+#include "third_party/glew/include/GL/glew.h"
+
+namespace gl {
+
+class FragmentShader;
+class VertexShader;
+
+// An individual shader.
+//
+// The shader life cycle looks like this:
+//
+// Shader s;
+// s.SetSource(source_location);
+// s.Compile();
+// std::cerr << s.compile_log();
+//
+// The Compile function throws if errors occur during compilation, but warnings
+// are merely saved. You must retrieve the compilation log if you wish to report
+// warnings.
+class Shader {
+ public:
+ Shader(Shader&&) noexcept = default;
+ Shader& operator=(Shader&&) noexcept = default;
+
+ virtual ~Shader() noexcept(!(gl_internal::kThreadSafetyChecks ||
+ gl_internal::kAggressiveErrorChecking)) {
+ gl_internal::CheckThreadSafety();
+ glDeleteShader(shader_);
+ gl_internal::UnnecessaryErrorCheck();
+ }
+
+ // Loads the shader source. This function copies the source buffer, so the
+ // source buffer need not stay valid after this call.
+ //
+ // You may call this function repeatedly. Doing so invalidates the shader, and
+ // you must recompile it with Compile before using it.
+ void SetSource(absl::string_view source);
+
+ // Compiles the shader. Throws a std::runtime_error including the compilation
+ // log if compilation fails. If compilation succeeds but generates warnings,
+ // this function silently returns; check the compile_log member to see what
+ // the warnings were.
+ //
+ // You must call SetSource before calling this function.
+ //
+ // You may call this function repeatedly.
+ void Compile();
+
+ // Fetches the shader compilation log. If this is empty, there were no
+ // warnings during the compilation.
+ //
+ // You must call Compile before calling this function.
+ //
+ // You may call this function repeatedly.
+ const std::string& compile_log() const;
+
+ // The GL identifier for this shader.
+ unsigned int id() const noexcept { return shader_; }
+
+ protected:
+ GLuint shader_;
+ bool source_set_;
+ bool compile_attempted_;
+ std::string compile_log_;
+
+ private:
+ friend class FragmentShader;
+ friend class VertexShader;
+
+ explicit Shader(GLenum type);
+};
+
+// A fragment shader.
+class FragmentShader final : public Shader {
+ public:
+ explicit FragmentShader() : Shader(GL_FRAGMENT_SHADER) {}
+};
+
+// A vertex shader.
+class VertexShader final : public Shader {
+ public:
+ explicit VertexShader() : Shader(GL_VERTEX_SHADER) {}
+};
+
+// A shader program consisting of multiple shaders.
+//
+// The shader program life cycle looks like this:
+//
+// ShaderProgram p;
+// p.attach(s);
+// p.SetFragmentDataLocation("output_color", framebuffer_id);
+// p.Link();
+// std::cerr << s.link_log();
+// DoSomethingWith(p.active_vertex_attribute("whatever"));
+//
+// Analogously to Shader::Compile, Link throws if errors occur during linking,
+// but warnings are merely saved. You must retrieve the link log if you wish to
+// report warnings.
+class ShaderProgram final {
+ public:
+ explicit ShaderProgram();
+
+ ShaderProgram(ShaderProgram&&) noexcept = default;
+ ShaderProgram& operator=(ShaderProgram&&) noexcept = default;
+
+ ~ShaderProgram() noexcept(!(gl_internal::kThreadSafetyChecks ||
+ gl_internal::kAggressiveErrorChecking)) {
+ gl_internal::CheckThreadSafety();
+ glDeleteProgram(program_);
+ gl_internal::UnnecessaryErrorCheck();
+ }
+
+ // Attach shaders to the program. The shaders need not be compiled, but you
+ // must compile them before calling Link.
+ //
+ // You may call these functions repeatedly, so long as you never pass the same
+ // shader twice. In any case, calling these functions invalidates the program,
+ // and you must relink it with Link before using it.
+ void Attach(const VertexShader& shader) { AttachShader(shader); }
+ void Attach(const FragmentShader& shader) {
+ AttachShader(shader);
+ has_fragment_shader_ = true;
+ }
+
+ // Associates a fragment shader output variable with an output framebuffer.
+ // The variable name must not start with "gl_". The default framebuffer has ID
+ // 0.
+ //
+ // You must have a fragment shader attached to this program to call this
+ // function.
+ //
+ // You may call this function repeatedly. Doing so invalidates the program,
+ // and you must relink it with Link before using it.
+ void SetFragmentDataLocation(const char* name, int framebuffer_id);
+
+ // Links the shader program. Throws a std::runtime_error including the link
+ // log if linking fails. If linking succeeds but generates warnings, this
+ // function silently returns; check the link_log member to see what the
+ // warnings were.
+ //
+ // You may call this function repeatedly.
+ void Link();
+
+ // Fetches the program link log. If this is empty, there were no warnings
+ // during the link.
+ //
+ // You must call Link before calling this function.
+ //
+ // You may call this function repeatedly.
+ const std::string& link_log() const;
+
+ // The index of the specified active vertex attribute. The variable name must
+ // not start with "gl_".
+ //
+ // Throws std::invalid_argument if the specified name is reserved, inactive,
+ // or nonexistent. Throws std::logic_error if the program has not been linked.
+ int active_vertex_attribute(const char* name) const;
+
+ // The index of the specified active uniform variable. The variable name must
+ // not start with "gl_".
+ //
+ // Throws std::invalid_argument if the specified name is reserved, inactive,
+ // or nonexistent. Throws std::logic_error if the program has not been linked.
+ int active_uniform(const char* name) const;
+
+ // The GL identifier for this program.
+ unsigned int id() const noexcept { return program_; }
+
+ private:
+ void AttachShader(const Shader& shader) {
+ gl_internal::CheckThreadSafety();
+ glAttachShader(program_, shader.id());
+ // This error check is necessary because the user might have passed in the
+ // same shader twice.
+ gl_internal::ErrorCheck();
+ link_attempted_ = false;
+ }
+
+ GLuint program_;
+ bool has_fragment_shader_;
+ bool link_attempted_;
+ std::string link_log_;
+};
+
+} // namespace gl
+
+#endif // GLPLANET_SRC_GL_SHADER_H_