From c403a71564def731f4d3b80d6ff63f08aa3c7ea3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 11 Mar 2026 20:45:05 +0000 Subject: Reuse vertex array objects for common structures with DSA Slashes the number of VAOs required and the amount of setup required. --- assetFactory/assetFactory.cpp | 4 ++++ game/network/rail.cpp | 4 +++- game/scenary/foliage.cpp | 35 +++++++++++++++++++++-------------- game/scenary/foliage.h | 4 ++-- game/scenary/illuminator.cpp | 42 ++++++++++++++++++++++++++---------------- game/scenary/illuminator.h | 5 ++--- game/terrain.cpp | 33 ++++++++++++++------------------- game/terrain.h | 6 ++++-- game/water.cpp | 2 ++ gfx/models/mesh.cpp | 11 ++++++++--- gfx/models/mesh.h | 26 ++++++++++++++++++++------ 11 files changed, 106 insertions(+), 66 deletions(-) diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index b96e527..a8d6036 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -3,6 +3,7 @@ #include "cuboid.h" #include "cylinder.h" #include "filesystem.h" +#include "gfx/gl/gldebug.h" #include "gfx/image.h" #include "gfx/models/texture.h" #include "gfx/renderable.h" @@ -26,6 +27,7 @@ AssetFactory::AssetFactory() : std::shared_ptr AssetFactory::loadXML(const std::filesystem::path & filename) { + glDebugScope _ {0, filename.native()}; filesystem::FileStar file {filename.c_str(), "r"}; return Persistence::SAXParsePersistence {}.loadState>(file); } @@ -33,6 +35,7 @@ AssetFactory::loadXML(const std::filesystem::path & filename) AssetFactory::Assets AssetFactory::loadAll(const std::filesystem::path & root) { + glDebugScope _ {0}; return std::accumulate(std::filesystem::recursive_directory_iterator {root}, std::filesystem::recursive_directory_iterator {}, Assets {}, [](auto out, const auto & path) { if (path.path().extension() == ".xml") { @@ -115,6 +118,7 @@ void AssetFactory::createTexutre() const { if (!textureFragments.empty() && !texture) { + glDebugScope _ {0}; // * layout images std::map> images; std::transform( diff --git a/game/network/rail.cpp b/game/network/rail.cpp index 13d5ef5..e8cc1b6 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -138,6 +138,7 @@ RailLink::vehiclePositionOffset() const template<> NetworkLinkHolder::NetworkLinkHolder() { + glDebugScope _ {0}; vao.configure() .addAttribs(0); @@ -145,6 +146,7 @@ template<> NetworkLinkHolder::NetworkLinkHolder() template<> NetworkLinkHolder::NetworkLinkHolder() { + glDebugScope _ {0}; vao.configure() .addAttribsbind(0); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1, 0); diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index b5b409e..ff00af4 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -20,6 +20,8 @@ namespace { } } +std::weak_ptr Foliage::commonInstanceVAO, Foliage::commonInstancePointVAO; + std::any Foliage::createAt(const Location & position) const { @@ -38,10 +40,15 @@ Foliage::postLoad() { texture = getTexture(); glDebugScope _ {0}; - bodyMesh->configureVAO(instanceVAO, 0) - .addAttribs(1); - instancePointVAO.configure().addAttribs(0); - + if (!(instanceVAO = commonInstanceVAO.lock())) { + commonInstanceVAO = instanceVAO = std::make_shared(); + bodyMesh->configureVAO(*instanceVAO, 0) + .addAttribs(1); + } + if (!(instancePointVAO = commonInstancePointVAO.lock())) { + commonInstancePointVAO = instancePointVAO = std::make_shared(); + instancePointVAO->configure().addAttribs(0); + } const auto & size = bodyMesh->getDimensions().size; billboardSize = billboardTextureSizeForObject(size); ShadowStenciller::configureStencilTexture(shadowStencil, {billboardSize, billboardSize}); @@ -93,7 +100,7 @@ void Foliage::render(const SceneShader & shader, const Frustum &) const { if (instancePartitions.first) { - glDebugScope _ {instanceVAO}; + glDebugScope _ {*instanceVAO}; std::ignore = instances.size(); if (const auto count = instancePartitions.first - instancePartitions.second.first) { glDebugScope _ {0, "Billboard"}; @@ -102,8 +109,8 @@ Foliage::render(const SceneShader & shader, const Frustum &) const billboard[0].bind(0); billboard[1].bind(1); billboard[2].bind(2); - glBindVertexArray(instancePointVAO); - instancePointVAO.useBuffer(0, instances); + glBindVertexArray(*instancePointVAO); + instancePointVAO->useBuffer(0, instances); glDrawArrays(GL_POINTS, static_cast(instancePartitions.second.first), static_cast(count)); glBindVertexArray(0); } @@ -113,8 +120,8 @@ Foliage::render(const SceneShader & shader, const Frustum &) const if (texture) { texture->bind(0); } - instanceVAO.useBuffer(1, instances); - bodyMesh->drawInstanced(instanceVAO, static_cast(count)); + instanceVAO->useBuffer(1, instances); + bodyMesh->drawInstanced(*instanceVAO, static_cast(count)); } } } @@ -123,15 +130,15 @@ void Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const { if (instancePartitions.second.second) { - glDebugScope _ {instanceVAO}; + glDebugScope _ {*instanceVAO}; std::ignore = instances.size(); if (const auto count = instancePartitions.second.second - instancePartitions.second.first) { glDebugScope _ {0, "Billboard"}; const auto dimensions = bodyMesh->getDimensions(); mapper.stencilShadowProgram.use(dimensions.centre, dimensions.size); shadowStencil.bind(0); - glBindVertexArray(instancePointVAO); - instancePointVAO.useBuffer(0, instances); + glBindVertexArray(*instancePointVAO); + instancePointVAO->useBuffer(0, instances); glDrawArrays(GL_POINTS, static_cast(instancePartitions.second.first), static_cast(count)); glBindVertexArray(0); } @@ -144,8 +151,8 @@ Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const else { mapper.dynamicPointInst.use(); } - instanceVAO.useBuffer(1, instances); - bodyMesh->drawInstanced(instanceVAO, static_cast(count)); + instanceVAO->useBuffer(1, instances); + bodyMesh->drawInstanced(*instanceVAO, static_cast(count)); } } } diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h index bf98d55..c599649 100644 --- a/game/scenary/foliage.h +++ b/game/scenary/foliage.h @@ -12,8 +12,8 @@ class Location; class Foliage : public Asset, public Renderable, public StdTypeDefs { Mesh::Ptr bodyMesh; Texture::Ptr texture; - glVertexArray instanceVAO; - glVertexArray instancePointVAO; + std::shared_ptr instanceVAO, instancePointVAO; + static std::weak_ptr commonInstanceVAO, commonInstancePointVAO; public: [[nodiscard]] std::any createAt(const Location &) const override; diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp index a0ea5be..4398853 100644 --- a/game/scenary/illuminator.cpp +++ b/game/scenary/illuminator.cpp @@ -5,6 +5,9 @@ static_assert(std::is_constructible_v); +std::weak_ptr Illuminator::commonInstanceVAO, Illuminator::commonInstancesSpotLightVAO, + Illuminator::commonInstancesPointLightVAO; + std::any Illuminator::createAt(const Location & position) const { @@ -41,25 +44,32 @@ Illuminator::postLoad() } texture = getTexture(); glDebugScope _ {0}; - bodyMesh->configureVAO(instanceVAO, 0) - .addAttribs(1); - if (!spotLight.empty()) { - instancesSpotLightVAO.emplace(); - instancesSpotLightVAO->configure() - .addAttribs(0) + if (!(instanceVAO = commonInstanceVAO.lock())) { + commonInstanceVAO = instanceVAO = std::make_shared(); + bodyMesh->configureVAO(*instanceVAO, 0) .addAttribs(1); + } + if (!spotLight.empty()) { + if (!(instancesSpotLightVAO = commonInstancesSpotLightVAO.lock())) { + commonInstancesSpotLightVAO = instancesSpotLightVAO = std::make_shared(); + instancesSpotLightVAO->configure() + .addAttribs(0) + .addAttribs(1); + } std::transform( spotLight.begin(), spotLight.end(), std::back_inserter(spotLightInstances), [this](const auto & s) { return instancesSpotLight.acquire(*s); }); } if (!pointLight.empty()) { - instancesPointLightVAO.emplace(); - instancesPointLightVAO->configure() - .addAttribs(0) - .addAttribs(1); + if (!(instancesPointLightVAO = commonInstancesPointLightVAO.lock())) { + commonInstancesPointLightVAO = instancesPointLightVAO = std::make_shared(); + instancesPointLightVAO->configure() + .addAttribs(0) + .addAttribs(1); + } std::transform( pointLight.begin(), pointLight.end(), std::back_inserter(pointLightInstances), [this](const auto & s) { return instancesPointLight.acquire(*s); @@ -71,13 +81,13 @@ void Illuminator::render(const SceneShader & shader, const Frustum &) const { if (const auto count = instances.size()) { - glDebugScope _ {instanceVAO}; + glDebugScope _ {*instanceVAO}; shader.basicInst.use(); if (texture) { texture->bind(0); } - instanceVAO.useBuffer(1, instances); - bodyMesh->drawInstanced(instanceVAO, static_cast(count)); + instanceVAO->useBuffer(1, instances); + bodyMesh->drawInstanced(*instanceVAO, static_cast(count)); } } @@ -85,7 +95,7 @@ void Illuminator::lights(const SceneShader & shader) const { if (const auto count = instances.size()) { - glDebugScope _ {instanceVAO}; + glDebugScope _ {*instanceVAO}; if (const auto scount = instancesSpotLight.size()) { glDebugScope _ {*instancesSpotLightVAO, "Spot lights"}; shader.spotLightInst.use(); diff --git a/game/scenary/illuminator.h b/game/scenary/illuminator.h index 200ba40..7b6e7ad 100644 --- a/game/scenary/illuminator.h +++ b/game/scenary/illuminator.h @@ -11,8 +11,8 @@ class Location; class Illuminator : public Asset, public Renderable, public StdTypeDefs { Mesh::Ptr bodyMesh; Texture::Ptr texture; - glVertexArray instanceVAO; - std::optional instancesSpotLightVAO, instancesPointLightVAO; + std::shared_ptr instanceVAO, instancesSpotLightVAO, instancesPointLightVAO; + static std::weak_ptr commonInstanceVAO, commonInstancesSpotLightVAO, commonInstancesPointLightVAO; public: [[nodiscard]] std::any createAt(const Location &) const override; @@ -42,7 +42,6 @@ public: bool persist(Persistence::PersistenceStore & store) override; }; -public: using LocationVertex = std::pair; mutable InstanceVertices instances; mutable InstanceVertices instancesSpotLight; diff --git a/game/terrain.cpp b/game/terrain.cpp index 43b1152..f3d7a7d 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -16,11 +16,13 @@ static constexpr RGB OPEN_SURFACE {-1}; static constexpr GlobalDistance TILE_SIZE = 1024 * 1024; // ~1km, power of 2, fast divide -template<> -Impl::VertexArrayConfigurator & -Impl::VertexArrayConfigurator::addAttribsFor(const GLuint divisor) +void +Terrain::initialise() { - return addAttribs(divisor); + glDebugScope _ {0}; + vertexArray.configure().addAttribs( + 0, verticesBuffer); + generateMeshes(); } bool @@ -76,19 +78,10 @@ void Terrain::copyIndicesToBuffers(const SurfaceIndices & surfaceIndices) { for (const auto & [surfaceKey, indices] : surfaceIndices) { - auto meshItr = meshes.find(surfaceKey); - if (meshItr == meshes.end()) { - meshItr = meshes.emplace(surfaceKey, SurfaceArrayBuffer {}).first; - meshItr->second.vertexArray.configure() - .addAttribsFor(0, verticesBuffer) - .addIndices(meshItr->second.indicesBuffer); - } - else { - 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( + auto & mesh = meshes[surfaceKey]; + mesh.indicesBuffer.data(indices, GL_DYNAMIC_DRAW); + mesh.count = static_cast(indices.size()); + mesh.aabb = AxisAlignedBoundingBox::fromPoints( indices | std::views::transform([this](const auto vertex) { return this->point(VertexHandle {static_cast(vertex)}); })); @@ -130,6 +123,7 @@ void Terrain::render(const SceneShader & shader, const Frustum & frustum) const { glDebugScope _ {0}; + glBindVertexArray(vertexArray); grass->bind(0); const auto chunkBySurface = std::views::chunk_by([](const auto & itr1, const auto & itr2) { @@ -140,7 +134,7 @@ Terrain::render(const SceneShader & shader, const Frustum & frustum) const shader.landmass.use(surface ? surface->colorBias : OPEN_SURFACE); for (const auto & sab : surfaceRange) { if (frustum.contains(sab.second.aabb)) { - glBindVertexArray(sab.second.vertexArray); + glVertexArrayElementBuffer(vertexArray, sab.second.indicesBuffer); glDrawElements(GL_TRIANGLES, sab.second.count, GL_UNSIGNED_INT, nullptr); } } @@ -152,10 +146,11 @@ void Terrain::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const { glDebugScope _ {0}; + glBindVertexArray(vertexArray); shadowMapper.landmess.use(); for (const auto & [surface, sab] : meshes) { if (frustum.contains(sab.aabb)) { - glBindVertexArray(sab.vertexArray); + glVertexArrayElementBuffer(vertexArray, sab.indicesBuffer); glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr); } } diff --git a/game/terrain.h b/game/terrain.h index 1a63296..f1170f2 100644 --- a/game/terrain.h +++ b/game/terrain.h @@ -13,7 +13,7 @@ class Terrain : public GeoData, public WorldObject, public Renderable { public: template explicit Terrain(P &&... params) : GeoData {std::forward

