diff options
-rw-r--r-- | Jamroot.jam | 6 | ||||
-rw-r--r-- | assetFactory/texturePacker.cpp | 2 | ||||
-rw-r--r-- | gfx/gl/instanceVertices.h | 56 | ||||
-rw-r--r-- | gfx/models/mesh.cpp | 7 | ||||
-rw-r--r-- | gfx/models/mesh.h | 3 | ||||
-rw-r--r-- | test/Jamfile.jam | 6 | ||||
-rw-r--r-- | test/perf-instancing.cpp | 43 | ||||
-rw-r--r-- | test/test-instancing.cpp | 94 | ||||
-rw-r--r-- | test/testHelpers.h | 8 | ||||
-rw-r--r-- | thirdparty/Jamfile.jam | 7 | ||||
-rw-r--r-- | thirdparty/glad.jam | 4 | ||||
-rw-r--r-- | thirdparty/stb_image.c (renamed from gfx/models/stb_image.impl.c) | 0 | ||||
-rw-r--r-- | ui/applicationBase.cpp | 3 |
13 files changed, 205 insertions, 34 deletions
diff --git a/Jamroot.jam b/Jamroot.jam index 81a3831..b07dfdd 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -90,8 +90,8 @@ lib ilt : <toolset>gcc,<variant>debug:<cflags>-Wuseless-cast <include>. <include>lib - <library>thirdparty//glad - <implicit-dependency>thirdparty//glad + <library>thirdparty/<variant>release + <implicit-dependency>thirdparty <library>sdl2 <library>thirdparty//imguisdl2 <library>freetype2 @@ -104,7 +104,7 @@ lib ilt : <define>GLM_ENABLE_EXPERIMENTAL <include>. <include>lib - <implicit-dependency>thirdparty//glad + <implicit-dependency>thirdparty <library>OpenMeshCore ; diff --git a/assetFactory/texturePacker.cpp b/assetFactory/texturePacker.cpp index 0d0fdb6..64b2131 100644 --- a/assetFactory/texturePacker.cpp +++ b/assetFactory/texturePacker.cpp @@ -53,7 +53,7 @@ TexturePacker::pack(Size size) const } } else { - const auto x = pack({size.x * 2, size.y}), y = pack({size.x, size.y * 2}); + auto x = pack({size.x * 2, size.y}), y = pack({size.x, size.y * 2}); if (!x.first.empty() && (y.first.empty() || area(x.second) < area(y.second))) { return x; } diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h index a89aa78..28e11ee 100644 --- a/gfx/gl/instanceVertices.h +++ b/gfx/gl/instanceVertices.h @@ -1,7 +1,6 @@ #pragma once #include "glContainer.h" -#include "pack.h" #include <cassert> #include <special_members.h> #include <utility> @@ -14,7 +13,8 @@ public: public: InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { } - InstanceProxy(InstanceProxy && other) : instances {std::exchange(other.instances, nullptr)}, index {other.index} + InstanceProxy(InstanceProxy && other) noexcept : + instances {std::exchange(other.instances, nullptr)}, index {other.index} { } @@ -28,7 +28,7 @@ public: } InstanceProxy & - operator=(InstanceProxy && other) + operator=(InstanceProxy && other) noexcept { if (instances) { instances->release(index); @@ -39,17 +39,20 @@ public: } template<typename U> - T & + InstanceProxy & operator=(U && v) { - return instances->lookup(index) = std::forward<U>(v); + instances->lookup(index) = std::forward<U>(v); + return *this; } + // NOLINTNEXTLINE)hicpp-explicit-conversions [[nodiscard]] operator T &() { return instances->lookup(index); } + // NOLINTNEXTLINE)hicpp-explicit-conversions [[nodiscard]] operator const T &() const { return instances->lookup(index); @@ -104,10 +107,12 @@ public: auto idx = unused.back(); unused.pop_back(); index[idx] = base::size(); + reverseIndex.emplace_back(idx); base::emplace_back(std::forward<Params>(params)...); return InstanceProxy {this, idx}; } index.emplace_back(base::size()); + reverseIndex.push_back(base::size()); base::emplace_back(std::forward<Params>(params)...); return InstanceProxy {this, index.size() - 1}; } @@ -121,25 +126,35 @@ public: return base::size(); } + template<typename Pred> + glContainer<T>::iterator + partition(Pred pred) + { + return partition(base::begin(), base::end(), pred); + } + protected: + static constexpr auto npos = static_cast<size_t>(-1); friend InstanceProxy; void release(const size_t pidx) { - if (base::size() - 1 != index[pidx]) { + if (const size_t last = base::size() - 1; last != 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]; + const auto movedKey = reverseIndex[last]; + index[movedKey] = std::exchange(index[pidx], npos); + reverseIndex[index[movedKey]] = movedKey; } base::pop_back(); + reverseIndex.pop_back(); if (pidx == index.size() - 1) { index.pop_back(); } else { - // Remember p.index is free index now, keeping it sorted - unused.insert(std::upper_bound(unused.begin(), unused.end(), pidx), pidx); + index[pidx] = npos; + // Remember p.index is free index now + unused.emplace_back(pidx); } } @@ -149,8 +164,27 @@ protected: return base::data()[index[pindex]]; } + template<typename Pred> + glContainer<T>::iterator + partition(glContainer<T>::iterator first, glContainer<T>::iterator last, Pred pred) + { + while (first < last) { + first = std::find_if_not(first, last, pred); + last = --std::find_if(std::make_reverse_iterator(last), std::make_reverse_iterator(first), pred).base(); + if (first < last) { + std::iter_swap(first, last); + const auto fidx = static_cast<size_t>(first - base::begin()), + lidx = static_cast<size_t>(last - base::begin()); + std::swap(index[reverseIndex[fidx]], index[reverseIndex[lidx]]); + std::swap(reverseIndex[fidx], reverseIndex[lidx]); + } + } + return first; + } + // Index into buffer given to nth proxy std::vector<size_t> index; + std::vector<size_t> reverseIndex; // List of free spaces in index std::vector<size_t> unused; }; diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index 1f4dc09..e7474ca 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -1,7 +1,4 @@ #include "mesh.h" -#include "glArrays.h" -#include "vertex.h" -#include <cstddef> MeshBase::MeshBase(GLsizei m_numIndices, GLenum mode) : m_numIndices {m_numIndices}, mode {mode} { } @@ -16,11 +13,11 @@ MeshBase::Draw() const } void -MeshBase::DrawInstanced(GLuint vao, GLsizei count) const +MeshBase::DrawInstanced(GLuint vao, GLsizei count, GLuint base) const { glBindVertexArray(vao); - glDrawElementsInstanced(mode, m_numIndices, GL_UNSIGNED_INT, nullptr, count); + glDrawElementsInstancedBaseInstance(mode, m_numIndices, GL_UNSIGNED_INT, nullptr, count, base); glBindVertexArray(0); } diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index 71f6dce..248cb8f 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -3,7 +3,6 @@ #include "gfx/gl/vertexArrayObject.h" #include <glArrays.h> #include <glad/gl.h> -#include <memory> #include <span> #include <stdTypeDefs.h> @@ -12,7 +11,7 @@ class Vertex; class MeshBase { public: void Draw() const; - void DrawInstanced(GLuint vao, GLsizei count) const; + void DrawInstanced(GLuint vao, GLsizei count, GLuint base = 0) const; protected: MeshBase(GLsizei m_numIndices, GLenum mode); diff --git a/test/Jamfile.jam b/test/Jamfile.jam index f021d85..1266854 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -55,11 +55,12 @@ run test-enumDetails.cpp ; run test-render.cpp : -- : test-assetFactory : <library>test ; run test-glContextBhvr.cpp ; run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt test-instancing ] : <library>test ; -run perf-assetFactory.cpp : -- : test-assetFactory : <library>benchmark <library>test ; +run perf-assetFactory.cpp : <\ : test-assetFactory : <library>benchmark <library>test ; run perf-geoData.cpp : : : <library>test <library>benchmark ; -run perf-persistence.cpp : -- : test-persistence : <library>benchmark <library>test ; +run perf-persistence.cpp : \< : test-persistence : <library>benchmark <library>test ; run test-worker.cpp ; run test-instancing.cpp : -- : test-glContainer : <library>test ; +run perf-instancing.cpp : \< : test-instancing : <library>benchmark <library>test ; run test-glContainer.cpp : : : <library>test ; run test-pack.cpp : : : <library>test ; compile test-static-enumDetails.cpp ; @@ -67,5 +68,6 @@ compile test-static-stream_support.cpp ; explicit perf-assetFactory ; explicit perf-persistence ; explicit perf-terrain ; +explicit perf-instancing ; alias perf : perf-assetFactory perf-persistence perf-terrain ; explicit perf ; diff --git a/test/perf-instancing.cpp b/test/perf-instancing.cpp new file mode 100644 index 0000000..829ea50 --- /dev/null +++ b/test/perf-instancing.cpp @@ -0,0 +1,43 @@ +#include "gfx/gl/instanceVertices.h" +#include "testMainWindow.h" +#include <benchmark/benchmark.h> +#include <random> + +struct Instance { + GlobalPosition3D pos; + glm::mat3 rot; +}; + +struct data { + explicit data(size_t n) + { + std::mt19937 gen(std::random_device {}()); + std::uniform_int_distribution<GlobalDistance> xy(0, 1000000); + std::uniform_int_distribution<GlobalDistance> z(0, 10000); + while (n--) { + proxies.emplace_back(instances.acquire(GlobalPosition3D {xy(gen), xy(gen), z(gen)}, glm::mat3 {})); + } + } + + InstanceVertices<Instance> instances; + std::vector<InstanceVertices<Instance>::InstanceProxy> proxies; +}; + +static void +partition(benchmark::State & state) +{ + TestMainWindow window; + data d(static_cast<size_t>(state.range())); + GlobalPosition2D pos {}; + for (auto _ : state) { + d.instances.partition([&pos](const auto & i) { + return std::abs(i.pos.x - pos.x) < 5 && std::abs(i.pos.y - pos.y) < 5; + }); + pos += GlobalPosition2D {33, 17}; + pos %= 1000000; + } +} + +BENCHMARK(partition)->Range(0, 1 << 20); + +BENCHMARK_MAIN(); diff --git a/test/test-instancing.cpp b/test/test-instancing.cpp index 3244bad..40c2c4f 100644 --- a/test/test-instancing.cpp +++ b/test/test-instancing.cpp @@ -10,12 +10,33 @@ BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); -BOOST_FIXTURE_TEST_SUITE(i, InstanceVertices<int>) +struct TestInstanceVertices : public InstanceVertices<int> { + void + checkReverseIndex(std::source_location ctx = std::source_location::current()) + { + BOOST_TEST_CONTEXT(ctx.function_name() << ":" << ctx.line()) { + std::vector<size_t> genIndexIndex(size(), npos); + for (size_t i {}; i < index.size(); i++) { + if (index[i] != npos) { + BOOST_REQUIRE_EQUAL(genIndexIndex.at(index[i]), npos); + genIndexIndex.at(index[i]) = i; + } + } + + BOOST_TEST_CONTEXT(reverseIndex << genIndexIndex) { + BOOST_CHECK_EQUAL_COLCOL(reverseIndex, genIndexIndex); + } + } + } +}; + +BOOST_FIXTURE_TEST_SUITE(i, TestInstanceVertices) BOOST_AUTO_TEST_CASE(createDestroy) { BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); + BOOST_CHECK(reverseIndex.empty()); } BOOST_AUTO_TEST_CASE(acquireRelease) @@ -26,11 +47,14 @@ BOOST_AUTO_TEST_CASE(acquireRelease) BOOST_CHECK_EQUAL(1, size()); BOOST_REQUIRE_EQUAL(1, index.size()); BOOST_CHECK_EQUAL(0, index.front()); + BOOST_REQUIRE_EQUAL(1, reverseIndex.size()); + BOOST_CHECK_EQUAL(0, reverseIndex.front()); BOOST_CHECK(unused.empty()); } BOOST_CHECK_EQUAL(0, size()); BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); + BOOST_CHECK(reverseIndex.empty()); } BOOST_AUTO_TEST_CASE(acquireReleaseMove) @@ -47,6 +71,7 @@ BOOST_AUTO_TEST_CASE(acquireReleaseMove) BOOST_CHECK_EQUAL(0, size()); BOOST_CHECK(unused.empty()); BOOST_CHECK(index.empty()); + BOOST_CHECK(reverseIndex.empty()); } BOOST_AUTO_TEST_CASE(autoMapUnmap) @@ -97,15 +122,13 @@ BOOST_AUTO_TEST_CASE(shuffle) 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]); - BOOST_CHECK_EQUAL(1, index[1]); - BOOST_CHECK_EQUAL(2, index[2]); - BOOST_CHECK_EQUAL(3, index[3]); + BOOST_CHECK_EQUAL_COLVALS(index, 0, 1, 2, 3); + checkReverseIndex(); // Remove 1, 3 moves to [1] proxies.erase(proxies.begin() + 1); BOOST_REQUIRE_EQUAL(3, proxies.size()); - BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_CHECK_EQUAL_COLVALS(index, 0, npos, 2, 1); + BOOST_CHECK_EQUAL_COLVALS(reverseIndex, 0, 3, 2); BOOST_REQUIRE_EQUAL(1, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); @@ -114,6 +137,8 @@ BOOST_AUTO_TEST_CASE(shuffle) // Remove 1, 2 moves to [1] proxies.erase(proxies.begin() + 1); BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_CHECK_EQUAL_COLVALS(index, 0, npos, npos, 1); + checkReverseIndex(); BOOST_REQUIRE_EQUAL(2, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); BOOST_CHECK_EQUAL(2, unused[1]); @@ -121,12 +146,33 @@ BOOST_AUTO_TEST_CASE(shuffle) 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_CHECK_EQUAL_COLVALS(index, 0, npos, 2, 1); + checkReverseIndex(); BOOST_REQUIRE_EQUAL(1, unused.size()); BOOST_CHECK_EQUAL(1, unused[0]); BOOST_CHECK_EQUAL(at(0), *proxies[0].get()); BOOST_CHECK_EQUAL(at(1), *proxies[1].get()); BOOST_CHECK_EQUAL(at(2), *proxies[2].get()); + // Add new, takes 1 at [3] + BOOST_CHECK_EQUAL(5, proxies.emplace_back(acquire(5))); + BOOST_REQUIRE_EQUAL(proxies.size(), reverseIndex.size()); + BOOST_CHECK_EQUAL_COLVALS(index, 0, 3, 2, 1); + checkReverseIndex(); + // Add new, takes 4 at [4] + BOOST_CHECK_EQUAL(6, proxies.emplace_back(acquire(6))); + BOOST_REQUIRE_EQUAL(proxies.size(), reverseIndex.size()); + BOOST_CHECK_EQUAL_COLVALS(index, 0, 3, 2, 1, 4); + checkReverseIndex(); + // Remove [0] + proxies.erase(proxies.begin()); + BOOST_REQUIRE_EQUAL(proxies.size(), reverseIndex.size()); + BOOST_CHECK_EQUAL_COLVALS(index, npos, 3, 2, 1, 0); + checkReverseIndex(); + // Remove [3] + proxies.erase(proxies.begin()); + BOOST_REQUIRE_EQUAL(proxies.size(), reverseIndex.size()); + BOOST_CHECK_EQUAL_COLVALS(index, npos, 1, 2, npos, 0); + checkReverseIndex(); } BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) @@ -160,6 +206,7 @@ BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) iused.emplace(index[i]); } } + checkReverseIndex(); BOOST_TEST_CONTEXT(index) { BOOST_REQUIRE_EQUAL(iused.size(), size()); @@ -171,4 +218,35 @@ BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) } } +BOOST_AUTO_TEST_CASE(partition_by, *boost::unit_test::timeout(1)) +{ + std::mt19937 gen(std::random_device {}()); + std::uniform_int_distribution<int> dist(0, 100000); + static constexpr auto N = 1000; + reserve(N); + std::vector<decltype(acquire(0))> instances; + instances.reserve(N); + // At least one of each + instances.push_back(acquire(1)); + instances.push_back(acquire(3)); + while (instances.size() < N) { + instances.push_back(acquire(dist(gen))); + } + const std::vector<int> values(instances.begin(), instances.end()); + BOOST_REQUIRE_EQUAL(size(), N); + + const auto pred = [](auto x) { + return (x % 3) == 0; + }; + auto matchedEnd = partition(pred); + // The underlying data is partitioned... + BOOST_REQUIRE(std::is_partitioned(mkcspan().cbegin(), mkcspan().cend(), pred)); + // The external view of the data is unchanged... + BOOST_CHECK_EQUAL_COLLECTIONS(values.cbegin(), values.cend(), instances.cbegin(), instances.cend()); + // The partition point is right... + BOOST_CHECK(!pred(*matchedEnd)); + BOOST_CHECK(pred(*--matchedEnd)); + checkReverseIndex(); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/testHelpers.h b/test/testHelpers.h index c9fd6dc..a261b3d 100644 --- a/test/testHelpers.h +++ b/test/testHelpers.h @@ -51,3 +51,11 @@ loadFixtureJson(const std::filesystem::path & path) BOOST_CHECK(VAR); \ } \ else + +#define BOOST_CHECK_EQUAL_COLCOL(cola_, colb_) \ + BOOST_CHECK_EQUAL_COLLECTIONS(cola_.begin(), cola_.end(), colb_.begin(), colb_.end()) +#define BOOST_CHECK_EQUAL_COLVALS(col_, ...) \ + { \ + const std::initializer_list<decltype(col_)::value_type> vals {__VA_ARGS__}; \ + BOOST_CHECK_EQUAL_COLLECTIONS(col_.begin(), col_.end(), vals.begin(), vals.end()); \ + } diff --git a/thirdparty/Jamfile.jam b/thirdparty/Jamfile.jam index 06ab869..7a47589 100644 --- a/thirdparty/Jamfile.jam +++ b/thirdparty/Jamfile.jam @@ -4,6 +4,13 @@ lib glad : gl.xml : <cflags>-fPIC <warnings>off <warnings-as-errors>off + <glad.version>3.3 + ; +lib stb : stb_image.c : + <link>static + <cflags>-fPIC + <warnings>off + <warnings-as-errors>off ; lib imguisdl2 : diff --git a/thirdparty/glad.jam b/thirdparty/glad.jam index f89750c..12e9011 100644 --- a/thirdparty/glad.jam +++ b/thirdparty/glad.jam @@ -4,8 +4,8 @@ import toolset : flags ; generators.register-standard glad.generate.c : XML : C(%) H(glad/%) XML ; feature glad.pythonpath : thirdparty/glad : path ; -feature glad.profile : core compatibility ; -feature glad.version : 4.6 ; +feature glad.profile : core compatibility : symmetric ; +feature glad.version : 4.6 4.5 4.4 4.3 4.2 4.1 4.0 3.3 3.2 3.1 3.0 2.1 2.0 1.5 1.4 1.3 1.2 1.1 1.0 : symmetric ; flags glad.generate.c PYTHONPATH <glad.pythonpath> ; flags glad.generate.c PROFILE <glad.profile> ; flags glad.generate.c VERSION <glad.version> ; diff --git a/gfx/models/stb_image.impl.c b/thirdparty/stb_image.c index 38c72e9..38c72e9 100644 --- a/gfx/models/stb_image.impl.c +++ b/thirdparty/stb_image.c diff --git a/ui/applicationBase.cpp b/ui/applicationBase.cpp index b945dca..961007b 100644 --- a/ui/applicationBase.cpp +++ b/ui/applicationBase.cpp @@ -29,6 +29,9 @@ ApplicationBase::initSDL() const setGlAttribute(SDL_GL_DEPTH_SIZE, 16); setGlAttribute(SDL_GL_DOUBLEBUFFER, 1); + setGlAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + setGlAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + setGlAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); setGlAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); } |