summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamroot.jam6
-rw-r--r--assetFactory/texturePacker.cpp2
-rw-r--r--gfx/gl/instanceVertices.h56
-rw-r--r--gfx/models/mesh.cpp7
-rw-r--r--gfx/models/mesh.h3
-rw-r--r--test/Jamfile.jam6
-rw-r--r--test/perf-instancing.cpp43
-rw-r--r--test/test-instancing.cpp94
-rw-r--r--test/testHelpers.h8
-rw-r--r--thirdparty/Jamfile.jam7
-rw-r--r--thirdparty/glad.jam4
-rw-r--r--thirdparty/stb_image.c (renamed from gfx/models/stb_image.impl.c)0
-rw-r--r--ui/applicationBase.cpp3
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);
}