Skip to content

Commit 4cb901b

Browse files
committed
Added OpenGL Buffer functions, fixes and unit tests.
Added a Buffer functions to clear using offsets, download_data returning an array and check for empty. Fixed incorrect usage for GLEnum format from GL_R8 to GL_RGBA, bad used_capacity assignment in shrink to size.
1 parent 353801c commit 4cb901b

File tree

6 files changed

+162
-4
lines changed

6 files changed

+162
-4
lines changed

source/OpenGL/GLSL/prefix_sum_second_pass.comp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
1313

1414
uniform uint offset; // The offset into the array for outputting the sum results per reduction stage.
1515

16-
layout(std430) buffer readonly GlobalSum
16+
layout(std430) readonly buffer GlobalSum
1717
{
1818
uint data[];
1919
} global_sum;

source/OpenGL/Types.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@ namespace OpenGL
111111
}
112112
void Buffer::shrink_to_size(size_t p_size)
113113
{
114-
if (p_size >= m_used_capacity)
114+
if (p_size >= m_capacity)
115115
return;
116116

117117
Buffer new_buffer{m_flags, p_size};
118118
copy_named_buffer_sub_data(m_handle, new_buffer.m_handle, 0, 0, p_size);
119-
new_buffer.m_used_capacity = p_size;
119+
new_buffer.m_used_capacity = std::min(m_used_capacity, p_size);
120+
120121
*this = std::move(new_buffer);
121122

122123
if constexpr (LogGLTypeEvents || LogGLBufferEvents) LOG("[OPENGL][BUFFER] Shrinking buffer {} from {}B to {}B", m_handle, m_used_capacity, p_size);
@@ -135,11 +136,21 @@ namespace OpenGL
135136
}
136137
void Buffer::clear()
137138
{
138-
clear_named_buffer_sub_data(m_handle, GL_R8, 0, m_used_capacity, GL_R8, GL_UNSIGNED_BYTE, nullptr);
139+
clear_named_buffer_sub_data(m_handle, GL_R8, 0, m_used_capacity, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
139140
m_used_capacity = 0;
140141

141142
if constexpr (LogGLTypeEvents || LogGLBufferEvents) LOG("[OPENGL][BUFFER] Clearing {}B of buffer data from buffer {}", m_used_capacity, m_handle);
142143
}
144+
void Buffer::clear(size_t p_start_offset, size_t p_size)
145+
{
146+
clear_named_buffer_sub_data(m_handle, GL_R8, p_start_offset, p_size, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
147+
148+
// Only adjust m_used_capacity if the cleared range affects the end of the buffer. Gaps in the buffer are not considered used.
149+
if (p_start_offset + p_size >= m_used_capacity)
150+
m_used_capacity = p_start_offset;
151+
152+
if constexpr (LogGLTypeEvents || LogGLBufferEvents) LOG("[OPENGL][BUFFER] Clearing {}B of buffer data from buffer {} starting at offset {}", p_size, m_handle, p_start_offset);
153+
}
143154
bool Buffer::is_immutable() const
144155
{
145156
GLint is_immutable = 0;

source/OpenGL/Types.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ namespace OpenGL
9999
get_named_buffer_sub_data(m_handle, 0, m_used_capacity, data.data());
100100
return data;
101101
}
102+
template<typename T, size_t N>
103+
std::array<T, N> download_data_array(size_t offset = 0) const
104+
{
105+
ASSERT(N * sizeof(T) <= m_used_capacity - offset, "Downloading data out of bounds.");
106+
107+
std::array<T, N> data;
108+
get_named_buffer_sub_data(m_handle, offset, m_used_capacity, data.data());
109+
return data;
110+
}
102111

103112
// Set the section of the buffer starting at p_offset to p_data.
104113
template<typename T>
@@ -177,10 +186,15 @@ namespace OpenGL
177186
void shrink_to_fit();
178187
// Clears the buffer object's data store. All existing data is lost.
179188
void clear();
189+
// Clears a section of the buffer starting at p_start_offset and p_size bytes long.
190+
//@param p_start_offset The offset in bytes from the start of the buffer to the start of the data to clear.
191+
//@param p_size The number of bytes to clear.
192+
void clear(size_t p_start_offset, size_t p_size);
180193

181194
size_t capacity() const { return m_capacity; }
182195
size_t used_capacity() const { return m_used_capacity; }
183196
float used_capacity_ratio() const { return (float)m_used_capacity / (float)m_capacity; }
197+
bool empty() const { return m_used_capacity == 0; }
184198
bool is_immutable() const;
185199
};
186200
// Meta struct to hold information about a vertex attribute.

source/Test/TestManager.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313

1414
namespace std
1515
{
16+
template<>
17+
struct formatter<std::byte>
18+
{
19+
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
20+
auto format(const std::byte& v, format_context& ctx) const
21+
{
22+
unsigned int byte = static_cast<unsigned int>(v);
23+
return format_to(ctx.out(), "{}", byte);
24+
}
25+
};
1626
template<>
1727
struct formatter<glm::vec3>
1828
{
@@ -41,6 +51,16 @@ namespace Test
4151
#define CHECK_TRUE(p_conditional, p_test_name) { run_unit_test(p_conditional, p_test_name, std::format("Expected: '{}' to be true\n{}", #p_conditional, to_string(std::source_location::current()))); }
4252
#define CHECK_EQUAL(p_value, p_expected_value, p_test_name) { run_unit_test(p_value == p_expected_value, p_test_name, std::format("Expected {} ({}) to equal {} ({})\n{}", #p_value, p_value, #p_expected_value, p_expected_value, to_string(std::source_location::current()))); }
4353
#define CHECK_EQUAL_FLOAT(p_value, p_expected_value, p_test_name, p_epsilon) { run_unit_test(glm::epsilonEqual(p_value, p_expected_value, p_epsilon), p_test_name, std::format("Expected {} ({}) to equal {} ({}) with epsilon {}\n{}", #p_value, p_value, #p_expected_value, p_expected_value, p_epsilon, to_string(std::source_location::current()))); }
54+
#define CHECK_CONTAINER_EQUAL(p_container, p_expected_container, p_test_name)\
55+
{\
56+
if (p_container.size() != p_expected_container.size())\
57+
{\
58+
CHECK_EQUAL(p_container.size(), p_expected_container.size(), "Container size mismatch");\
59+
return;\
60+
}\
61+
for (size_t i = 0; i < p_container.size(); ++i)\
62+
CHECK_EQUAL(p_container[i], p_expected_container[i], std::format("{} {}", p_test_name, i));\
63+
}
4464
#define SCOPE_SECTION(p_section_name) auto a_random_name_that_will_never_collide_with_anything = ScopeSection(p_section_name, *this);
4565

4666
// A pure-virtual API for running unit tests and performance tests.

source/Test/Tests/GraphicsTester.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,37 @@ namespace Test
2020
Platform::Core::initialise_OpenGL();
2121

2222

23+
{SCOPE_SECTION("Buffer")
24+
{SCOPE_SECTION("Memory handling")
25+
OpenGL::Buffer buffer = OpenGL::Buffer({OpenGL::BufferStorageFlag::DynamicStorageBit}, 1024);
26+
27+
CHECK_EQUAL(buffer.capacity(), 1024, "Constructor capacity");
28+
CHECK_EQUAL(buffer.used_capacity(), 0, "Constructor used capacity");
29+
30+
buffer.reserve(2048);
31+
CHECK_EQUAL(buffer.capacity(), 2048, "Reserve capacity");
32+
CHECK_EQUAL(buffer.used_capacity(), 0, "Reserve used capacity");
33+
34+
buffer.shrink_to_size(1024);
35+
CHECK_EQUAL(buffer.capacity(), 1024, "Shrink to size capacity");
36+
CHECK_EQUAL(buffer.used_capacity(), 0, "Shrink to size used capacity");
37+
38+
buffer.shrink_to_fit();
39+
CHECK_EQUAL(buffer.capacity(), 0, "Shrink to fit capacity");
40+
CHECK_EQUAL(buffer.used_capacity(), 0, "Shrink to fit used capacity");
41+
42+
buffer.reserve(1024);
43+
CHECK_EQUAL(buffer.capacity(), 1024, "Reserve capacity");
44+
CHECK_EQUAL(buffer.used_capacity(), 0, "Reserve used capacity");
45+
46+
test_buffer<std::byte>();
47+
test_buffer<float>();
48+
test_buffer<int>();
49+
test_buffer<uint16_t>();
50+
}
51+
}
52+
53+
2354
{SCOPE_SECTION("Compute")
2455
{SCOPE_SECTION("Increment")
2556
std::array<unsigned int, 8> data = { 1, 2, 3, 4, 5, 6, 7, 8 };

source/Test/Tests/GraphicsTester.hpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "Test/TestManager.hpp"
44

5+
#include "OpenGL/Types.hpp"
6+
57
namespace Test
68
{
79
class GraphicsTester : public TestManager
@@ -11,5 +13,85 @@ namespace Test
1113

1214
void run_unit_tests() override;
1315
void run_performance_tests() override;
16+
17+
private:
18+
19+
template <typename value_type>
20+
void test_buffer()
21+
{
22+
{SCOPE_SECTION("Byte")
23+
OpenGL::Buffer byte_buffer = OpenGL::Buffer({OpenGL::BufferStorageFlag::DynamicStorageBit}, sizeof(value_type) * 4);
24+
const auto arr = std::array<value_type, 2>{ value_type{2}, value_type{3} };
25+
const auto min_data = std::numeric_limits<value_type>::min();
26+
const auto max_data = std::numeric_limits<value_type>::max();
27+
28+
{SCOPE_SECTION("Baseline")
29+
bool caught_exception = false;
30+
try { byte_buffer.download_data_array<value_type, 4>(); }
31+
catch (const std::exception& e) { caught_exception = true; }
32+
33+
// Buffer = {0, 0, 0, 0}, We expect the buffer to be zeroed out on creation.
34+
// Cannot download data from an empty buffer so tets for thrown exception instead.
35+
CHECK_TRUE(caught_exception, "Download data from empty buffer");
36+
CHECK_EQUAL(byte_buffer.used_capacity(), 0, "Used capacity");
37+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
38+
}
39+
40+
{SCOPE_SECTION("Set index 0")
41+
byte_buffer.set_data(min_data);
42+
// Buffer = {0, 0, 0, 0}, Set index 0 to min (0).
43+
std::array<value_type, 1> expected_result = { min_data };
44+
auto result = byte_buffer.download_data_array<value_type, 1>();
45+
46+
CHECK_CONTAINER_EQUAL(result, expected_result, "Check data after setting index 0");
47+
CHECK_EQUAL(byte_buffer.used_capacity(), sizeof(value_type), "Used capacity");
48+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
49+
}
50+
{SCOPE_SECTION("Set index 1")
51+
byte_buffer.set_data(max_data);
52+
// Buffer = {0, 255, 0, 0}, Set index 1 to max.
53+
std::array<value_type, 2> expected_result = { min_data, max_data };
54+
auto result = byte_buffer.download_data_array<value_type, 2>();
55+
56+
CHECK_CONTAINER_EQUAL(result, expected_result, "Check data after setting index 1");
57+
CHECK_EQUAL(byte_buffer.used_capacity(), sizeof(value_type) * 2, "Used capacity");
58+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
59+
}
60+
{SCOPE_SECTION("Set array to index 2")
61+
byte_buffer.set_data(arr);
62+
std::array<value_type, 4> expected_result = { min_data, max_data, arr[0], arr[1] };
63+
// Buffer = {0, 255, 2, 3}, Set index 2 and 3 to array values.
64+
auto result = byte_buffer.download_data_array<value_type, 4>();
65+
66+
CHECK_CONTAINER_EQUAL(result, expected_result, "Check data after setting array");
67+
CHECK_EQUAL(byte_buffer.used_capacity(), sizeof(value_type) * 4, "Used capacity");
68+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
69+
}
70+
{SCOPE_SECTION("Clear index 1")
71+
byte_buffer.clear(sizeof(value_type), sizeof(value_type));
72+
// Clear data not at the end, should not affect used capacity.
73+
// V clearing this byte.
74+
// Buffer = {0, 0, 2, 3}, Clear index 1.
75+
std::array<value_type, 4> expected_result_non_end_remove = { min_data, value_type{0}, arr[0], arr[1] };
76+
auto result = byte_buffer.download_data_array<value_type, 4>();
77+
78+
CHECK_CONTAINER_EQUAL(result, expected_result_non_end_remove, "Clear byte in middle download data");
79+
CHECK_EQUAL(byte_buffer.used_capacity(), sizeof(value_type) * 4, "Clear byte in middle used capacity");
80+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
81+
}
82+
{SCOPE_SECTION("Clear index 3")
83+
byte_buffer.clear(3 * sizeof(value_type), sizeof(value_type));
84+
// Clear data from the end should reduce used capacity.
85+
// C V Despite clearing off the end, the value is still there in the current implementation.
86+
// Buffer = {0, 0, 2, 0}, Clear index 3.
87+
std::array<value_type, 3> expected_result_end = { min_data, value_type{0}, arr[0] };
88+
auto result = byte_buffer.download_data_array<value_type, 3>();
89+
90+
CHECK_CONTAINER_EQUAL(result, expected_result_end, "Clear byte at end download data");
91+
CHECK_EQUAL(byte_buffer.used_capacity(), sizeof(value_type) * 3, "Clear byte at end used capacity");
92+
CHECK_EQUAL(byte_buffer.capacity(), sizeof(value_type) * 4, "Capacity");
93+
}
94+
}
95+
}
1496
};
1597
} // namespace Test

0 commit comments

Comments
 (0)