diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-03-15 02:15:41 +0000 |
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-03-15 02:15:41 +0000 |
| commit | 200b96d780598fe5ec59f2fc7e2e3eb6ac69d0de (patch) | |
| tree | e078fdf6027a9914ad96c66e8f3849958dbad862 | |
| parent | Add missing algorithm include (diff) | |
| download | ilt-200b96d780598fe5ec59f2fc7e2e3eb6ac69d0de.tar.bz2 ilt-200b96d780598fe5ec59f2fc7e2e3eb6ac69d0de.tar.xz ilt-200b96d780598fe5ec59f2fc7e2e3eb6ac69d0de.zip | |
Remove the map of buffers, now a fat pointer containing the buffer's
name. This is accessible via the container's begin/end iterator. Move
the bulk of the logic out of the template, it's mostly void * from the
mapping anyway. Add allocate_at_least support.
| -rw-r--r-- | gfx/gl/instanceVertices.h | 2 | ||||
| -rw-r--r-- | lib/glAllocator.cpp | 28 | ||||
| -rw-r--r-- | lib/glAllocator.h | 191 | ||||
| -rw-r--r-- | test/test-glAllocator.cpp | 5 |
4 files changed, 164 insertions, 62 deletions
diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h index e2bf46e..d984938 100644 --- a/gfx/gl/instanceVertices.h +++ b/gfx/gl/instanceVertices.h @@ -124,7 +124,7 @@ public: [[nodiscard]] GLuint bufferName() const { - return base::get_allocator().getNameFor(static_cast<const base &>(*this)); + return base::begin().base().bufferName(); } using typename base::value_type; diff --git a/lib/glAllocator.cpp b/lib/glAllocator.cpp new file mode 100644 index 0000000..633f7ab --- /dev/null +++ b/lib/glAllocator.cpp @@ -0,0 +1,28 @@ +#include "glAllocator.h" + +namespace Detail { + std::pair<void *, GLuint> + allocateBuffer(size_t count, size_t objSize) + { + 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 * objSize); + glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS); + const auto data = (glMapNamedBufferRange(name, 0, size, MAPPING_FLAGS)); + if (!data) { + glDeleteBuffers(1, &name); + throw std::bad_alloc(); + } + return {data, name}; + } + + void + deallocateBuffer(GLuint name) + { + glUnmapNamedBuffer(name); + glDeleteBuffers(1, &name); + } +} diff --git a/lib/glAllocator.h b/lib/glAllocator.h index a9015da..02690c3 100644 --- a/lib/glAllocator.h +++ b/lib/glAllocator.h @@ -1,22 +1,124 @@ -#include <concepts> -#include <flat_map> +#include "special_members.h" #include <glad/gl.h> +#include <iterator> #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 glPointer { + public: + constexpr glPointer(const glPointer<std::remove_const_t<T>> & other) + requires(std::is_const_v<T>) + : ptr {other.get()}, name {other.bufferName()} + { + } + + DEFAULT_MOVE_COPY(glPointer); + + constexpr glPointer() : ptr {nullptr}, name {0} { } + + constexpr glPointer(T * ptr, GLuint name) : ptr {ptr}, name {name} { } + + auto operator<=>(const glPointer &) const noexcept = default; + + operator T *() const noexcept + { + return ptr; + } + + operator bool() const noexcept + { + return ptr; + } + + std::ptrdiff_t + operator-(const glPointer & other) const noexcept + { + return ptr - other.ptr; + } + + T * + get() const noexcept + { + return ptr; + } + + T & + operator*() const noexcept + { + return *ptr; + } + + [[nodiscard]] + T & + operator[](std::unsigned_integral auto index) const noexcept + { + return ptr[index]; + } + + T * + operator->() const noexcept + { + return ptr; + } + + glPointer<T> & + operator++() noexcept + { + ++ptr; + return *this; + } + + glPointer<T> & + operator--() noexcept + { + --ptr; + return *this; + } + + [[nodiscard]] glPointer<T> + operator+(std::integral auto offset) const noexcept + { + return {ptr + offset, name}; + } + + [[nodiscard]] glPointer<T> + operator-(std::integral auto offset) const noexcept + { + return {ptr - offset, name}; + } + + [[nodiscard]] glPointer<T> + operator+=(std::integral auto offset) const noexcept + { + return {ptr += offset, name}; + } + + [[nodiscard]] glPointer<T> + operator-=(std::integral auto offset) const noexcept + { + return {ptr -= offset, name}; + } + + [[nodiscard]] GLuint + bufferName() const noexcept + { + return name; + } + + private: + T * ptr; + GLuint name; }; + std::pair<void *, GLuint> allocateBuffer(size_t count, size_t objSize); + void deallocateBuffer(GLuint name); + template<typename T> class glAllocator { public: // NOLINTBEGIN(readability-identifier-naming) - STL like - using pointer = T *; - using const_pointer = const T *; + using pointer = glPointer<T>; + using const_pointer = glPointer<const T>; using value_type = T; // NOLINTEND(readability-identifier-naming) @@ -24,67 +126,38 @@ namespace Detail { 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); + auto allocated = allocateBuffer(count, sizeof(T)); + return {static_cast<T *>(allocated.first), allocated.second}; } - [[nodiscard]] GLuint - getNameFor(const_pointer ptr) const +#if (__cpp_lib_allocate_at_least >= 202302L) + std::allocation_result<pointer> + allocate_at_least(size_t count) { - const auto itr = buffers->find(ptr); - if (itr != buffers->end()) { - return itr->second; - } - return 0; + count = std::min(count, 32ZU); + return {allocate(count), count}; } +#endif - template<IsGlBufferAllocated<T> C> - [[nodiscard]] - GLuint - getNameFor(const C & container) const + void + deallocate(pointer ptr, size_t) { - return getNameFor(container.data()); + deallocateBuffer(ptr.bufferName()); } - bool operator==(const glAllocator &) const = default; - - private: - using BufferMap = std::flat_map<const_pointer, GLuint>; - std::shared_ptr<BufferMap> buffers = std::make_shared<BufferMap>(); + using is_always_equal = std::true_type; }; } +template<typename T> struct std::iterator_traits<Detail::glPointer<T>> { + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::contiguous_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using reference = T &; + using pointer = T *; +}; + 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/test/test-glAllocator.cpp b/test/test-glAllocator.cpp index 96457a2..baf658c 100644 --- a/test/test-glAllocator.cpp +++ b/test/test-glAllocator.cpp @@ -5,6 +5,7 @@ #include <boost/test/unit_test.hpp> #include "glAllocator.h" +#include <ranges> BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); @@ -14,9 +15,9 @@ namespace { GLuint name = 0; { glVector<long double> list; - BOOST_REQUIRE_EQUAL(list.get_allocator().getNameFor(list), 0); + BOOST_REQUIRE_EQUAL(list.begin().base().bufferName(), 0); list.reserve(5); - name = list.get_allocator().getNameFor(list); + name = list.begin().base().bufferName(); BOOST_REQUIRE_GT(name, 0); std::ranges::copy(std::views::iota(0, 10), std::back_inserter(list)); BOOST_REQUIRE_EQUAL(10, list.size()); |
