From 914fd31f5ce90d0660efe2c418586d9c77c26f66 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 30 Apr 2023 13:27:39 +0100 Subject: Initial commit of glContainer A std::vector like container backed by an OpenGL buffer. --- lib/glContainer.h | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 lib/glContainer.h (limited to 'lib') diff --git a/lib/glContainer.h b/lib/glContainer.h new file mode 100644 index 0000000..9e942b4 --- /dev/null +++ b/lib/glContainer.h @@ -0,0 +1,406 @@ +#pragma once + +#include "glArrays.h" +#include +#include +#include + +template class basic_glContainer_iterator { +public: + explicit basic_glContainer_iterator(I * i) : i {i} { } + + auto & + operator++() noexcept + { + i = Direction {}(i, 1); + return *this; + } + auto + operator++(int) noexcept + { + return basic_glContainer_iterator {std::exchange(i, Direction {}(i, 1))}; + } + auto & + operator--() noexcept + { + i = Direction {}(i, -1); + return *this; + } + auto + operator--(int) noexcept + { + return basic_glContainer_iterator {std::exchange(i, Direction {}(i, -1))}; + } + + [[nodiscard]] auto + operator-(const basic_glContainer_iterator & other) const noexcept + { + if constexpr (std::is_same_v>) { + return this->i - other.i; + } + else { + return other.i - this->i; + } + } + + [[nodiscard]] bool + operator==(const basic_glContainer_iterator & other) const noexcept + { + return this->i == other.i; + } + [[nodiscard]] bool + operator!=(const basic_glContainer_iterator & other) const noexcept + { + return this->i != other.i; + } + + [[nodiscard]] auto + operator->() const noexcept + { + return i; + } + [[nodiscard]] auto & + operator*() const noexcept + { + return *i; + } + +private: + I * i; +}; + +template class glContainer { +public: + 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 = basic_glContainer_iterator>; + using const_iterator = basic_glContainer_iterator>; + using reserve_iterator = basic_glContainer_iterator>; + using const_reserve_iterator = basic_glContainer_iterator>; + + glContainer() + { + allocBuffer(1); + } + + ~glContainer() + { + clear(); + } + + [[nodiscard]] iterator + begin() + { + map(); + return iterator {data_}; + } + + [[nodiscard]] iterator + end() + { + map(); + return iterator {data_ + size_}; + } + + [[nodiscard]] const_iterator + begin() const + { + map(); + return const_iterator {data_}; + } + + [[nodiscard]] const_iterator + end() const + { + map(); + return const_iterator {data_ + size_}; + } + + [[nodiscard]] const_iterator + cbegin() const + { + map(); + return const_iterator {data_}; + } + + [[nodiscard]] const_iterator + cend() const + { + map(); + return const_iterator {data_ + size_}; + } + + [[nodiscard]] reserve_iterator + rbegin() + { + map(); + return reserve_iterator {data_ + size_ - 1}; + } + + [[nodiscard]] reserve_iterator + rend() + { + map(); + return reserve_iterator {data_ - 1}; + } + + [[nodiscard]] const_reserve_iterator + rbegin() const + { + map(); + return const_reserve_iterator {data_ + size_ - 1}; + } + + [[nodiscard]] const_reserve_iterator + rend() const + { + map(); + return const_reserve_iterator {data_ - 1}; + } + + [[nodiscard]] const_reserve_iterator + crbegin() const + { + map(); + return const_reserve_iterator {data_ + size_ - 1}; + } + + [[nodiscard]] const_reserve_iterator + crend() const + { + map(); + return const_reserve_iterator {data_ - 1}; + } + + [[nodiscard]] size_type + size() const + { + return size_; + } + + [[nodiscard]] reference_type + at(size_type pos) + { + if (pos >= size()) { + throw std::out_of_range {__FUNCTION__}; + } + map(); + return data_[pos]; + } + + [[nodiscard]] const_reference_type + at(size_type pos) const + { + if (pos >= size()) { + throw std::out_of_range {__FUNCTION__}; + } + map(); + return data_[pos]; + } + + [[nodiscard]] reference_type + operator[](size_type pos) + { + map(); + return data_[pos]; + } + + [[nodiscard]] const_reference_type + operator[](size_type pos) const + { + map(); + return data_[pos]; + } + + [[nodiscard]] pointer_type + data() + { + map(); + return data_; + } + + [[nodiscard]] const_pointer_type + data() const + { + map(); + return data_; + } + + [[nodiscard]] reference_type + front() + { + map(); + return *data_; + } + + [[nodiscard]] reference_type + back() + { + map(); + return *(data_ + size_ - 1); + } + + [[nodiscard]] const_reference_type + front() const + { + map(); + return *data_; + } + + [[nodiscard]] const_reference_type + back() const + { + map(); + return *(data_ + size_ - 1); + } + + [[nodiscard]] bool + empty() const + { + return !size(); + } + + [[nodiscard]] size_type + capacity() const + { + return capacity_; + } + + void + unmap() const + { + if (data_) { + glUnmapNamedBuffer(buffer_); + data_ = nullptr; + } + } + + void + reserve(size_type newCapacity) + { + if (newCapacity <= capacity_) { + return; + } + + std::vector existing; + existing.reserve(size_); + map(); + std::move(begin(), end(), std::back_inserter(existing)); + allocBuffer(newCapacity); + map(); + std::move(existing.begin(), existing.end(), begin()); + } + + void + resize(size_type newSize) + { + if (newSize == size_) { + return; + } + + const auto maintain = std::min(newSize, capacity_); + std::vector existing; + const auto maintaind = static_cast(maintain); + existing.reserve(maintain); + map(); + std::move(data_, data_ + maintain, std::back_inserter(existing)); + for (auto uninitialised = data_ + newSize; uninitialised < data_ + size_; ++uninitialised) { + uninitialised->~T(); + } + allocBuffer(newSize); + map(); + std::move(existing.begin(), existing.begin() + maintaind, data_); + for (auto uninitialised = data_ + size_; uninitialised < data_ + newSize; ++uninitialised) { + new (uninitialised) T {}; + } + size_ = newSize; + } + + void + shrink_to_fit() + { + if (capacity_ <= size_) { + return; + } + + std::vector existing; + existing.reserve(size_); + map(); + std::move(begin(), end(), std::back_inserter(existing)); + allocBuffer(size_); + map(); + std::move(existing.begin(), existing.end(), begin()); + } + + void + clear() + { + std::for_each(begin(), end(), [](auto && v) { + v.~T(); + }); + size_ = 0; + } + + template + reference_type + emplace_back(P &&... ps) + { + auto newSize = size_ + 1; + reserve(newSize); + map(); + new (data_ + size_) T {std::forward

