Add templated wrapper objects

- Shader
- Program
- Buffer
- VertexArray

Along with template magic to deduce VertexArray formats

- Format, IFormat, LFormat
- AutoFormat
- Binder
- ATTR
This commit is contained in:
2022-02-13 20:28:00 -05:00
committed by David Allemang
parent 669d04845d
commit 89b9780f6c
7 changed files with 360 additions and 144 deletions

48
src/gl/buffer.hpp Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <glad/glad.h>
#include <utility>
template<typename T_>
class Buffer {
public:
using Type = T_;
private:
GLuint id = 0;
public:
Buffer() {
glCreateBuffers(1, &id);
}
Buffer(Buffer&& o) noexcept {
id = std::exchange(o.id, 0);
}
Buffer(const Buffer&) = delete; // this is doable, but would be slow.
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
template<typename RandomIt>
GLuint upload(RandomIt first, RandomIt last, GLenum mode = GL_STATIC_DRAW) {
size_t count = last - first;
// todo StaticBuffer that uses BufferStorage
glNamedBufferData(id, sizeof(Type) * count, nullptr, mode);
Type* out = (Type*) glMapNamedBuffer(id, GL_WRITE_ONLY);
std::copy(first, last, out);
glUnmapNamedBuffer(id);
return count;
}
~Buffer() {
// delete silently ignores 0.
glDeleteBuffers(1, &id);
}
};

80
src/gl/debug.hpp Normal file
View File

@@ -0,0 +1,80 @@
#pragma once
#ifndef NDEBUG
void GLAPIENTRY log_gl_debug_callback(
GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam
) {
std::string s_source;
switch(type){
case GL_DEBUG_SOURCE_API:
s_source = "API:";
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
s_source = "WINDOW:";
case GL_DEBUG_SOURCE_SHADER_COMPILER:
s_source = "SHADER:";
case GL_DEBUG_SOURCE_THIRD_PARTY:
s_source = "3P:";
case GL_DEBUG_SOURCE_APPLICATION:
s_source = "APP:";
default:
s_source = "";
}
std::string s_type;
switch (type) {
case GL_DEBUG_TYPE_ERROR:
s_type = "ERROR:";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
s_type = "DEPRECATED:";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
s_type = "UNDEFINED:";
break;
case GL_DEBUG_TYPE_PORTABILITY:
s_type = "PORTABILITY:";
break;
case GL_DEBUG_TYPE_PERFORMANCE:
s_type = "PERFORMANCE:";
break;
case GL_DEBUG_TYPE_MARKER:
s_type = "MARKER:";
break;
case GL_DEBUG_TYPE_PUSH_GROUP:
s_type = "PUSH_GROUP:";
break;
case GL_DEBUG_TYPE_POP_GROUP:
s_type = "POP_GROUP:";
break;
default:
s_type = "";
break;
}
std::string s_severity;
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH:
s_severity = "HIGH:";
break;
case GL_DEBUG_SEVERITY_MEDIUM:
s_severity = "MED:";
break;
case GL_DEBUG_SEVERITY_LOW:
s_severity = "LOW:";
break;
default:
s_severity = "INFO:";
break;
}
std::cerr << "GL:" << s_source << s_type << s_severity << " " << message << std::endl;
}
#endif

79
src/gl/shader.hpp Normal file
View File

