From 200b96d780598fe5ec59f2fc7e2e3eb6ac69d0de Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 15 Mar 2026 02:15:41 +0000 Subject: glAllocator revamp 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. --- gfx/gl/instanceVertices.h | 2 +- lib/glAllocator.cpp | 28 +++++++ lib/glAllocator.h | 191 ++++++++++++++++++++++++++++++++-------------- test/test-glAllocator.cpp | 5 +- 4 files changed, 164 insertions(+), 62 deletions(-) create mode 100644 lib/glAllocator.cpp 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(*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 + 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(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 -#include +#include "special_members.h" #include +#include #include -#include #include namespace Detail { - template class glAllocator; - template - concept IsGlBufferAllocated = requires(const C & container) { - { container.get_allocator() } -> std::same_as>; + template class glPointer { + public: + constexpr glPointer(const glPointer> & other) + requires(std::is_const_v) + : 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 & + operator++() noexcept + { + ++ptr; + return *this; + } + + glPointer & + operator--() noexcept + { + --ptr; + return *this; + } + + [[nodiscard]] glPointer + operator+(std::integral auto offset) const noexcept + { + return {ptr + offset, name}; + } + + [[nodiscard]] glPointer + operator-(std::integral auto offset) const noexcept + { + return {ptr - offset, name}; + } + + [[nodiscard]] glPointer + operator+=(std::integral auto offset) const noexcept + { + return {ptr += offset, name}; + } + + [[nodiscard]] glPointer + 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 allocateBuffer(size_t count, size_t objSize); + void deallocateBuffer(GLuint name); + template class glAllocator { public: // NOLINTBEGIN(readability-identifier-naming) - STL like - using pointer = T *; - using const_pointer = const T *; + using pointer = glPointer; + using const_pointer = glPointer; 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(count * sizeof(T)); - glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS); - const auto data = static_cast(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(allocated.first), allocated.second}; } - [[nodiscard]] GLuint - getNameFor(const_pointer ptr) const +#if (__cpp_lib_allocate_at_least >= 202302L) + std::allocation_result + 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 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; - std::shared_ptr buffers = std::make_shared(); + using is_always_equal = std::true_type; }; } +template struct std::iterator_traits> { + 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 // NOLINTNEXTLINE(readability-identifier-naming) - OpenGL like using glVector = std::vector>::allocator_type>; - -template -GLuint -operator*(const C & container) - requires Detail::IsGlBufferAllocated -{ - return container.get_allocator().getNameFor(container); -} - -static_assert(Detail::IsGlBufferAllocated, 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 #include "glAllocator.h" +#include BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); @@ -14,9 +15,9 @@ namespace { GLuint name = 0; { glVector 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()); -- cgit v1.3