summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2026-03-07 11:50:47 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2026-03-07 11:50:47 +0000
commitadb0096046d357a6463ae2ce66c182546c8de9c2 (patch)
tree0e06a281efc2da637ebc19dca38f160e86516d9f /lib
parentUpdate stencils and billboards less often (diff)
parentReplace glContainer with glAllocator (diff)
downloadilt-adb0096046d357a6463ae2ce66c182546c8de9c2.tar.bz2
ilt-adb0096046d357a6463ae2ce66c182546c8de9c2.tar.xz
ilt-adb0096046d357a6463ae2ce66c182546c8de9c2.zip
Merge branch 'glalloc'
Diffstat (limited to 'lib')
-rw-r--r--lib/collections.h6
-rw-r--r--lib/glAllocator.h90
-rw-r--r--lib/glArrays.cpp14
-rw-r--r--lib/glArrays.h4
-rw-r--r--lib/glContainer.h496
-rw-r--r--lib/gl_traits.h84
6 files changed, 145 insertions, 549 deletions
diff --git a/lib/collections.h b/lib/collections.h
index e182af5..3c80125 100644
--- a/lib/collections.h
+++ b/lib/collections.h
@@ -10,10 +10,8 @@
#include <vector>
template<typename T, typename E>
-concept SequentialCollection = requires(T c) {
- { c.size() } -> std::integral;
- { c.data() } -> std::same_as<const E *>;
-};
+concept SequentialCollection
+ = std::ranges::contiguous_range<T> && std::is_same_v<const E, const typename T::value_type>;
template<typename T>
concept IterableCollection = std::is_same_v<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>;
diff --git a/lib/glAllocator.h b/lib/glAllocator.h
new file mode 100644
index 0000000..a9015da
--- /dev/null
+++ b/lib/glAllocator.h
@@ -0,0 +1,90 @@
+#include <concepts>
+#include <flat_map>
+#include <glad/gl.h>
+#include <memory>
+#include <stream_support.h>
+#include <vector>
+
+namespace Detail {
+ template<typename T> class glAllocator;
+ template<typename C, typename T>
+ concept IsGlBufferAllocated = requires(const C & container) {
+ { container.get_allocator() } -> std::same_as<glAllocator<T>>;
+ };
+
+ template<typename T> class glAllocator {
+ public:
+ // NOLINTBEGIN(readability-identifier-naming) - STL like
+ using pointer = T *;
+ using const_pointer = const T *;
+ using value_type = T;
+
+ // NOLINTEND(readability-identifier-naming)
+
+ pointer
+ allocate(size_t count)
+ {
+ constexpr static GLbitfield MAPPING_FLAGS
+ = GL_MAP_WRITE_BIT | GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
+ constexpr static GLbitfield STORAGE_FLAGS = GL_DYNAMIC_STORAGE_BIT | MAPPING_FLAGS;
+ GLuint name = 0;
+ glCreateBuffers(1, &name);
+ const auto size = static_cast<GLsizeiptr>(count * sizeof(T));
+ glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS);
+ const auto data = static_cast<pointer>(glMapNamedBufferRange(name, 0, size, MAPPING_FLAGS));
+ if (!data) {
+ glDeleteBuffers(1, &name);
+ throw std::bad_alloc();
+ }
+ buffers->emplace(data, name);
+ return data;
+ }
+
+ void
+ deallocate(const_pointer ptr, size_t)
+ {
+ const auto itr = buffers->find(ptr);
+ glUnmapNamedBuffer(itr->second);
+ glDeleteBuffers(1, &itr->second);
+ buffers->erase(itr);
+ }
+
+ [[nodiscard]] GLuint
+ getNameFor(const_pointer ptr) const
+ {
+ const auto itr = buffers->find(ptr);
+ if (itr != buffers->end()) {
+ return itr->second;
+ }
+ return 0;
+ }
+
+ template<IsGlBufferAllocated<T> C>
+ [[nodiscard]]
+ GLuint
+ getNameFor(const C & container) const
+ {
+ return getNameFor(container.data());
+ }
+
+ bool operator==(const glAllocator &) const = default;
+
+ private:
+ using BufferMap = std::flat_map<const_pointer, GLuint>;
+ std::shared_ptr<BufferMap> buffers = std::make_shared<BufferMap>();
+ };
+}
+
+template<typename T>
+// NOLINTNEXTLINE(readability-identifier-naming) - OpenGL like
+using glVector = std::vector<T, typename std::allocator_traits<Detail::glAllocator<T>>::allocator_type>;
+
+template<typename C>
+GLuint
+operator*(const C & container)
+ requires Detail::IsGlBufferAllocated<C, typename C::value_type>
+{
+ return container.get_allocator().getNameFor(container);
+}
+
+static_assert(Detail::IsGlBufferAllocated<glVector<int>, int>);
diff --git a/lib/glArrays.cpp b/lib/glArrays.cpp
deleted file mode 100644
index cb12f91..0000000
--- a/lib/glArrays.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "glArrays.h"
-#include <type_traits>
-
-// Specialisations (glBuffer is an example of the typedef)
-static_assert(std::is_nothrow_default_constructible_v<glBuffer>);
-static_assert(!std::is_trivially_default_constructible_v<glBuffer>);
-static_assert(std::is_nothrow_destructible_v<glBuffer>);
-static_assert(!std::is_trivially_destructible_v<glBuffer>);
-static_assert(std::is_default_constructible_v<glBuffer>);
-static_assert(!std::is_copy_constructible_v<glBuffer>);
-static_assert(!std::is_copy_assignable_v<glBuffer>);
-static_assert(std::is_nothrow_move_constructible_v<glBuffer>);
-static_assert(std::is_nothrow_move_assignable_v<glBuffer>);
-static_assert(sizeof(glBuffer) == sizeof(GLuint));
diff --git a/lib/glArrays.h b/lib/glArrays.h
index 48bd577..1cf2fbf 100644
--- a/lib/glArrays.h
+++ b/lib/glArrays.h
@@ -119,10 +119,6 @@ struct glManagedArray : public std::array<Named, N> {
};
// NOLINTBEGIN(readability-identifier-naming)
-template<size_t N> using glVertexArrays = glManagedArray<Detail::glNamed, N, &glGenVertexArrays, &glDeleteVertexArrays>;
-using glVertexArray = glManagedSingle<Detail::glNamed, &glGenVertexArrays, &glDeleteVertexArrays>;
-template<size_t N> using glBuffers = glManagedArray<Detail::glNamed, N, &glGenBuffers, &glDeleteBuffers>;
-using glBuffer = glManagedSingle<Detail::glNamed, &glGenBuffers, &glDeleteBuffers>;
template<size_t N> using glFrameBuffers = glManagedArray<Detail::glNamed, N, &glGenFramebuffers, &glDeleteFramebuffers>;
using glFrameBuffer = glManagedSingle<Detail::glNamed, &glGenFramebuffers, &glDeleteFramebuffers>;
template<size_t N>
diff --git a/lib/glContainer.h b/lib/glContainer.h
deleted file mode 100644
index 4119ef5..0000000
--- a/lib/glContainer.h
+++ /dev/null
@@ -1,496 +0,0 @@
-#pragma once
-
-#include "glArrays.h"
-#include <cassert>
-#include <span>
-#include <stdexcept>
-#include <utility>
-#include <vector>
-
-static_assert(GL_READ_ONLY < GL_READ_WRITE);
-static_assert(GL_WRITE_ONLY < GL_READ_WRITE);
-
-template<typename T> class glContainer {
-public:
- using span = std::span<T>;
- using const_span = std::span<const T>;
- using value_type = T;
- using reference_type = T &;
- using const_reference_type = const T &;
- using pointer_type = T *;
- using const_pointer_type = const T *;
- using size_type = std::size_t;
- using iterator = span::iterator;
- using const_iterator = const_span::iterator;
- using reverse_iterator = span::reverse_iterator;
- using const_reverse_iterator = const_span::reverse_iterator;
- static constexpr bool is_trivial_dest = std::is_trivially_destructible_v<T>;
-
- explicit glContainer(GLenum target = GL_ARRAY_BUFFER) : target_ {target}
- {
- allocBuffer(1);
- }
-
- ~glContainer()
- requires(is_trivial_dest)
- = default;
-
- ~glContainer()
- requires(!is_trivial_dest)
- {
- clear();
- }
-
- template<template<typename, typename...> typename C>
- explicit glContainer(const C<T> & src, GLenum target = GL_ARRAY_BUFFER) : target_ {target}
- {
- reserve(src.size());
- std::copy(src.begin(), src.end(), std::back_inserter(*this));
- }
-
- DEFAULT_MOVE_NO_COPY(glContainer);
-
- [[nodiscard]] iterator
- begin()
- {
- map(GL_READ_WRITE);
- return mkspan().begin();
- }
-
- [[nodiscard]] iterator
- end()
- {
- map(GL_READ_WRITE);
- return mkspan().end();
- }
-
- [[nodiscard]] const_iterator
- begin() const
- {
- map(GL_READ_ONLY);
- return mkcspan().begin();
- }
-
- [[nodiscard]] const_iterator
- end() const
- {
- map(GL_READ_ONLY);
- return mkcspan().end();
- }
-
- [[nodiscard]] const_iterator
- cbegin() const
- {
- map(GL_READ_ONLY);
- return mkcspan().begin();
- }
-
- [[nodiscard]] const_iterator
- cend() const
- {
- map(GL_READ_ONLY);
- return mkcspan().end();
- }
-
- [[nodiscard]] reverse_iterator
- rbegin()
- {
- map(GL_READ_WRITE);
- return mkspan().rbegin();
- }
-
- [[nodiscard]] reverse_iterator
- rend()
- {
- map(GL_READ_WRITE);
- return mkspan().rend();
- }
-
- [[nodiscard]] const_reverse_iterator
- rbegin() const
- {
- map(GL_READ_ONLY);
- return mkcspan().rbegin();
- }
-
- [[nodiscard]] const_reverse_iterator
- rend() const
- {
- map(GL_READ_ONLY);
- return mkcspan().rend();
- }
-
- [[nodiscard]] const_reverse_iterator
- crbegin() const
- {
- map(GL_READ_ONLY);
- return mkcspan().rbegin();
- }
-
- [[nodiscard]] const_reverse_iterator
- crend() const
- {
- map(GL_READ_ONLY);
- return mkcspan().rend();
- }
-
- [[nodiscard]] const auto &
- bufferName() const
- {
- return buffer_;
- }
-
- [[nodiscard]] size_type
- size() const
- {
- return size_;
- }
-
- void
- at(size_type pos, const T & value)
- {
- if (pos >= size()) {
- throw std::out_of_range {__FUNCTION__};
- }
- if (data_) {
- mkspan()[pos] = value;
- }
- else {
- glBindBuffer(target_, buffer_);
- glBufferSubData(target_, static_cast<GLintptr>(pos * sizeof(T)), sizeof(value), &value);
- glBindBuffer(target_, 0);
- }
- }
-
- [[nodiscard]] reference_type
- at(size_type pos)
- {
- if (pos >= size()) {
- throw std::out_of_range {__FUNCTION__};
- }
- map(GL_READ_WRITE);
- return mkspan()[pos];
- }
-
- [[nodiscard]] const_reference_type
- at(size_type pos) const
- {
- if (pos >= size()) {
- throw std::out_of_range {__FUNCTION__};
- }
- map(GL_READ_ONLY);
- return mkcspan()[pos];
- }
-
- [[nodiscard]] reference_type
- operator[](size_type pos)
- {
- map(GL_READ_WRITE);
- return mkspan()[pos];
- }
-
- [[nodiscard]] const_reference_type
- operator[](size_type pos) const
- {
- map(GL_READ_ONLY);
- return mkcspan()[pos];
- }
-
- [[nodiscard]] pointer_type
- data()
- {
- map(GL_READ_WRITE);
- return data_;
- }
-
- [[nodiscard]] const_pointer_type
- data() const
- {
- map(GL_READ_ONLY);
- return data_;
- }
-
- [[nodiscard]] reference_type
- front()
- {
- map(GL_READ_WRITE);
- return mkspan().front();
- }
-
- [[nodiscard]] reference_type
- back()
- {
- map(GL_READ_WRITE);
- return mkspan().back();
- }
-
- [[nodiscard]] const_reference_type
- front() const
- {
- map(GL_READ_ONLY);
- return mkcspan().front();
- }
-
- [[nodiscard]] const_reference_type
- back() const
- {
- map(GL_READ_ONLY);
- return mkcspan().back();
- }
-
- [[nodiscard]] bool
- empty() const
- {
- return !size();
- }
-
- [[nodiscard]] size_type
- capacity() const
- {
- return capacity_;
- }
-
- void
- unmap() const
- {
- if (data_) {
- glBindBuffer(target_, buffer_);
- glUnmapBuffer(target_);
- glBindBuffer(target_, 0);
- data_ = {};
- access_ = {};
- }
- }
-
- void
- reserve(size_type newCapacity)
- {
- if (newCapacity <= capacity_) {
- return;
- }
- newCapacity = std::max(newCapacity, capacity_ * 2);
-
- std::vector<T> existing;
- existing.reserve(size_);
- std::move(begin(), end(), std::back_inserter(existing));
- allocBuffer(newCapacity);
- std::move(existing.begin(), existing.end(), begin());
- }
-
- void
- resize(size_type newSize)
- requires std::is_default_constructible_v<T>
- {
- if (newSize == size_) {
- return;
- }
-
- if (const auto maintain = std::min(newSize, size_)) {
- std::vector<T> existing;
- const auto maintaind = static_cast<typename decltype(existing)::difference_type>(maintain);
- existing.reserve(maintain);
- std::move(begin(), end(), std::back_inserter(existing));
- allocBuffer(newSize);
- mapForAdd();
- std::move(existing.begin(), existing.begin() + maintaind, begin());
- }
- else {
- allocBuffer(newSize);
- mapForAdd();
- }
- if (const auto prevSize = setSize(newSize); newSize > prevSize) {
- for (auto & uninitialised : mkspan().subspan(prevSize, newSize - prevSize)) {
- new (&uninitialised) T {};
- }
- }
- }
-
- void
- shrink_to_fit()
- {
- if (capacity_ <= size_) {
- return;
- }
-
- std::vector<T> existing;
- existing.reserve(size_);
- map(is_trivial_dest ? GL_READ_ONLY : GL_READ_WRITE);
- std::move(begin(), end(), std::back_inserter(existing));
- allocBuffer(size_);
- map(GL_READ_WRITE);
- std::move(existing.begin(), existing.end(), begin());
- }
-
- void
- clear() noexcept(is_trivial_dest)
- {
- if constexpr (!is_trivial_dest) {
- map(GL_READ_WRITE);
- std::for_each(begin(), end(), [](auto && v) {
- v.~T();
- });
- }
- setSize(0);
- }
-
- template<typename... P>
- reference_type
- emplace_back(P &&... ps)
- requires std::is_constructible_v<T, P...>
- {
- auto newSize = size_ + 1;
- reserve(newSize);
- mapForAdd();
- new (&*end()) T {std::forward<P>(ps)...};
- setSize(newSize);
- return back();
- }
-
- template<typename... P>
- iterator
- emplace(iterator pos, P &&... ps)
- requires std::is_nothrow_constructible_v<T, P...>
- {
- auto newSize = size_ + 1;
- const auto idx = pos - begin();
- reserve(newSize);
- mapForAdd();
- pos = begin() + idx;
- std::move_backward(pos, end(), end() + 1);
- pos->~T();
- new (&*pos) T {std::forward<P>(ps)...};
- setSize(newSize);
- return pos;
- }
-
- reference_type
- push_back(T p)
- requires std::is_move_constructible_v<T>
- {
- auto newSize = size_ + 1;
- reserve(newSize);
- mapForAdd();
- new (&*end()) T {std::move(p)};
- setSize(newSize);
- return back();
- }
-
- iterator
- insert(iterator pos, T p)
- requires std::is_nothrow_move_constructible_v<T>
- {
- auto newSize = size_ + 1;
- const auto idx = pos - begin();
- reserve(newSize);
- mapForAdd();
- pos = begin() + idx;
- std::move_backward(pos, end(), end() + 1);
- pos->~T();
- new (&*pos) T {std::move(p)};
- setSize(newSize);
- return pos;
- }
-
- void
- pop_back()
- {
- if constexpr (!is_trivial_dest) {
- map(GL_READ_WRITE);
- back().~T();
- }
- --size_;
- }
-
- void
- erase(iterator pos)
- {
- erase(pos, pos + 1);
- }
-
- void
- erase(iterator pos, iterator to)
- {
- const auto eraseSize = to - pos;
- map(GL_READ_WRITE);
- std::move(to, end(), pos);
- if constexpr (!is_trivial_dest) {
- std::for_each(end() - eraseSize, end(), [](auto && v) {
- v.~T();
- });
- }
- setSize(size_ - static_cast<size_type>(eraseSize));
- }
-
-protected:
- size_type
- setSize(size_type s)
- {
- return std::exchange(size_, s);
- }
-
- void
- allocBuffer(size_type newCapacity)
- {
- if (newCapacity == 0) {
- return allocBuffer(1);
- }
- glBindBuffer(target_, buffer_);
- glBufferData(target_, static_cast<GLsizeiptr>(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW);
- glBindBuffer(target_, 0);
- capacity_ = newCapacity;
- data_ = {};
- access_ = {};
- }
-
- void
- map(GLenum access) const
- {
- if (size_ > 0) {
- mapMode(access);
- }
- }
-
- void
- mapForAdd() const
- {
- if (!data_) {
- mapMode(GL_READ_WRITE);
- }
- }
-
- void
- mapMode(GLenum access) const
- {
- if (data_ && access_ < access) {
- unmap();
- }
- if (!data_) {
- glBindBuffer(target_, buffer_);
- data_ = static_cast<T *>(glMapBuffer(target_, access));
- glBindBuffer(target_, 0);
- assert(data_);
- access_ = access;
- }
- }
-
- span
- mkspan() const
- {
- assert(!size_ || data_);
- return span {data_, size_};
- }
-
- const_span
- mkcspan() const
- {
- assert(!size_ || data_);
- return const_span {data_, size_};
- }
-
- glBuffer buffer_;
- GLenum target_;
- std::size_t capacity_ {};
- std::size_t size_ {};
- mutable T * data_;
- mutable GLenum access_ {};
-};
diff --git a/lib/gl_traits.h b/lib/gl_traits.h
index b3c6909..35ae9e8 100644
--- a/lib/gl_traits.h
+++ b/lib/gl_traits.h
@@ -14,27 +14,33 @@ struct gl_traits_base {
};
struct gl_traits_float : public gl_traits_base {
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer);
- return 1;
- }};
+ template<GLenum type, GLint size>
+ static GLuint
+ vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset)
+ {
+ glVertexArrayAttribFormat(vao, index, size, type, GL_FALSE, offset);
+ return 1;
+ }
};
struct gl_traits_longfloat : public gl_traits_base {
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- glVertexAttribLPointer(index, size, type, stride, pointer);
- return 1;
- }};
+ template<GLenum type, GLint size>
+ static GLuint
+ vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset)
+ {
+ glVertexArrayAttribLFormat(vao, index, size, type, offset);
+ return 1;
+ }
};
struct gl_traits_integer : public gl_traits_base {
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- glVertexAttribIPointer(index, size, type, stride, pointer);
- return 1;
- }};
+ template<GLenum type, GLint size>
+ static GLuint
+ vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset)
+ {
+ glVertexArrayAttribIFormat(vao, index, size, type, offset);
+ return 1;
+ }
};
template<> struct gl_traits<glm::f32> : public gl_traits_float {
@@ -44,18 +50,22 @@ template<> struct gl_traits<glm::f32> : public gl_traits_float {
static constexpr std::array glUniformmFunc {&glUniformMatrix2fv, &glUniformMatrix3fv, &glUniformMatrix4fv};
static constexpr auto glTexParameterFunc {&glTexParameterf};
static constexpr auto glTexParameterfFunc {&glTexParameterfv};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::f64> : public gl_traits_longfloat {
static constexpr GLenum type {GL_DOUBLE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int8> : public gl_traits_integer {
static constexpr GLenum type {GL_BYTE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int16> : public gl_traits_integer {
static constexpr GLenum type {GL_SHORT};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int32> : public gl_traits_integer {
@@ -64,14 +74,17 @@ template<> struct gl_traits<glm::int32> : public gl_traits_integer {
static constexpr std::array glUniformvFunc {&glUniform1iv, &glUniform2iv, &glUniform3iv, &glUniform4iv};
static constexpr auto glTexParameterFunc {&glTexParameteri};
static constexpr auto glTexParameterfFunc {&glTexParameteriv};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::uint8> : public gl_traits_integer {
static constexpr GLenum type {GL_UNSIGNED_BYTE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::uint16> : public gl_traits_integer {
static constexpr GLenum type {GL_UNSIGNED_SHORT};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::uint32> : public gl_traits_integer {
@@ -79,35 +92,44 @@ template<> struct gl_traits<glm::uint32> : public gl_traits_integer {
static constexpr auto glUniformFunc {&glUniform1ui};
static constexpr std::array<decltype(&glUniform1uiv), 5> glUniformvFunc {
&glUniform1uiv, &glUniform2uiv, &glUniform3uiv, &glUniform4uiv};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<typename T, std::size_t S> struct gl_traits<std::array<T, S>> : public gl_traits<T> {
static constexpr GLint size {S * gl_traits<T>::size};
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- const auto base = static_cast<const T *>(pointer);
- for (GLuint e = 0; e < S; e++) {
- glVertexAttribPointer(index + e, gl_traits<T>::size, type, GL_FALSE, stride, base + e);
- }
- return S;
- }};
+ static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) {
+ if constexpr (std::is_pod_v<T>) {
+ return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, S>(vao, index, offset);
+ }
+ else {
+ GLuint used = 0;
+ for (GLuint e = 0; e < S; e++) {
+ used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, 1>(
+ vao, index + e, offset + (e * sizeof(T)));
+ }
+ return used;
+ }
+ }};
};
template<glm::length_t L, typename T, glm::qualifier Q> struct gl_traits<glm::vec<L, T, Q>> : public gl_traits<T> {
static constexpr GLint size {L};
+ static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) {
+ return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, L>(vao, index, offset);
+ }};
};
template<glm::length_t C, glm::length_t R, typename T, glm::qualifier Q>
struct gl_traits<glm::mat<C, R, T, Q>> : public gl_traits<T> {
static constexpr GLint size {C * R};
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- const auto base = static_cast<const T *>(pointer);
- for (GLuint r = 0; r < R; r++) {
- glVertexAttribPointer(index + r, C, type, GL_FALSE, stride, base + (r * C));
- }
- return R;
- }};
+ static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) {
+ GLuint used = 0;
+ for (GLuint row = 0; row < R; row++) {
+ used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, C>(
+ vao, index + row, offset + (C * row * static_cast<GLuint>(sizeof(T))));
+ }
+ return used;
+ }};
};
template<typename T>