(params)...} { - generateMeshes(); + initialise(); } void render(const SceneShader & shader, const Frustum &) const override; @@ -29,15 +29,17 @@ public: void generateMeshes(); private: + void initialise(); void afterChange() override; struct SurfaceArrayBuffer { - glVertexArray vertexArray; glBuffer indicesBuffer; GLsizei count; AxisAlignedBoundingBox aabb; }; + glVertexArray vertexArray; + struct SurfaceKey { const Surface * surface; GlobalPosition2D basePosition; diff --git a/game/water.cpp b/game/water.cpp index a3c1452..96f35cf 100644 --- a/game/water.cpp +++ b/game/water.cpp @@ -1,5 +1,6 @@ #include "water.h" #include "game/geoData.h" +#include "gfx/gl/gldebug.h" #include "gfx/models/texture.h" #include #include @@ -42,6 +43,7 @@ static constexpr GlobalDistance BORDER = TILE_SIZE / 2; void Water::generateMeshes() { + glDebugScope _ {0}; // Map out where a water square needs to exist to cover all terrain faces with a low vertex std::set waterPositions; std::for_each(geoData->vertices_sbegin(), geoData->vertices_end(), [this, &waterPositions](const auto vh) { diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index 88314ae..6a53f52 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -1,7 +1,8 @@ #include "mesh.h" -MeshBase::MeshBase(GLsizei m_numIndices, GLenum mode, const std::vector & positions) : - numIndices {m_numIndices}, mode {mode}, dimensions {positions} +MeshBase::MeshBase( + GLsizei m_numIndices, GLenum mode, const std::vector & positions, GLsizei vertexStride) : + vertexStride {vertexStride}, numIndices {m_numIndices}, mode {mode}, dimensions {positions} { } @@ -31,7 +32,9 @@ MeshBase::Dimensions::extents(const std::span position void MeshBase::draw() const { - glBindVertexArray(vertexArrayObject); + glBindVertexArray(*vertexArrayObject); + glVertexArrayVertexBuffer(*vertexArrayObject, 0, vertexArrayBuffers[0], 0, vertexStride); + glVertexArrayElementBuffer(*vertexArrayObject, vertexArrayBuffers[1]); glDrawElements(mode, numIndices, GL_UNSIGNED_INT, nullptr); @@ -42,6 +45,8 @@ void MeshBase::drawInstanced(GLuint vao, GLsizei count, GLuint base) const { glBindVertexArray(vao); + glVertexArrayVertexBuffer(vao, 0, vertexArrayBuffers[0], 0, vertexStride); + glVertexArrayElementBuffer(vao, vertexArrayBuffers[1]); glDrawElementsInstancedBaseInstance(mode, numIndices, GL_UNSIGNED_INT, nullptr, count, base); diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index d4dbd68..fa12ae8 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -2,13 +2,15 @@ #include "config/types.h" #include "gfx/gl/glBuffer.h" -#include "gfx/models/vertex.h" +#include "gfx/gl/gldebug.h" #include #include #include #include #include +class Vertex; + class MeshBase { public: class Dimensions { @@ -35,10 +37,11 @@ public: } protected: - MeshBase(GLsizei numIndices, GLenum mode, const std::vector &); + MeshBase(GLsizei numIndices, GLenum mode, const std::vector &, GLsizei vertexStride); - glVertexArray vertexArrayObject; + std::shared_ptr vertexArrayObject; glBuffers<2> vertexArrayBuffers; + GLsizei vertexStride; GLsizei numIndices; GLenum mode; Dimensions dimensions; @@ -50,18 +53,29 @@ public: MeshBase {static_cast(indices.size()), mode, materializeRange(vertices | std::views::transform([](const auto & vertex) { return static_cast(vertex.pos); - }))} + })), + sizeof(V)} { + glDebugScope _ {0}; vertexArrayBuffers[0].storage(vertices, 0); vertexArrayBuffers[1].storage(indices, 0); - configureVAO(vertexArrayObject, 0); + if (!(vertexArrayObject = commonVertexArrayObject.lock())) { + commonVertexArrayObject = vertexArrayObject = std::make_shared(); + configureVAO(*vertexArrayObject, 0); + } } auto configureVAO(glVertexArray & vao, GLuint divisor) const { - return vao.configure().addAttribsFor(divisor, vertexArrayBuffers[0]).addIndices(vertexArrayBuffers[1]); + glDebugScope _ {0}; + return vao.configure().addAttribsFor(divisor); } + +protected: + static std::weak_ptr commonVertexArrayObject; }; +template std::weak_ptr MeshT::commonVertexArrayObject; + using Mesh = MeshT; -- cgit v1.3