(ps)...}; + size_ = newSize; + return back(); + } + + reference_type + push_back(T p) + { + auto newSize = size_ + 1; + reserve(newSize); + map(); + new (data_ + size_) T {std::move(p)}; + size_ = newSize; + return back(); + } + +protected: + void + allocBuffer(size_type newCapacity) + { + if (newCapacity == 0) { + return allocBuffer(1); + } + glBindBuffer(GL_ARRAY_BUFFER, buffer_); + glBufferData(GL_ARRAY_BUFFER, static_cast(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + capacity_ = newCapacity; + data_ = nullptr; + } + + void + map() const + { + if (!data_) { + data_ = static_cast(glMapNamedBuffer(buffer_, GL_READ_WRITE)); + assert(data_); + } + } + + glBuffer buffer_; + std::size_t capacity_ {}; + std::size_t size_ {}; + mutable T * data_ {}; +}; + +template struct std::iterator_traits> { + using difference_type = ssize_t; + using value_type = T; + using pointer = T *; + using reference = T &; + using iterator_category = std::random_access_iterator_tag; +}; -- cgit v1.2.3 From 10f9c0e3aac7a3b540ac34b4ff515954219599a5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 16:03:49 +0100 Subject: Extend glContainer with most of the interface expected of an STL container --- lib/glContainer.h | 149 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/glContainer.h b/lib/glContainer.h index 9e942b4..3dff6b1 100644 --- a/lib/glContainer.h +++ b/lib/glContainer.h @@ -8,6 +8,10 @@ template class basic_glContainer_iterator { public: explicit basic_glContainer_iterator(I * i) : i {i} { } + template + basic_glContainer_iterator(const basic_glContainer_iterator & other) : i {&*other} + { + } auto & operator++() noexcept @@ -43,6 +47,29 @@ public: } } + [[nodiscard]] auto + operator<(const basic_glContainer_iterator & other) const noexcept + { + if constexpr (std::is_same_v>) { + return this->i < other.i; + } + else { + return other.i < this->i; + } + } + + auto + operator+(std::integral auto n) const noexcept + { + return basic_glContainer_iterator {Direction {}(i, n)}; + } + + auto + operator-(std::integral auto n) const noexcept + { + return basic_glContainer_iterator {Direction {}(i, -n)}; + } + [[nodiscard]] bool operator==(const basic_glContainer_iterator & other) const noexcept { @@ -81,15 +108,18 @@ public: using const_iterator = basic_glContainer_iterator>; using reserve_iterator = basic_glContainer_iterator>; using const_reserve_iterator = basic_glContainer_iterator>; + static constexpr bool is_trivial_dest = std::is_trivially_destructible_v; glContainer() { allocBuffer(1); } - ~glContainer() + ~glContainer() noexcept(is_trivial_dest) { - clear(); + if constexpr (!is_trivial_dest) { + clear(); + } } [[nodiscard]] iterator @@ -302,18 +332,25 @@ public: return; } - const auto maintain = std::min(newSize, capacity_); - std::vector existing; - const auto maintaind = static_cast(maintain); - existing.reserve(maintain); - map(); - std::move(data_, data_ + maintain, std::back_inserter(existing)); - for (auto uninitialised = data_ + newSize; uninitialised < data_ + size_; ++uninitialised) { - uninitialised->~T(); + if (const auto maintain = std::min(newSize, size_)) { + std::vector existing; + const auto maintaind = static_cast(maintain); + existing.reserve(maintain); + map(); + std::move(data_, data_ + maintain, std::back_inserter(existing)); + if constexpr (!is_trivial_dest) { + for (auto uninitialised = data_ + newSize; uninitialised < data_ + size_; ++uninitialised) { + uninitialised->~T(); + } + } + allocBuffer(newSize); + mapForAdd(); + std::move(existing.begin(), existing.begin() + maintaind, data_); + } + else { + allocBuffer(newSize); + mapForAdd(); } - allocBuffer(newSize); - map(); - std::move(existing.begin(), existing.begin() + maintaind, data_); for (auto uninitialised = data_ + size_; uninitialised < data_ + newSize; ++uninitialised) { new (uninitialised) T {}; } @@ -337,11 +374,14 @@ public: } void - clear() + clear() noexcept(is_trivial_dest) { - std::for_each(begin(), end(), [](auto && v) { - v.~T(); - }); + if constexpr (!is_trivial_dest) { + map(); + std::for_each(begin(), end(), [](auto && v) { + v.~T(); + }); + } size_ = 0; } @@ -351,23 +391,86 @@ public: { auto newSize = size_ + 1; reserve(newSize); - map(); + mapForAdd(); new (data_ + size_) T {std::forward

(ps)...}; size_ = newSize; return back(); } + template + iterator + emplace(iterator pos, P &&... ps) + { + static_assert(std::is_nothrow_constructible_v); + auto newSize = size_ + 1; + const auto idx = pos - begin(); + reserve(newSize); + mapForAdd(); + std::move_backward(begin() + idx, end(), end() + 1); + (data_ + idx)->~T(); + new (data_ + idx) T {std::forward

(ps)...}; + size_ = newSize; + return pos; + } + reference_type push_back(T p) { auto newSize = size_ + 1; reserve(newSize); - map(); + mapForAdd(); new (data_ + size_) T {std::move(p)}; size_ = newSize; return back(); } + iterator + insert(iterator pos, T p) + { + static_assert(std::is_nothrow_move_constructible_v); + auto newSize = size_ + 1; + const auto idx = pos - begin(); + reserve(newSize); + mapForAdd(); + std::move_backward(begin() + idx, end(), end() + 1); + (data_ + idx)->~T(); + new (data_ + idx) T {std::move(p)}; + size_ = newSize; + return pos; + } + + void + pop_back() + { + if constexpr (!is_trivial_dest) { + map(); + data_[--size_].~T(); + } + else { + --size_; + } + } + + void + erase(iterator pos) + { + erase(pos, pos + 1); + } + + void + erase(iterator pos, iterator to) + { + const auto eraseSize = to - pos; + map(); + std::move(to, end(), pos); + if constexpr (!is_trivial_dest) { + std::for_each(end() - eraseSize, end(), [](auto && v) { + v.~T(); + }); + } + size_ -= static_cast(eraseSize); + } + protected: void allocBuffer(size_type newCapacity) @@ -384,6 +487,14 @@ protected: void map() const + { + if (size_ > 0) { + mapForAdd(); + } + } + + void + mapForAdd() const { if (!data_) { data_ = static_cast(glMapNamedBuffer(buffer_, GL_READ_WRITE)); -- cgit v1.2.3 From d23fbdbe85404da8f8e0f3697852e83cc2192c38 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 16:08:25 +0100 Subject: glContainer should at least double in capacity as required --- lib/glContainer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/glContainer.h b/lib/glContainer.h index 3dff6b1..4b85005 100644 --- a/lib/glContainer.h +++ b/lib/glContainer.h @@ -315,6 +315,7 @@ public: if (newCapacity <= capacity_) { return; } + newCapacity = std::max(newCapacity, capacity_ * 2); std::vector existing; existing.reserve(size_); -- cgit v1.2.3 From 5ef2a29e0aeb15ab2c455f8e4c8d69262ca0f622 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 18:08:52 +0100 Subject: Add method to get GL buffer name of glContainer --- lib/glContainer.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/glContainer.h b/lib/glContainer.h index 4b85005..94ba118 100644 --- a/lib/glContainer.h +++ b/lib/glContainer.h @@ -206,6 +206,12 @@ public: return const_reserve_iterator {data_ - 1}; } + [[nodiscard]] const auto & + bufferName() const + { + return buffer_; + } + [[nodiscard]] size_type size() const { -- cgit v1.2.3 From a6457aea04d1705f5b03f9c9e628bebbefdcf64c Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 18:14:49 +0100 Subject: Add the pack container Keeps its elements densely packed together without any interest in order --- lib/pack.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/pack.h (limited to 'lib') diff --git a/lib/pack.h b/lib/pack.h new file mode 100644 index 0000000..92c8b20 --- /dev/null +++ b/lib/pack.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +template typename Container> class pack : protected Container { +public: + using Container::Container; + + using Container::begin; + using Container::end; + using Container::rbegin; + using Container::rend; + using Container::cbegin; + using Container::cend; + using Container::crbegin; + using Container::crend; + using Container::clear; + using Container::empty; + using Container::size; + using Container::capacity; + using Container::shrink_to_fit; + using Container::at; + using Container::data; + using Container::operator[]; + + template + decltype(auto) + emplace(Ps &&... ps) + { + return Container::emplace_back(std::forward(ps)...); + } + + void + erase(typename Container::iterator pos) + { + pos->~T(); + if (&*pos != &Container::back()) { + new (&*pos) T(std::move(Container::back())); + } + Container::pop_back(); + } +}; -- cgit v1.2.3 From ca05c4a3c1e536cdf300b49c49004e4e0e54dfea Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 20:31:50 +0100 Subject: Don't explictly call destructor of contained element --- lib/pack.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pack.h b/lib/pack.h index 92c8b20..e63d7cc 100644 --- a/lib/pack.h +++ b/lib/pack.h @@ -33,9 +33,8 @@ public: void erase(typename Container::iterator pos) { - pos->~T(); if (&*pos != &Container::back()) { - new (&*pos) T(std::move(Container::back())); + *pos = std::move(Container::back()); } Container::pop_back(); } -- cgit v1.2.3 From c977c02ba3a6ad6ada3f1a458b41a77f2be2b871 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 21:29:01 +0100 Subject: Refactor InstanceVertices in terms of glContainer --- lib/glContainer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/glContainer.h b/lib/glContainer.h index 94ba118..993170e 100644 --- a/lib/glContainer.h +++ b/lib/glContainer.h @@ -1,6 +1,7 @@ #pragma once #include "glArrays.h" +#include #include #include #include -- cgit v1.2.3