breaking change, new VAO/VBO. Updated renderers not tested.

main
Brett 2025-06-12 02:16:37 -04:00
parent c28f8e6109
commit dd3a242b41
10 changed files with 405 additions and 771 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25)
include(FetchContent)
set(BLT_GRAPHICS_VERSION 2.0.11)
set(BLT_GRAPHICS_VERSION 3.0.0)
set(BLT_GRAPHICS_TEST_VERSION 0.0.1)
project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION})

View File

@ -1,243 +0,0 @@
/*
* <Short Description>
* Copyright (C) 2023 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_WITH_GRAPHICS_MODEL_H
#define BLT_WITH_GRAPHICS_MODEL_H
#include <blt/math/vectors.h>
#include <vector>
#include <blt/gfx/gl_includes.h>
#include <variant>
#include <array>
#include <blt/std/hashmap.h>
namespace blt::gfx
{
struct vbo_t_owner;
class static_dynamic_array;
class vertex_array_t;
// vbo
struct vertex_buffer_t
{
friend vbo_t_owner;
friend vertex_array_t;
friend static_dynamic_array;
private:
GLuint bufferID_ = 0;
GLsizeiptr size_ = 0;
GLint buffer_type = 0;
GLint memory_type = 0;
public:
void create(GLint type = GL_ARRAY_BUFFER);
void bind() const;
void unbind() const;
void allocate(GLsizeiptr size, GLint mem_type = GL_STATIC_DRAW, const void* data = nullptr);
template<typename T>
void allocate(GLsizeiptr size, const T* data, GLint mem_type = GL_STATIC_DRAW)
{
allocate(size, mem_type, static_cast<const void*>(data));
}
void sub_update(GLsizeiptr offset, GLsizeiptr size, const void* data) const;
void update(GLsizeiptr size, const void* data);
void destroy();
};
// ssbo
struct shader_buffer_t : public vertex_buffer_t
{
public:
inline void create()
{
vertex_buffer_t::create(GL_SHADER_STORAGE_BUFFER);
}
};
// ebo
struct element_buffer_t : public vertex_buffer_t
{
public:
inline void create()
{
vertex_buffer_t::create(GL_ELEMENT_ARRAY_BUFFER);
}
};
struct vbo_t_owner
{
vertex_buffer_t vbo;
vbo_t_owner() = default;
explicit vbo_t_owner(vertex_buffer_t vbo): vbo(vbo)
{}
vertex_buffer_t* operator->()
{
return &vbo;
}
~vbo_t_owner()
{
if (!vbo.bufferID_)
return;
vbo.destroy();
vbo.unbind();
}
};
/**
* Since most VAOs will not use more than 8 VBOs it makes no sense to heap allocate memory to store them
* This class is used to make that easier to handle
*/
class static_dynamic_array
{
public:
using vbo_type = std::shared_ptr<vbo_t_owner>;
private:
static constexpr size_t DATA_SIZE = 8;
using array_t = std::array<vbo_type, DATA_SIZE>;
std::variant<array_t, vbo_type*> data_;
size_t size_ = DATA_SIZE;
size_t max = 0;
void swap();
public:
static_dynamic_array();
static_dynamic_array(const static_dynamic_array& copy) = delete;
static_dynamic_array(static_dynamic_array&& move) noexcept = default;
static_dynamic_array& operator=(const static_dynamic_array& copy) = delete;
static_dynamic_array& operator=(static_dynamic_array&& move) noexcept = default;
vbo_type& operator[](size_t index);
[[nodiscard]] inline size_t used() const noexcept
{
return max;
}
~static_dynamic_array()
{
if (std::holds_alternative<vbo_type*>(data_))
delete[] std::get<vbo_type*>(data_);
}
};
/**
* basic VAO class.
*/
class vertex_array_t
{
private:
GLuint vaoID;
static_dynamic_array VBOs;
blt::hashset_t<GLuint> used_attributes;
vertex_buffer_t element;
void handle_vbo(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset);
public:
vertex_array_t();
vertex_array_t(const vertex_array_t&) = delete;
vertex_array_t(vertex_array_t&&) = delete;
vertex_array_t& operator=(const vertex_array_t&) = delete;
vertex_array_t& operator=(vertex_array_t&&) = delete;
/**
* This function takes ownership of the underlying VBO (GPU side). It will be freed when the basic vertex array is deleted
* @param vbo vbo to bind to this attribute
* @param attribute_number attribute number to bind to
* @param coordinate_size size of the data (number of
* @param type type of data
* @param stride how many bytes this data takes (for the entire per-vertex data structure) 0 will assume packed data
* This is in effect how many bytes until the next block of data
* @param offset offset into the data structure to where the data is stored
* @return a shared pointer to the stored vbo. used for chaining VAOs with multiple shared VBOs
*/
void bindVBO(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset);
// same as the other bind method except you provide the shared reference.
void bindVBO(const static_dynamic_array::vbo_type& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset);
inline void bindElement(const vertex_buffer_t& vbo)
{
bind();
element = vbo;
vbo.bind();
unbind();
}
inline vertex_buffer_t& getElement()
{
return element;
}
/**
* Returns a non-owning reference to a vbo allowing for updating the VBO
* The VBO is considered invalid if its ID is 0
*/
inline vertex_buffer_t& operator[](size_t index)
{
return getBuffer(index);
}
inline vertex_buffer_t& getBuffer(size_t index)
{
return VBOs[index]->vbo;
}
inline void bind() const
{
glBindVertexArray(vaoID);
}
static inline void unbind()
{
glBindVertexArray(0);
}
static inline static_dynamic_array::vbo_type make_vbo(const vertex_buffer_t& vbo)
{
return std::make_shared<vbo_t_owner>(vbo);
}
~vertex_array_t();
};
}
#endif //BLT_WITH_GRAPHICS_MODEL_H

