StapleGL
Header-only C++20 OpenGL wrapper
Loading...
Searching...
No Matches
vertex_buffer_inst.hpp
Go to the documentation of this file.
1
19#pragma once
20
21#include "gl_functions.hpp"
22#include "vertex_buffer.hpp"
24
25#include <cassert>
26#include <numbers>
27#include <span>
28
29namespace staplegl {
30
57
58private:
63 std::size_t m_capacity {};
64
69 std::int32_t m_count {};
70
78 [[nodiscard]] constexpr auto calc_capacity(std::size_t capacity) const noexcept -> std::size_t
79 {
80 std::size_t new_cap { 0 };
81
82 if (capacity == 0) [[unlikely]] {
83 new_cap = instance_size();
84 } else if (capacity == instance_size()) [[unlikely]] {
85 new_cap = instance_size() * static_cast<size_t>(32);
86 } else [[likely]] {
87 new_cap = static_cast<size_t>(static_cast<double>(capacity) * std::numbers::phi);
88 }
89
90 return new_cap;
91 }
92
103 void resize_buffer(std::size_t old_capacity, std::size_t new_capacity) noexcept
104 {
105 // 1. create new buffer as big as old buffer
106 // 2. copy old buffer to new buffer
107 // 3. grow old buffer to new size
108 // 4. copy new buffer to old buffer
109 // 5. delete new buffer
110
111 std::uint32_t new_id {};
112 glGenBuffers(1, &new_id);
113
114 glBindBuffer(GL_COPY_WRITE_BUFFER, new_id);
115 glBufferData(GL_COPY_WRITE_BUFFER, static_cast<ptrdiff_t>(old_capacity), nullptr, m_hint);
116
117 glBindBuffer(GL_COPY_READ_BUFFER, m_id);
118
119 // copy the data from the old buffer to the new one
120
121 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, static_cast<ptrdiff_t>(m_count * m_layout.stride()));
122
123 glBindBuffer(GL_COPY_WRITE_BUFFER, m_id);
124 glBufferData(GL_COPY_WRITE_BUFFER, static_cast<ptrdiff_t>(new_capacity), nullptr, m_hint);
125
126 glBindBuffer(GL_COPY_READ_BUFFER, new_id);
127
128 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, static_cast<ptrdiff_t>(m_count * m_layout.stride()));
129
130 glDeleteBuffers(1, &new_id);
131 glBindBuffer(GL_ARRAY_BUFFER, m_id);
132 this->m_capacity = new_capacity;
133 }
134
135public:
136 vertex_buffer_inst(std::span<const float> instance_data,
137 vertex_buffer_layout&& layout) noexcept
138 : vertex_buffer { instance_data, std::move(layout), driver_draw_hint::DYNAMIC_DRAW }
139 , m_capacity { instance_data.size() } {};
140
141 vertex_buffer_inst(std::span<const float> instance_data) noexcept
143 , m_capacity { instance_data.size() } {};
144
145 ~vertex_buffer_inst() noexcept = default;
146
148 auto operator=(const vertex_buffer_inst&) -> vertex_buffer_inst& = delete;
149
151 [[nodiscard]] auto operator=(vertex_buffer_inst&&) noexcept -> vertex_buffer_inst& = default;
152
158 void add_instance(std::span<const float> instance_data) noexcept;
159
169 auto delete_instance(std::int32_t index) noexcept -> int32_t;
170
182 void update_instance(std::int32_t index,
183 std::span<const float> instance_data) noexcept;
184
185 // UTLITIES
186
187 [[nodiscard]] constexpr auto instance_count() const noexcept -> std::int32_t { return m_count; }
188 [[nodiscard]] constexpr auto instance_size() const noexcept -> std::size_t { return m_layout.stride(); }
189 [[nodiscard]] constexpr auto capacity() const noexcept -> std::size_t { return m_capacity; }
190
191}; // class vertex_buffer_inst
192
193/*
194
195 IMPLEMENTATIONS
196
197*/
198
199inline void vertex_buffer_inst::add_instance(std::span<const float> instance_data) noexcept
200{
201 if ((m_count + 1) * m_layout.stride() > m_capacity) [[unlikely]] {
202 auto new_capacity = calc_capacity(m_capacity);
203 resize_buffer(m_capacity, new_capacity);
204 }
205
206 update_instance(m_count, instance_data);
207 ++m_count;
208 ++m_size;
209}
210
211inline void vertex_buffer_inst::update_instance(std::int32_t index, std::span<const float> instance_data) noexcept
212{
213#ifdef STAPLEGL_DEBUG
214 // too costly for release builds
215 assert(index < m_count || instance_data.size_bytes() == m_layout.stride());
216#endif // STAPLEGL_DEBUG
217
218 glBindBuffer(GL_ARRAY_BUFFER, m_id);
219 glBufferSubData(GL_ARRAY_BUFFER,
220 static_cast<ptrdiff_t>(index * m_layout.stride()),
221 static_cast<ptrdiff_t>(m_layout.stride()),
222 instance_data.data());
223}
224
225inline auto vertex_buffer_inst::delete_instance(std::int32_t index) noexcept -> std::int32_t
226{
227 if (index >= m_count || index < 0) [[unlikely]] {
228 return m_count - 1;
229 } // pretend we did something
230
231 // move the last instance to the position of the deleted instance
232
233 auto* last_instance_ptr = static_cast<float*>(glMapBufferRange(
234 GL_ARRAY_BUFFER,
235 static_cast<ptrdiff_t>((m_count - 1) * m_layout.stride()),
236 static_cast<uint32_t>(m_layout.stride()),
237 GL_MAP_WRITE_BIT | GL_MAP_READ_BIT)); // read the last instance
238
239 // write the last instance to the position of the deleted instance (overwriting it)
240
241 auto last_instance = std::span<const float> { last_instance_ptr, m_layout.stride_elements() };
242
243 update_instance(index, last_instance);
244
245 // we are done with the last instance, so we can unmap it
246 glUnmapBuffer(GL_ARRAY_BUFFER);
247
248 // by reducing the count, we effectively delete the last instance (preventing duplicates)
249 --m_count;
250 --m_size;
251
252 return index;
253}
254
255} // namespace staplegl
A vertex buffer object for instanced rendering.
constexpr auto instance_count() const noexcept -> std::int32_t
void resize_buffer(std::size_t old_capacity, std::size_t new_capacity) noexcept
Resize the buffer to the given capacity.
auto delete_instance(std::int32_t index) noexcept -> int32_t
Delete an instance from the buffer, does not preserve the order of the instances.
std::size_t m_capacity
The capacity of the buffer, in bytes.
constexpr auto instance_size() const noexcept -> std::size_t
std::int32_t m_count
The number of instances in the buffer.
~vertex_buffer_inst() noexcept=default
vertex_buffer_inst(std::span< const float > instance_data, vertex_buffer_layout &&layout) noexcept
void add_instance(std::span< const float > instance_data) noexcept
Add an instance to the buffer.
vertex_buffer_inst(std::span< const float > instance_data) noexcept
constexpr auto calc_capacity(std::size_t capacity) const noexcept -> std::size_t
Compute the new capacity of the buffer, given the current capacity.
constexpr auto capacity() const noexcept -> std::size_t
void update_instance(std::int32_t index, std::span< const float > instance_data) noexcept
Update the data of an instance in the buffer.
constexpr auto stride() const noexcept -> std::size_t
Get the stride of the vertex buffer layout.
Vertex Buffer Object (VBO) wrapper.
constexpr auto layout() const -> const vertex_buffer_layout &
vertex_buffer_layout m_layout
staplegl::driver_draw_hint m_hint
constexpr auto size() const noexcept -> std::size_t
Get the number of vertices in the vertex buffer object.
Loads OpenGL functions.
Vertex Buffer Object (VBO) wrapper.
Vertex Buffer Layout abstraction.