From a6052168340f9831c766f76a3ec7b94da38da78a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 4 Mar 2026 16:53:58 +0000 Subject: Replace basic glVertexArray with a specific class ready for helpers --- game/network/network.h | 1 + gfx/gl/glVertexArray.h | 15 +++++++++++++++ gfx/models/mesh.h | 2 +- lib/glArrays.h | 2 -- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 gfx/gl/glVertexArray.h diff --git a/game/network/network.h b/game/network/network.h index 4f5d2b0..46b84d4 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -1,6 +1,7 @@ #pragma once #include "collection.h" +#include "gfx/gl/glVertexArray.h" #include "gfx/gl/instanceVertices.h" #include "gfx/models/texture.h" #include "gfx/renderable.h" diff --git a/gfx/gl/glVertexArray.h b/gfx/gl/glVertexArray.h new file mode 100644 index 0000000..891f21d --- /dev/null +++ b/gfx/gl/glVertexArray.h @@ -0,0 +1,15 @@ +#pragma once + +#include "config/types.h" +#include "glArrays.h" + +namespace Impl { + // NOLINTNEXTLINE(readability-identifier-naming) + struct glVertexArray : Detail::glNamed { }; +} + +// NOLINTBEGIN(readability-identifier-naming) +template +using glVertexArrays = glManagedArray; +using glVertexArray = glManagedSingle; +// NOLINTEND(readability-identifier-naming) diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index 8791aed..d6ac171 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -2,7 +2,7 @@ #include "config/types.h" #include "gfx/gl/vertexArrayObject.h" -#include +#include #include #include #include diff --git a/lib/glArrays.h b/lib/glArrays.h index 48bd577..e949ccd 100644 --- a/lib/glArrays.h +++ b/lib/glArrays.h @@ -119,8 +119,6 @@ struct glManagedArray : public std::array { }; // NOLINTBEGIN(readability-identifier-naming) -template using glVertexArrays = glManagedArray; -using glVertexArray = glManagedSingle; template using glBuffers = glManagedArray; using glBuffer = glManagedSingle; template using glFrameBuffers = glManagedArray; -- cgit v1.3 From 6f339810f23bdd32311726fb2736e26e2cdb7edb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 13:36:18 +0000 Subject: Include a stacktrace in Boost test context reported for OpenGL errors --- test/Jamfile.jam | 3 ++- test/testMainWindow.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/Jamfile.jam b/test/Jamfile.jam index bdaaa45..a96ca2a 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -4,6 +4,7 @@ import path : glob-tree ; lib boost_unit_test_framework ; lib benchmark ; +lib stdc++exp ; path-constant res : ../res ; path-constant fixtures : fixtures ; @@ -42,7 +43,7 @@ project : requirements tidy:hicpp-vararg tidy:boost ; -lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] ; +lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] : stdc++exp ; rule perfrun ( main + : extra-requirements * : runtime-dependency * : command-args * ) { local name = $(main[0]:S=) ; local benchmark = $(main[0]:S=.benchmark) ; diff --git a/test/testMainWindow.cpp b/test/testMainWindow.cpp index d048682..40f5567 100644 --- a/test/testMainWindow.cpp +++ b/test/testMainWindow.cpp @@ -1,15 +1,19 @@ #include "testMainWindow.h" +#include #include #include +#include TestMainWindow::TestMainWindow() : MainWindow {{1, 1}, __FILE__, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN} { glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback( [](GLenum /*source*/, GLenum type, GLuint /*id*/, GLenum severity, GLsizei /*length*/, const GLchar * message, const void *) { const auto msg = std::format("GL CALLBACK: {} type = 0x{:x}, severity = 0x{:x}, message = {}", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message); + BOOST_TEST_INFO(std::stacktrace::current()); switch (type) { case GL_DEBUG_TYPE_ERROR: case GL_DEBUG_TYPE_PORTABILITY: -- cgit v1.3 From 47c0dd2b46fce3a5535ee457549ebd5bcea91c61 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 13:48:04 +0000 Subject: Add vertexAttribFormatFunc to gl_traits for DSA vertex configuration --- lib/gl_traits.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/gl_traits.h b/lib/gl_traits.h index b3c6909..97183f0 100644 --- a/lib/gl_traits.h +++ b/lib/gl_traits.h @@ -19,6 +19,14 @@ struct gl_traits_float : public gl_traits_base { glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer); return 1; }}; + + template + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribFormat(vao, index, size, type, GL_FALSE, offset); + return 1; + } }; struct gl_traits_longfloat : public gl_traits_base { @@ -27,6 +35,14 @@ struct gl_traits_longfloat : public gl_traits_base { glVertexAttribLPointer(index, size, type, stride, pointer); return 1; }}; + + template + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribLFormat(vao, index, size, type, offset); + return 1; + } }; struct gl_traits_integer : public gl_traits_base { @@ -35,6 +51,14 @@ struct gl_traits_integer : public gl_traits_base { glVertexAttribIPointer(index, size, type, stride, pointer); return 1; }}; + + template + static GLuint + vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) + { + glVertexArrayAttribIFormat(vao, index, size, type, offset); + return 1; + } }; template<> struct gl_traits : public gl_traits_float { @@ -44,18 +68,22 @@ template<> struct gl_traits : public gl_traits_float { static constexpr std::array glUniformmFunc {&glUniformMatrix2fv, &glUniformMatrix3fv, &glUniformMatrix4fv}; static constexpr auto glTexParameterFunc {&glTexParameterf}; static constexpr auto glTexParameterfFunc {&glTexParameterfv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_longfloat { static constexpr GLenum type {GL_DOUBLE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { static constexpr GLenum type {GL_BYTE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { static constexpr GLenum type {GL_SHORT}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { @@ -64,14 +92,17 @@ template<> struct gl_traits : public gl_traits_integer { static constexpr std::array glUniformvFunc {&glUniform1iv, &glUniform2iv, &glUniform3iv, &glUniform4iv}; static constexpr auto glTexParameterFunc {&glTexParameteri}; static constexpr auto glTexParameterfFunc {&glTexParameteriv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_BYTE}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { static constexpr GLenum type {GL_UNSIGNED_SHORT}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template<> struct gl_traits : public gl_traits_integer { @@ -79,6 +110,7 @@ template<> struct gl_traits : public gl_traits_integer { static constexpr auto glUniformFunc {&glUniform1ui}; static constexpr std::array glUniformvFunc { &glUniform1uiv, &glUniform2uiv, &glUniform3uiv, &glUniform4uiv}; + static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc}; }; template struct gl_traits> : public gl_traits { @@ -91,10 +123,26 @@ template struct gl_traits> : public } return S; }}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + if constexpr (std::is_pod_v) { + return gl_traits::template vertexAttribFormatFunc::type, S>(vao, index, offset); + } + else { + GLuint used = 0; + for (GLuint e = 0; e < S; e++) { + used += gl_traits::template vertexAttribFormatFunc::type, 1>( + vao, index + e, offset + (e * sizeof(T))); + } + return used; + } + }}; }; template struct gl_traits> : public gl_traits { static constexpr GLint size {L}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + return gl_traits::template vertexAttribFormatFunc::type, L>(vao, index, offset); + }}; }; template @@ -108,6 +156,14 @@ struct gl_traits> : public gl_traits { } return R; }}; + static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { + GLuint used = 0; + for (GLuint row = 0; row < R; row++) { + used += gl_traits::template vertexAttribFormatFunc::type, C>( + vao, index + row, offset + (C * row * static_cast(sizeof(T)))); + } + return used; + }}; }; template -- cgit v1.3 From 28e1b2d2bfd8c3228b42a151aa746306bd8024ab Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 14:01:52 +0000 Subject: Define SequentialCollection in terms of contiguous_range --- lib/collections.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/collections.h b/lib/collections.h index e182af5..3c80125 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -10,10 +10,8 @@ #include template -concept SequentialCollection = requires(T c) { - { c.size() } -> std::integral; - { c.data() } -> std::same_as; -}; +concept SequentialCollection + = std::ranges::contiguous_range && std::is_same_v; template concept IterableCollection = std::is_same_v().begin()), decltype(std::declval().end())>; -- cgit v1.3 From 182f823116458ad357823f4cc56638980aafa0f6 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 14:11:38 +0000 Subject: Replace generic glBuffer type alias with a full implementation Adds wrappers for DSA storage and data uploads. --- gfx/gl/glBuffer.h | 27 +++++++++++++++++++++++++++ gfx/models/mesh.h | 1 + lib/glArrays.cpp | 14 -------------- lib/glArrays.h | 2 -- lib/glContainer.h | 2 +- 5 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 gfx/gl/glBuffer.h delete mode 100644 lib/glArrays.cpp diff --git a/gfx/gl/glBuffer.h b/gfx/gl/glBuffer.h new file mode 100644 index 0000000..f43c223 --- /dev/null +++ b/gfx/gl/glBuffer.h @@ -0,0 +1,27 @@ +#pragma once + +#include "glArrays.h" + +namespace Impl { + // NOLINTNEXTLINE(readability-identifier-naming) + struct glBuffer : Detail::glNamed { + void + storage(const std::ranges::contiguous_range auto & data, GLenum flags) + { + glNamedBufferStorage( + name, static_cast(data.size() * sizeof(decltype(*data.data()))), data.data(), flags); + } + + void + data(const std::ranges::contiguous_range auto & data, GLenum flags) + { + glNamedBufferData( + name, static_cast(data.size() * sizeof(decltype(*data.data()))), data.data(), flags); + } + }; +} + +// NOLINTBEGIN(readability-identifier-naming) +template using glBuffers = glManagedArray; +using glBuffer = glManagedSingle; +// NOLINTEND(readability-identifier-naming) diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index d6ac171..4135a30 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -1,6 +1,7 @@ #pragma once #include "config/types.h" +#include "gfx/gl/glBuffer.h" #include "gfx/gl/vertexArrayObject.h" #include #include diff --git a/lib/glArrays.cpp b/lib/glArrays.cpp deleted file mode 100644 index cb12f91..0000000 --- a/lib/glArrays.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "glArrays.h" -#include - -// Specialisations (glBuffer is an example of the typedef) -static_assert(std::is_nothrow_default_constructible_v); -static_assert(!std::is_trivially_default_constructible_v); -static_assert(std::is_nothrow_destructible_v); -static_assert(!std::is_trivially_destructible_v); -static_assert(std::is_default_constructible_v); -static_assert(!std::is_copy_constructible_v); -static_assert(!std::is_copy_assignable_v); -static_assert(std::is_nothrow_move_constructible_v); -static_assert(std::is_nothrow_move_assignable_v); -static_assert(sizeof(glBuffer) == sizeof(GLuint)); diff --git a/lib/glArrays.h b/lib/glArrays.h index e949ccd..1cf2fbf 100644 --- a/lib/glArrays.h +++ b/lib/glArrays.h @@ -119,8 +119,6 @@ struct glManagedArray : public std::array { }; // NOLINTBEGIN(readability-identifier-naming) -template using glBuffers = glManagedArray; -using glBuffer = glManagedSingle; template using glFrameBuffers = glManagedArray; using glFrameBuffer = glManagedSingle; template diff --git a/lib/glContainer.h b/lib/glContainer.h index 4119ef5..45f1030 100644 --- a/lib/glContainer.h +++ b/lib/glContainer.h @@ -1,6 +1,6 @@ #pragma once -#include "glArrays.h" +#include "gfx/gl/glBuffer.h" #include #include #include -- cgit v1.3 From a1aa3594e75e611a69fbce1c6f9beef65d284533 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 14:16:17 +0000 Subject: Extend glVertexArray with VertexArrayConfigurator DSA version of VertexArrayObject. --- gfx/gl/glVertexArray.h | 114 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/gfx/gl/glVertexArray.h b/gfx/gl/glVertexArray.h index 891f21d..3594cbf 100644 --- a/gfx/gl/glVertexArray.h +++ b/gfx/gl/glVertexArray.h @@ -1,11 +1,121 @@ #pragma once -#include "config/types.h" +#include "collections.h" #include "glArrays.h" +#include "glBuffer.h" +#include "gl_traits.h" namespace Impl { + class VertexArrayConfigurator { + public: + template struct MP { + constexpr MP(M T::* ptr) : ptr {ptr} { } + + operator GLuint() const + { + return static_cast(reinterpret_cast(&(static_cast(nullptr)->*ptr)) + - static_cast(nullptr)); + } + + M T::* ptr; + using ValueType = M; + }; + + template MP(M T::*) -> MP; + + explicit VertexArrayConfigurator(GLuint name) : name {name} { } + + VertexArrayConfigurator & + addIndices(const glBuffer & buffer) + { + glVertexArrayElementBuffer(name, buffer); + return *this; + } + + VertexArrayConfigurator & + addIndices(glBuffer & buffer, const SequentialCollection auto & indices) + { + buffer.storage(indices, 0); + return addIndices(buffer); + } + + // Customisation point + template VertexArrayConfigurator & addAttribsFor(GLuint divisor, const glBuffer & buffer); + + template + VertexArrayConfigurator & + addAttribs(const GLuint divisor) + { + configureAttribs(divisor); + return *this; + } + + template + VertexArrayConfigurator & + addAttribs(const GLuint divisor, const glBuffer & buffer) + { + glVertexArrayVertexBuffer(name, binding, buffer, 0, sizeof(VertexT)); + return addAttribs(divisor); + } + + template + VertexArrayConfigurator & + addAttribs(const GLuint divisor, glBuffer & buffer, const SequentialCollection auto & data) + { + buffer.storage(data, 0); + return addAttribs(divisor, buffer); + } + + private: + void + setPointerMeta(const GLuint usedAttribs) + { + while (attrib < usedAttribs) { + glEnableVertexArrayAttrib(name, attrib); + glVertexArrayAttribBinding(name, attrib++, binding); + } + } + + template + void + setPointer(const GLuint offset) + { + setPointerMeta(attrib + gl_traits::vertexArrayAttribFormat(name, attrib, offset)); + } + + template + void + setPointer() + { + setPointer(Attrib); + } + + template + void + configureAttribs(const GLuint divisor) + { + if constexpr (sizeof...(Attribs) == 0) { + setPointer(0); + } + else { + ((setPointer()), ...); + } + glVertexArrayBindingDivisor(name, binding++, divisor); + } + + GLuint name; + GLuint binding = 0; + GLuint attrib = 0; + }; + // NOLINTNEXTLINE(readability-identifier-naming) - struct glVertexArray : Detail::glNamed { }; + struct glVertexArray : Detail::glNamed { + VertexArrayConfigurator + configure() + { + return VertexArrayConfigurator {name}; + } + }; } // NOLINTBEGIN(readability-identifier-naming) -- cgit v1.3 From cf0ecc35ed114c6bf54fce9c6228d60dbba1a3b5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 14:22:28 +0000 Subject: Replace use of VertexArrayObject with glVertexArray/glBuffer DSA versions --- game/network/rail.cpp | 9 ++++----- game/scenary/foliage.cpp | 9 ++++----- game/scenary/illuminator.cpp | 17 ++++++++--------- game/terrain.cpp | 18 ++++++++---------- game/vehicles/railVehicleClass.cpp | 13 ++++++------- game/water.cpp | 6 +++--- gfx/gl/sceneRenderer.cpp | 3 +-- gfx/gl/sceneShader.cpp | 2 +- gfx/models/mesh.h | 13 ++++++------- gfx/models/vertex.cpp | 8 ++++---- 10 files changed, 45 insertions(+), 53 deletions(-) diff --git a/game/network/rail.cpp b/game/network/rail.cpp index f265d6b..8d85f35 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -3,7 +3,6 @@ #include "network.h" #include // IWYU pragma: keep #include -#include #include template class NetworkOf; @@ -139,18 +138,18 @@ RailLink::vehiclePositionOffset() const template<> NetworkLinkHolder::NetworkLinkHolder() { - VertexArrayObject {vao} + vao.configure() .addAttribs( - vertices.bufferName()); + 0, vertices.bufferName()); } template<> NetworkLinkHolder::NetworkLinkHolder() { - VertexArrayObject {vao} + vao.configure() .addAttribs(vertices.bufferName()); + &RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(0, vertices.bufferName()); } namespace { diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index 0981ffc..49a4831 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -4,7 +4,6 @@ #include "gfx/gl/sceneShader.h" #include "gfx/gl/shadowMapper.h" #include "gfx/gl/shadowStenciller.h" -#include "gfx/gl/vertexArrayObject.h" #include static_assert(std::is_constructible_v); @@ -38,11 +37,11 @@ void Foliage::postLoad() { texture = getTexture(); - bodyMesh->configureVAO(instanceVAO) + bodyMesh->configureVAO(instanceVAO, 0) .addAttribs( - instances.bufferName(), 1); - VertexArrayObject {instancePointVAO}.addAttribs( - instances.bufferName()); + 1, instances.bufferName()); + instancePointVAO.configure().addAttribs( + 0, instances.bufferName()); const auto & size = bodyMesh->getDimensions().size; billboardSize = billboardTextureSizeForObject(size); diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp index d8e4c4e..590062e 100644 --- a/game/scenary/illuminator.cpp +++ b/game/scenary/illuminator.cpp @@ -1,6 +1,5 @@ #include "illuminator.h" #include "gfx/gl/sceneShader.h" -#include "gfx/gl/vertexArrayObject.h" #include "gfx/models/texture.h" // IWYU pragma: keep #include @@ -41,15 +40,15 @@ Illuminator::postLoad() throw std::logic_error {"Illuminator has no lights"}; } texture = getTexture(); - bodyMesh->configureVAO(instanceVAO) - .addAttribs(instances.bufferName(), 1); + bodyMesh->configureVAO(instanceVAO, 0) + .addAttribs(1, instances.bufferName()); if (!spotLight.empty()) { instancesSpotLightVAO.emplace(); - VertexArrayObject {*instancesSpotLightVAO} + instancesSpotLightVAO->configure() .addAttribs( - instancesSpotLight.bufferName(), 0) - .addAttribs(instances.bufferName(), 1); + 0, instancesSpotLight.bufferName()) + .addAttribs(1, instances.bufferName()); std::transform( spotLight.begin(), spotLight.end(), std::back_inserter(spotLightInstances), [this](const auto & s) { return instancesSpotLight.acquire(*s); @@ -57,10 +56,10 @@ Illuminator::postLoad() } if (!pointLight.empty()) { instancesPointLightVAO.emplace(); - VertexArrayObject {*instancesPointLightVAO} + instancesPointLightVAO->configure() .addAttribs(instancesPointLight.bufferName(), 0) - .addAttribs(instances.bufferName(), 1); + &PointLightVertex::kq>(0, instancesPointLight.bufferName()) + .addAttribs(1, instances.bufferName()); std::transform( pointLight.begin(), pointLight.end(), std::back_inserter(pointLightInstances), [this](const auto & s) { return instancesPointLight.acquire(*s); diff --git a/game/terrain.cpp b/game/terrain.cpp index 187d035..25ecca9 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -17,10 +17,10 @@ static constexpr RGB OPEN_SURFACE {-1}; static constexpr GlobalDistance TILE_SIZE = 1024 * 1024; // ~1km, power of 2, fast divide template<> -VertexArrayObject & -VertexArrayObject::addAttribsFor(const GLuint arrayBuffer, const GLuint divisor) +Impl::VertexArrayConfigurator & +Impl::VertexArrayConfigurator::addAttribsFor(const GLuint divisor, const glBuffer & buffer) { - return addAttribs(arrayBuffer, divisor); + return addAttribs(divisor, buffer); } bool @@ -78,16 +78,14 @@ Terrain::copyIndicesToBuffers(const SurfaceIndices & surfaceIndices) auto meshItr = meshes.find(surfaceKey); if (meshItr == meshes.end()) { meshItr = meshes.emplace(surfaceKey, SurfaceArrayBuffer {}).first; - VertexArrayObject {meshItr->second.vertexArray} - .addAttribsFor(verticesBuffer) - .addIndices(meshItr->second.indicesBuffer, indices) - .data(verticesBuffer, GL_ARRAY_BUFFER); + meshItr->second.vertexArray.configure() + .addAttribsFor(0, verticesBuffer) + .addIndices(meshItr->second.indicesBuffer); } else { - VertexArrayObject {meshItr->second.vertexArray} - .addIndices(meshItr->second.indicesBuffer, indices) - .data(verticesBuffer, GL_ARRAY_BUFFER); + meshItr->second.vertexArray.configure().addIndices(meshItr->second.indicesBuffer); } + meshItr->second.indicesBuffer.data(indices, GL_DYNAMIC_DRAW); meshItr->second.count = static_cast(indices.size()); meshItr->second.aabb = AxisAlignedBoundingBox::fromPoints( indices | std::views::transform([this](const auto vertex) { diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 21f01c8..a11b435 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -1,7 +1,6 @@ #include "railVehicleClass.h" #include "gfx/gl/sceneShader.h" #include "gfx/gl/shadowMapper.h" -#include "gfx/gl/vertexArrayObject.h" #include #include #include @@ -34,14 +33,14 @@ void RailVehicleClass::postLoad() { texture = getTexture(); - bodyMesh->configureVAO(instanceVAO) - .addAttribs(instances.bufferName(), 1); + bodyMesh->configureVAO(instanceVAO, 0) + .addAttribs(1, instances.bufferName()); bogies.front() - ->configureVAO(instancesBogiesVAO.front()) - .addAttribs(instances.bufferName(), 1); + ->configureVAO(instancesBogiesVAO.front(), 0) + .addAttribs(1, instances.bufferName()); bogies.back() - ->configureVAO(instancesBogiesVAO.back()) - .addAttribs(instances.bufferName(), 1); + ->configureVAO(instancesBogiesVAO.back(), 0) + .addAttribs(1, instances.bufferName()); static_assert(sizeof(LocationVertex) == 144UL); } diff --git a/game/water.cpp b/game/water.cpp index 527e85a..0657214 100644 --- a/game/water.cpp +++ b/game/water.cpp @@ -24,10 +24,10 @@ namespace glm { } template<> -VertexArrayObject & -VertexArrayObject::addAttribsFor(const GLuint arrayBuffer, const GLuint divisor) +Impl::VertexArrayConfigurator & +Impl::VertexArrayConfigurator::addAttribsFor(const GLuint divisor, const glBuffer & buffer) { - return addAttribs(arrayBuffer, divisor); + return addAttribs(divisor, buffer); } Water::Water(std::shared_ptr tm) : geoData {std::move(tm)}, water {std::make_shared("water.png")} diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index 15dde1b..e67711b 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -1,6 +1,5 @@ #include "sceneRenderer.h" #include "maths.h" -#include "vertexArrayObject.h" #include #include #include @@ -22,7 +21,7 @@ SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o, glDebugScope) : lighting {lighting_vert, lighting_frag}, shadowMapper {{2048, 2048}} { shader.setViewPort({0, 0, size.x, size.y}); - VertexArrayObject {displayVAO}.addAttribs(displayVBO, displayVAOdata); + displayVAO.configure().addAttribs(0, displayVBO, displayVAOdata); const auto configuregdata = [this](const auto & data, const std::initializer_list iformats, const GLenum format, const GLenum attachment) { diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp index 43e54a5..302edda 100644 --- a/gfx/gl/sceneShader.cpp +++ b/gfx/gl/sceneShader.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index 4135a30..e78d27e 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -2,7 +2,6 @@ #include "config/types.h" #include "gfx/gl/glBuffer.h" -#include "gfx/gl/vertexArrayObject.h" #include #include #include @@ -54,15 +53,15 @@ public: return static_cast(v.pos); }))} { - VertexArrayObject::data(vertices, m_vertexArrayBuffers[0], GL_ARRAY_BUFFER); - VertexArrayObject::data(indices, m_vertexArrayBuffers[1], GL_ARRAY_BUFFER); - configureVAO(m_vertexArrayObject); + m_vertexArrayBuffers[0].storage(vertices, 0); + m_vertexArrayBuffers[1].storage(indices, 0); + configureVAO(m_vertexArrayObject, 0); } - VertexArrayObject & - configureVAO(VertexArrayObject && vao) const + auto + configureVAO(glVertexArray & vao, GLuint divisor) const { - return vao.addAttribsFor(m_vertexArrayBuffers[0]).addIndices(m_vertexArrayBuffers[1]); + return vao.configure().addAttribsFor(divisor, m_vertexArrayBuffers[0]).addIndices(m_vertexArrayBuffers[1]); } }; diff --git a/gfx/models/vertex.cpp b/gfx/models/vertex.cpp index c144db3..dc32f72 100644 --- a/gfx/models/vertex.cpp +++ b/gfx/models/vertex.cpp @@ -1,10 +1,10 @@ #include "vertex.h" -#include "gfx/gl/vertexArrayObject.h" +#include "gfx/gl/glVertexArray.h" template<> -VertexArrayObject & -VertexArrayObject::addAttribsFor(const GLuint arrayBuffer, const GLuint divisor) +Impl::VertexArrayConfigurator & +Impl::VertexArrayConfigurator::addAttribsFor(const GLuint divisor, const glBuffer & buffer) { return addAttribs( - arrayBuffer, divisor); + divisor, buffer); } -- cgit v1.3 From 1db3aead80d9209175aa5547363ad34b881a3660 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 6 Mar 2026 14:26:59 +0000 Subject: Remove VertexArrayObject and supporting non-DSA gl_traits helpers --- gfx/gl/vertexArrayObject.h | 125 --------------------------------------------- lib/gl_traits.h | 34 ------------ 2 files changed, 159 deletions(-) delete mode 100644 gfx/gl/vertexArrayObject.h diff --git a/gfx/gl/vertexArrayObject.h b/gfx/gl/vertexArrayObject.h deleted file mode 100644 index d008897..0000000 --- a/gfx/gl/vertexArrayObject.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "collections.h" -#include "gl_traits.h" -#include "special_members.h" -#include - -class VertexArrayObject { -public: - template [[nodiscard]] VertexArrayObject(const T & arrayObject) - { - glBindVertexArray(arrayObject); - } - - ~VertexArrayObject() - { - glBindVertexArray(0); - } - - NO_MOVE(VertexArrayObject); - NO_COPY(VertexArrayObject); - - template struct MP { - constexpr MP(m T::* p) : P {p} { } - - constexpr - operator void *() const - { - return &(static_cast(nullptr)->*P); - } - - m T::* P; - using value_type = m; - }; - - template MP(m T::*) -> MP; - - template - VertexArrayObject & - addAttribs(const GLuint arrayBuffer, const SequentialCollection auto & vertices, const GLuint divisor = 0) - { - addAttribs(arrayBuffer, divisor); - data(vertices, arrayBuffer, GL_ARRAY_BUFFER); - return *this; - } - - template - VertexArrayObject & - addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0) - { - configure_attribs(arrayBuffer, divisor); - return *this; - } - - // Customisation point - template VertexArrayObject & addAttribsFor(const GLuint arrayBuffer, const GLuint divisor = 0); - - template - VertexArrayObject & - addIndices(const GLuint arrayBuffer, const Indices & indices) - { - data(indices, arrayBuffer, GL_ELEMENT_ARRAY_BUFFER); - return *this; - } - - VertexArrayObject & - addIndices(const GLuint arrayBuffer) - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrayBuffer); - return *this; - } - - VertexArrayObject & - data(const GLuint arrayBuffer, GLenum target) - { - glBindBuffer(target, arrayBuffer); - return *this; - } - - template - static void - data(const Data & data, const GLuint arrayBuffer, GLenum target) - { - using Value = typename Data::value_type; - glBindBuffer(target, arrayBuffer); - glBufferData(target, static_cast(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW); - } - -private: - template - static auto - set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor) - { - using traits = gl_traits; - const auto usedAttribs - = traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(VertexT), ptr); - for (GLuint i {}; i < usedAttribs; i++) { - glEnableVertexAttribArray(vertexArrayId + i); - glVertexAttribDivisor(vertexArrayId + i, divisor); - } - return usedAttribs; - } - - template - static auto - set_pointer(const GLuint vertexArrayId, const GLuint divisor) - { - return set_pointer(vertexArrayId, attrib, divisor); - } - - template - void - configure_attribs(const GLuint arrayBuffer, const GLuint divisor) - { - glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); - if constexpr (sizeof...(attribs) == 0) { - vertexArrayId += set_pointer(vertexArrayId, nullptr, divisor); - } - else { - ((vertexArrayId += set_pointer(vertexArrayId, divisor)), ...); - } - } - - GLuint vertexArrayId {}; -}; diff --git a/lib/gl_traits.h b/lib/gl_traits.h index 97183f0..35ae9e8 100644 --- a/lib/gl_traits.h +++ b/lib/gl_traits.h @@ -14,12 +14,6 @@ struct gl_traits_base { }; struct gl_traits_float : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer); - return 1; - }}; - template static GLuint vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) @@ -30,12 +24,6 @@ struct gl_traits_float : public gl_traits_base { }; struct gl_traits_longfloat : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribLPointer(index, size, type, stride, pointer); - return 1; - }}; - template static GLuint vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) @@ -46,12 +34,6 @@ struct gl_traits_longfloat : public gl_traits_base { }; struct gl_traits_integer : public gl_traits_base { - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - glVertexAttribIPointer(index, size, type, stride, pointer); - return 1; - }}; - template static GLuint vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset) @@ -115,14 +97,6 @@ template<> struct gl_traits : public gl_traits_integer { template struct gl_traits> : public gl_traits { static constexpr GLint size {S * gl_traits::size}; - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - const auto base = static_cast(pointer); - for (GLuint e = 0; e < S; e++) { - glVertexAttribPointer(index + e, gl_traits::size, type, GL_FALSE, stride, base + e); - } - return S; - }}; static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { if constexpr (std::is_pod_v) { return gl_traits::template vertexAttribFormatFunc::type, S>(vao, index, offset); @@ -148,14 +122,6 @@ template struct gl_traits struct gl_traits> : public gl_traits { static constexpr GLint size {C * R}; - static constexpr auto vertexAttribFunc { - [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint { - const auto base = static_cast(pointer); - for (GLuint r = 0; r < R; r++) { - glVertexAttribPointer(index + r, C, type, GL_FALSE, stride, base + (r * C)); - } - return R; - }}; static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) { GLuint used = 0; for (GLuint row = 0; row < R; row++) { -- cgit v1.3 From c89a633f59d0e393695c10f28c4ba8635eadffba Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 7 Mar 2026 11:42:46 +0000 Subject: Replace glContainer with glAllocator glContainer is no longer required, as we can use std::vector with a custom allocator which uses OpenGL buffers for storage. Minor irritation is that the mapped buffers aren't guaranteed to be flushed in the tests, so sometimes we're missing bits in a test render. --- game/network/rail.cpp | 7 +- game/scenary/foliage.cpp | 10 +- game/scenary/illuminator.cpp | 18 +- game/vehicles/railVehicleClass.cpp | 10 +- gfx/gl/instanceVertices.h | 39 ++- lib/glAllocator.h | 90 +++++++ lib/glContainer.h | 496 ------------------------------------- test/Jamfile.jam | 8 +- test/test-glAllocator.cpp | 28 +++ test/test-glContainer.cpp | 453 --------------------------------- test/test-instancing.cpp | 33 +-- 11 files changed, 187 insertions(+), 1005 deletions(-) create mode 100644 lib/glAllocator.h delete mode 100644 lib/glContainer.h create mode 100644 test/test-glAllocator.cpp delete mode 100644 test/test-glContainer.cpp diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 8d85f35..545620d 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -140,8 +140,7 @@ template<> NetworkLinkHolder::NetworkLinkHolder() { vao.configure() .addAttribs( - 0, vertices.bufferName()); + &RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(0); } template<> NetworkLinkHolder::NetworkLinkHolder() @@ -149,7 +148,7 @@ template<> NetworkLinkHolder::NetworkLinkHolder() vao.configure() .addAttribs(0, vertices.bufferName()); + &RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(0); } namespace { @@ -160,6 +159,8 @@ namespace { if (auto count = networkLinks.vertices.size()) { shader.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS); glBindVertexArray(networkLinks.vao); + glVertexArrayVertexBuffer( + networkLinks.vao, 0, networkLinks.vertices.bufferName(), 0, sizeof(typename LinkType::Vertex)); glDrawArrays(mode, 0, static_cast(count)); } }; diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index 49a4831..cf2d82e 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -38,10 +38,8 @@ Foliage::postLoad() { texture = getTexture(); bodyMesh->configureVAO(instanceVAO, 0) - .addAttribs( - 1, instances.bufferName()); - instancePointVAO.configure().addAttribs( - 0, instances.bufferName()); + .addAttribs(1); + instancePointVAO.configure().addAttribs(0); const auto & size = bodyMesh->getDimensions().size; billboardSize = billboardTextureSizeForObject(size); @@ -104,6 +102,7 @@ Foliage::render(const SceneShader & shader, const Frustum &) const billboard[1].bind(GL_TEXTURE_2D_ARRAY, GL_TEXTURE1); billboard[2].bind(GL_TEXTURE_2D_ARRAY, GL_TEXTURE2); glBindVertexArray(instancePointVAO); + glVertexArrayVertexBuffer(instancePointVAO, 0, instances.bufferName(), 0, sizeof(LocationVertex)); glDrawArrays(GL_POINTS, static_cast(instancePartitions.second.first), static_cast(count)); glBindVertexArray(0); } @@ -113,6 +112,7 @@ Foliage::render(const SceneShader & shader, const Frustum &) const if (texture) { texture->bind(); } + glVertexArrayVertexBuffer(instanceVAO, 1, instances.bufferName(), 0, sizeof(LocationVertex)); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); } } @@ -129,6 +129,7 @@ Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const mapper.stencilShadowProgram.use(dimensions.centre, dimensions.size); shadowStencil.bind(GL_TEXTURE_2D_ARRAY, GL_TEXTURE0); glBindVertexArray(instancePointVAO); + glVertexArrayVertexBuffer(instancePointVAO, 0, instances.bufferName(), 0, sizeof(LocationVertex)); glDrawArrays(GL_POINTS, static_cast(instancePartitions.second.first), static_cast(count)); glBindVertexArray(0); } @@ -140,6 +141,7 @@ Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const else { mapper.dynamicPointInst.use(); } + glVertexArrayVertexBuffer(instanceVAO, 1, instances.bufferName(), 0, sizeof(LocationVertex)); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); } } diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp index 590062e..7ab17fe 100644 --- a/game/scenary/illuminator.cpp +++ b/game/scenary/illuminator.cpp @@ -41,14 +41,13 @@ Illuminator::postLoad() } texture = getTexture(); bodyMesh->configureVAO(instanceVAO, 0) - .addAttribs(1, instances.bufferName()); + .addAttribs(1); if (!spotLight.empty()) { instancesSpotLightVAO.emplace(); instancesSpotLightVAO->configure() .addAttribs( - 0, instancesSpotLight.bufferName()) - .addAttribs(1, instances.bufferName()); + &SpotLightVertex::colour, &SpotLightVertex::kq, &SpotLightVertex::arc>(0) + .addAttribs(1); std::transform( spotLight.begin(), spotLight.end(), std::back_inserter(spotLightInstances), [this](const auto & s) { return instancesSpotLight.acquire(*s); @@ -58,8 +57,8 @@ Illuminator::postLoad() instancesPointLightVAO.emplace(); instancesPointLightVAO->configure() .addAttribs(0, instancesPointLight.bufferName()) - .addAttribs(1, instances.bufferName()); + &PointLightVertex::kq>(0) + .addAttribs(1); std::transform( pointLight.begin(), pointLight.end(), std::back_inserter(pointLightInstances), [this](const auto & s) { return instancesPointLight.acquire(*s); @@ -75,6 +74,7 @@ Illuminator::render(const SceneShader & shader, const Frustum &) const if (texture) { texture->bind(); } + glVertexArrayVertexBuffer(instanceVAO, 1, instances.bufferName(), 0, sizeof(LocationVertex)); bodyMesh->DrawInstanced(instanceVAO, static_cast(count)); } } @@ -86,11 +86,17 @@ Illuminator::lights(const SceneShader & shader) const if (const auto scount = instancesSpotLight.size()) { shader.spotLightInst.use(); glBindVertexArray(*instancesSpotLightVAO); + glVertexArrayVertexBuffer( + *instancesSpotLightVAO, 0, instancesSpotLight.bufferName(), 0, sizeof(SpotLightVertex)); + glVertexArrayVertexBuffer(*instancesSpotLightVAO, 1, instances.bufferName(), 0, sizeof(LocationVertex)); glDrawArraysInstanced(GL_POINTS, 0, static_cast(scount), static_cast(count)); } if (const auto pcount = instancesPointLight.size()) { shader.pointLightInst.use(); glBindVertexArray(*instancesPointLightVAO); + glVertexArrayVertexBuffer( + *instancesPointLightVAO, 0, instancesPointLight.bufferName(), 0, sizeof(PointLightVertex)); + glVertexArrayVertexBuffer(*instancesPointLightVAO, 1, instances.bufferName(), 0, sizeof(LocationVertex)); glDrawArraysInstanced(GL_POINTS, 0, static_cast(pcount), static_cast(count)); } diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index a11b435..100073d 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -34,13 +34,13 @@ RailVehicleClass::postLoad() { texture = getTexture(); bodyMesh->configureVAO(instanceVAO, 0) - .addAttribs(1, instances.bufferName()); + .addAttribs(1); bogies.front() ->configureVAO(instancesBogiesVAO.front(), 0) - .addAttribs(1, instances.bufferName()); + .addAttribs(1); bogies.back() ->configureVAO(instancesBogiesVAO.back(), 0) - .addAttribs(1, instances.bufferName()); + .addAttribs(1); static_assert(sizeof(LocationVertex) == 144UL); } @@ -52,6 +52,10 @@ RailVehicleClass::render(const SceneShader & shader, const Frustum &) const texture->bind(); } shader.basicInst.use(); + const auto instancesBuffer = instances.bufferName(); + glVertexArrayVertexBuffer(instanceVAO, 1, instancesBuffer, 0, sizeof(LocationVertex)); + glVertexArrayVertexBuffer(instancesBogiesVAO.front(), 1, instancesBuffer, 0, sizeof(LocationVertex)); + glVertexArrayVertexBuffer(instancesBogiesVAO.back(), 1, instancesBuffer, 0, sizeof(LocationVertex)); bodyMesh->DrawInstanced(instanceVAO, count); bogies.front()->DrawInstanced(instancesBogiesVAO.front(), count); bogies.back()->DrawInstanced(instancesBogiesVAO.back(), count); diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h index ecadf8f..f24eaa3 100644 --- a/gfx/gl/instanceVertices.h +++ b/gfx/gl/instanceVertices.h @@ -1,13 +1,13 @@ #pragma once -#include "glContainer.h" +#include "glAllocator.h" #include #include #include #include -template class InstanceVertices : protected glContainer { - using base = glContainer; +template class InstanceVertices : protected glVector { + using base = glVector; public: class [[nodiscard]] InstanceProxy { @@ -120,16 +120,31 @@ public: return InstanceProxy {this, index.size() - 1}; } - using base::bufferName; - using base::reserve; - - [[nodiscard]] auto - size() const + [[nodiscard]] GLuint + bufferName() const { - base::unmap(); - return base::size(); + return base::get_allocator().getNameFor(static_cast(*this)); } + using base::at; + using base::begin; + using base::cbegin; + using base::cend; + using base::crbegin; + using base::crend; + using base::end; + using base::rbegin; + using base::rend; + using base::size; + using base::operator[]; + using base::back; + using base::capacity; + using base::data; + using base::empty; + using base::front; + using base::reserve; + using base::shrink_to_fit; + template base::size_type partition(Pred pred) @@ -195,8 +210,8 @@ protected: } template - glContainer::iterator - partition(glContainer::iterator first, glContainer::iterator last, Pred pred) + base::iterator + partition(base::iterator first, base::iterator last, Pred pred) { while (first < last) { first = std::find_if_not(first, last, pred); diff --git a/lib/glAllocator.h b/lib/glAllocator.h new file mode 100644 index 0000000..a9015da --- /dev/null +++ b/lib/glAllocator.h @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +namespace Detail { + template class glAllocator; + template + concept IsGlBufferAllocated = requires(const C & container) { + { container.get_allocator() } -> std::same_as>; + }; + + template 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(count * sizeof(T)); + glNamedBufferStorage(name, size, nullptr, STORAGE_FLAGS); + const auto data = static_cast(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 C> + [[nodiscard]] + GLuint + getNameFor(const C & container) const + { + return getNameFor(container.data()); + } + + bool operator==(const glAllocator &) const = default; + + private: + using BufferMap = std::flat_map; + std::shared_ptr buffers = std::make_shared(); + }; +} + +template +// NOLINTNEXTLINE(readability-identifier-naming) - OpenGL like +using glVector = std::vector>::allocator_type>; + +template +GLuint +operator*(const C & container) + requires Detail::IsGlBufferAllocated +{ + return container.get_allocator().getNameFor(container); +} + +static_assert(Detail::IsGlBufferAllocated, int>); diff --git a/lib/glContainer.h b/lib/glContainer.h deleted file mode 100644 index 45f1030..0000000 --- a/lib/glContainer.h +++ /dev/null @@ -1,496 +0,0 @@ -#pragma once - -#include "gfx/gl/glBuffer.h" -#include -#include -#include -#include -#include - -static_assert(GL_READ_ONLY < GL_READ_WRITE); -static_assert(GL_WRITE_ONLY < GL_READ_WRITE); - -template class glContainer { -public: - using span = std::span; - using const_span = std::span; - using value_type = T; - using reference_type = T &; - using const_reference_type = const T &; - using pointer_type = T *; - using const_pointer_type = const T *; - using size_type = std::size_t; - using iterator = span::iterator; - using const_iterator = const_span::iterator; - using reverse_iterator = span::reverse_iterator; - using const_reverse_iterator = const_span::reverse_iterator; - static constexpr bool is_trivial_dest = std::is_trivially_destructible_v; - - explicit glContainer(GLenum target = GL_ARRAY_BUFFER) : target_ {target} - { - allocBuffer(1); - } - - ~glContainer() - requires(is_trivial_dest) - = default; - - ~glContainer() - requires(!is_trivial_dest) - { - clear(); - } - - template typename C> - explicit glContainer(const C & src, GLenum target = GL_ARRAY_BUFFER) : target_ {target} - { - reserve(src.size()); - std::copy(src.begin(), src.end(), std::back_inserter(*this)); - } - - DEFAULT_MOVE_NO_COPY(glContainer); - - [[nodiscard]] iterator - begin() - { - map(GL_READ_WRITE); - return mkspan().begin(); - } - - [[nodiscard]] iterator - end() - { - map(GL_READ_WRITE); - return mkspan().end(); - } - - [[nodiscard]] const_iterator - begin() const - { - map(GL_READ_ONLY); - return mkcspan().begin(); - } - - [[nodiscard]] const_iterator - end() const - { - map(GL_READ_ONLY); - return mkcspan().end(); - } - - [[nodiscard]] const_iterator - cbegin() const - { - map(GL_READ_ONLY); - return mkcspan().begin(); - } - - [[nodiscard]] const_iterator - cend() const - { - map(GL_READ_ONLY); - return mkcspan().end(); - } - - [[nodiscard]] reverse_iterator - rbegin() - { - map(GL_READ_WRITE); - return mkspan().rbegin(); - } - - [[nodiscard]] reverse_iterator - rend() - { - map(GL_READ_WRITE); - return mkspan().rend(); - } - - [[nodiscard]] const_reverse_iterator - rbegin() const - { - map(GL_READ_ONLY); - return mkcspan().rbegin(); - } - - [[nodiscard]] const_reverse_iterator - rend() const - { - map(GL_READ_ONLY); - return mkcspan().rend(); - } - - [[nodiscard]] const_reverse_iterator - crbegin() const - { - map(GL_READ_ONLY); - return mkcspan().rbegin(); - } - - [[nodiscard]] const_reverse_iterator - crend() const - { - map(GL_READ_ONLY); - return mkcspan().rend(); - } - - [[nodiscard]] const auto & - bufferName() const - { - return buffer_; - } - - [[nodiscard]] size_type - size() const - { - return size_; - } - - void - at(size_type pos, const T & value) - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - if (data_) { - mkspan()[pos] = value; - } - else { - glBindBuffer(target_, buffer_); - glBufferSubData(target_, static_cast(pos * sizeof(T)), sizeof(value), &value); - glBindBuffer(target_, 0); - } - } - - [[nodiscard]] reference_type - at(size_type pos) - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - map(GL_READ_WRITE); - return mkspan()[pos]; - } - - [[nodiscard]] const_reference_type - at(size_type pos) const - { - if (pos >= size()) { - throw std::out_of_range {__FUNCTION__}; - } - map(GL_READ_ONLY); - return mkcspan()[pos]; - } - - [[nodiscard]] reference_type - operator[](size_type pos) - { - map(GL_READ_WRITE); - return mkspan()[pos]; - } - - [[nodiscard]] const_reference_type - operator[](size_type pos) const - { - map(GL_READ_ONLY); - return mkcspan()[pos]; - } - - [[nodiscard]] pointer_type - data() - { - map(GL_READ_WRITE); - return data_; - } - - [[nodiscard]] const_pointer_type - data() const - { - map(GL_READ_ONLY); - return data_; - } - - [[nodiscard]] reference_type - front() - { - map(GL_READ_WRITE); - return mkspan().front(); - } - - [[nodiscard]] reference_type - back() - { - map(GL_READ_WRITE); - return mkspan().back(); - } - - [[nodiscard]] const_reference_type - front() const - { - map(GL_READ_ONLY); - return mkcspan().front(); - } - - [[nodiscard]] const_reference_type - back() const - { - map(GL_READ_ONLY); - return mkcspan().back(); - } - - [[nodiscard]] bool - empty() const - { - return !size(); - } - - [[nodiscard]] size_type - capacity() const - { - return capacity_; - } - - void - unmap() const - { - if (data_) { - glBindBuffer(target_, buffer_); - glUnmapBuffer(target_); - glBindBuffer(target_, 0); - data_ = {}; - access_ = {}; - } - } - - void - reserve(size_type newCapacity) - { - if (newCapacity <= capacity_) { - return; - } - newCapacity = std::max(newCapacity, capacity_ * 2); - - std::vector existing; - existing.reserve(size_); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(newCapacity); - std::move(existing.begin(), existing.end(), begin()); - } - - void - resize(size_type newSize) - requires std::is_default_constructible_v - { - if (newSize == size_) { - return; - } - - if (const auto maintain = std::min(newSize, size_)) { - std::vector existing; - const auto maintaind = static_cast(maintain); - existing.reserve(maintain); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(newSize); - mapForAdd(); - std::move(existing.begin(), existing.begin() + maintaind, begin()); - } - else { - allocBuffer(newSize); - mapForAdd(); - } - if (const auto prevSize = setSize(newSize); newSize > prevSize) { - for (auto & uninitialised : mkspan().subspan(prevSize, newSize - prevSize)) { - new (&uninitialised) T {}; - } - } - } - - void - shrink_to_fit() - { - if (capacity_ <= size_) { - return; - } - - std::vector existing; - existing.reserve(size_); - map(is_trivial_dest ? GL_READ_ONLY : GL_READ_WRITE); - std::move(begin(), end(), std::back_inserter(existing)); - allocBuffer(size_); - map(GL_READ_WRITE); - std::move(existing.begin(), existing.end(), begin()); - } - - void - clear() noexcept(is_trivial_dest) - { - if constexpr (!is_trivial_dest) { - map(GL_READ_WRITE); - std::for_each(begin(), end(), [](auto && v) { - v.~T(); - }); - } - setSize(0); - } - - template - reference_type - emplace_back(P &&... ps) - requires std::is_constructible_v - { - auto newSize = size_ + 1; - reserve(newSize); - mapForAdd(); - new (&*end()) T {std::forward

(ps)...}; - setSize(newSize); - return back(); - } - - template - iterator - emplace(iterator pos, P &&... ps) - requires std::is_nothrow_constructible_v - { - auto newSize = size_ + 1; - const auto idx = pos - begin(); - reserve(newSize); - mapForAdd(); - pos = begin() + idx; - std::move_backward(pos, end(), end() + 1); - pos->~T(); - new (&*pos) T {std::forward

(ps)...}; - setSize(newSize); - return pos; - } - - reference_type - push_back(T p) - requires std::is_move_constructible_v - { - auto newSize = size_ + 1; - reserve(newSize); - mapForAdd(); - new (&*end()) T {std::move(p)}; - setSize(newSize); - return back(); - } - - iterator - insert(iterator pos, T p) - requires std::is_nothrow_move_constructible_v - { - auto newSize = size_ + 1; - const auto idx = pos - begin(); - reserve(newSize); - mapForAdd(); - pos = begin() + idx; - std::move_backward(pos, end(), end() + 1); - pos->~T(); - new (&*pos) T {std::move(p)}; - setSize(newSize); - return pos; - } - - void - pop_back() - { - if constexpr (!is_trivial_dest) { - map(GL_READ_WRITE); - back().~T(); - } - --size_; - } - - void - erase(iterator pos) - { - erase(pos, pos + 1); - } - - void - erase(iterator pos, iterator to) - { - const auto eraseSize = to - pos; - map(GL_READ_WRITE); - std::move(to, end(), pos); - if constexpr (!is_trivial_dest) { - std::for_each(end() - eraseSize, end(), [](auto && v) { - v.~T(); - }); - } - setSize(size_ - static_cast(eraseSize)); - } - -protected: - size_type - setSize(size_type s) - { - return std::exchange(size_, s); - } - - void - allocBuffer(size_type newCapacity) - { - if (newCapacity == 0) { - return allocBuffer(1); - } - glBindBuffer(target_, buffer_); - glBufferData(target_, static_cast(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW); - glBindBuffer(target_, 0); - capacity_ = newCapacity; - data_ = {}; - access_ = {}; - } - - void - map(GLenum access) const - { - if (size_ > 0) { - mapMode(access); - } - } - - void - mapForAdd() const - { - if (!data_) { - mapMode(GL_READ_WRITE); - } - } - - void - mapMode(GLenum access) const - { - if (data_ && access_ < access) { - unmap(); - } - if (!data_) { - glBindBuffer(target_, buffer_); - data_ = static_cast(glMapBuffer(target_, access)); - glBindBuffer(target_, 0); - assert(data_); - access_ = access; - } - } - - span - mkspan() const - { - assert(!size_ || data_); - return span {data_, size_}; - } - - const_span - mkcspan() const - { - assert(!size_ || data_); - return const_span {data_, size_}; - } - - glBuffer buffer_; - GLenum target_; - std::size_t capacity_ {}; - std::size_t size_ {}; - mutable T * data_; - mutable GLenum access_ {}; -}; diff --git a/test/Jamfile.jam b/test/Jamfile.jam index a96ca2a..da2a61a 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -61,9 +61,9 @@ run test-lib.cpp ; run [ glob test-geoData*.cpp ] : -- : [ sequence.insertion-sort [ glob-tree $(fixtures)/geoData : *.json ] fixtures/height/SD19.asc ] : test ; run test-network.cpp : : : test ; run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob-tree $(fixtures)/json : *.json ] ] : test ; -run test-text.cpp : -- : test-glContainer : test ; +run test-text.cpp : -- : test-glAllocator : test ; run test-enumDetails.cpp ; -run test-render.cpp : -- : test-assetFactory : test ; +run test-render.cpp : -- : : test ; run test-glContextBhvr.cpp ; run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt test-instancing ] : test ; perfrun perf-assetFactory.cpp : : test-assetFactory ; @@ -71,9 +71,9 @@ perfrun perf-geoData.cpp : : test-geoData ; perfrun perf-terrain.cpp : : test-geoData ; perfrun perf-persistence.cpp : : test-persistence ; run test-worker.cpp ; -run test-instancing.cpp : -- : test-glContainer : test ; +run test-instancing.cpp : -- : test-glAllocator : test ; perfrun perf-instancing.cpp : : test-instancing ; -run test-glContainer.cpp : : : test ; +run test-glAllocator.cpp : : : test ; run test-pack.cpp : : : test ; run test-environment.cpp : : : test ; run test-ui.cpp : : : test ; diff --git a/test/test-glAllocator.cpp b/test/test-glAllocator.cpp new file mode 100644 index 0000000..96457a2 --- /dev/null +++ b/test/test-glAllocator.cpp @@ -0,0 +1,28 @@ +#define BOOST_TEST_MODULE glAllocator + +#include "testMainWindow.h" +#include +#include + +#include "glAllocator.h" + +BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); + +namespace { + BOOST_AUTO_TEST_CASE(Simple) + { + GLuint name = 0; + { + glVector list; + BOOST_REQUIRE_EQUAL(list.get_allocator().getNameFor(list), 0); + list.reserve(5); + name = list.get_allocator().getNameFor(list); + BOOST_REQUIRE_GT(name, 0); + std::ranges::copy(std::views::iota(0, 10), std::back_inserter(list)); + BOOST_REQUIRE_EQUAL(10, list.size()); + BOOST_CHECK_EQUAL(0, list.front()); + BOOST_CHECK_EQUAL(9, list.back()); + } + BOOST_CHECK(!glIsBuffer(name)); + } +} diff --git a/test/test-glContainer.cpp b/test/test-glContainer.cpp deleted file mode 100644 index 7f82e13..0000000 --- a/test/test-glContainer.cpp +++ /dev/null @@ -1,453 +0,0 @@ -#define BOOST_TEST_MODULE glContainer - -#include "testMainWindow.h" -#include -#include - -#include "glContainer.h" - -// Force generation of all functions -template class glContainer; - -BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::iterator); -BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::const_iterator); -BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::reverse_iterator); -BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer::const_reverse_iterator); - -BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); - -BOOST_FIXTURE_TEST_SUITE(i, glContainer) - -BOOST_AUTO_TEST_CASE(CreateDestroy, *boost::unit_test::timeout(1)) -{ - // Unmapped - BOOST_CHECK(!data_); - BOOST_CHECK(!access_); - // Request map, but empty - BOOST_CHECK_NO_THROW(map(GL_READ_ONLY)); - BOOST_REQUIRE(!data_); - BOOST_REQUIRE(!access_); - BOOST_CHECK_NO_THROW(map(GL_READ_WRITE)); - BOOST_REQUIRE(!data_); - BOOST_REQUIRE(!access_); - // Add something - BOOST_CHECK_NO_THROW(emplace_back(0)); - BOOST_REQUIRE(data_); - BOOST_REQUIRE_EQUAL(access_, GL_READ_WRITE); - // Unmap - BOOST_CHECK_NO_THROW(unmap()); - BOOST_REQUIRE(!data_); - BOOST_REQUIRE(!access_); - // Map RO - BOOST_CHECK_NO_THROW(map(GL_READ_ONLY)); - BOOST_REQUIRE(data_); - BOOST_REQUIRE_EQUAL(access_, GL_READ_ONLY); - // Map RW upgradde - BOOST_CHECK_NO_THROW(map(GL_READ_WRITE)); - BOOST_REQUIRE(data_); - BOOST_REQUIRE_EQUAL(access_, GL_READ_WRITE); - // Map RO downgradde, no change - BOOST_CHECK_NO_THROW(map(GL_READ_ONLY)); - BOOST_REQUIRE(data_); - BOOST_REQUIRE_EQUAL(access_, GL_READ_WRITE); - // Unmap - BOOST_CHECK_NO_THROW(unmap()); - BOOST_CHECK(!data_); - BOOST_CHECK(!access_); -} - -BOOST_AUTO_TEST_CASE(MapModes) -{ - BOOST_CHECK_EQUAL(std::accumulate(begin(), end(), 0), 0); - BOOST_CHECK(!data_); - BOOST_CHECK(!access_); - - BOOST_CHECK_NO_THROW(push_back(1)); - BOOST_CHECK_NO_THROW(push_back(2)); - BOOST_CHECK_NO_THROW(push_back(3)); - BOOST_CHECK_NO_THROW(push_back(4)); - BOOST_CHECK_NO_THROW(unmap()); - - BOOST_CHECK_EQUAL(std::accumulate(cbegin(), cend(), 0), 10); - BOOST_CHECK(data_); - BOOST_CHECK_EQUAL(access_, GL_READ_ONLY); - BOOST_CHECK_NO_THROW(unmap()); - - BOOST_CHECK_EQUAL(std::accumulate(begin(), end(), 0), 10); - BOOST_CHECK(data_); - BOOST_CHECK_EQUAL(access_, GL_READ_WRITE); - BOOST_CHECK_NO_THROW(unmap()); - - BOOST_CHECK_EQUAL(std::ranges::fold_left(std::as_const(*this), 0, std::plus {}), 10); - BOOST_CHECK(data_); - BOOST_CHECK_EQUAL(access_, GL_READ_ONLY); - BOOST_CHECK_NO_THROW(unmap()); -} - -BOOST_AUTO_TEST_CASE(PushBackTest, *boost::unit_test::timeout(1)) -{ - BOOST_CHECK_EQUAL(capacity_, 1); - BOOST_CHECK_EQUAL(size_, 0); - BOOST_CHECK_NO_THROW(push_back(1)); - BOOST_CHECK_NO_THROW(push_back(2)); - BOOST_CHECK_NO_THROW(push_back(3)); - BOOST_CHECK_NO_THROW(push_back(4)); - BOOST_CHECK_NO_THROW(push_back(5)); - BOOST_CHECK_EQUAL(capacity_, 8); - BOOST_CHECK_EQUAL(size_, 5); - { - std::array expected1 {1, 2, 3, 4, 5}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected1.rbegin(), expected1.rend()); - } -} - -BOOST_AUTO_TEST_CASE(EmplaceBackTest, *boost::unit_test::timeout(1)) -{ - BOOST_CHECK_EQUAL(capacity_, 1); - BOOST_CHECK_EQUAL(size_, 0); - - BOOST_CHECK_NO_THROW(emplace_back(1)); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_EQUAL(capacity_, 2); - BOOST_CHECK_EQUAL(size_, 2); - - BOOST_CHECK_NO_THROW(reserve(5)); - BOOST_CHECK_EQUAL(capacity_, 5); - BOOST_CHECK_EQUAL(size_, 2); - - BOOST_CHECK_NO_THROW(emplace_back(3)); - BOOST_CHECK_NO_THROW(emplace_back(4)); - BOOST_CHECK_EQUAL(capacity_, 5); - BOOST_CHECK_EQUAL(size_, 4); - - { - std::array expected1 {1, 2, 3, 4}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected1.rbegin(), expected1.rend()); - } - - BOOST_CHECK_NO_THROW(emplace_back(5)); - BOOST_CHECK_EQUAL(capacity_, 5); - BOOST_CHECK_EQUAL(size_, 5); - BOOST_CHECK_NO_THROW(emplace_back(6)); - BOOST_CHECK_NO_THROW(emplace_back(7)); - BOOST_CHECK_EQUAL(capacity_, 10); - BOOST_CHECK_EQUAL(size_, 7); - - { - std::array expected2 {1, 2, 3, 4, 5, 6, 7}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected2.begin(), expected2.end()); - BOOST_CHECK_EQUAL_COLLECTIONS(rbegin(), rend(), expected2.rbegin(), expected2.rend()); - } - - BOOST_CHECK_EQUAL(7, end() - begin()); - BOOST_CHECK_EQUAL(7, rend() - rbegin()); -} - -BOOST_AUTO_TEST_CASE(ResizeTest) -{ - BOOST_CHECK_NO_THROW(push_back(1)); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_NO_THROW(resize(4)); - BOOST_CHECK_EQUAL(capacity_, 4); - BOOST_CHECK_EQUAL(size_, 4); - { - std::array expected1 {1, 2, 0, 0}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - - BOOST_CHECK_NO_THROW(resize(1)); - BOOST_CHECK_EQUAL(capacity_, 1); - BOOST_CHECK_EQUAL(size_, 1); - { - std::array expected2 {1}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected2.begin(), expected2.end()); - } - - BOOST_CHECK_NO_THROW(resize(1)); - BOOST_CHECK_EQUAL(capacity_, 1); - BOOST_CHECK_EQUAL(size_, 1); - - BOOST_CHECK_NO_THROW(resize(0)); - BOOST_CHECK_EQUAL(capacity_, 1); - BOOST_CHECK_EQUAL(size_, 0); - BOOST_CHECK_EQUAL(begin(), end()); - BOOST_CHECK_EQUAL(rbegin(), rend()); -} - -BOOST_AUTO_TEST_CASE(ShrinkToFitTest) -{ - BOOST_CHECK_NO_THROW(reserve(4)); - BOOST_CHECK_NO_THROW(emplace_back(1)); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_EQUAL(capacity_, 4); - BOOST_CHECK_EQUAL(size_, 2); - { - std::array expected1 {1, 2}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - BOOST_CHECK_NO_THROW(shrink_to_fit()); - BOOST_CHECK_EQUAL(capacity_, 2); - BOOST_CHECK_EQUAL(size_, 2); - { - std::array expected1 {1, 2}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - - BOOST_CHECK_NO_THROW(shrink_to_fit()); - BOOST_CHECK_EQUAL(capacity(), 2); - BOOST_CHECK_EQUAL(size(), 2); - - BOOST_CHECK_NO_THROW(clear()); - BOOST_CHECK_EQUAL(capacity(), 2); - BOOST_CHECK_EQUAL(size(), 0); -} - -BOOST_AUTO_TEST_CASE(Getters) -{ - BOOST_CHECK(empty()); - BOOST_CHECK_NO_THROW(emplace_back(1)); - BOOST_CHECK(!empty()); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_EQUAL(1, front()); - BOOST_CHECK_EQUAL(2, back()); - BOOST_CHECK_EQUAL(1, at(0)); - BOOST_CHECK_EQUAL(2, at(1)); - BOOST_CHECK_EQUAL(1, (*this)[0]); - BOOST_CHECK_EQUAL(2, (*this)[1]); - BOOST_CHECK_EQUAL(data_, data()); - BOOST_CHECK_THROW(std::ignore = at(2), std::out_of_range); - - const auto & constCont {*this}; - BOOST_CHECK_EQUAL(1, constCont.front()); - BOOST_CHECK_EQUAL(2, constCont.back()); - { - std::array expected1 {1, 2}; - BOOST_CHECK_EQUAL_COLLECTIONS(constCont.begin(), constCont.end(), expected1.begin(), expected1.end()); - BOOST_CHECK_EQUAL_COLLECTIONS(constCont.rbegin(), constCont.rend(), expected1.rbegin(), expected1.rend()); - BOOST_CHECK_EQUAL_COLLECTIONS(constCont.cbegin(), constCont.cend(), expected1.cbegin(), expected1.cend()); - BOOST_CHECK_EQUAL_COLLECTIONS(constCont.crbegin(), constCont.crend(), expected1.crbegin(), expected1.crend()); - } - BOOST_CHECK_EQUAL(1, constCont.at(0)); - BOOST_CHECK_EQUAL(2, constCont.at(1)); - BOOST_CHECK_EQUAL(1, constCont[0]); - BOOST_CHECK_EQUAL(2, constCont[1]); - BOOST_CHECK_EQUAL(data_, constCont.data()); - BOOST_CHECK_THROW(std::ignore = constCont.at(2), std::out_of_range); -} - -BOOST_AUTO_TEST_CASE(RandomAccess) -{ - BOOST_CHECK_NO_THROW(emplace_back(1)); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_NO_THROW(emplace_back(3)); - - auto iterator = begin(); - BOOST_CHECK_EQUAL(1, *iterator); - BOOST_CHECK_EQUAL(2, *++iterator); - BOOST_CHECK_EQUAL(2, *iterator++); - BOOST_CHECK_EQUAL(3, *iterator); - BOOST_CHECK_EQUAL(3, *iterator--); - BOOST_CHECK_EQUAL(2, *iterator); - BOOST_CHECK_EQUAL(1, *--iterator); - BOOST_CHECK_EQUAL(1, *iterator); -} - -BOOST_AUTO_TEST_CASE(RandomWrite) -{ - BOOST_CHECK_NO_THROW(resize(3)); - BOOST_CHECK_EQUAL(size(), 3); - BOOST_CHECK_NO_THROW(unmap()); - BOOST_REQUIRE(!data_); - BOOST_CHECK_NO_THROW(at(0, 10)); - BOOST_CHECK_NO_THROW(at(1, 20)); - BOOST_CHECK_NO_THROW(at(2, 30)); - BOOST_CHECK(!data_); - { - std::array expected1 {10, 20, 30}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - BOOST_CHECK(data_); - BOOST_CHECK_NO_THROW(at(1, 40)); - { - std::array expected1 {10, 40, 30}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - BOOST_CHECK_THROW(at(4, 0), std::out_of_range); -} - -BOOST_AUTO_TEST_CASE(InsertRemoveTest) -{ - BOOST_CHECK_NO_THROW(emplace_back(1)); - BOOST_CHECK_NO_THROW(emplace_back(2)); - BOOST_CHECK_NO_THROW(emplace_back(3)); - BOOST_CHECK_NO_THROW(emplace_back(4)); - BOOST_CHECK_NO_THROW(pop_back()); - BOOST_CHECK_EQUAL(size_, 3); - BOOST_CHECK_EQUAL(capacity_, 4); - - BOOST_CHECK_NO_THROW(emplace(begin(), 5)); - BOOST_CHECK_EQUAL(size_, 4); - BOOST_CHECK_EQUAL(capacity_, 4); - { - std::array expected1 {5, 1, 2, 3}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - - { - std::array expected1 {2, 3}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin() + 2, end(), expected1.begin(), expected1.end()); - } - BOOST_CHECK_NO_THROW(insert(begin() + 2, 6)); - BOOST_CHECK_EQUAL(size_, 5); - BOOST_CHECK_EQUAL(capacity_, 8); - { - std::array expected1 {5, 1, 6, 2, 3}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - erase(begin() + 1); - BOOST_CHECK_EQUAL(size_, 4); - BOOST_CHECK_EQUAL(capacity_, 8); - { - std::array expected1 {5, 6, 2, 3}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - erase(begin() + 1, end() - 1); - BOOST_CHECK_EQUAL(size_, 2); - BOOST_CHECK_EQUAL(capacity_, 8); - { - std::array expected1 {5, 3}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } -} - -BOOST_AUTO_TEST_CASE(StlCompatilibty) -{ - BOOST_CHECK_NO_THROW(resize(10)); - BOOST_CHECK_NO_THROW(std::generate(begin(), end(), [value = 0]() mutable { - return value++; - })); - BOOST_CHECK_NO_THROW(std::sort(rbegin(), rend())); - { - std::array expected1 {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), end(), expected1.begin(), expected1.end()); - } - const auto newend = std::remove_if(begin(), end(), [](const auto & value) { - return value % 2 == 0; - }); - { - std::array expected1 {9, 7, 5, 3, 1}; - BOOST_CHECK_EQUAL_COLLECTIONS(begin(), newend, expected1.begin(), expected1.end()); - } -} - -BOOST_AUTO_TEST_CASE(IterCompare) -{ - BOOST_CHECK_EQUAL(begin(), end()); - BOOST_CHECK_EQUAL(rbegin(), rend()); - emplace_back(); - BOOST_CHECK_LT(begin(), end()); - BOOST_CHECK_LT(rbegin(), rend()); - BOOST_CHECK_LT(cbegin(), cend()); - BOOST_CHECK_LT(crbegin(), crend()); -} - -BOOST_AUTO_TEST_SUITE_END(); - -BOOST_AUTO_TEST_CASE(CreateCopySource, *boost::unit_test::timeout(1)) -{ - const std::vector src {4, 6, 2, 4, 6, 0}; - glContainer dst {src}; - static_assert(std::is_same_v); - dst.unmap(); - BOOST_CHECK_EQUAL_COLLECTIONS(src.begin(), src.end(), dst.begin(), dst.end()); -} - -namespace { - struct C { - int x; - float y; - }; - - static_assert(std::is_trivially_destructible_v); - - struct CC { - CC() = default; - - CC(int intValue, float floatValue) noexcept : x {intValue}, y {floatValue} { } - - ~CC() - { - ++x; - } - - DEFAULT_MOVE_COPY(CC); - - int x; - float y; - }; - - static_assert(!std::is_trivially_destructible_v); -} - -BOOST_FIXTURE_TEST_SUITE(c, glContainer) - -BOOST_AUTO_TEST_CASE(Basic) -{ - BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); - BOOST_CHECK_EQUAL(1, begin()->x); - BOOST_CHECK_EQUAL(2.F, begin()->y); - BOOST_CHECK_NO_THROW(begin()->x = 3); - BOOST_CHECK_EQUAL(3, begin()->x); - - BOOST_CHECK_NO_THROW(push_back(C {4, 5.F})); - BOOST_CHECK_EQUAL(3, begin()->x); - BOOST_CHECK_EQUAL(2.F, begin()->y); - BOOST_CHECK_EQUAL(4, rbegin()->x); - BOOST_CHECK_EQUAL(5.F, rbegin()->y); -} - -BOOST_AUTO_TEST_SUITE_END(); - -BOOST_FIXTURE_TEST_SUITE(cc, glContainer) - -BOOST_AUTO_TEST_CASE(Basic) -{ - BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); - BOOST_CHECK_EQUAL(1, begin()->x); - BOOST_CHECK_EQUAL(2.F, begin()->y); - BOOST_CHECK_NO_THROW(begin()->x = 3); - BOOST_CHECK_EQUAL(3, begin()->x); - - BOOST_CHECK_NO_THROW(push_back(CC {4, 5.F})); - BOOST_CHECK_EQUAL(3, begin()->x); - BOOST_CHECK_EQUAL(2.F, begin()->y); - BOOST_CHECK_EQUAL(4, rbegin()->x); - BOOST_CHECK_EQUAL(5.F, rbegin()->y); - BOOST_CHECK_NO_THROW(pop_back()); - BOOST_CHECK_EQUAL(size(), 1); - BOOST_CHECK_EQUAL(capacity(), 2); - BOOST_CHECK_NO_THROW(resize(3)); - BOOST_CHECK_EQUAL(size(), 3); - BOOST_CHECK_EQUAL(capacity(), 3); - BOOST_CHECK_NO_THROW(resize(1)); - BOOST_CHECK_EQUAL(size(), 1); - BOOST_CHECK_EQUAL(capacity(), 1); -} - -BOOST_AUTO_TEST_CASE(InsertRemoveTest) -{ - BOOST_CHECK_NO_THROW(emplace_back(1, 2.F)); - BOOST_CHECK_NO_THROW(emplace_back(3, 4.F)); - BOOST_CHECK_NO_THROW(emplace(begin(), 5, 6.F)); - BOOST_CHECK_NO_THROW(emplace(begin() + 1, 7, 8.F)); - BOOST_CHECK_NO_THROW(emplace(begin() + 2, 9, 10.F)); - BOOST_CHECK_EQUAL(capacity(), 8); - BOOST_CHECK_EQUAL(size(), 5); - BOOST_CHECK_NO_THROW(shrink_to_fit()); - BOOST_CHECK_EQUAL(capacity(), 5); - BOOST_CHECK_EQUAL(size(), 5); -} - -BOOST_AUTO_TEST_SUITE_END(); diff --git a/test/test-instancing.cpp b/test/test-instancing.cpp index 4748f93..399a84c 100644 --- a/test/test-instancing.cpp +++ b/test/test-instancing.cpp @@ -77,19 +77,6 @@ BOOST_AUTO_TEST_CASE(AcquireReleaseMove) BOOST_CHECK(reverseIndex.empty()); } -BOOST_AUTO_TEST_CASE(AutoMapUnmap) -{ - { - auto proxy = acquire(); - BOOST_CHECK(data_); - std::ignore = bufferName(); - BOOST_CHECK(data_); - BOOST_CHECK_EQUAL(1, size()); - BOOST_CHECK(!data_); - } - BOOST_CHECK_EQUAL(0, size()); -} - BOOST_AUTO_TEST_CASE(Initialize) { auto proxy = acquire(5); @@ -246,7 +233,7 @@ BOOST_AUTO_TEST_CASE(PartitionBy, *boost::unit_test::timeout(1)) }; auto matchedEnd = partition(pred); // The underlying data is partitioned... - BOOST_REQUIRE(std::is_partitioned(mkcspan().cbegin(), mkcspan().cend(), pred)); + BOOST_REQUIRE(std::is_partitioned(cbegin(), cend(), pred)); // The external view of the data is unchanged... BOOST_CHECK_EQUAL_COLLECTIONS(values.cbegin(), values.cend(), instances.cbegin(), instances.cend()); // The partition point is right... @@ -281,7 +268,7 @@ BOOST_AUTO_TEST_CASE(PartitionBy2, *boost::unit_test::timeout(1)) auto matchedBoundaries = partition(pred3, pred5); // As PartitionBy... primary partition is normal layout // The underlying data is partitioned... - BOOST_REQUIRE(std::is_partitioned(mkcspan().cbegin(), mkcspan().cend(), pred3)); + BOOST_REQUIRE(std::is_partitioned(cbegin(), cend(), pred3)); // The external view of the data is unchanged... BOOST_CHECK_EQUAL_COLLECTIONS(values.cbegin(), values.cend(), instances.cbegin(), instances.cend()); // The partition point is right... @@ -291,19 +278,17 @@ BOOST_AUTO_TEST_CASE(PartitionBy2, *boost::unit_test::timeout(1)) // Secondary partition lives contiguous in the middle somewhere, with two falsy blocks at each end const auto p2bndry = matchedBoundaries.second; - BOOST_TEST_CONTEXT(mkcspan()) { + BOOST_TEST_CONTEXT(std::span(cbegin(), cend())) { BOOST_TEST_CONTEXT(matchedBoundaries.first) { - BOOST_CHECK(std::all_of( - mkcspan().cbegin(), mkcspan().cbegin() + static_cast(matchedBoundaries.first), pred3)); - BOOST_CHECK(std::none_of( - mkcspan().cbegin() + static_cast(matchedBoundaries.first), mkcspan().cend(), pred3)); + BOOST_CHECK(std::all_of(cbegin(), cbegin() + static_cast(matchedBoundaries.first), pred3)); + BOOST_CHECK(std::none_of(cbegin() + static_cast(matchedBoundaries.first), cend(), pred3)); } BOOST_TEST_CONTEXT(p2bndry) { - BOOST_CHECK(std::all_of(mkcspan().cbegin() + static_cast(p2bndry.first), - mkcspan().begin() + static_cast(p2bndry.second), pred5)); - BOOST_CHECK(std::none_of(mkcspan().cbegin(), mkcspan().cbegin() + static_cast(p2bndry.first), pred5)); - BOOST_CHECK(std::none_of(mkcspan().cbegin() + static_cast(p2bndry.second), mkcspan().cend(), pred5)); + BOOST_CHECK(std::all_of( + cbegin() + static_cast(p2bndry.first), cbegin() + static_cast(p2bndry.second), pred5)); + BOOST_CHECK(std::none_of(cbegin(), cbegin() + static_cast(p2bndry.first), pred5)); + BOOST_CHECK(std::none_of(cbegin() + static_cast(p2bndry.second), cend(), pred5)); } } -- cgit v1.3