feat: add OpenGL abstraction layer with RAII resources

- Replace window class with cbt::opengl::context
- Add buffer resource (VBO, EBO, UBO, SSBO) with move semantics
- Add texture resource with format/type enums and filtering
- Add descriptor_set for Vulkan-style resource binding
- All resources use RAII with proper cleanup
This commit is contained in:
2026-05-05 21:58:34 +02:00
parent f18e5e4adc
commit 90d013695d
36 changed files with 2139 additions and 55 deletions
+72
View File
@@ -0,0 +1,72 @@
#include "cbt/opengl/buffer.hpp"
#include "glad/glad.h"
namespace cbt::opengl {
buffer::buffer() {
glGenBuffers(1, &m_id);
}
buffer::buffer(buffer_type type)
: m_type(type) {
glGenBuffers(1, &m_id);
}
buffer::~buffer() {
if (m_id) {
glDeleteBuffers(1, &m_id);
}
}
buffer::buffer(buffer&& other) noexcept
: m_id(other.m_id)
, m_type(other.m_type) {
other.m_id = 0;
}
buffer& buffer::operator=(buffer&& other) noexcept {
if (this != &other) {
if (m_id) {
glDeleteBuffers(1, &m_id);
}
m_id = other.m_id;
m_type = other.m_type;
other.m_id = 0;
}
return *this;
}
auto buffer::bind() const -> void {
glBindBuffer(static_cast<GLenum>(m_type), m_id);
}
auto buffer::unbind() const -> void {
glBindBuffer(static_cast<GLenum>(m_type), 0);
}
auto buffer::upload(const void* data, size_t size) -> void {
bind();
glBufferData(static_cast<GLenum>(m_type), size, data, GL_STATIC_DRAW);
unbind();
}
auto buffer::upload(std::vector<uint8_t> data) -> void {
upload(data.data(), data.size());
}
auto buffer::upload_dynamic(const void* data, size_t size) -> void {
bind();
glBufferData(static_cast<GLenum>(m_type), size, data, GL_DYNAMIC_DRAW);
unbind();
}
auto buffer::id() const -> GLuint {
return m_id;
}
auto buffer::valid() const -> bool {
return m_id != 0;
}
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <cstddef>
#include <vector>
#include "glad/glad.h"
namespace cbt::opengl {
enum class buffer_type {
vertex = GL_ARRAY_BUFFER,
index = GL_ELEMENT_ARRAY_BUFFER,
uniform = GL_UNIFORM_BUFFER,
storage = GL_SHADER_STORAGE_BUFFER,
};
class buffer {
GLuint m_id = 0;
buffer_type m_type = buffer_type::vertex;
public:
buffer();
explicit buffer(buffer_type type);
~buffer();
buffer(const buffer&) = delete;
buffer& operator=(const buffer&) = delete;
buffer(buffer&& other) noexcept;
buffer& operator=(buffer&& other) noexcept;
auto bind() const -> void;
auto unbind() const -> void;
auto upload(const void* data, size_t size) -> void;
auto upload(std::vector<uint8_t> data) -> void;
auto upload_dynamic(const void* data, size_t size) -> void;
auto id() const -> GLuint;
auto valid() const -> bool;
};
}
+23 -18
View File
@@ -1,16 +1,16 @@
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
#include "window.hpp"
#include "cbt/opengl/context.hpp"
#include <string_view>
#include "glad/glad.h"
#include "fmt/std.h"
namespace cbt {
namespace cbt::opengl {
auto window::init() -> bool {
auto context::init() -> bool {
if (!glfwInit()) {
fmt::print("Failed to initialize GLFW\n");
return false;
@@ -18,28 +18,29 @@ auto window::init() -> bool {
return true;
}
auto window::terminate() -> void {
auto context::terminate() -> void {
glfwTerminate();
}
auto window::setup_opengl() -> bool {
auto context::setup_gl() -> bool {
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
fmt::print("Failed to initialize GLAD\n");
return false;
}
info_opengl();
print_info();
return true;
}
auto window::info_opengl() -> void {
auto context::print_info() -> void {
fmt::print("OpenGL Info:\n");
fmt::print(" vendor | {}\n", std::string_view(reinterpret_cast<const char*>(glGetString(GL_VENDOR))));
fmt::print(" renderer| {}\n", std::string_view(reinterpret_cast<const char*>(glGetString(GL_RENDERER))));
fmt::print(" version | {}\n", std::string_view(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
fmt::print(" glsl | {}\n", std::string_view(reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION))));
fmt::print("\n");
}
window::window(std::string title, int width, int height) {
context::context(std::string title, int width, int height) {
if (!init()) {
return;
}
@@ -56,37 +57,41 @@ window::window(std::string title, int width, int height) {
return;
}
glfwMakeContextCurrent(static_cast<GLFWwindow*>(m_window));
glfwMakeContextCurrent(m_window);
if (!setup_opengl()) {
if (!setup_gl()) {
terminate();
return;
}
}
window::~window() {
context::~context() {
if (m_window) {
glfwDestroyWindow(static_cast<GLFWwindow*>(m_window));
glfwDestroyWindow(m_window);
}
if (m_initialized) {
terminate();
}
}
auto window::should_close() const -> bool {
return m_window && glfwWindowShouldClose(static_cast<GLFWwindow*>(m_window));
auto context::should_close() const -> bool {
return m_window && glfwWindowShouldClose(m_window);
}
auto window::valid() const -> bool {
auto context::valid() const -> bool {
return m_window != nullptr;
}
auto window::swap_buffers() -> void {
glfwSwapBuffers(static_cast<GLFWwindow*>(m_window));
auto context::swap_buffers() -> void {
glfwSwapBuffers(m_window);
}
auto window::poll_events() -> void {
auto context::poll_events() -> void {
glfwPollEvents();
}
auto context::raw() const -> GLFWwindow* {
return m_window;
}
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <string>
#define GLFW_INCLUDE_NONE
#include "GLFW/glfw3.h"
namespace cbt::opengl {
class context {
GLFWwindow* m_window = nullptr;
bool m_initialized = false;
static auto init() -> bool;
static auto terminate() -> void;
static auto setup_gl() -> bool;
static auto print_info() -> void;
public:
explicit context(std::string title, int width, int height);
~context();
auto should_close() const -> bool;
auto valid() const -> bool;
auto swap_buffers() -> void;
auto poll_events() -> void;
auto raw() const -> GLFWwindow*;
};
}
+32
View File
@@ -0,0 +1,32 @@
#include "cbt/opengl/descriptor.hpp"
#include "glad/glad.h"
namespace cbt::opengl {
descriptor_set::descriptor_set() {}
auto descriptor_set::add_texture(texture tex, GLuint unit) -> void {
m_bindings.push_back({unit, std::move(tex), std::nullopt});
}
auto descriptor_set::add_buffer(buffer buf, GLuint unit) -> void {
m_bindings.push_back({unit, std::nullopt, std::move(buf)});
}
auto descriptor_set::bind_all() -> void {
for (auto& binding : m_bindings) {
if (binding.tex) {
binding.tex->bind(binding.texture_unit);
}
if (binding.buf) {
binding.buf->bind();
}
}
}
auto descriptor_set::count() const -> size_t {
return m_bindings.size();
}
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "cbt/opengl/buffer.hpp"
#include "cbt/opengl/texture.hpp"
#include <array>
#include <optional>
#include <vector>
namespace cbt::opengl {
struct descriptor_binding {
GLuint texture_unit = 0;
std::optional<texture> tex;
std::optional<buffer> buf;
};
class descriptor_set {
std::vector<descriptor_binding> m_bindings;
public:
descriptor_set();
auto add_texture(texture tex, GLuint unit = 0) -> void;
auto add_buffer(buffer buf, GLuint unit = 0) -> void;
auto bind_all() -> void;
auto count() const -> size_t;
};
}
+85
View File
@@ -0,0 +1,85 @@
#include "cbt/opengl/texture.hpp"
#include "glad/glad.h"
namespace cbt::opengl {
texture::texture() {
glGenTextures(1, &m_id);
}
texture::texture(texture_target target)
: m_target(target) {
glGenTextures(1, &m_id);
}
texture::~texture() {
if (m_id) {
glDeleteTextures(1, &m_id);
}
}
texture::texture(texture&& other) noexcept
: m_id(other.m_id)
, m_target(other.m_target) {
other.m_id = 0;
}
texture& texture::operator=(texture&& other) noexcept {
if (this != &other) {
if (m_id) {
glDeleteTextures(1, &m_id);
}
m_id = other.m_id;
m_target = other.m_target;
other.m_id = 0;
}
return *this;
}
auto texture::bind(GLuint unit) -> void {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(static_cast<GLenum>(m_target), m_id);
}
auto texture::unbind() -> void {
glBindTexture(static_cast<GLenum>(m_target), 0);
}
auto texture::upload(const void* data, int width, int height, texture_format format, texture_type type) -> void {
bind();
glTexImage2D(static_cast<GLenum>(m_target), 0,
static_cast<GLenum>(format), width, height, 0,
static_cast<GLenum>(format), static_cast<GLenum>(type), data);
unbind();
}
auto texture::set_filter(GLenum min_filter, GLenum mag_filter) -> void {
bind();
glTexParameteri(static_cast<GLenum>(m_target), GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(static_cast<GLenum>(m_target), GL_TEXTURE_MAG_FILTER, mag_filter);
unbind();
}
auto texture::set_wrap(GLenum wrap_s, GLenum wrap_t) -> void {
bind();
glTexParameteri(static_cast<GLenum>(m_target), GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(static_cast<GLenum>(m_target), GL_TEXTURE_WRAP_T, wrap_t);
unbind();
}
auto texture::generate_mipmaps() -> void {
bind();
glGenerateMipmap(static_cast<GLenum>(m_target));
unbind();
}
auto texture::id() const -> GLuint {
return m_id;
}
auto texture::valid() const -> bool {
return m_id != 0;
}
}
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <cstddef>
#include <vector>
#include "glad/glad.h"
namespace cbt::opengl {
enum class texture_target {
_1d = GL_TEXTURE_1D,
_2d = GL_TEXTURE_2D,
_3d = GL_TEXTURE_3D,
cube = GL_TEXTURE_CUBE_MAP,
};
enum class texture_format {
rgb = GL_RGB,
rgba = GL_RGBA,
rg = GL_RG,
red = GL_RED,
depth = GL_DEPTH_COMPONENT,
};
enum class texture_type {
ubyte = GL_UNSIGNED_BYTE,
ushort = GL_UNSIGNED_SHORT,
uint = GL_UNSIGNED_INT,
float_ = GL_FLOAT,
};
class texture {
GLuint m_id = 0;
texture_target m_target = texture_target::_2d;
public:
texture();
explicit texture(texture_target target);
~texture();
texture(const texture&) = delete;
texture& operator=(const texture&) = delete;
texture(texture&& other) noexcept;
texture& operator=(texture&& other) noexcept;
auto bind(GLuint unit = 0) -> void;
auto unbind() -> void;
auto upload(const void* data, int width, int height, texture_format format, texture_type type) -> void;
auto set_filter(GLenum min_filter, GLenum mag_filter) -> void;
auto set_wrap(GLenum wrap_s, GLenum wrap_t) -> void;
auto generate_mipmaps() -> void;
auto id() const -> GLuint;
auto valid() const -> bool;
};
}
-27
View File
@@ -1,27 +0,0 @@
#pragma once
#include <string>
namespace cbt {
class window {
void* m_window = nullptr;
bool m_initialized = false;
static auto init() -> bool;
static auto terminate() -> void;
static auto setup_opengl() -> bool;
static auto info_opengl() -> void;
public:
explicit window(std::string title, int width, int height);
~window();
auto should_close() const -> bool;
auto valid() const -> bool;
auto swap_buffers() -> void;
auto poll_events() -> void;
auto raw() const -> void* { return m_window; }
};
}