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 ++++++++++++++++++++++++++++++++++++++++++++++ test/Jamfile.jam | 1 + test/test-glContainer.cpp | 223 +++++++++++++++++++++++++ 3 files changed, 630 insertions(+) create mode 100644 lib/glContainer.h create mode 100644 test/test-glContainer.cpp 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; +}; diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 1ce73b7..fb9a996 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -58,6 +58,7 @@ run perf-assetFactory.cpp : -- : test-assetFactory : benchmark benchmark test ; run test-worker.cpp ; run test-instancing.cpp : : : test ; +run test-glContainer.cpp : : : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp new file mode 100644 index 0000000..efb8f71 --- /dev/null +++ b/test/test-glContainer.cpp @@ -0,0 +1,223 @@ +#define BOOST_TEST_MODULE glContainer + +#include "testHelpers.h" +#include "testMainWindow.h" +#include "ui/applicationBase.h" +#include +#include + +#include "glContainer.h" + +BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::iterator); +BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::const_iterator); +BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::reserve_iterator); +BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::const_reserve_iterator); + +BOOST_GLOBAL_FIXTURE(ApplicationBase); +BOOST_GLOBAL_FIXTURE(TestMainWindow); + +BOOST_FIXTURE_TEST_SUITE(i, glContainer) + +BOOST_AUTO_TEST_CASE(createDestroy, *boost::unit_test::timeout(1)) +{ + BOOST_CHECK(!data_); + BOOST_CHECK_NO_THROW(map()); + BOOST_REQUIRE(data_); + BOOST_CHECK_NO_THROW(unmap()); + BOOST_CHECK(!data_); +} + +BOOST_AUTO_TEST_CASE(push_back_test, *boost::unit_test::timeout(1)) +{ + BOOST_CHECK_EQUAL(capacity_, 1); + BOOST_CHECK_EQUAL(size_, 0); + BOOST_CHECK_NO_THROW(push_back(1)); + BOOST_CHECK_NO_THROW(push_back(2)); + BOOST_CHECK_NO_THROW(push_back(3)); + BOOST_CHECK_NO_THROW(push_back(4)); + BOOST_CHECK_EQUAL(capacity_, 4); + BOOST_CHECK_EQUAL(size_, 4); + { + std::array expected1 {1, 2, 3, 4}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected1.rbegin(), expected1.rend()); + } +} + +BOOST_AUTO_TEST_CASE(emplace_back_test, *boost::unit_test::timeout(1)) +{ + BOOST_CHECK_EQUAL(capacity_, 1); + BOOST_CHECK_EQUAL(size_, 0); + + BOOST_CHECK_NO_THROW(emplace_back(1)); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_EQUAL(capacity_, 2); + BOOST_CHECK_EQUAL(size_, 2); + + BOOST_CHECK_NO_THROW(reserve(5)); + BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(size_, 2); + + BOOST_CHECK_NO_THROW(emplace_back(3)); + BOOST_CHECK_NO_THROW(emplace_back(4)); + BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(size_, 4); + + { + std::array expected1 {1, 2, 3, 4}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected1.rbegin(), expected1.rend()); + } + + BOOST_CHECK_NO_THROW(emplace_back(5)); + BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(size_, 5); + BOOST_CHECK_NO_THROW(emplace_back(6)); + BOOST_CHECK_NO_THROW(emplace_back(7)); + BOOST_CHECK_EQUAL(capacity_, 7); + BOOST_CHECK_EQUAL(size_, 7); + + { + std::array expected2 {1, 2, 3, 4, 5, 6, 7}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected2.begin(), expected2.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected2.rbegin(), expected2.rend()); + } + + BOOST_CHECK_EQUAL(7, end() - begin()); + BOOST_CHECK_EQUAL(7, rend() - rbegin()); +} + +BOOST_AUTO_TEST_CASE(resize_test) +{ + BOOST_CHECK_NO_THROW(push_back(1)); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_NO_THROW(resize(4)); + BOOST_CHECK_EQUAL(capacity_, 4); + BOOST_CHECK_EQUAL(size_, 4); + { + std::array expected1 {1, 2, 0, 0}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + + BOOST_CHECK_NO_THROW(resize(1)); + BOOST_CHECK_EQUAL(capacity_, 1); + BOOST_CHECK_EQUAL(size_, 1); + { + std::array expected2 {1}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected2.begin(), expected2.end()); + } + + BOOST_CHECK_NO_THROW(resize(1)); + BOOST_CHECK_EQUAL(capacity_, 1); + BOOST_CHECK_EQUAL(size_, 1); + + BOOST_CHECK_NO_THROW(resize(0)); + BOOST_CHECK_EQUAL(capacity_, 1); + BOOST_CHECK_EQUAL(size_, 0); + BOOST_CHECK_EQUAL(begin(), end()); + BOOST_CHECK_EQUAL(rbegin(), rend()); +} + +BOOST_AUTO_TEST_CASE(shrink_to_fit_test) +{ + BOOST_CHECK_NO_THROW(reserve(4)); + BOOST_CHECK_NO_THROW(emplace_back(1)); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_EQUAL(capacity_, 4); + BOOST_CHECK_EQUAL(size_, 2); + { + std::array expected1 {1, 2}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + BOOST_CHECK_NO_THROW(shrink_to_fit()); + BOOST_CHECK_EQUAL(capacity_, 2); + BOOST_CHECK_EQUAL(size_, 2); + { + std::array expected1 {1, 2}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + + BOOST_CHECK_NO_THROW(shrink_to_fit()); + BOOST_CHECK_EQUAL(capacity(), 2); + BOOST_CHECK_EQUAL(size(), 2); + + BOOST_CHECK_NO_THROW(clear()); + BOOST_CHECK_EQUAL(capacity(), 2); + BOOST_CHECK_EQUAL(size(), 0); +} + +BOOST_AUTO_TEST_CASE(getters) +{ + BOOST_CHECK(empty()); + BOOST_CHECK_NO_THROW(emplace_back(1)); + BOOST_CHECK(!empty()); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_EQUAL(1, front()); + BOOST_CHECK_EQUAL(2, back()); + BOOST_CHECK_EQUAL(1, at(0)); + BOOST_CHECK_EQUAL(2, at(1)); + BOOST_CHECK_EQUAL(1, (*this)[0]); + BOOST_CHECK_EQUAL(2, (*this)[1]); + BOOST_CHECK_EQUAL(data_, data()); + BOOST_CHECK_THROW(std::ignore = at(2), std::out_of_range); + + const auto & constCont {*this}; + BOOST_CHECK_EQUAL(1, constCont.front()); + BOOST_CHECK_EQUAL(2, constCont.back()); + { + std::array expected1 {1, 2}; + BOOST_CHECK_EQUAL_COLLECTIONS(constCont.begin(), constCont.end(), expected1.begin(), expected1.end()); + BOOST_CHECK_EQUAL_COLLECTIONS(constCont.rbegin(), constCont.rend(), expected1.rbegin(), expected1.rend()); + BOOST_CHECK_EQUAL_COLLECTIONS(constCont.cbegin(), constCont.cend(), expected1.cbegin(), expected1.cend()); + BOOST_CHECK_EQUAL_COLLECTIONS(constCont.crbegin(), constCont.crend(), expected1.crbegin(), expected1.crend()); + } + BOOST_CHECK_EQUAL(1, constCont.at(0)); + BOOST_CHECK_EQUAL(2, constCont.at(1)); + BOOST_CHECK_EQUAL(1, constCont[0]); + BOOST_CHECK_EQUAL(2, constCont[1]); + BOOST_CHECK_EQUAL(data_, constCont.data()); + BOOST_CHECK_THROW(std::ignore = constCont.at(2), std::out_of_range); +} + +BOOST_AUTO_TEST_CASE(random_access) +{ + BOOST_CHECK_NO_THROW(emplace_back(1)); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_NO_THROW(emplace_back(3)); + + auto i = begin(); + BOOST_CHECK_EQUAL(1, *i); + BOOST_CHECK_EQUAL(2, *++i); + BOOST_CHECK_EQUAL(2, *i++); + BOOST_CHECK_EQUAL(3, *i); + BOOST_CHECK_EQUAL(3, *i--); + BOOST_CHECK_EQUAL(2, *i); + BOOST_CHECK_EQUAL(1, *--i); + BOOST_CHECK_EQUAL(1, *i); +} + +BOOST_AUTO_TEST_SUITE_END(); + +struct C { + int x; + float y; +}; + +BOOST_FIXTURE_TEST_SUITE(c, glContainer) + +BOOST_AUTO_TEST_CASE(basic) +{ + BOOST_CHECK_NO_THROW(emplace_back(1, 2.f)); + BOOST_CHECK_EQUAL(1, begin()->x); + BOOST_CHECK_EQUAL(2.f, begin()->y); + BOOST_CHECK_NO_THROW(begin()->x = 3); + BOOST_CHECK_EQUAL(3, begin()->x); + + BOOST_CHECK_NO_THROW(push_back(C {4, 5.f})); + BOOST_CHECK_EQUAL(3, begin()->x); + BOOST_CHECK_EQUAL(2.f, begin()->y); + BOOST_CHECK_EQUAL(4, rbegin()->x); + BOOST_CHECK_EQUAL(5.f, rbegin()->y); +} + +BOOST_AUTO_TEST_SUITE_END(); -- 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 ++++++++++++++++++++++++++++++++++++++++------ test/test-glContainer.cpp | 117 ++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 19 deletions(-) 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)); diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp index efb8f71..5df56d4 100644 --- a/test/test-glContainer.cpp +++ b/test/test-glContainer.cpp @@ -22,6 +22,9 @@ BOOST_AUTO_TEST_CASE(createDestroy, *boost::unit_test::timeout(1)) { BOOST_CHECK(!data_); BOOST_CHECK_NO_THROW(map()); + BOOST_REQUIRE(!data_); + BOOST_CHECK_NO_THROW(emplace_back(0)); + BOOST_CHECK_NO_THROW(map()); BOOST_REQUIRE(data_); BOOST_CHECK_NO_THROW(unmap()); BOOST_CHECK(!data_); @@ -196,12 +199,78 @@ BOOST_AUTO_TEST_CASE(random_access) BOOST_CHECK_EQUAL(1, *i); } +BOOST_AUTO_TEST_CASE(insert_remove_test) +{ + BOOST_CHECK_NO_THROW(emplace_back(1)); + BOOST_CHECK_NO_THROW(emplace_back(2)); + BOOST_CHECK_NO_THROW(emplace_back(3)); + BOOST_CHECK_NO_THROW(emplace_back(4)); + BOOST_CHECK_NO_THROW(pop_back()); + BOOST_CHECK_EQUAL(size_, 3); + BOOST_CHECK_EQUAL(capacity_, 4); + + BOOST_CHECK_NO_THROW(emplace(begin(), 5)); + BOOST_CHECK_EQUAL(size_, 4); + BOOST_CHECK_EQUAL(capacity_, 4); + { + std::array expected1 {5, 1, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + + { + std::array expected1 {2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin() + 2, end(), expected1.begin(), expected1.end()); + } + BOOST_CHECK_NO_THROW(insert(begin() + 2, 6)); + BOOST_CHECK_EQUAL(size_, 5); + BOOST_CHECK_EQUAL(capacity_, 5); + { + std::array expected1 {5, 1, 6, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + erase(begin() + 1); + BOOST_CHECK_EQUAL(size_, 4); + BOOST_CHECK_EQUAL(capacity_, 5); + { + std::array expected1 {5, 6, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + erase(begin() + 1, end() - 1); + BOOST_CHECK_EQUAL(size_, 2); + BOOST_CHECK_EQUAL(capacity_, 5); + { + std::array expected1 {5, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } +} + +BOOST_AUTO_TEST_CASE(stl) +{ + BOOST_CHECK_NO_THROW(resize(10)); + BOOST_CHECK_NO_THROW(std::generate(begin(), end(), [x = 0]() mutable { + return x++; + })); + BOOST_CHECK_NO_THROW(std::sort(rbegin(), rend())); + { + std::array expected1 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + const auto newend = std::remove_if(begin(), end(), [](const auto & x) { + return x % 2 == 0; + }); + { + std::array expected1 {9, 7, 5, 3, 1}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), newend, expected1.begin(), expected1.end()); + } +} + BOOST_AUTO_TEST_SUITE_END(); struct C { int x; float y; }; +static_assert(std::is_trivially_destructible_v); BOOST_FIXTURE_TEST_SUITE(c, glContainer) @@ -221,3 +290,51 @@ BOOST_AUTO_TEST_CASE(basic) } BOOST_AUTO_TEST_SUITE_END(); + +struct CC { + ~CC() + { + ++x; + } + int x; + float y; +}; +static_assert(!std::is_trivially_destructible_v); + +BOOST_FIXTURE_TEST_SUITE(cc, glContainer) + +BOOST_AUTO_TEST_CASE(basic) +{ + BOOST_CHECK_NO_THROW(emplace_back(1, 2.f)); + BOOST_CHECK_EQUAL(1, begin()->x); + BOOST_CHECK_EQUAL(2.f, begin()->y); + BOOST_CHECK_NO_THROW(begin()->x = 3); + BOOST_CHECK_EQUAL(3, begin()->x); + + BOOST_CHECK_NO_THROW(push_back(CC {4, 5.f})); + BOOST_CHECK_EQUAL(3, begin()->x); + BOOST_CHECK_EQUAL(2.f, begin()->y); + BOOST_CHECK_EQUAL(4, rbegin()->x); + BOOST_CHECK_EQUAL(5.f, rbegin()->y); + BOOST_CHECK_NO_THROW(pop_back()); + BOOST_CHECK_EQUAL(size(), 1); + BOOST_CHECK_EQUAL(capacity(), 2); + BOOST_CHECK_NO_THROW(resize(3)); + BOOST_CHECK_EQUAL(size(), 3); + BOOST_CHECK_EQUAL(capacity(), 3); + BOOST_CHECK_NO_THROW(resize(1)); + BOOST_CHECK_EQUAL(size(), 1); + BOOST_CHECK_EQUAL(capacity(), 1); +} +BOOST_AUTO_TEST_CASE(insert_remove_test) +{ + BOOST_CHECK_NO_THROW(emplace_back(1, 2.f)); + BOOST_CHECK_NO_THROW(emplace_back(3, 4.f)); + BOOST_CHECK_NO_THROW(emplace(begin(), 5, 6.f)); + BOOST_CHECK_NO_THROW(emplace(begin() + 1, 7, 8.f)); + BOOST_CHECK_NO_THROW(emplace(begin() + 2, 9, 10.f)); + BOOST_CHECK_EQUAL(capacity(), 5); + BOOST_CHECK_EQUAL(size(), 5); +} + +BOOST_AUTO_TEST_SUITE_END(); -- 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 + test/test-glContainer.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) 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_); diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp index 5df56d4..09d21d1 100644 --- a/test/test-glContainer.cpp +++ b/test/test-glContainer.cpp @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(emplace_back_test, *boost::unit_test::timeout(1)) BOOST_CHECK_EQUAL(size_, 5); BOOST_CHECK_NO_THROW(emplace_back(6)); BOOST_CHECK_NO_THROW(emplace_back(7)); - BOOST_CHECK_EQUAL(capacity_, 7); + BOOST_CHECK_EQUAL(capacity_, 10); BOOST_CHECK_EQUAL(size_, 7); { @@ -223,21 +223,21 @@ BOOST_AUTO_TEST_CASE(insert_remove_test) } BOOST_CHECK_NO_THROW(insert(begin() + 2, 6)); BOOST_CHECK_EQUAL(size_, 5); - BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(capacity_, 8); { std::array expected1 {5, 1, 6, 2, 3}; BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); } erase(begin() + 1); BOOST_CHECK_EQUAL(size_, 4); - BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(capacity_, 8); { std::array expected1 {5, 6, 2, 3}; BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); } erase(begin() + 1, end() - 1); BOOST_CHECK_EQUAL(size_, 2); - BOOST_CHECK_EQUAL(capacity_, 5); + BOOST_CHECK_EQUAL(capacity_, 8); { std::array expected1 {5, 3}; BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); @@ -333,6 +333,9 @@ BOOST_AUTO_TEST_CASE(insert_remove_test) BOOST_CHECK_NO_THROW(emplace(begin(), 5, 6.f)); BOOST_CHECK_NO_THROW(emplace(begin() + 1, 7, 8.f)); BOOST_CHECK_NO_THROW(emplace(begin() + 2, 9, 10.f)); + BOOST_CHECK_EQUAL(capacity(), 8); + BOOST_CHECK_EQUAL(size(), 5); + BOOST_CHECK_NO_THROW(shrink_to_fit()); BOOST_CHECK_EQUAL(capacity(), 5); BOOST_CHECK_EQUAL(size(), 5); } -- cgit v1.2.3 From 90214f9707f603741b84798c3672b5b13244a817 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 1 May 2023 16:21:30 +0100 Subject: Add missing test over iterator comparison --- test/test-glContainer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp index 09d21d1..33ec992 100644 --- a/test/test-glContainer.cpp +++ b/test/test-glContainer.cpp @@ -264,6 +264,17 @@ BOOST_AUTO_TEST_CASE(stl) } } +BOOST_AUTO_TEST_CASE(iter_compare) +{ + BOOST_CHECK_EQUAL(begin(), end()); + BOOST_CHECK_EQUAL(rbegin(), rend()); + emplace_back(); + BOOST_CHECK_LT(begin(), end()); + BOOST_CHECK_LT(rbegin(), rend()); + BOOST_CHECK_LT(cbegin(), cend()); + BOOST_CHECK_LT(crbegin(), crend()); +} + BOOST_AUTO_TEST_SUITE_END(); struct C { @@ -326,6 +337,7 @@ BOOST_AUTO_TEST_CASE(basic) BOOST_CHECK_EQUAL(size(), 1); BOOST_CHECK_EQUAL(capacity(), 1); } + BOOST_AUTO_TEST_CASE(insert_remove_test) { BOOST_CHECK_NO_THROW(emplace_back(1, 2.f)); -- 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(+) 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 ++++++++++++++++++++++++++++++++++++++++++ test/Jamfile.jam | 1 + test/test-pack.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 lib/pack.h create mode 100644 test/test-pack.cpp 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(); + } +}; diff --git a/test/Jamfile.jam b/test/Jamfile.jam index fb9a996..3b4e891 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -59,6 +59,7 @@ run perf-persistence.cpp : -- : test-persistence : benchmark t run test-worker.cpp ; run test-instancing.cpp : : : test ; run test-glContainer.cpp : : : test ; +run test-pack.cpp : : : test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/test-pack.cpp b/test/test-pack.cpp new file mode 100644 index 0000000..1f9f061 --- /dev/null +++ b/test/test-pack.cpp @@ -0,0 +1,41 @@ +#define BOOST_TEST_MODULE pack + +#include +#include + +#include "pack.h" +#include + +using IntegerVectorPack = pack; + +BOOST_FIXTURE_TEST_SUITE(pint, IntegerVectorPack) + +BOOST_AUTO_TEST_CASE(basics) +{ + BOOST_CHECK_EQUAL(size(), 0); + BOOST_CHECK_NO_THROW(emplace(1)); + BOOST_CHECK_NO_THROW(emplace(2)); + BOOST_CHECK_NO_THROW(emplace(3)); + BOOST_CHECK_NO_THROW(emplace(4)); + BOOST_CHECK_EQUAL(size(), 4); + { + std::array expected1 {1, 2, 3, 4}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + + BOOST_CHECK_NO_THROW(erase(begin() + 1)); + BOOST_CHECK_EQUAL(size(), 3); + { + std::array expected1 {1, 4, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } + + BOOST_CHECK_NO_THROW(erase(--end())); + BOOST_CHECK_EQUAL(size(), 2); + { + std::array expected1 {1, 4}; + BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); + } +} + +BOOST_AUTO_TEST_SUITE_END() -- 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(-) 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 --- game/scenary/foliage.cpp | 4 +- game/vehicles/railVehicleClass.cpp | 14 ++--- gfx/gl/instanceVertices.h | 124 +++++++++---------------------------- lib/glContainer.h | 1 + test/test-instancing.cpp | 65 +++++++++---------- 5 files changed, 68 insertions(+), 140 deletions(-) diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index 9304842..702a52c 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -21,7 +21,7 @@ Foliage::postLoad() void Foliage::render(const SceneShader & shader) const { - if (const auto count = instances.count()) { + if (const auto count = instances.size()) { shader.basicInst.use(); if (texture) { texture->bind(); @@ -33,7 +33,7 @@ Foliage::render(const SceneShader & shader) const void Foliage::shadows(const ShadowMapper & mapper) const { - if (const auto count = instances.count()) { + if (const auto count = instances.size()) { mapper.dynamicPointInst.use(); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); } diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 64a21be..148081f 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -46,26 +46,24 @@ RailVehicleClass::postLoad() void RailVehicleClass::render(const SceneShader & shader) const { - if (const auto count = instancesBody.count()) { + if (const auto count = instancesBody.size()) { if (texture) { texture->bind(); } shader.basicInst.use(); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); - bogies.front()->DrawInstanced( - instancesBogiesVAO.front(), static_cast(instancesBogies.front().count())); - bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().count())); + bogies.front()->DrawInstanced(instancesBogiesVAO.front(), static_cast(instancesBogies.front().size())); + bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().size())); } } void RailVehicleClass::shadows(const ShadowMapper & mapper) const { - if (const auto count = instancesBody.count()) { + if (const auto count = instancesBody.size()) { mapper.dynamicPointInst.use(); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); - bogies.front()->DrawInstanced( - instancesBogiesVAO.front(), static_cast(instancesBogies.front().count())); - bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().count())); + bogies.front()->DrawInstanced(instancesBogiesVAO.front(), static_cast(instancesBogies.front().size())); + bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().size())); } } diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h index 9df6e12..7b0341b 100644 --- a/gfx/gl/instanceVertices.h +++ b/gfx/gl/instanceVertices.h @@ -1,20 +1,15 @@ #pragma once -#include "glArrays.h" +#include "glContainer.h" +#include "pack.h" #include -#include -#include #include #include -#include -template class InstanceVertices { -public: - InstanceVertices(size_t initialSize = 16) - { - allocBuffer(initialSize); - } +template class InstanceVertices : protected glContainer { + using base = glContainer; +public: class [[nodiscard]] InstanceProxy { public: InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { } @@ -44,27 +39,27 @@ public: T & operator=(U && v) { - return instances->at(index) = std::forward(v); + return instances->lookup(index) = std::forward(v); } [[nodiscard]] operator T &() { - return instances->at(index); + return instances->lookup(index); } [[nodiscard]] operator const T &() const { - return instances->at(index); + return instances->lookup(index); } [[nodiscard]] T * get() { - return &instances->at(index); + return &instances->lookup(index); } [[nodiscard]] const T * get() const { - return &instances->at(index); + return &instances->lookup(index); } [[nodiscard]] T * operator->() @@ -79,12 +74,12 @@ public: [[nodiscard]] T & operator*() { - return instances->at(index); + return instances->lookup(index); } [[nodiscard]] const T & operator*() const { - return instances->at(index); + return instances->lookup(index); } private: @@ -96,33 +91,25 @@ public: [[nodiscard]] InstanceProxy acquire(Params &&... params) { - map(); if (!unused.empty()) { auto idx = unused.back(); unused.pop_back(); - index[idx] = next++; - new (&at(idx)) T(std::forward(params)...); + index[idx] = base::size(); + base::emplace_back(std::forward(params)...); return InstanceProxy {this, idx}; } - if (next >= capacity) { - resize(capacity * 2); - } - index.emplace_back(next++); - new (data + index.back()) T(std::forward(params)...); + index.emplace_back(base::size()); + base::emplace_back(std::forward(params)...); return InstanceProxy {this, index.size() - 1}; } - [[nodiscard]] const auto & - bufferName() const - { - return buffer; - } + using base::bufferName; [[nodiscard]] auto - count() const + size() const { - unmap(); - return next; + base::unmap(); + return base::size(); } protected: @@ -131,16 +118,13 @@ protected: void release(const size_t pidx) { - // Destroy p's object - at(pidx).~T(); - if (--next != index[pidx]) { - // Move last object into p's slot - new (&at(pidx)) T {std::move(data[next])}; - (data[next]).~T(); - *std::find_if(index.begin(), index.end(), [this](const auto & i) { - return i == next && !std::binary_search(unused.begin(), unused.end(), &i - index.data()); + if (base::size() - 1 != index[pidx]) { + lookup(pidx) = std::move(base::back()); + *std::find_if(index.begin(), index.end(), [this, old = base::size() - 1](const auto & i) { + return i == old && !std::binary_search(unused.begin(), unused.end(), &i - index.data()); }) = index[pidx]; } + base::pop_back(); if (pidx == index.size() - 1) { index.pop_back(); } @@ -150,64 +134,14 @@ protected: } } - void - allocBuffer(std::size_t newCapacity) - { - 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 - resize(size_t newCapacity) - { - const auto maintain = std::min(newCapacity, capacity); - std::vector existing; - const auto maintaind = static_cast(maintain); - existing.reserve(maintain); - map(); - std::move(data, data + maintain, std::back_inserter(existing)); - allocBuffer(newCapacity); - map(); - std::move(existing.begin(), existing.begin() + maintaind, data); - capacity = newCapacity; - } - [[nodiscard]] T & - at(size_t pindex) - { - map(); - return data[index[pindex]]; - } - - void - map() const + lookup(size_t pindex) { - if (!data) { - data = static_cast(glMapNamedBuffer(buffer, GL_READ_WRITE)); - assert(data); - } - } - - void - unmap() const - { - if (data) { - glUnmapNamedBuffer(buffer); - data = nullptr; - } + return base::data()[index[pindex]]; } - glBuffer buffer; - mutable T * data {}; - // Size of buffer - std::size_t capacity {}; - // # used of capacity - std::size_t next {}; - // Index into buffer given to nth proxy + // Index into buffer given to nth proxy std::vector index; - // List of free spaces in index + // List of free spaces in index std::vector unused; }; 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 diff --git a/test/test-instancing.cpp b/test/test-instancing.cpp index 8745484..7d8ea55 100644 --- a/test/test-instancing.cpp +++ b/test/test-instancing.cpp @@ -17,14 +17,8 @@ BOOST_FIXTURE_TEST_SUITE(i, InstanceVertices) BOOST_AUTO_TEST_CASE(createDestroy) { - BOOST_CHECK(!data); - map(); - BOOST_REQUIRE(data); - BOOST_CHECK_EQUAL(0, next); BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); - unmap(); - BOOST_CHECK(!data); } BOOST_AUTO_TEST_CASE(acquireRelease) @@ -32,12 +26,12 @@ BOOST_AUTO_TEST_CASE(acquireRelease) { auto proxy = acquire(); *proxy = 20; - BOOST_CHECK_EQUAL(1, next); + BOOST_CHECK_EQUAL(1, size()); BOOST_REQUIRE_EQUAL(1, index.size()); BOOST_CHECK_EQUAL(0, index.front()); BOOST_CHECK(unused.empty()); } - BOOST_CHECK_EQUAL(0, next); + BOOST_CHECK_EQUAL(0, size()); BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); } @@ -47,13 +41,13 @@ BOOST_AUTO_TEST_CASE(acquireReleaseMove) { auto proxy1 = acquire(); *proxy1 = 20; - BOOST_CHECK_EQUAL(1, next); + BOOST_CHECK_EQUAL(1, size()); auto proxy2 = std::move(proxy1); proxy2 = 40; - BOOST_CHECK_EQUAL(1, next); - BOOST_CHECK_EQUAL(data[0], 40); + BOOST_CHECK_EQUAL(1, size()); + BOOST_CHECK_EQUAL(at(0), 40); } - BOOST_CHECK_EQUAL(0, next); + BOOST_CHECK_EQUAL(0, size()); BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); } @@ -62,13 +56,13 @@ BOOST_AUTO_TEST_CASE(autoMapUnmap) { { auto proxy = acquire(); - BOOST_CHECK(data); + BOOST_CHECK(data_); std::ignore = bufferName(); - BOOST_CHECK(data); - BOOST_CHECK_EQUAL(1, count()); - BOOST_CHECK(!data); + BOOST_CHECK(data_); + BOOST_CHECK_EQUAL(1, size()); + BOOST_CHECK(!data_); } - BOOST_CHECK_EQUAL(0, count()); + BOOST_CHECK_EQUAL(0, size()); } BOOST_AUTO_TEST_CASE(initialize) @@ -89,7 +83,7 @@ BOOST_AUTO_TEST_CASE(resize) proxies.push_back(acquire(n)); expected.emplace_back(n); } - BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), data, data + COUNT); + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), data(), data() + COUNT); BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), proxies.begin(), proxies.end()); } @@ -100,11 +94,11 @@ BOOST_AUTO_TEST_CASE(shuffle) BOOST_CHECK_EQUAL(1, proxies.emplace_back(acquire(1))); BOOST_CHECK_EQUAL(2, proxies.emplace_back(acquire(2))); BOOST_CHECK_EQUAL(3, proxies.emplace_back(acquire(3))); - BOOST_CHECK_EQUAL(4, next); - BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); - BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); - BOOST_CHECK_EQUAL(data + 2, proxies[2].get()); - BOOST_CHECK_EQUAL(data + 3, proxies[3].get()); + BOOST_CHECK_EQUAL(4, size()); + BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); + BOOST_CHECK_EQUAL(at(1), *proxies[1].get()); + BOOST_CHECK_EQUAL(at(2), *proxies[2].get()); + BOOST_CHECK_EQUAL(at(3), *proxies[3].get()); BOOST_CHECK(unused.empty()); BOOST_REQUIRE_EQUAL(4, index.size()); BOOST_CHECK_EQUAL(0, index[0]); @@ -113,28 +107,29 @@ BOOST_AUTO_TEST_CASE(shuffle) BOOST_CHECK_EQUAL(3, index[3]); // Remove 1, 3 moves to [1] proxies.erase(proxies.begin() + 1); + BOOST_REQUIRE_EQUAL(3, proxies.size()); BOOST_REQUIRE_EQUAL(4, index.size()); BOOST_REQUIRE_EQUAL(1, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); - BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); - BOOST_CHECK_EQUAL(data + 2, proxies[1].get()); - BOOST_CHECK_EQUAL(data + 1, proxies[2].get()); - // Remove 1, 2 moves to [1] + BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); + BOOST_CHECK_EQUAL(at(2), *proxies[1].get()); + BOOST_CHECK_EQUAL(at(1), *proxies[2].get()); + // Remove 1, 2 moves to [1] proxies.erase(proxies.begin() + 1); BOOST_REQUIRE_EQUAL(4, index.size()); BOOST_REQUIRE_EQUAL(2, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); BOOST_CHECK_EQUAL(2, unused[1]); - BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); - BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); + BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); + BOOST_CHECK_EQUAL(at(1), *proxies[1].get()); // Add new, takes 2 at [2] BOOST_CHECK_EQUAL(4, proxies.emplace_back(acquire(4))); BOOST_REQUIRE_EQUAL(4, index.size()); BOOST_REQUIRE_EQUAL(1, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); - BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); - BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); - BOOST_CHECK_EQUAL(data + 2, proxies[2].get()); + BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); + BOOST_CHECK_EQUAL(at(1), *proxies[1].get()); + BOOST_CHECK_EQUAL(at(2), *proxies[2].get()); } BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) @@ -158,7 +153,7 @@ BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) break; } - BOOST_REQUIRE_EQUAL(next, proxies.size()); + BOOST_REQUIRE_EQUAL(size(), proxies.size()); for (const auto & [n, p] : proxies) { BOOST_REQUIRE_EQUAL(n, p); } @@ -169,10 +164,10 @@ BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) } } BOOST_TEST_CONTEXT(index) { - BOOST_REQUIRE_EQUAL(iused.size(), next); + BOOST_REQUIRE_EQUAL(iused.size(), size()); if (!iused.empty()) { BOOST_REQUIRE_EQUAL(*iused.begin(), 0); - BOOST_REQUIRE_EQUAL(*iused.rbegin(), next - 1); + BOOST_REQUIRE_EQUAL(*iused.rbegin(), size() - 1); } } } -- cgit v1.2.3 From 72ab29786f6d4c97a92cb95c75091537490e2d0f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 6 May 2023 16:42:08 +0100 Subject: Templated BufferedLocation and single buffer storage for RVC locations --- game/vehicles/railVehicle.cpp | 11 +++++------ game/vehicles/railVehicle.h | 12 ++++++------ game/vehicles/railVehicleClass.cpp | 23 ++++++++++++----------- game/vehicles/railVehicleClass.h | 8 ++++++-- gfx/gl/bufferedLocation.cpp | 16 ++-------------- gfx/gl/bufferedLocation.h | 32 +++++++++++++++++++++++++++----- test/test-render.cpp | 2 +- 7 files changed, 59 insertions(+), 45 deletions(-) diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp index c4a9e96..2d820b6 100644 --- a/game/vehicles/railVehicle.cpp +++ b/game/vehicles/railVehicle.cpp @@ -12,13 +12,12 @@ #include RailVehicle::RailVehicle(RailVehicleClassPtr rvc) : - rvClass {std::move(rvc)}, location {rvClass->instancesBody}, bogies { - rvClass->instancesBogies.front(), - rvClass->instancesBogies.back(), - } + RailVehicleClass::Instance {rvc->instances.acquire()}, rvClass {std::move(rvc)}, location {&LV::body, *this}, + bogies {{ + {&LV::front, *this, glm::vec3 {0, rvClass->wheelBase / 2.F, 0}}, + {&LV::back, *this, glm::vec3 {0, -rvClass->wheelBase / 2.F, 0}}, + }} { - bogies.front().setPosition({0, rvClass->wheelBase / 2.F, 0}); - bogies.back().setPosition({0, -bogies.front().position().y, 0}); } void diff --git a/game/vehicles/railVehicle.h b/game/vehicles/railVehicle.h index 247bf66..f34643e 100644 --- a/game/vehicles/railVehicle.h +++ b/game/vehicles/railVehicle.h @@ -8,12 +8,10 @@ #include #include -class SceneShader; -class ShadowMapper; class Ray; - class Train; -class RailVehicle : Selectable { + +class RailVehicle : Selectable, RailVehicleClass::Instance { public: explicit RailVehicle(RailVehicleClassPtr rvc); @@ -22,7 +20,9 @@ public: [[nodiscard]] bool intersectRay(const Ray &, glm::vec2 *, float *) const override; RailVehicleClassPtr rvClass; - BufferedLocation location; - std::array bogies; + using LV = RailVehicleClass::LocationVertex; + using BLocation = BufferedLocationT; + BLocation location; + std::array bogies; }; using RailVehiclePtr = std::unique_ptr; diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 148081f..324148c 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -4,6 +4,7 @@ #include "gfx/gl/vertexArrayObject.h" #include "gfx/models/mesh.h" #include "gfx/models/texture.h" +#include "stream_support.h" #include #include #include @@ -34,36 +35,36 @@ void RailVehicleClass::postLoad() { texture = getTexture(); - bodyMesh->configureVAO(instanceVAO).addAttribs(instancesBody.bufferName(), 1); + bodyMesh->configureVAO(instanceVAO).addAttribs(instances.bufferName(), 1); bogies.front() ->configureVAO(instancesBogiesVAO.front()) - .addAttribs(instancesBogies.front().bufferName(), 1); + .addAttribs(instances.bufferName(), 1); bogies.back() ->configureVAO(instancesBogiesVAO.back()) - .addAttribs(instancesBogies.back().bufferName(), 1); + .addAttribs(instances.bufferName(), 1); } void RailVehicleClass::render(const SceneShader & shader) const { - if (const auto count = instancesBody.size()) { + if (const auto count = static_cast(instances.size())) { if (texture) { texture->bind(); } shader.basicInst.use(); - bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); - bogies.front()->DrawInstanced(instancesBogiesVAO.front(), static_cast(instancesBogies.front().size())); - bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().size())); + bodyMesh->DrawInstanced(instanceVAO, count); + bogies.front()->DrawInstanced(instancesBogiesVAO.front(), count); + bogies.back()->DrawInstanced(instancesBogiesVAO.back(), count); } } void RailVehicleClass::shadows(const ShadowMapper & mapper) const { - if (const auto count = instancesBody.size()) { + if (const auto count = static_cast(instances.size())) { mapper.dynamicPointInst.use(); - bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); - bogies.front()->DrawInstanced(instancesBogiesVAO.front(), static_cast(instancesBogies.front().size())); - bogies.back()->DrawInstanced(instancesBogiesVAO.back(), static_cast(instancesBogies.back().size())); + bodyMesh->DrawInstanced(instanceVAO, count); + bogies.front()->DrawInstanced(instanceVAO, count); + bogies.back()->DrawInstanced(instanceVAO, count); } } diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index 2fda8f7..80b3fda 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -18,6 +18,10 @@ public: void render(const SceneShader & shader) const override; void shadows(const ShadowMapper & shadowMapper) const override; + struct LocationVertex { + glm::mat4 body, front, back; + }; + std::array bogies; Mesh::Ptr bodyMesh; std::shared_ptr texture; @@ -25,8 +29,8 @@ public: float length; float maxSpeed; - mutable InstanceVertices instancesBody; - mutable std::array, 2> instancesBogies; + mutable InstanceVertices instances; + using Instance = decltype(instances)::InstanceProxy; protected: friend Persistence::SelectionPtrBase>; diff --git a/gfx/gl/bufferedLocation.cpp b/gfx/gl/bufferedLocation.cpp index 7027b4c..eb3dac3 100644 --- a/gfx/gl/bufferedLocation.cpp +++ b/gfx/gl/bufferedLocation.cpp @@ -3,15 +3,9 @@ #include "maths.h" #include -BufferedLocation::BufferedLocation(InstanceVertices & i, glm::vec3 p, glm::vec3 r) : - BufferedLocation {i, Location {p, r}} -{ -} +BufferedLocation::BufferedLocation(glm::vec3 p, glm::vec3 r) : BufferedLocation {Location {p, r}} { } -BufferedLocation::BufferedLocation(InstanceVertices & i, const Location & l) : - loc {l}, buffer {i.acquire(getTransform())} -{ -} +BufferedLocation::BufferedLocation(const Location & l) : loc {l} { } BufferedLocation::operator const Location &() const { @@ -64,12 +58,6 @@ BufferedLocation::setLocation(glm::vec3 p, glm::vec3 r) updateBuffer(); } -void -BufferedLocation::updateBuffer() -{ - buffer = getTransform(); -} - glm::mat4 BufferedLocation::getTransform() const { diff --git a/gfx/gl/bufferedLocation.h b/gfx/gl/bufferedLocation.h index 6d148cd..8096489 100644 --- a/gfx/gl/bufferedLocation.h +++ b/gfx/gl/bufferedLocation.h @@ -1,14 +1,16 @@ #pragma once -#include "instanceVertices.h" #include "location.h" +#include #include #include +#include class BufferedLocation { public: - BufferedLocation(InstanceVertices &, glm::vec3 = {}, glm::vec3 = {}); - BufferedLocation(InstanceVertices &, const Location &); + BufferedLocation(glm::vec3 = {}, glm::vec3 = {}); + BufferedLocation(const Location &); + virtual ~BufferedLocation() = default; BufferedLocation & operator=(const Location &); @@ -23,8 +25,28 @@ public: glm::mat4 getTransform() const; private: - void updateBuffer(); + virtual void updateBuffer() = 0; Location loc; - InstanceVertices::InstanceProxy buffer; +}; + +template class BufferedLocationT : public BufferedLocation { +public: + template + BufferedLocationT(Target &&... target, LocationArgs &&... t) : + BufferedLocation {std::forward(t)...}, target {std::forward(target)...} + { + updateBuffer(); + } + + using BufferedLocation::operator=; + +private: + void + updateBuffer() override + { + std::apply(std::invoke, target) = getTransform(); + } + + std::tuple target; }; diff --git a/test/test-render.cpp b/test/test-render.cpp index 4bfcad2..0d384a3 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -21,9 +21,9 @@ #include class TestScene : public SceneProvider { - std::shared_ptr train1, train2; const RailVehicleClassPtr brush47rvc = std::dynamic_pointer_cast( AssetFactory::loadXML(RESDIR "/brush47.xml")->assets.at("brush-47")); + std::shared_ptr train1, train2; Terrain terrain {[]() { auto gd = std::make_shared(GeoData::Limits {{0, 0}, {100, 100}}); -- cgit v1.2.3