summaryrefslogtreecommitdiff
path: root/lib/glAllocator.h
blob: a9015dab839f5d8176f793fe3900e4be5bd8da6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <concepts>
#include <flat_map>
#include <glad/gl.h>
#include <memory>
#include <stream_support.h>
#include <vector>

namespace Detail {
	template<typename T> class glAllocator;
	template<typename C, typename T>
	concept IsGlBufferAllocated = requires(const C & container) {
		{ container.get_allocator() } -> std::same_as<glAllocator<T>>;
	};

	template<typename T> class glAllocator {
	public:
		// NOLINTBEGIN(readability-identifier-naming) - STL like
		using pointer = T *;
		using const_pointer = const T *;
		using value_type = T;

		// NOLINTEND(readability-identifier-naming)

		pointer
		allocate(size_t count)
		{
			constexpr static GLbitfield MAPPING_FLAGS
					= GL_MAP_WRITE_BIT | GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
			constexpr static GLbitfield STORAGE_FLAGS = GL_DYNAMIC_STORAGE_BIT | MAPPING_FLAGS;
			GLuint name = 0;
			glCreateBuffers(1, &name);
			const auto size = static_cast<GLsizeiptr>(count * sizeof(T));
			glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS);
			const auto data = static_cast<pointer>(glMapNamedBufferRange(name, 0, size, MAPPING_FLAGS));
			if (!data) {
				glDeleteBuffers(1, &name);
				throw std::bad_alloc();
			}
			buffers->emplace(data, name);
			return data;
		}

		void
		deallocate(const_pointer ptr, size_t)
		{
			const auto itr = buffers->find(ptr);
			glUnmapNamedBuffer(itr->second);
			glDeleteBuffers(1, &itr->second);
			buffers->erase(itr);
		}

		[[nodiscard]] GLuint
		getNameFor(const_pointer ptr) const
		{
			const auto itr = buffers->find(ptr);
			if (itr != buffers->end()) {
				return itr->second;
			}
			return 0;
		}

		template<IsGlBufferAllocated<T> C>
		[[nodiscard]]
		GLuint
		getNameFor(const C & container) const
		{
			return getNameFor(container.data());
		}

		bool operator==(const glAllocator &) const = default;

	private:
		using BufferMap = std::flat_map<const_pointer, GLuint>;
		std::shared_ptr<BufferMap> buffers = std::make_shared<BufferMap>();
	};
}

template<typename T>
// NOLINTNEXTLINE(readability-identifier-naming) - OpenGL like
using glVector = std::vector<T, typename std::allocator_traits<Detail::glAllocator<T>>::allocator_type>;

template<typename C>
GLuint
operator*(const C & container)
	requires Detail::IsGlBufferAllocated<C, typename C::value_type>
{
	return container.get_allocator().getNameFor(container);
}

static_assert(Detail::IsGlBufferAllocated<glVector<int>, int>);