268 lines
8.0 KiB
C++

/**
This file is a part of our_dick
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OUR_DICK_GRAPHICS_OGL_UBO_TPP
#define OUR_DICK_GRAPHICS_OGL_UBO_TPP
#include <utility> //swap, exchange
#include <type_traits> //a lot of stuff
#include <array>
#include <cstring> //memcpy, memset
#include "config.hpp"
namespace gfx::ogl{
template<class... Types>
ubo<Types...>::ubo(buffer::usage usage){
glCreateBuffers(1, &m_buffer);
glNamedBufferData(m_buffer, get_total_size_(), NULL, static_cast<GLenum>(usage));
}
template<class... Types>
ubo<Types...>::ubo(const ubo& u){
glCreateBuffers(1, &m_buffer);
auto data = u.map(buffer::maptype::READ);
if(!data)
return;
glNamedBufferData(m_buffer, u.get_size(), data, static_cast<GLenum>(u.get_usage()));
}
template<class... Types>
ubo<Types...>::ubo(ubo&& u):
m_buffer(std::exchange(u.m_buffer, 0)){}
template<class... Types>
ubo<Types...>::~ubo(void){
if(m_buffer)
glDeleteBuffers(1, &m_buffer);
}
template<class... Types>
auto ubo<Types...>::operator=(const ubo& u) -> ubo&{
return (*this = ubo(u));
}
template<class... Types>
auto ubo<Types...>::operator=(ubo&& u) -> ubo&{
std::swap(m_buffer, u.m_buffer);
return *this;
}
template<class... Types>
template<size_t I>
constexpr size_t ubo<Types...>::get_offset_of_(void){
static_assert(I < sizeof...(Types), "Index out of bounds");
if constexpr(I == 0){
return 0;
}else{
using this_type = detail::get_type_t<I,Types...>;
using prev_type = detail::get_type_t<I-1,Types...>;
size_t prev_offset = get_offset_of_<I-1>();
constexpr size_t prev_alignment = detail::get_alignment_of_<prev_type>();
constexpr size_t this_alignment = detail::get_alignment_of_<this_type>();
prev_offset += (prev_alignment * (detail::get_alignment_multiple_<prev_type>() - 1));
bool is_on_alignment_multiple = (((prev_offset + prev_alignment) % this_alignment) == 0);
if(is_on_alignment_multiple){
return prev_offset + prev_alignment;
}else if(this_alignment > prev_alignment){
return prev_offset + this_alignment;
}else{
size_t i;
for(i = this_alignment * 2;i < prev_alignment;i += this_alignment);
return prev_offset + i;
}
}
}
template<class... Types>
template<size_t I>
constexpr size_t ubo<Types...>::get_alignment_of_(void){
return detail::get_alignment_of_<detail::get_type_t<I,Types...>>();
}
template<class... Types>
constexpr size_t ubo<Types...>::get_total_size_(void){
constexpr size_t type_num = sizeof...(Types) - 1;
using last_type = detail::get_type_t<type_num,Types...>;
return get_offset_of_<type_num>() + (detail::get_alignment_of_<last_type>() * detail::get_alignment_multiple_<last_type>());
}
template<class... Types>
scoped_buffer_map<void> ubo<Types...>::map(buffer::maptype m){
return scoped_buffer_map<void>(m_buffer, m);
}
template<class... Types>
void ubo<Types...>::buffer(const void* data, size_t datasize, size_t start){
rexy::debug::print_warn("Manually editing values of ubo!\n");
if(start > get_size())
return;
glNamedBufferSubData(m_buffer, start, std::min(datasize, get_size() - start), data);
}
template<class... Types>
void ubo<Types...>::initialize(unsigned char value){
rexy::debug::print_warn("Manually editing values of ubo!\n");
const size_t size = get_size();
auto m = map(buffer::maptype::WRITE);
if(!m){
return;
}
memset(m, size, value);
}
template<class... Types>
template<size_t I>
auto ubo<Types...>::get(void){
using type = detail::get_type_t<I, Types...>;
constexpr size_t offset = get_offset_of_<I>();
constexpr size_t align = detail::get_alignment_of_<type>();
auto m = map(buffer::maptype::READ);
unsigned char* data = static_cast<unsigned char*>(m.raw());
data += offset;
if constexpr(detail::is_valid_uniform_matrix<type>::value){
type t{};
constexpr size_t rows = type::Rows;
constexpr size_t cols = type::Columns;
for(size_t i = 0;i < cols;++i){
memcpy(t.raw() + (rows * i), data + (align * i), sizeof(typename type::value_type) * rows);
}
return t;
}else if constexpr(detail::is_bounded_array<type>::value){
static constexpr size_t arr_size = std::extent<type>::value;
using this_value = std::decay_t<decltype(std::declval<type>()[0])>;
std::array<this_value,arr_size> t;
for(size_t i = 0;i < arr_size;++i){
t[i] = *reinterpret_cast<this_value*>(data + (align * i));
}
return t;
}else{
type t{};
memcpy(&t, data, sizeof(type));
return t;
}
}
template<class... Types>
template<size_t I, class T>
void ubo<Types...>::set(T&& t){
using type = detail::get_type_t<I, Types...>;
constexpr size_t offset = get_offset_of_<I>();
constexpr size_t align = detail::get_alignment_of_<type>();
auto m = map(buffer::maptype::WRITE);
unsigned char* data = static_cast<unsigned char*>(m.raw());
data += offset;
if constexpr(detail::is_valid_uniform_matrix<type>::value){
static_assert(std::is_convertible_v<std::decay_t<T>,type>);
constexpr size_t rows = type::Rows;
constexpr size_t cols = type::Columns;
for(size_t i = 0;i < cols;++i){
memcpy(data + (align * i), t.raw() + (rows * i), sizeof(typename type::value_type) * rows);
}
}else if constexpr(detail::is_bounded_array<type>::value){
using this_value = std::decay_t<decltype(std::declval<type>()[0])>;
using that_value = std::decay_t<decltype(std::declval<T>()[0])>;
static_assert(std::is_convertible_v<that_value,this_value>);
for(size_t i = 0;i < std::extent<type>::value;++i){
this_value tmp = t[i];
memcpy(data + (align * i), &tmp, sizeof(this_value));
}
}else{
static_assert(std::is_convertible_v<std::decay_t<T>,type>);
type tmp = t;
memcpy(data, &tmp, sizeof(type));
}
}
template<class... Types>
GLuint ubo<Types...>::raw(void)const{
return m_buffer;
}
template<class... Types>
size_t ubo<Types...>::get_size(void)const{
return get_total_size_();
}
template<class... Types>
size_t ubo<Types...>::get_cap(void)const{
return get_total_size_();
}
template<class... Types>
buffer::usage ubo<Types...>::get_usage(void)const{
GLint retval;
glGetNamedBufferParameteriv(m_buffer, GL_BUFFER_USAGE, &retval);
return static_cast<buffer::usage>(retval);
}
template<class... Types>
void ubo<Types...>::bind(size_t index)const{
glBindBufferBase(GL_UNIFORM_BUFFER, index, m_buffer);
}
namespace detail{
template<class T>
constexpr size_t get_alignment_of_(void){
if constexpr(detail::is_valid_uniform_scalar<T>::value){ //scalar
if constexpr(std::is_same_v<T,GLdouble>){
return 8;
}else{
return 4;
}
}else if constexpr(detail::is_bounded_array<T>::value){
return get_alignment_of_<rml::vec4f>();
}else{
constexpr size_t element_alignment = get_alignment_of_<typename T::value_type>();
if constexpr(T::Columns > 1){ //matrix
return element_alignment * 4;
}else{ //vector
if constexpr(T::Rows == 2){
return element_alignment * 2;
}else{
return element_alignment * 4;
}
}
}
}
template<class T>
constexpr size_t get_alignment_multiple_(void){
if constexpr(detail::is_valid_uniform_scalar<T>::value){
return 1;
}else if constexpr(detail::is_bounded_array<T>::value){
return std::extent<T>::value;
}else{
return T::Columns;
}
}
template<class T>
constexpr size_t size_of_(void){
if constexpr(std::is_same<bool,std::decay_t<T>>::value){
return 4;
}
return sizeof(T);
}
}
}
#endif