From c89a633f59d0e393695c10f28c4ba8635eadffba Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 7 Mar 2026 11:42:46 +0000 Subject: Replace glContainer with glAllocator glContainer is no longer required, as we can use std::vector with a custom allocator which uses OpenGL buffers for storage. Minor irritation is that the mapped buffers aren't guaranteed to be flushed in the tests, so sometimes we're missing bits in a test render. --- lib/glAllocator.h | 90 ++++++++++ lib/glContainer.h | 496 ------------------------------------------------------ 2 files changed, 90 insertions(+), 496 deletions(-) create mode 100644 lib/glAllocator.h delete mode 100644 lib/glContainer.h (limited to 'lib') 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 +#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 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(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); + } + + [[nodiscard]] GLuint + getNameFor(const_pointer ptr) const + { + const auto itr = buffers->find(ptr); + if (itr != buffers->end()) { + return itr->second; + } + return 0; + } + + template C> + [[nodiscard]] + GLuint + getNameFor(const C & container) const + { + return getNameFor(container.data()); + } + + bool operator==(const glAllocator &) const = default; + + private: + using BufferMap = std::flat_map; + std::shared_ptr buffers = std::make_shared(); + }; +} + +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/lib/glContainer.h b/lib/glContainer.h deleted file mode 100644 index 45f1030..0000000 --- a/lib/glContainer.h +++ /dev/null @@ -1,496 +0,0 @@ -#pragma once - -#include "gfx/gl/glBuffer.h" -#include -#include -#include -#include -#include - -static_assert(GL_READ_ONLY < GL_READ_WRITE); -static_assert(GL_WRITE_ONLY < GL_READ_WRITE); - -template class glContainer { -public: - using span = std::span; - using const_span = std::span; - 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; - - explicit glContainer(GLenum target = GL_ARRAY_BUFFER) : target_ {target} - { - allocBuffer(1); - } - - ~glContainer() - requires(is_trivial_dest) - = default; - - ~glContainer() - requires(!is_trivial_dest) - { - clear(); - } - - template typename C> - explicit glContainer(const C & 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(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 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 - { - if (newSize == size_) { - return; - } - - if (const auto maintain = std::min(newSize, size_)) { - std::vector existing; - const auto maintaind = static_cast(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 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 - reference_type - emplace_back(P &&... ps) - requires std::is_constructible_v - { - auto newSize = size_ + 1; - reserve(newSize); - mapForAdd(); - new (&*end()) T {std::forward

(ps)...}; - setSize(newSize); - return back(); - } - - template - iterator - emplace(iterator pos, P &&... ps) - requires std::is_nothrow_constructible_v - { - 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

(ps)...}; - setSize(newSize); - return pos; - } - - reference_type - push_back(T p) - requires std::is_move_constructible_v - { - 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 - { - 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(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(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(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_ {}; -}; -- cgit v1.3