From 85bf771ea6d8d584f1c735cc963e17571ca0a0d1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 17 Apr 2023 11:54:07 +0100 Subject: First cut of instance vertices and proxy Untested outside unit test, allows the use of a glBuffer as a storage container. To be combined with a vertex array and/or mesh etc for massing drawing with glDrawElementsInstanced --- gfx/gl/instanceVertices.h | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 gfx/gl/instanceVertices.h (limited to 'gfx/gl') diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h new file mode 100644 index 0000000..cf3b75b --- /dev/null +++ b/gfx/gl/instanceVertices.h @@ -0,0 +1,147 @@ +#pragma once + +#include "glArrays.h" +#include +#include +#include +#include +#include + +template class InstanceVertices { +public: + InstanceVertices(size_t initialSize = 16) : data {allocBuffer(buffer, initialSize)}, next {} { } + + ~InstanceVertices() + { + glUnmapNamedBuffer(buffer); + } + + class 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(*this); + } + } + + InstanceProxy & + operator=(InstanceProxy && other) + { + instances = std::exchange(other.instances, nullptr); + index = other.index; + } + + operator T &() + { + return *get(); + } + operator const T &() const + { + return *get(); + } + template + T & + operator=(U && v) + { + return instances->data[index] = std::forward(v); + } + + T * + get() + { + return &instances->data[index]; + } + const T * + get() const + { + return &instances->data[index]; + } + T * + operator->() + { + return get(); + } + const T * + operator->() const + { + return get(); + } + T & + operator*() + { + return *get(); + } + const T & + operator*() const + { + return *get(); + } + + private: + friend InstanceVertices; + + InstanceVertices * instances; + std::size_t index; + }; + + template + InstanceProxy + acquire(Params &&... params) + { + if (!unused.empty()) { + auto idx = unused.back(); + unused.pop_back(); + new (&data[idx]) T(std::forward(params)...); + return InstanceProxy {this, idx}; + } + if (next >= data.size()) { + resize(data.size() * 2); + } + new (&data[next]) T(std::forward(params)...); + return InstanceProxy {this, next++}; + } + + void + release(const InstanceProxy & p) + { + data[p.index].~T(); + unused.push_back(p.index); + } + +protected: + friend InstanceProxy; + + static std::span + allocBuffer(const glBuffer & buffer, std::size_t count) + { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, static_cast(sizeof(T) * count), nullptr, GL_DYNAMIC_DRAW); + auto data = static_cast(glMapNamedBuffer(buffer, GL_READ_WRITE)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + return {data, count}; + } + + void + resize(size_t newCapacity) + { + const auto maintain = std::min(newCapacity, data.size()); + const auto maintaind = static_cast(maintain); + std::vector existing; + existing.reserve(maintain); + std::move(data.begin(), data.begin() + maintaind, std::back_inserter(existing)); + data = allocBuffer(buffer, newCapacity); + std::move(existing.begin(), existing.begin() + maintaind, data.begin()); + } + + glBuffer buffer; + std::span data; + std::size_t next; + std::vector unused; +}; -- cgit v1.2.3