@@ -0,0 +1,79 @@
#pragma once
#include <glad/glad.h>
#include <string>
#include <memory>
#include <vector>
template<GLenum mode>
class Shader {
public:
private:
GLuint id = 0;
public:
explicit Shader(const std::string &src) {
id = glCreateShader(mode);
const char *str = src.c_str();
glShaderSource(id, 1, &str, nullptr);
glCompileShader(id);
// todo throw if compile failed
}
explicit Shader(std::ifstream source)
: Shader(std::string(
std::istreambuf_iterator<char>(source),
std::istreambuf_iterator<char>()
)) {}
Shader(const Shader &) = delete;
Shader(Shader &&o) noexcept {
id = std::exchange(o.id, 0);
}
~Shader() {
glDeleteShader(id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
};
using VertexShader = Shader<GL_VERTEX_SHADER>;
using FragmentShader = Shader<GL_FRAGMENT_SHADER>;
class Program {
private:
GLuint id = 0;
public:
template<GLenum ...mode>
explicit Program(const Shader<mode> &...shader) {
id = glCreateProgram();
(glAttachShader(id, shader), ...);
glLinkProgram(id);
// todo throw if link failed
}
Program(const Program &) = delete;
Program(Program &&o) noexcept {
id = std::exchange(o.id, 0);
}
~Program() {
glDeleteProgram(id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
};

113
src/gl/types.hpp Normal file
View File

@@ -0,0 +1,113 @@
#pragma once
#include <glad/glad.h>
#include <Eigen/Eigen>
template<class T_>
struct Buffer;
template<class ...Fmt_>
struct VertexArray;
template<typename ctype, GLenum type, GLint size, GLuint offset = 0, GLboolean norm = GL_FALSE>
struct Format {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
static constexpr GLboolean Norm = norm;
template<typename ctype_, GLint size_>
using As = Format<ctype_, type, size_, offset, norm>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribFormat(vao, idx, size, type, norm, offset);
}
};
template<typename ctype, GLenum type, GLint size, GLuint offset = 0>
struct IFormat {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
template<typename ctype_, GLint size_>
using As = IFormat<ctype_, type, size_, offset>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribIFormat(vao, idx, size, type, offset);
}
};
template<typename ctype, GLenum type, GLint size, GLuint offset = 0>
struct LFormat {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
template<typename ctype_, GLint size_>
using As = LFormat<ctype_, type, size_, offset>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribLFormat(vao, idx, size, type, offset);
}
};
template<typename Fmt_>
struct AutoFormat {
using Fmt = Fmt_;
};
template<>
struct AutoFormat<double> {
using Fmt = LFormat<double, GL_DOUBLE, 1>;
};
template<>
struct AutoFormat<float> {
using Fmt = Format<float, GL_FLOAT, 1>;
};
template<>
struct AutoFormat<int> {
using Fmt = IFormat<int, GL_INT, 1>;
};
template<typename Scalar, int Dim>
struct AutoFormat<Eigen::Vector<Scalar, Dim>> {
static_assert((Dim >= 1) && (Dim <= 4), "Dim must be in range 1-4");
using Fmt = typename AutoFormat<Scalar>::Fmt::template As<Eigen::Vector<Scalar, Dim>, Dim>;
};
template<typename Fmt_>
struct Binder {
using CType = typename AutoFormat<Fmt_>::Fmt::CType;
const GLuint buf;
const GLuint offset;
const GLuint stride;
Binder(const Buffer<CType> &buf) // NOLINT(google-explicit-constructor)
: buf((GLuint) buf), offset(0), stride(sizeof(CType)) {}
template<typename CType_>
Binder(const Buffer<CType_> &buf, GLuint offset)
: buf((GLuint) buf), offset(offset), stride(sizeof(CType_)) {}
Binder(const Binder<Fmt_> &o)
: buf(o.buf), offset(o.offset), stride(o.stride) {}
inline void bind(GLuint vao, GLuint idx) const {
glVertexArrayVertexBuffer(vao, idx, buf, offset, stride);
}
};
#define ATTR(buf, field) Binder<decltype(decltype(buf)::Type::field)>(buf, offsetof(decltype(buf)::Type, field))

67
src/gl/vertexarray.hpp Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include <glad/glad.h>
#include <utility>
#include "buffer.hpp"
#include "types.hpp"
template<typename ...Fmt_>
class VertexArray {
public:
template<size_t idx>
using Fmt = std::tuple_element_t<idx, std::tuple<Fmt_...>>;
private:
GLuint id = 0;
template<size_t ...idx>
inline void formatall(
std::integer_sequence<size_t, idx...>
) {
(AutoFormat<Fmt_>::Fmt::apply(id, idx), ...);
}
template<size_t ...idx>
inline void bindall(
const Binder<Fmt_> &...buf,
std::integer_sequence<size_t, idx...>
) {
(buf.bind(id, idx), ...);
}
public:
explicit VertexArray() {
glCreateVertexArrays(1, &id);
formatall(std::make_index_sequence<sizeof...(Fmt_)>());
}
explicit VertexArray(const Binder<Fmt_> &...buf)
: VertexArray() {
bind(buf...);
}
VertexArray(VertexArray &&o) noexcept {
id = std::exchange(o.id, 0);
}
VertexArray(const VertexArray &) = delete; // this is doable, but would be slow.
~VertexArray() {
glDeleteVertexArrays(1, &id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
void bind(const Binder<Fmt_> &...buf) {
bindall(buf..., std::make_index_sequence<sizeof...(Fmt_)>());
}
template<size_t idx>
void bind(const Binder<Fmt<idx>> &buf) {
buf.bind(id, idx);
}
};