View File

@ -19,16 +19,16 @@
#ifndef BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H
#define BLT_WITH_GRAPHICS_BATCH_2D_RENDERER_H
#include "blt/gfx/model.h"
#include "blt/gfx/shader.h"
#include "blt/gfx/framebuffer.h"
#include "blt/gfx/renderer/resource_manager.h"
#include "blt/gfx/renderer/postprocess.h"
#include <blt/std/hashmap.h>
#include <blt/std/memory_util.h>
#include <blt/math/vectors.h>
#include <string_view>
#include <vector>
#include <blt/math/vectors.h>
#include <blt/std/hashmap.h>
#include <blt/std/memory_util.h>
#include "blt/gfx/framebuffer.h"
#include "blt/gfx/shader.h"
#include "blt/gfx/vao.h"
#include "blt/gfx/renderer/postprocess.h"
#include "blt/gfx/renderer/resource_manager.h"
namespace blt::gfx
{
@ -121,12 +121,12 @@ namespace blt::gfx
struct draw_t
{
std::unique_ptr<vertex_array_t> vao;
std::optional<unique_vao_t> vao;
i32 count;
};
[[nodiscard]] draw_t to_vertex_array() const;
size_t populate_vertex_array(vertex_array_t& va) const;
[[nodiscard]] size_t populate_vertex_array(const unique_vao_t& va) const;
[[nodiscard]] std::vector<line_vertex_t> calculate_vertices() const;
@ -254,9 +254,9 @@ namespace blt::gfx
using object_container = hashmap_t<std::string, std::vector<std::pair<render_info_t, T>>>;
private:
std::unique_ptr<vertex_array_t> square_vao;
std::unique_ptr<vertex_array_t> line_vao;
std::unique_ptr<vertex_array_t> curve_vao;
std::optional<unique_vao_t> square_vao;
std::optional<unique_vao_t> line_vao;
std::optional<unique_vao_t> curve_vao;
std::unique_ptr<shader_t> square_shader;
std::unique_ptr<shader_t> point_shader;
resource_manager& resources;

View File

@ -22,7 +22,7 @@
#include <blt/gfx/font/font.h>
#include <blt/gfx/texture.h>
#include <blt/gfx/shader.h>
#include <blt/gfx/model.h>
#include <blt/gfx/vao.h>
#include <blt/gfx/framebuffer.h>
#include <blt/std/hashmap.h>
#include <blt/std/binary_tree.h>
@ -368,7 +368,7 @@ namespace blt::gfx
font_generator_t& generator;
std::string contents, font;
vertex_array_t vao{};
unique_vao_t vao{};
std::vector<text_render_info_t> renders;
blt::vec2f position, bounds;
blt::vec2f scale = DEFAULT_SCALE;

View File

@ -22,7 +22,7 @@
#include <blt/std/types.h>
#include <blt/gfx/framebuffer.h>
#include <blt/gfx/shader.h>
#include <blt/gfx/model.h>
#include <blt/gfx/vao.h>
#include <blt/gfx/state.h>
#include <vector>
#include <memory>
@ -271,10 +271,10 @@ namespace blt::gfx
private:
pp_engine_t() = default;
pp_step_t& find_last_frame_buffer(size_t index);
pp_step_t& find_last_frame_buffer(size_t index) const;
std::vector<std::unique_ptr<pp_step_t>> steps;
std::unique_ptr<vertex_array_t> screen_vao;
std::optional<unique_vao_t> screen_vao{};
};
}

View File

