summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2026-03-07 11:50:47 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2026-03-07 11:50:47 +0000
commitadb0096046d357a6463ae2ce66c182546c8de9c2 (patch)
tree0e06a281efc2da637ebc19dca38f160e86516d9f
parentUpdate stencils and billboards less often (diff)
parentReplace glContainer with glAllocator (diff)
downloadilt-adb0096046d357a6463ae2ce66c182546c8de9c2.tar.bz2
ilt-adb0096046d357a6463ae2ce66c182546c8de9c2.tar.xz
ilt-adb0096046d357a6463ae2ce66c182546c8de9c2.zip
Merge branch 'glalloc'
-rw-r--r--game/network/network.h1
-rw-r--r--game/network/rail.cpp12
-rw-r--r--game/scenary/foliage.cpp13
-rw-r--r--game/scenary/illuminator.cpp25
-rw-r--r--game/terrain.cpp18
-rw-r--r--game/vehicles/railVehicleClass.cpp17
-rw-r--r--game/water.cpp6
-rw-r--r--gfx/gl/glBuffer.h27
-rw-r--r--gfx/gl/glVertexArray.h125
-rw-r--r--gfx/gl/instanceVertices.h39
-rw-r--r--gfx/gl/sceneRenderer.cpp3
-rw-r--r--gfx/gl/sceneShader.cpp2
-rw-r--r--gfx/gl/vertexArrayObject.h125
-rw-r--r--gfx/models/mesh.h16
-rw-r--r--gfx/models/vertex.cpp8
-rw-r--r--lib/collections.h6
-rw-r--r--lib/glAllocator.h90
-rw-r--r--lib/glArrays.cpp14
-rw-r--r--lib/glArrays.h4
-rw-r--r--lib/glContainer.h496
-rw-r--r--lib/gl_traits.h84
-rw-r--r--test/Jamfile.jam11
-rw-r--r--test/test-glAllocator.cpp28
-rw-r--r--test/test-glContainer.cpp453
-rw-r--r--test/test-instancing.cpp33
-rw-r--r--test/testMainWindow.cpp4
26 files changed, 435 insertions, 1225 deletions
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/game/network/rail.cpp b/game/network/rail.cpp
index f265d6b..545620d 100644
--- a/game/network/rail.cpp
+++ b/game/network/rail.cpp
@@ -3,7 +3,6 @@
#include "network.h"
#include <game/network/network.impl.h> // IWYU pragma: keep
#include <gfx/gl/sceneShader.h>
-#include <gfx/gl/vertexArrayObject.h>
#include <gfx/models/texture.h>
template class NetworkOf<RailLink, RailLinkStraight, RailLinkCurve>;
@@ -139,18 +138,17 @@ RailLink::vehiclePositionOffset() const
template<> NetworkLinkHolder<RailLinkStraight>::NetworkLinkHolder()
{
- VertexArrayObject {vao}
+ vao.configure()
.addAttribs<RailLinkStraight::Vertex, &RailLinkStraight::Vertex::a, &RailLinkStraight::Vertex::b,
- &RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(
- vertices.bufferName());
+ &RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(0);
}
template<> NetworkLinkHolder<RailLinkCurve>::NetworkLinkHolder()
{
- VertexArrayObject {vao}
+ vao.configure()
.addAttribs<RailLinkCurve::Vertex, &RailLinkCurve::Vertex::a, &RailLinkCurve::Vertex::b,
&RailLinkCurve::Vertex::c, &RailLinkCurve::Vertex::textureRepeats, &RailLinkCurve::Vertex::aangle,
- &RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(vertices.bufferName());
+ &RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(0);
}
namespace {
@@ -161,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<GLsizei>(count));
}
};
diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp
index 0981ffc..cf2d82e 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 <location.h>
static_assert(std::is_constructible_v<Foliage>);
@@ -38,11 +37,9 @@ void
Foliage::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::rotation, &LocationVertex::position>(
- instances.bufferName(), 1);
- VertexArrayObject {instancePointVAO}.addAttribs<LocationVertex, &LocationVertex::position, &LocationVertex::yaw>(
- instances.bufferName());
+ bodyMesh->configureVAO(instanceVAO, 0)
+ .addAttribs<LocationVertex, &LocationVertex::rotation, &LocationVertex::position>(1);
+ instancePointVAO.configure().addAttribs<LocationVertex, &LocationVertex::position, &LocationVertex::yaw>(0);
const auto & size = bodyMesh->getDimensions().size;
billboardSize = billboardTextureSizeForObject(size);
@@ -105,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<GLint>(instancePartitions.second.first), static_cast<GLsizei>(count));
glBindVertexArray(0);
}
@@ -114,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<GLsizei>(count));
}
}
@@ -130,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<GLint>(instancePartitions.second.first), static_cast<GLsizei>(count));
glBindVertexArray(0);
}
@@ -141,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<GLsizei>(count));
}
}
diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp
index d8e4c4e..7ab17fe 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 <location.h>
@@ -41,15 +40,14 @@ Illuminator::postLoad()
throw std::logic_error {"Illuminator has no lights"};
}
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
+ bodyMesh->configureVAO(instanceVAO, 0)
+ .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(1);
if (!spotLight.empty()) {
instancesSpotLightVAO.emplace();
- VertexArrayObject {*instancesSpotLightVAO}
+ instancesSpotLightVAO->configure()
.addAttribs<SpotLightVertex, &SpotLightVertex::position, &SpotLightVertex::direction,
- &SpotLightVertex::colour, &SpotLightVertex::kq, &SpotLightVertex::arc>(
- instancesSpotLight.bufferName(), 0)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
+ &SpotLightVertex::colour, &SpotLightVertex::kq, &SpotLightVertex::arc>(0)
+ .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(1);
std::transform(
spotLight.begin(), spotLight.end(), std::back_inserter(spotLightInstances), [this](const auto & s) {
return instancesSpotLight.acquire(*s);
@@ -57,10 +55,10 @@ Illuminator::postLoad()
}
if (!pointLight.empty()) {
instancesPointLightVAO.emplace();
- VertexArrayObject {*instancesPointLightVAO}
+ instancesPointLightVAO->configure()
.addAttribs<PointLightVertex, &PointLightVertex::position, &PointLightVertex::colour,
- &PointLightVertex::kq>(instancesPointLight.bufferName(), 0)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
+ &PointLightVertex::kq>(0)
+ .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(1);
std::transform(
pointLight.begin(), pointLight.end(), std::back_inserter(pointLightInstances), [this](const auto & s) {
return instancesPointLight.acquire(*s);
@@ -76,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<GLsizei>(count));
}
}
@@ -87,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<GLsizei>(scount), static_cast<GLsizei>(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<GLsizei>(pcount), static_cast<GLsizei>(count));
}
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<Terrain::Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+Impl::VertexArrayConfigurator &
+Impl::VertexArrayConfigurator::addAttribsFor<Terrain::Vertex>(const GLuint divisor, const glBuffer & buffer)
{
- return addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal>(arrayBuffer, divisor);
+ return addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal>(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<Vertex>(verticesBuffer)
- .addIndices(meshItr->second.indicesBuffer, indices)
- .data(verticesBuffer, GL_ARRAY_BUFFER);
+ meshItr->second.vertexArray.configure()
+ .addAttribsFor<Vertex>(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<GLsizei>(indices.size());
meshItr->second.aabb = AxisAlignedBoundingBox<GlobalDistance>::fromPoints(
indices | std::views::transform([this](const auto vertex) {
diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp
index 21f01c8..100073d 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 <array>
#include <glm/glm.hpp>
#include <lib/resource.h>
@@ -34,14 +33,14 @@ void
RailVehicleClass::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::body, &LocationVertex::bodyPos>(instances.bufferName(), 1);
+ bodyMesh->configureVAO(instanceVAO, 0)
+ .addAttribs<LocationVertex, &LocationVertex::body, &LocationVertex::bodyPos>(1);
bogies.front()
- ->configureVAO(instancesBogiesVAO.front())
- .addAttribs<LocationVertex, &LocationVertex::front, &LocationVertex::frontPos>(instances.bufferName(), 1);
+ ->configureVAO(instancesBogiesVAO.front(), 0)
+ .addAttribs<LocationVertex, &LocationVertex::front, &LocationVertex::frontPos>(1);
bogies.back()
- ->configureVAO(instancesBogiesVAO.back())
- .addAttribs<LocationVertex, &LocationVertex::back, &LocationVertex::backPos>(instances.bufferName(), 1);
+ ->configureVAO(instancesBogiesVAO.back(), 0)
+ .addAttribs<LocationVertex, &LocationVertex::back, &LocationVertex::backPos>(1);
static_assert(sizeof(LocationVertex) == 144UL);
}
@@ -53,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/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<Water::Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+Impl::VertexArrayConfigurator &
+Impl::VertexArrayConfigurator::addAttribsFor<Water::Vertex>(const GLuint divisor, const glBuffer & buffer)
{
- return addAttribs<Water::Vertex, &Water::Vertex::pos>(arrayBuffer, divisor);
+ return addAttribs<Water::Vertex, &Water::Vertex::pos>(divisor, buffer);
}
Water::Water(std::shared_ptr<GeoData> tm) : geoData {std::move(tm)}, water {std::make_shared<Texture>("water.png")}
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<GLsizeiptr>(data.size() * sizeof(decltype(*data.data()))), data.data(), flags);
+ }
+
+ void
+ data(const std::ranges::contiguous_range auto & data, GLenum flags)
+ {
+ glNamedBufferData(
+ name, static_cast<GLsizeiptr>(data.size() * sizeof(decltype(*data.data()))), data.data(), flags);
+ }
+ };
+}
+
+// NOLINTBEGIN(readability-identifier-naming)
+template<size_t N> using glBuffers = glManagedArray<Impl::glBuffer, N, &glCreateBuffers, &glDeleteBuffers>;
+using glBuffer = glManagedSingle<Impl::glBuffer, &glCreateBuffers, &glDeleteBuffers>;
+// NOLINTEND(readability-identifier-naming)
diff --git a/gfx/gl/glVertexArray.h b/gfx/gl/glVertexArray.h
new file mode 100644
index 0000000..3594cbf
--- /dev/null
+++ b/gfx/gl/glVertexArray.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include "collections.h"
+#include "glArrays.h"
+#include "glBuffer.h"
+#include "gl_traits.h"
+
+namespace Impl {
+ class VertexArrayConfigurator {
+ public:
+ template<typename M, typename T> struct MP {
+ constexpr MP(M T::* ptr) : ptr {ptr} { }
+
+ operator GLuint() const
+ {
+ return static_cast<GLuint>(reinterpret_cast<const char *>(&(static_cast<T *>(nullptr)->*ptr))
+ - static_cast<const char *>(nullptr));
+ }
+
+ M T::* ptr;
+ using ValueType = M;
+ };
+
+ template<typename M, typename T> MP(M T::*) -> MP<M, T>;
+
+ explicit VertexArrayConfigurator(GLuint name) : name {name} { }
+
+ VertexArrayConfigurator &
+ addIndices(const glBuffer & buffer)
+ {
+ glVertexArrayElementBuffer(name, buffer);
+ return *this;
+ }
+
+ VertexArrayConfigurator &
+ addIndices(glBuffer & buffer, const SequentialCollection<GLuint> auto & indices)
+ {
+ buffer.storage(indices, 0);
+ return addIndices(buffer);
+ }
+
+ // Customisation point
+ template<typename VertexT> VertexArrayConfigurator & addAttribsFor(GLuint divisor, const glBuffer & buffer);
+
+ template<typename VertexT, MP... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor)
+ {
+ configureAttribs<VertexT, Attribs...>(divisor);
+ return *this;
+ }
+
+ template<typename VertexT, MP... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor, const glBuffer & buffer)
+ {
+ glVertexArrayVertexBuffer(name, binding, buffer, 0, sizeof(VertexT));
+ return addAttribs<VertexT, Attribs...>(divisor);
+ }
+
+ template<typename VertexT, MP... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor, glBuffer & buffer, const SequentialCollection<VertexT> auto & data)
+ {
+ buffer.storage(data, 0);
+ return addAttribs<VertexT, Attribs...>(divisor, buffer);
+ }
+
+ private:
+ void
+ setPointerMeta(const GLuint usedAttribs)
+ {
+ while (attrib < usedAttribs) {
+ glEnableVertexArrayAttrib(name, attrib);
+ glVertexArrayAttribBinding(name, attrib++, binding);
+ }
+ }
+
+ template<typename T>
+ void
+ setPointer(const GLuint offset)
+ {
+ setPointerMeta(attrib + gl_traits<T>::vertexArrayAttribFormat(name, attrib, offset));
+ }
+
+ template<MP Attrib>
+ void
+ setPointer()
+ {
+ setPointer<typename decltype(Attrib)::ValueType>(Attrib);
+ }
+
+ template<typename VertexT, MP... Attribs>
+ void
+ configureAttribs(const GLuint divisor)
+ {
+ if constexpr (sizeof...(Attribs) == 0) {
+ setPointer<VertexT>(0);
+ }
+ else {
+ ((setPointer<Attribs>()), ...);
+ }
+ glVertexArrayBindingDivisor(name, binding++, divisor);
+ }
+
+ GLuint name;
+ GLuint binding = 0;
+ GLuint attrib = 0;
+ };
+
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ struct glVertexArray : Detail::glNamed {
+ VertexArrayConfigurator
+ configure()
+ {
+ return VertexArrayConfigurator {name};
+ }
+ };
+}
+
+// NOLINTBEGIN(readability-identifier-naming)
+template<size_t N>
+using glVertexArrays = glManagedArray<Impl::glVertexArray, N, &glCreateVertexArrays, &glDeleteVertexArrays>;
+using glVertexArray = glManagedSingle<Impl::glVertexArray, &glCreateVertexArrays, &glDeleteVertexArrays>;
+// NOLINTEND(readability-identifier-naming)
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 <cassert>
#include <functional>
#include <special_members.h>
#include <utility>
-template<typename T> class InstanceVertices : protected glContainer<T> {
- using base = glContainer<T>;
+template<typename T> class InstanceVertices : protected glVector<T> {
+ using base = glVector<T>;
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<const base &>(*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<typename Pred>
base::size_type
partition(Pred pred)
@@ -195,8 +210,8 @@ protected:
}
template<typename Pred>
- glContainer<T>::iterator
- partition(glContainer<T>::iterator first, glContainer<T>::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/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 <gfx/gl/shaders/directionalLight-frag.h>
#include <gfx/gl/shaders/lighting-frag.h>
#include <gfx/gl/shaders/lighting-vert.h>
@@ -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<glm::i8vec4>(displayVBO, displayVAOdata);
+ displayVAO.configure().addAttribs<glm::i8vec4>(0, displayVBO, displayVAOdata);
const auto configuregdata = [this](const auto & data, const std::initializer_list<GLint> 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 <gfx/gl/shaders/spotLight-vert.h>
#include <gfx/gl/shaders/water-frag.h>
#include <gfx/gl/shaders/water-vert.h>
-#include <gfx/gl/vertexArrayObject.h>
+#include <gl_traits.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <location.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 <glad/gl.h>
-
-class VertexArrayObject {
-public:
- template<typename T> [[nodiscard]] VertexArrayObject(const T & arrayObject)
- {
- glBindVertexArray(arrayObject);
- }
-
- ~VertexArrayObject()
- {
- glBindVertexArray(0);
- }
-
- NO_MOVE(VertexArrayObject);
- NO_COPY(VertexArrayObject);
-
- template<typename m, typename T> struct MP {
- constexpr MP(m T::* p) : P {p} { }
-
- constexpr
- operator void *() const
- {
- return &(static_cast<T *>(nullptr)->*P);
- }
-
- m T::* P;
- using value_type = m;
- };
-
- template<typename m, typename T> MP(m T::*) -> MP<m, T>;
-
- template<typename VertexT, MP... attribs>
- VertexArrayObject &
- addAttribs(const GLuint arrayBuffer, const SequentialCollection<VertexT> auto & vertices, const GLuint divisor = 0)
- {
- addAttribs<VertexT, attribs...>(arrayBuffer, divisor);
- data(vertices, arrayBuffer, GL_ARRAY_BUFFER);
- return *this;
- }
-
- template<typename VertexT, MP... attribs>
- VertexArrayObject &
- addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0)
- {
- configure_attribs<VertexT, attribs...>(arrayBuffer, divisor);
- return *this;
- }
-
- // Customisation point
- template<typename VertexT> VertexArrayObject & addAttribsFor(const GLuint arrayBuffer, const GLuint divisor = 0);
-
- template<typename Indices>
- 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<typename Data>
- static void
- data(const Data & data, const GLuint arrayBuffer, GLenum target)
- {
- using Value = typename Data::value_type;
- glBindBuffer(target, arrayBuffer);
- glBufferData(target, static_cast<GLsizeiptr>(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW);
- }
-
-private:
- template<typename VertexT, typename T>
- static auto
- set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor)
- {
- using traits = gl_traits<T>;
- 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<typename VertexT, MP attrib>
- static auto
- set_pointer(const GLuint vertexArrayId, const GLuint divisor)
- {
- return set_pointer<VertexT, typename decltype(attrib)::value_type>(vertexArrayId, attrib, divisor);
- }
-
- template<typename VertexT, MP... attribs>
- void
- configure_attribs(const GLuint arrayBuffer, const GLuint divisor)
- {
- glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
- if constexpr (sizeof...(attribs) == 0) {
- vertexArrayId += set_pointer<VertexT, VertexT>(vertexArrayId, nullptr, divisor);
- }
- else {
- ((vertexArrayId += set_pointer<VertexT, attribs>(vertexArrayId, divisor)), ...);
- }
- }
-
- GLuint vertexArrayId {};
-};
diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h
index 8791aed..e78d27e 100644
--- a/gfx/models/mesh.h
+++ b/gfx/models/mesh.h
@@ -1,8 +1,8 @@
#pragma once
#include "config/types.h"
-#include "gfx/gl/vertexArrayObject.h"
-#include <glArrays.h>
+#include "gfx/gl/glBuffer.h"
+#include <gfx/gl/glVertexArray.h>
#include <glad/gl.h>
#include <ranges>
#include <span>
@@ -53,15 +53,15 @@ public:
return static_cast<RelativePosition3D>(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<V>(m_vertexArrayBuffers[0]).addIndices(m_vertexArrayBuffers[1]);
+ return vao.configure().addAttribsFor<V>(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<Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+Impl::VertexArrayConfigurator &
+Impl::VertexArrayConfigurator::addAttribsFor<Vertex>(const GLuint divisor, const glBuffer & buffer)
{
return addAttribs<Vertex, &Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour, &Vertex::material>(
- arrayBuffer, divisor);
+ divisor, buffer);
}
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 <vector>
template<typename T, typename E>
-concept SequentialCollection = requires(T c) {
- { c.size() } -> std::integral;
- { c.data() } -> std::same_as<const E *>;
-};
+concept SequentialCollection
+ = std::ranges::contiguous_range<T> && std::is_same_v<const E, const typename T::value_type>;
template<typename T>
concept IterableCollection = std::is_same_v<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>;
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 <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>);
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 <type_traits>
-
-// Specialisations (glBuffer is an example of the typedef)
-static_assert(std::is_nothrow_default_constructible_v<glBuffer>);
-static_assert(!std::is_trivially_default_constructible_v<glBuffer>);
-static_assert(std::is_nothrow_destructible_v<glBuffer>);
-static_assert(!std::is_trivially_destructible_v<glBuffer>);
-static_assert(std::is_default_constructible_v<glBuffer>);
-static_assert(!std::is_copy_constructible_v<glBuffer>);
-static_assert(!std::is_copy_assignable_v<glBuffer>);
-static_assert(std::is_nothrow_move_constructible_v<glBuffer>);
-static_assert(std::is_nothrow_move_assignable_v<glBuffer>);
-static_assert(sizeof(glBuffer) == sizeof(GLuint));
diff --git a/lib/glArrays.h b/lib/glArrays.h
index 48bd577..1cf2fbf 100644
--- a/lib/glArrays.h
+++ b/lib/glArrays.h
@@ -119,10 +119,6 @@ struct glManagedArray : public std::array<Named, N> {
};
// NOLINTBEGIN(readability-identifier-naming)
-template<size_t N> using glVertexArrays = glManagedArray<Detail::glNamed, N, &glGenVertexArrays, &glDeleteVertexArrays>;
-using glVertexArray = glManagedSingle<Detail::glNamed, &glGenVertexArrays, &glDeleteVertexArrays>;
-template<size_t N> using glBuffers = glManagedArray<Detail::glNamed, N, &glGenBuffers, &glDeleteBuffers>;
-using glBuffer = glManagedSingle<Detail::glNamed, &glGenBuffers, &glDeleteBuffers>;
template<size_t N> using glFrameBuffers = glManagedArray<Detail::glNamed, N, &glGenFramebuffers, &glDeleteFramebuffers>;
using glFrameBuffer = glManagedSingle<Detail::glNamed, &glGenFramebuffers, &glDeleteFramebuffers>;
template<size_t N>
diff --git a/lib/glContainer.h b/lib/glContainer.h
deleted file mode 100644
index 4119ef5..0000000
--- a/lib/glContainer.h
+++ /dev/null
@@ -1,496 +0,0 @@
-#pragma once
-
-#include "glArrays.h"
-#include <cassert>
-#include <span>
-#include <stdexcept>
-#include <utility>
-#include <vector>
-
-static_assert(GL_READ_ONLY < GL_READ_WRITE);
-static_assert(GL_WRITE_ONLY < GL_READ_WRITE);
-
-template<typename T> class glContainer {
-public:
- using span = std::span<T>;
- using const_span = std::span<const T>;
- 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<T>;
-
- explicit glContainer(GLenum target = GL_ARRAY_BUFFER) : target_ {target}
- {
- allocBuffer(1);
- }
-
- ~glContainer()
- requires(is_trivial_dest)
- = default;
-
- ~glContainer()
- requires(!is_trivial_dest)
- {
- clear();
- }
-
- template<template<typename, typename...> typename C>
- explicit glContainer(const C<T> & 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<GLintptr>(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<T> 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<T>
- {
- if (newSize == size_) {
- return;
- }
-
- if (const auto maintain = std::min(newSize, size_)) {
- std::vector<T> existing;
- const auto maintaind = static_cast<typename decltype(existing)::difference_type>(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<T> 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<typename... P>
- reference_type
- emplace_back(P &&... ps)
- requires std::is_constructible_v<T, P...>
- {
- auto newSize = size_ + 1;
- reserve(newSize);
- mapForAdd();
- new (&*end()) T {std::forward<P>(ps)...};
- setSize(newSize);
- return back();
- }
-
- template<typename... P>
- iterator
- emplace(iterator pos, P &&... ps)
- requires std::is_nothrow_constructible_v<T, P...>
- {
- 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<P>(ps)...};
- setSize(newSize);
- return pos;
- }
-
- reference_type
- push_back(T p)
- requires std::is_move_constructible_v<T>
- {
- 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<T>
- {
- 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<size_type>(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<GLsizeiptr>(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<T *>(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/lib/gl_traits.h b/lib/gl_traits.h
index b3c6909..35ae9e8 100644
--- a/lib/gl_traits.h
+++ b/lib/gl_traits.h
@@ -14,27 +14,33 @@ 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<GLenum type, GLint size>
+ 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 {
- 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<GLenum type, GLint size>
+ 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 {
- 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<GLenum type, GLint size>
+ static GLuint
+ vertexAttribFormatFunc(GLuint vao, GLuint index, GLuint offset)
+ {
+ glVertexArrayAttribIFormat(vao, index, size, type, offset);
+ return 1;
+ }
};
template<> struct gl_traits<glm::f32> : public gl_traits_float {
@@ -44,18 +50,22 @@ template<> struct gl_traits<glm::f32> : 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<type, 1>};
};
template<> struct gl_traits<glm::f64> : public gl_traits_longfloat {
static constexpr GLenum type {GL_DOUBLE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int8> : public gl_traits_integer {
static constexpr GLenum type {GL_BYTE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int16> : public gl_traits_integer {
static constexpr GLenum type {GL_SHORT};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::int32> : public gl_traits_integer {
@@ -64,14 +74,17 @@ template<> struct gl_traits<glm::int32> : 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<type, 1>};
};
template<> struct gl_traits<glm::uint8> : public gl_traits_integer {
static constexpr GLenum type {GL_UNSIGNED_BYTE};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::uint16> : public gl_traits_integer {
static constexpr GLenum type {GL_UNSIGNED_SHORT};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<> struct gl_traits<glm::uint32> : public gl_traits_integer {
@@ -79,35 +92,44 @@ template<> struct gl_traits<glm::uint32> : public gl_traits_integer {
static constexpr auto glUniformFunc {&glUniform1ui};
static constexpr std::array<decltype(&glUniform1uiv), 5> glUniformvFunc {
&glUniform1uiv, &glUniform2uiv, &glUniform3uiv, &glUniform4uiv};
+ static constexpr auto vertexArrayAttribFormat {&vertexAttribFormatFunc<type, 1>};
};
template<typename T, std::size_t S> struct gl_traits<std::array<T, S>> : public gl_traits<T> {
static constexpr GLint size {S * gl_traits<T>::size};
- static constexpr auto vertexAttribFunc {
- [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint {
- const auto base = static_cast<const T *>(pointer);
- for (GLuint e = 0; e < S; e++) {
- glVertexAttribPointer(index + e, gl_traits<T>::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<T>) {
+ return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, S>(vao, index, offset);
+ }
+ else {
+ GLuint used = 0;
+ for (GLuint e = 0; e < S; e++) {
+ used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, 1>(
+ vao, index + e, offset + (e * sizeof(T)));
+ }
+ return used;
+ }
+ }};
};
template<glm::length_t L, typename T, glm::qualifier Q> struct gl_traits<glm::vec<L, T, Q>> : public gl_traits<T> {
static constexpr GLint size {L};
+ static constexpr auto vertexArrayAttribFormat {[](GLuint vao, GLuint index, GLuint offset) {
+ return gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, L>(vao, index, offset);
+ }};
};
template<glm::length_t C, glm::length_t R, typename T, glm::qualifier Q>
struct gl_traits<glm::mat<C, R, T, Q>> : public gl_traits<T> {
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<const T *>(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++) {
+ used += gl_traits<T>::template vertexAttribFormatFunc<gl_traits<T>::type, C>(
+ vao, index + row, offset + (C * row * static_cast<GLuint>(sizeof(T))));
+ }
+ return used;
+ }};
};
template<typename T>
diff --git a/test/Jamfile.jam b/test/Jamfile.jam
index bdaaa45..da2a61a 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
<toolset>tidy:<xcheckxx>hicpp-vararg
<toolset>tidy:<librarydef>boost
;
-lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] ;
+lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] : <library>stdc++exp ;
rule perfrun ( main + : extra-requirements * : runtime-dependency * : command-args * ) {
local name = $(main[0]:S=) ;
local benchmark = $(main[0]:S=.benchmark) ;
@@ -60,9 +61,9 @@ run test-lib.cpp ;
run [ glob test-geoData*.cpp ] : -- : [ sequence.insertion-sort [ glob-tree $(fixtures)/geoData : *.json ] fixtures/height/SD19.asc ] : <library>test ;
run test-network.cpp : : : <library>test ;
run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob-tree $(fixtures)/json : *.json ] ] : <library>test ;
-run test-text.cpp : -- : test-glContainer : <library>test ;
+run test-text.cpp : -- : test-glAllocator : <library>test ;
run test-enumDetails.cpp ;
-run test-render.cpp : -- : test-assetFactory : <library>test ;
+run test-render.cpp : -- : : <library>test ;
run test-glContextBhvr.cpp ;
run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt test-instancing ] : <library>test ;
perfrun perf-assetFactory.cpp : : test-assetFactory ;
@@ -70,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 : <library>test ;
+run test-instancing.cpp : -- : test-glAllocator : <library>test ;
perfrun perf-instancing.cpp : : test-instancing ;
-run test-glContainer.cpp : : : <library>test ;
+run test-glAllocator.cpp : : : <library>test ;
run test-pack.cpp : : : <library>test ;
run test-environment.cpp : : : <library>test ;
run test-ui.cpp : : : <library>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 <boost/test/data/test_case.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include "glAllocator.h"
+
+BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase);
+
+namespace {
+ BOOST_AUTO_TEST_CASE(Simple)
+ {
+ GLuint name = 0;
+ {
+ glVector<long double> 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 <boost/test/data/test_case.hpp>
-#include <boost/test/unit_test.hpp>
-
-#include "glContainer.h"
-
-// Force generation of all functions
-template class glContainer<int>;
-
-BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer<int>::iterator);
-BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer<int>::const_iterator);
-BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer<int>::reverse_iterator);
-BOOST_TEST_DONT_PRINT_LOG_VALUE(glContainer<int>::const_reverse_iterator);
-
-BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase);
-
-BOOST_FIXTURE_TEST_SUITE(i, glContainer<int>)
-
-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<decltype(src)::value_type, decltype(dst)::value_type>);
- 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<C>);
-
- 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<CC>);
-}
-
-BOOST_FIXTURE_TEST_SUITE(c, glContainer<C>)
-
-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<CC>)
-
-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<int>(matchedBoundaries.first), pred3));
- BOOST_CHECK(std::none_of(
- mkcspan().cbegin() + static_cast<int>(matchedBoundaries.first), mkcspan().cend(), pred3));
+ BOOST_CHECK(std::all_of(cbegin(), cbegin() + static_cast<int>(matchedBoundaries.first), pred3));
+ BOOST_CHECK(std::none_of(cbegin() + static_cast<int>(matchedBoundaries.first), cend(), pred3));
}
BOOST_TEST_CONTEXT(p2bndry) {
- BOOST_CHECK(std::all_of(mkcspan().cbegin() + static_cast<int>(p2bndry.first),
- mkcspan().begin() + static_cast<int>(p2bndry.second), pred5));
- BOOST_CHECK(std::none_of(mkcspan().cbegin(), mkcspan().cbegin() + static_cast<int>(p2bndry.first), pred5));
- BOOST_CHECK(std::none_of(mkcspan().cbegin() + static_cast<int>(p2bndry.second), mkcspan().cend(), pred5));
+ BOOST_CHECK(std::all_of(
+ cbegin() + static_cast<int>(p2bndry.first), cbegin() + static_cast<int>(p2bndry.second), pred5));
+ BOOST_CHECK(std::none_of(cbegin(), cbegin() + static_cast<int>(p2bndry.first), pred5));
+ BOOST_CHECK(std::none_of(cbegin() + static_cast<int>(p2bndry.second), cend(), pred5));
}
}
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 <boost/test/framework.hpp>
#include <boost/test/test_tools.hpp>
#include <format>
+#include <stacktrace>
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: