summaryrefslogtreecommitdiff
path: root/gfx/gl/instanceVertices.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/instanceVertices.h')
-rw-r--r--gfx/gl/instanceVertices.h211
1 files changed, 211 insertions, 0 deletions
diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h
new file mode 100644
index 0000000..228020d
--- /dev/null
+++ b/gfx/gl/instanceVertices.h
@@ -0,0 +1,211 @@
+#pragma once
+
+#include "glArrays.h"
+#include <iterator>
+#include <span>
+#include <special_members.hpp>
+#include <utility>
+#include <vector>
+
+template<typename T> class InstanceVertices {
+public:
+ InstanceVertices(size_t initialSize = 16)
+ {
+ allocBuffer(initialSize);
+ }
+
+ class [[nodiscard]] InstanceProxy {
+ public:
+ InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { }
+ InstanceProxy(InstanceProxy && other) : instances {std::exchange(other.instances, nullptr)}, index {other.index}
+ {
+ }
+ NO_COPY(InstanceProxy);
+
+ ~InstanceProxy()
+ {
+ if (instances) {
+ instances->release(index);
+ }
+ }
+
+ InstanceProxy &
+ operator=(InstanceProxy && other)
+ {
+ if (instances) {
+ instances->release(index);
+ }
+ instances = std::exchange(other.instances, nullptr);
+ index = other.index;
+ return *this;
+ }
+ template<typename U>
+ T &
+ operator=(U && v)
+ {
+ return instances->at(index) = std::forward<U>(v);
+ }
+
+ [[nodiscard]]
+ operator T &()
+ {
+ return instances->at(index);
+ }
+ [[nodiscard]] operator const T &() const
+ {
+ return instances->at(index);
+ }
+ [[nodiscard]] T *
+ get()
+ {
+ return &instances->at(index);
+ }
+ [[nodiscard]] const T *
+ get() const
+ {
+ return &instances->at(index);
+ }
+ [[nodiscard]] T *
+ operator->()
+ {
+ return get();
+ }
+ [[nodiscard]] const T *
+ operator->() const
+ {
+ return get();
+ }
+ [[nodiscard]] T &
+ operator*()
+ {
+ return instances->at(index);
+ }
+ [[nodiscard]] const T &
+ operator*() const
+ {
+ return instances->at(index);
+ }
+
+ private:
+ InstanceVertices<T> * instances;
+ std::size_t index;
+ };
+
+ template<typename... Params>
+ [[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>(params)...);
+ return InstanceProxy {this, idx};
+ }
+ if (next >= capacity) {
+ resize(capacity * 2);
+ }
+ index.emplace_back(next++);
+ new (data + index.back()) T(std::forward<Params>(params)...);
+ return InstanceProxy {this, index.size() - 1};
+ }
+
+ [[nodiscard]] const auto &
+ bufferName() const
+ {
+ return buffer;
+ }
+
+ [[nodiscard]] auto
+ count() const
+ {
+ unmap();
+ return next;
+ }
+
+protected:
+ friend InstanceProxy;
+
+ 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());
+ }) = index[pidx];
+ }
+ 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);
+ }
+ }
+
+ void
+ allocBuffer(std::size_t newCapacity)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(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<T> existing;
+ const auto maintaind = static_cast<typename decltype(existing)::difference_type>(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
+ {
+ if (!data) {
+ data = static_cast<T *>(glMapNamedBuffer(buffer, GL_READ_WRITE));
+ }
+ }
+
+ void
+ unmap() const
+ {
+ if (data) {
+ glUnmapNamedBuffer(buffer);
+ data = nullptr;
+ }
+ }
+
+ 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
+ std::vector<size_t> index;
+ // List of free spaces in index
+ std::vector<size_t> unused;
+};