@ -54,14 +54,6 @@ namespace blt::gfx
*/
vbo_context_t& resize(GLsizeiptr size, GLint mem_type);
/**
* Reserves a chunk of GPU memory for future use
*/
vbo_context_t& resize(const size_t size, const GLint mem_type)
{
return resize(static_cast<GLsizeiptr>(size), mem_type);
}
/**
* Uploads a chunk of memory to the GPU. If the existing VBO has enough space, the memory will be reused.
*/
@ -71,9 +63,9 @@ namespace blt::gfx
* Uploads a chunk of memory to the GPU. If the existing VBO has enough space, the memory will be reused.
*/
template <typename T, std::enable_if_t<!std::is_same_v<T, void>, bool> = true>
vbo_context_t& upload(const size_t size, T* ptr, const GLint mem_type)
vbo_context_t& upload(const size_t size, const T* ptr, const GLint mem_type)
{
return upload(size, static_cast<void*>(ptr), mem_type);
return upload(size, static_cast<const void*>(ptr), mem_type);
}
/**

View File

@ -1,155 +0,0 @@
/*
* <Short Description>
* Copyright (C) 2023 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/gfx/model.h>
#include <blt/std/memory_util.h>
#include "blt/logging/logging.h"
namespace blt::gfx
{
void static_dynamic_array::swap()
{
size_t next_size = blt::mem::next_byte_allocation(size_);
auto* new_data = new vbo_type[next_size];
if (std::holds_alternative<vbo_type*>(data_))
{
auto* ptr = std::get<vbo_type*>(data_);
for (size_t i = 0; i < size_; i++)
new_data[i] = ptr[i];
delete[] ptr;
} else
{
auto data = std::get<array_t>(data_);
for (size_t i = 0; i < DATA_SIZE; i++)
new_data[i] = data[i];
}
data_ = new_data;
size_ = next_size;
}
static_dynamic_array::static_dynamic_array() = default;
static_dynamic_array::vbo_type& static_dynamic_array::operator[](size_t index)
{
if (index >= size_)
swap();
max = std::max(index, max);
if (std::holds_alternative<vbo_type*>(data_))
return std::get<vbo_type*>(data_)[index];
else
return std::get<array_t>(data_)[index];
}
/*
* -----------------------------------
* vbo_t
* -----------------------------------
*/
void vertex_buffer_t::create(GLint type)
{
glGenBuffers(1, &bufferID_);
buffer_type = type;
}
void vertex_buffer_t::destroy()
{
// prevent multiple destroys of the same object, in cases of multi attribute binds
if (bufferID_ == 0)
return;
glDeleteBuffers(1, &bufferID_);
bufferID_ = 0;
}
void vertex_buffer_t::allocate(GLsizeiptr size, GLint mem_type, const void* data)
{
bind();
size_ = size;
glBufferData(buffer_type, size, data, mem_type);
memory_type = mem_type;
}
void vertex_buffer_t::update(GLsizeiptr size, const void* data)
{
if (size <= size_)
sub_update(0, size, data);
else
allocate(size, memory_type, data);
size_ = size;
}
void vertex_buffer_t::sub_update(GLsizeiptr offset, GLsizeiptr size, const void* data) const
{
bind();
glBufferSubData(buffer_type, offset, size, data);
}
void vertex_buffer_t::bind() const
{
glBindBuffer(buffer_type, bufferID_);
}
void vertex_buffer_t::unbind() const
{
glBindBuffer(buffer_type, 0);
}
/*
* ----------------------------
* basic_vertex_array
* ----------------------------
*/
vertex_array_t::vertex_array_t(): vaoID(0)
{
glGenVertexArrays(1, &vaoID);
}
vertex_array_t::~vertex_array_t()
{
// free all VBOs
for (size_t i = 0; i < VBOs.used(); i++)
VBOs[i] = nullptr;
element.destroy();
// then free the vertex array
glDeleteVertexArrays(1, &vaoID);
}
void vertex_array_t::bindVBO(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset)
{
handle_vbo(vbo, attribute_number, coordinate_size, type, stride, offset);
VBOs[attribute_number] = make_vbo(vbo);
}
void vertex_array_t::bindVBO(const static_dynamic_array::vbo_type& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset)
{
handle_vbo(vbo->vbo, attribute_number, coordinate_size, type, stride, offset);
VBOs[attribute_number] = vbo;
}
void vertex_array_t::handle_vbo(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset)
{
used_attributes.insert(attribute_number);
bind();
vbo.bind();
glEnableVertexAttribArray(attribute_number);
glVertexAttribPointer(attribute_number, coordinate_size, type, GL_FALSE, stride < 0 ? 0 : stride, (void*) offset);
unbind();
}
}

View File

@ -23,22 +23,58 @@
float square_vertices[20] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
0.5f,
0.5f,
0.0f,
1.0f,
1.0f, // top right
0.5f,
-0.5f,
0.0f,
1.0f,
0.0f, // bottom right
-0.5f,
-0.5f,
0.0f,
0.0f,
0.0f, // bottom left
-0.5f,
0.5f,
0.0f,
0.0f,
1.0f // top left
};
const unsigned int square_indices[6] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
0,
1,
3, // first triangle
1,
2,
3 // second triangle
};
float line_vertices[20] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
0.5f,
0.5f,
0.0f,
1.0f,
1.0f, // top right
0.5f,
-0.5f,
0.0f,
1.0f,
0.0f, // bottom right
-0.5f,
-0.5f,
0.0f,
0.0f,
0.0f, // bottom left
-0.5f,
0.5f,
0.0f,
0.0f,
1.0f // top left
};
// 0, 1 (top right)
@ -51,23 +87,23 @@ namespace blt::gfx
curve2d_mesh_data_t::draw_t curve2d_mesh_data_t::to_vertex_array() const
{
const auto vertices = calculate_vertices();
auto vao = std::make_unique<vertex_array_t>();
auto vao = unique_vao_t{};
vertex_buffer_t vertex_buffer;
vertex_buffer.create();
vertex_buffer.allocate(static_cast<long>(vertices.size() * sizeof(line_vertex_t)), vertices.data());
unique_vbo_t vertex_buffer{GL_ARRAY_BUFFER};
vertex_buffer.bind().upload(static_cast<long>(vertices.size() * sizeof(line_vertex_t)), vertices.data(), GL_STATIC_DRAW);
const auto vb = vertex_array_t::make_vbo(vertex_buffer);
vao->bindVBO(vb, 0, 3, GL_FLOAT, sizeof(line_vertex_t), 0);
vao->bindVBO(vb, 1, 2, GL_FLOAT, sizeof(line_vertex_t), sizeof(vec3));
const auto ctx = vao.configure();
auto vbo = ctx.attach_vbo(std::move(vertex_buffer));
vbo.attribute_ptr(0, 3, GL_FLOAT, sizeof(line_vertex_t), 0);
vbo.attribute_ptr(1, 2, GL_FLOAT, sizeof(line_vertex_t), sizeof(vec3));
return {std::move(vao), static_cast<i32>(vertices.size())};
}
size_t curve2d_mesh_data_t::populate_vertex_array(vertex_array_t& va) const
size_t curve2d_mesh_data_t::populate_vertex_array(const unique_vao_t& va) const
{
const auto vertices = calculate_vertices();
va.getBuffer(0).update(static_cast<long>(vertices.size() * sizeof(line_vertex_t)), vertices.data());
va.get_attribute(0)->get().bind().upload(static_cast<long>(vertices.size() * sizeof(line_vertex_t)), vertices.data(), GL_STATIC_DRAW);
return vertices.size();
}
@ -210,47 +246,43 @@ namespace blt::gfx
void batch_renderer_2d::create()
{
{
vertex_buffer_t vertices_vbo;
element_buffer_t indices_vbo;
unique_vbo_t vertices_vbo{GL_ARRAY_BUFFER};
unique_ebo_t indices_vbo;
vertices_vbo.create();
indices_vbo.create();
vertices_vbo.bind().upload(sizeof(square_vertices), square_vertices, GL_STATIC_DRAW);
indices_vbo.bind().upload(sizeof(square_indices), square_indices, GL_STATIC_DRAW);
vertices_vbo.allocate(sizeof(square_vertices), square_vertices);
indices_vbo.allocate(sizeof(square_indices), square_indices);
square_vao = std::make_unique<vertex_array_t>();
auto vb = vertex_array_t::make_vbo(vertices_vbo);
square_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0);
square_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
square_vao->bindElement(indices_vbo);
square_vao = unique_vao_t{};
const auto ctx = square_vao->configure();
auto vbo = ctx.attach_vbo(std::move(vertices_vbo));
vbo.attribute_ptr(0, 3, GL_FLOAT, 5 * sizeof(float), 0);
vbo.attribute_ptr(1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
ctx.attach_vbo(std::move(indices_vbo));
}
{
vertex_buffer_t vertices_vbo;
element_buffer_t indices_vbo;
unique_vbo_t vertices_vbo{GL_ARRAY_BUFFER};
unique_ebo_t indices_vbo;
vertices_vbo.create();
indices_vbo.create();
vertices_vbo.bind().upload(sizeof(line_vertices), line_vertices, GL_DYNAMIC_DRAW);
indices_vbo.bind().upload(sizeof(square_indices), square_indices, GL_DYNAMIC_DRAW);
vertices_vbo.allocate(sizeof(line_vertices), line_vertices, GL_DYNAMIC_DRAW);
indices_vbo.allocate(sizeof(square_indices), square_indices, GL_DYNAMIC_DRAW);
line_vao = std::make_unique<vertex_array_t>();
const auto vb = vertex_array_t::make_vbo(vertices_vbo);
line_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0);
line_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
line_vao->bindElement(indices_vbo);
line_vao = unique_vao_t{};
const auto ctx = line_vao->configure();
auto vbo = ctx.attach_vbo(std::move(vertices_vbo));
vbo.attribute_ptr(0, 3, GL_FLOAT, 5 * sizeof(float), 0);
vbo.attribute_ptr(1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
ctx.attach_vbo(std::move(indices_vbo));
}
{
curve_vao = std::make_unique<vertex_array_t>();
curve_vao = unique_vao_t{};
vertex_buffer_t vertex_buffer;
vertex_buffer.create();
vertex_buffer.allocate(0, GL_DYNAMIC_DRAW);
unique_vbo_t vertex_buffer{GL_ARRAY_BUFFER};
vertex_buffer.bind().resize(0, GL_DYNAMIC_DRAW);
const auto vb = vertex_array_t::make_vbo(vertex_buffer);
curve_vao->bindVBO(vb, 0, 3, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), 0);
curve_vao->bindVBO(vb, 1, 2, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), sizeof(vec3));
const auto ctx = curve_vao->configure();
auto vbo = ctx.attach_vbo(std::move(vertex_buffer));
vbo.attribute_ptr(0, 3, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), 0);
vbo.attribute_ptr(1, 2, GL_FLOAT, sizeof(curve2d_mesh_data_t::line_vertex_t), sizeof(vec3));
}
square_shader = shader_t::make_unique(shader_2d_textured_vert, shader_2d_textured_frag);
@ -340,9 +372,9 @@ namespace blt::gfx
{
engine->cleanup();
engine = nullptr;
square_vao = nullptr;
line_vao = nullptr;
curve_vao = nullptr;
square_vao.reset();
line_vao.reset();
curve_vao.reset();
square_shader = nullptr;
point_shader = nullptr;
}
@ -435,8 +467,8 @@ namespace blt::gfx
mat4x4 empty_model;
square_shader->setMatrix("model", empty_model);
line_vao->bind();
auto& buf = line_vao->getBuffer(0);
buf.bind();
auto& buf = line_vao->get_attribute(0)->get();
auto vbo = buf.bind();
for (auto& [texture, objects] : draw.complex_lines)
{
// resource manager handles the check for empty string
@ -479,7 +511,7 @@ namespace blt::gfx
line_vertices[15] = top_left.x();
line_vertices[16] = top_left.y();
buf.update(sizeof(line_vertices), line_vertices);
vbo.update(0, sizeof(line_vertices), line_vertices);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
draw.draw_count++;

View File

@ -328,12 +328,9 @@ namespace blt::gfx
font_renderer_t::compiled_text_t::compiled_text_t(font_generator_t& generator): generator(generator)
{
auto vbo = vertex_array_t::make_vbo({});
vbo->vbo.create();
vbo->vbo.bind();
vbo->vbo.allocate(sizeof(float) * 6 * 4, GL_DYNAMIC_DRAW);
vao.bind();
vao.bindVBO(vbo, 0, 4, GL_FLOAT, 4 * sizeof(float), 0);
unique_vbo_t vbo{GL_ARRAY_BUFFER};
vbo.bind().resize(sizeof(float) * 6 * 4, GL_DYNAMIC_DRAW);
vao.configure().attach_vbo(std::move(vbo)).attribute_ptr(0, 4, GL_FLOAT, 4 * sizeof(float), 0);
}
void font_renderer_t::compiled_text_t::draw()
@ -433,7 +430,7 @@ namespace blt::gfx
// BLT_TRACE("size: %ld %ld %ld", draw_count, vertices.size(), vertices.size() / 4);
renders.emplace_back(last_texture, draw_start, draw_count);
vao.getBuffer(0).update(static_cast<long>(vertices.size() * sizeof(float)), vertices.data());
vao.get_attribute(0)->get().bind().update(0, static_cast<long>(vertices.size() * sizeof(float)), vertices.data());
return *this;
}

