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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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*;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user