View File

@ -27,295 +27,306 @@
#include <blt/std/ranges.h>
#include <blt/math/log_util.h>
#define __EMSCRIPTEN__
// #define __EMSCRIPTEN__
float full_screen_vertices[20] = {
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
const unsigned int full_screen_indices[6] = {
0, 1, 3,
1, 2, 3
1.0f,
1.0f,
0.0f,
1.0f,
1.0f,
1.0f,
-1.0f,
0.0f,
1.0f,
0.0f,
-1.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
-1.0f,
1.0f,
0.0f,
0.0f,
1.0f
};
const unsigned int full_screen_indices[6] = {0, 1, 3, 1, 2, 3};
template<typename T>
template <typename T>
bool once()
{
static bool ran = true;
return std::exchange(ran, false);
static bool ran = true;
return std::exchange(ran, false);
}
namespace blt::gfx
{
void pp_engine_t::create()
{
{
vertex_buffer_t vertices_vbo;
element_buffer_t indices_vbo;
vertices_vbo.create();
indices_vbo.create();
vertices_vbo.allocate(sizeof(full_screen_vertices), full_screen_vertices);
indices_vbo.allocate(sizeof(full_screen_indices), full_screen_indices);
screen_vao = std::make_unique<vertex_array_t>();
auto vb = vertex_array_t::make_vbo(vertices_vbo);
screen_vao->bindVBO(vb, 0, 3, GL_FLOAT, 5 * sizeof(float), 0);
screen_vao->bindVBO(vb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
screen_vao->bindElement(indices_vbo);
}
#ifdef __EMSCRIPTEN__
addStep(std::make_unique<pp_to_screen_step_t>());
#endif
for (auto& step : steps)
step->create();
}
pp_step_t& pp_engine_t::find_last_frame_buffer(size_t index)
{
for (i64 i = static_cast<i64>(index); i >= 0; i--)
{
if (steps[i]->requests(pp_request_t::BIND_BUFFER))
return *steps[i];
}
BLT_FATAL("We reached the end trying to find a previous frame buffer, unable to do so! Please ensure this in-place step has a framebuffer!");
std::exit(1);
}
void pp_engine_t::render()
{
screen_vao->bind();
for (const auto& [index, step] : blt::enumerate(steps))
{
if (index == 0)
continue;
auto& previous = find_last_frame_buffer(index - 1);
if (step->requests(pp_request_t::BIND_BUFFER))
step->getBuffer().bind();
else
previous.getBuffer().bind();
if (step->requests(pp_request_t::CLEAR_BUFFER))
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
step->draw(previous.getBuffer());
if (step->hasShader())
step->getShader().bind();
render_quad();
step->post_draw(previous.getBuffer());
}
glBindTexture(GL_TEXTURE_2D, 0);
frame_buffer_t::unbind();
#ifndef __EMSCRIPTEN__
void pp_engine_t::create()
{
{
unique_vbo_t vertices_vbo{GL_ARRAY_BUFFER};
unique_ebo_t indices_vbo;
vertices_vbo.bind().upload(sizeof(full_screen_vertices), full_screen_vertices, GL_STATIC_DRAW);
indices_vbo.bind().upload(sizeof(full_screen_indices), full_screen_indices, GL_STATIC_DRAW);
screen_vao = unique_vao_t{};
const auto ctx = screen_vao->configure();
auto vbo = ctx.attach_vbo(std::move(vertices_vbo));
vbo.attribute_ptr(0, 3, GL_FLOAT, 5 * sizeof(float), 0);
vbo.attribute_ptr(1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
ctx.attach_vbo(std::move(indices_vbo));
}
#ifdef __EMSCRIPTEN__
addStep(std::make_unique<pp_to_screen_step_t>());
#endif
for (const auto& step : steps)
step->create();
}
pp_step_t& pp_engine_t::find_last_frame_buffer(const size_t index) const
{
for (i64 i = static_cast<i64>(index); i >= 0; i--)
{
if (steps[i]->requests(pp_request_t::BIND_BUFFER))
return *steps[i];
}
BLT_FATAL("We reached the end trying to find a previous frame buffer, unable to do so! Please ensure this in-place step has a framebuffer!");
std::exit(1);
}
void pp_engine_t::render()
{
screen_vao->bind();
for (const auto& [index, step] : blt::enumerate(steps))
{
if (index == 0)
continue;
auto& previous = find_last_frame_buffer(index - 1);
if (step->requests(pp_request_t::BIND_BUFFER))
step->getBuffer().bind();
else
previous.getBuffer().bind();
if (step->requests(pp_request_t::CLEAR_BUFFER))
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
step->draw(previous.getBuffer());
if (step->hasShader())
step->getShader().bind();
render_quad();
step->post_draw(previous.getBuffer());
}
glBindTexture(GL_TEXTURE_2D, 0);
frame_buffer_t::unbind();
#ifndef __EMSCRIPTEN__
steps.back()->getBuffer().blitToScreen(getWindowWidth(), getWindowHeight());
#endif
}
void pp_engine_t::cleanup()
{
screen_vao = nullptr;
for (auto& v : steps)
v = nullptr;
steps.clear();
}
std::unique_ptr<shader_t> pp_engine_t::createShader(std::string_view fragment, std::optional<std::reference_wrapper<template_engine_t>> engine)
{
std::unique_ptr<shader_t> shader;
if (engine)
{
shader = shader_t::make_unique(engine, shader_postprocess_vert, std::string(fragment));
} else
{
template_engine_t templateEngine;
templateEngine.set("LAYOUT_STRING", "");
shader = shader_t::make_unique(templateEngine, shader_postprocess_vert, std::string(fragment));
}
shader->bindAttribute(0, "vertex");
shader->bindAttribute(1, "uv_in");
return shader;
}
void pp_engine_t::bind()
{
steps.front()->getBuffer().bind();
steps.front()->draw();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void pp_engine_t::render_quad()
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
void pp_in_place_t::create()
{
template_engine_t engine;
engine.set("LAYOUT_STRING", "layout (location = ${IF(LAYOUT_LOCATION) { LAYOUT_LOCATION } ELSE { ~DISCARD }}) ");
engine.set("LAYOUT_LOCATION", std::to_string(static_cast<int>(to) - static_cast<int>(frame_buffer_t::attachment_t::COLOR0)));
shader_pass = pp_engine_t::createShader(shader_pp_screen_frag, engine);
draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight());
}
void pp_in_place_t::post_draw(frame_buffer_t& previous)
{
// draw_buffer.blitTexture(previous, 0, 0, 0, 0, GL_NEAREST, from, to);
handle_errors();
previous.bind();
GLenum buf[32];
auto to_val = static_cast<GLenum>(to);
auto size = to_val - GL_COLOR_ATTACHMENT0;
for (GLenum i = 0; i < size; i++)
buf[i] = GL_NONE;
buf[size] = to_val;
glDrawBuffers(static_cast<int>(size) + 1, buf);
glClear(GL_COLOR_BUFFER_BIT);
shader_pass->bind();
glActiveTexture(GL_TEXTURE0);
draw_buffer.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
pp_engine_t::render_quad();
}
void pp_in_place_t::draw(frame_buffer_t& previous)
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
glActiveTexture(GL_TEXTURE0);
previous.getTexture(from).bind();
draw_buffer.bind();
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
glClear(GL_COLOR_BUFFER_BIT);
}
void pp_to_screen_step_t::create()
{
shader = pp_engine_t::createShader(shader_pp_screen_frag);
}
void pp_to_screen_step_t::draw(frame_buffer_t& previous)
{
glActiveTexture(GL_TEXTURE0);
previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
GLenum buf[]{GL_BACK};
glDrawBuffers(1, buf);
}
void pp_render_target_t::create()
{
draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight());
}
void pp_render_target_t::draw()
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
}
void pp_outline_target_t::create()
{
draw_buffer.create();
draw_buffer.bind();
auto* texture = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8);
draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0);
auto* mask = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8);
draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1);
render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, getWindowWidth(), getWindowHeight());
draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL);
if (!frame_buffer_t::validate())
BLT_ERROR("Failed to create render texture framebuffer!");
frame_buffer_t::unbind();
}
void pp_outline_target_t::draw()
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
const GLenum buffers[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0), static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR1)};
glDrawBuffers(2, buffers);
}
void pp_outline_step_t::create()
{
draw_buffer = frame_buffer_t::make_render_texture(getWindowHeight(), getWindowHeight());
shader = pp_engine_t::createShader(shader_pp_outline_step_frag);
shader->setInt("albedo", 0);
shader->setInt("mask", 1);
}
void pp_outline_step_t::draw(frame_buffer_t& previous)
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
glActiveTexture(GL_TEXTURE0);
previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
glActiveTexture(GL_TEXTURE1);
previous.getTexture(frame_buffer_t::attachment_t::COLOR1).bind();
shader->bind();
//auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D()));
//BLT_TRACE_STREAM << v << '\n';
//shader->setVec2("viewportSize", static_cast<f32>(v.x()), static_cast<f32>(v.y()));
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
}
void pp_blur_step_inplace_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_gaussian_blur_frag);
}
void pp_blur_step_inplace_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
//auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D()));
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), x_blur, y_blur});
}
void pp_multiplier_step_inplace_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_multiplier_frag);
}
void pp_multiplier_step_inplace_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
shader->setVec4("multiplier", multiplier);
}
void pp_overlay_blur_step_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_overlay_blur_frag);
}
void pp_overlay_blur_step_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), x_blur, y_blur});
}
void pp_mouse_highlight_step_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_highlight_frag);
}
void pp_mouse_highlight_step_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4("mousePos", blt::make_vec4(blt::vec2{getMouseX(), v.y() - getMouseY()}));
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), 4, 4});
}
}
#endif
}
void pp_engine_t::cleanup()
{
screen_vao.reset();
for (auto& v : steps)
v = nullptr;
steps.clear();
}
std::unique_ptr<shader_t> pp_engine_t::createShader(std::string_view fragment, std::optional<std::reference_wrapper<template_engine_t>> engine)
{
std::unique_ptr<shader_t> shader;
if (engine)
{
shader = shader_t::make_unique(engine, shader_postprocess_vert, std::string(fragment));
} else
{
template_engine_t templateEngine;
templateEngine.set("LAYOUT_STRING", "");
shader = shader_t::make_unique(templateEngine, shader_postprocess_vert, std::string(fragment));
}
shader->bindAttribute(0, "vertex");
shader->bindAttribute(1, "uv_in");
return shader;
}
void pp_engine_t::bind()
{
steps.front()->getBuffer().bind();
steps.front()->draw();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void pp_engine_t::render_quad()
{
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
void pp_in_place_t::create()
{
template_engine_t engine;
engine.set("LAYOUT_STRING", "layout (location = ${IF(LAYOUT_LOCATION) { LAYOUT_LOCATION } ELSE { ~DISCARD }}) ");
engine.set("LAYOUT_LOCATION", std::to_string(static_cast<int>(to) - static_cast<int>(frame_buffer_t::attachment_t::COLOR0)));
shader_pass = pp_engine_t::createShader(shader_pp_screen_frag, engine);
draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight());
}
void pp_in_place_t::post_draw(frame_buffer_t& previous)
{
// draw_buffer.blitTexture(previous, 0, 0, 0, 0, GL_NEAREST, from, to);
handle_errors();
previous.bind();
GLenum buf[32];
auto to_val = static_cast<GLenum>(to);
auto size = to_val - GL_COLOR_ATTACHMENT0;
for (GLenum i = 0; i < size; i++)
buf[i] = GL_NONE;
buf[size] = to_val;
glDrawBuffers(static_cast<int>(size) + 1, buf);
glClear(GL_COLOR_BUFFER_BIT);
shader_pass->bind();
glActiveTexture(GL_TEXTURE0);
draw_buffer.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
pp_engine_t::render_quad();
}
void pp_in_place_t::draw(frame_buffer_t& previous)
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
glActiveTexture(GL_TEXTURE0);
previous.getTexture(from).bind();
draw_buffer.bind();
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
glClear(GL_COLOR_BUFFER_BIT);
}
void pp_to_screen_step_t::create()
{
shader = pp_engine_t::createShader(shader_pp_screen_frag);
}
void pp_to_screen_step_t::draw(frame_buffer_t& previous)
{
glActiveTexture(GL_TEXTURE0);
previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
GLenum buf[]{GL_BACK};
glDrawBuffers(1, buf);
}
void pp_render_target_t::create()
{
draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight());
}
void pp_render_target_t::draw()
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
}
void pp_outline_target_t::create()
{
draw_buffer.create();
draw_buffer.bind();
auto* texture = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8);
draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0);
auto* mask = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8);
draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1);
render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, getWindowWidth(), getWindowHeight());
draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL);
if (!frame_buffer_t::validate())
BLT_ERROR("Failed to create render texture framebuffer!");
frame_buffer_t::unbind();
}
void pp_outline_target_t::draw()
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
const GLenum buffers[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0), static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR1)};
glDrawBuffers(2, buffers);
}
void pp_outline_step_t::create()
{
draw_buffer = frame_buffer_t::make_render_texture(getWindowHeight(), getWindowHeight());
shader = pp_engine_t::createShader(shader_pp_outline_step_frag);
shader->setInt("albedo", 0);
shader->setInt("mask", 1);
}
void pp_outline_step_t::draw(frame_buffer_t& previous)
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
glActiveTexture(GL_TEXTURE0);
previous.getTexture(frame_buffer_t::attachment_t::COLOR0).bind();
glActiveTexture(GL_TEXTURE1);
previous.getTexture(frame_buffer_t::attachment_t::COLOR1).bind();
shader->bind();
//auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D()));
//BLT_TRACE_STREAM << v << '\n';
//shader->setVec2("viewportSize", static_cast<f32>(v.x()), static_cast<f32>(v.y()));
GLenum buf[]{static_cast<GLenum>(frame_buffer_t::attachment_t::COLOR0)};
glDrawBuffers(1, buf);
}
void pp_blur_step_inplace_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_gaussian_blur_frag);
}
void pp_blur_step_inplace_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
//auto v = vec2(getWindowWidth(), getWindowHeight()) * (1.0 / blt::make_vec2(manager.getScale2D()));
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), x_blur, y_blur});
}
void pp_multiplier_step_inplace_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_multiplier_frag);
}
void pp_multiplier_step_inplace_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
shader->setVec4("multiplier", multiplier);
}
void pp_overlay_blur_step_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_overlay_blur_frag);
}
void pp_overlay_blur_step_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), x_blur, y_blur});
}
void pp_mouse_highlight_step_t::create()
{
pp_in_place_t::create();
shader = pp_engine_t::createShader(shader_highlight_frag);
}
void pp_mouse_highlight_step_t::draw(frame_buffer_t& previous)
{
pp_in_place_t::draw(previous);
shader->bind();
auto v = vec2(getWindowWidth(), getWindowHeight());
shader->setVec4("mousePos", blt::make_vec4(blt::vec2{getMouseX(), v.y() - getMouseY()}));
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), 4, 4});
}
}