From 9570138a2b1639dc4d32cf8ec90e52d2967db4e2 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 6 Feb 2022 07:10:52 +0100 Subject: [PATCH] #5893: Winding registration is taking a render entity reference now. The material's WindingRenderer should group the incoming windings by entity and register them, such that they can be picked up by the back end renderer when iterating through entities. Not functional yet, this is WIP. --- include/irender.h | 4 +- include/iwindingrenderer.h | 11 +- libs/render/RenderableGeometry.h | 2 +- libs/render/RenderableSurface.h | 2 +- libs/render/WindingRenderer.h | 220 ++++++++++++++---- radiantcore/brush/RenderableWinding.h | 8 +- radiantcore/entity/EntityNode.cpp | 2 +- radiantcore/entity/EntityNode.h | 2 +- .../entity/RenderableObjectCollection.h | 4 +- .../rendersystem/backend/ColourShader.cpp | 3 +- .../backend/LightInteractions.cpp | 10 +- .../rendersystem/backend/LightInteractions.h | 2 +- .../rendersystem/backend/OpenGLShader.cpp | 6 +- .../rendersystem/backend/OpenGLShader.h | 2 +- 14 files changed, 211 insertions(+), 67 deletions(-) diff --git a/include/irender.h b/include/irender.h index 2bf9b5cec9..456e10c80f 100644 --- a/include/irender.h +++ b/include/irender.h @@ -174,14 +174,14 @@ class IRenderEntity * Associates the given object with this entity and the given shader. * It will be processed during the following lighting mode rendering passes. */ - virtual void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) = 0; + virtual void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) = 0; /** * Removes the object from this entity. */ virtual void removeRenderable(const render::IRenderableObject::Ptr& object) = 0; - using ObjectVisitFunction = std::function; + using ObjectVisitFunction = std::function; /** * Enumerate all entity object (partially) intersecting with the given bounds. diff --git a/include/iwindingrenderer.h b/include/iwindingrenderer.h index 520b7d07bd..d9862974a5 100644 --- a/include/iwindingrenderer.h +++ b/include/iwindingrenderer.h @@ -5,6 +5,8 @@ #include #include "render/ArbitraryMeshVertex.h" +class IRenderEntity; + namespace render { @@ -27,14 +29,17 @@ class IWindingRenderer using Slot = std::uint64_t; static constexpr Slot InvalidSlot = std::numeric_limits::max(); - // Allocate a slot to hold the vertex data of a winding of the given size + // Allocate a slot to hold the vertex data of a winding of the given size. + // The winding will be associated to the given render entity (causing it to be grouped internally + // by the render entities when the surfaces are processed in lit render views). // Returns the handle which can be used to update or deallocate the data later - virtual Slot addWinding(const std::vector& vertices) = 0; + virtual Slot addWinding(const std::vector& vertices, IRenderEntity* entity) = 0; // Releases a previously allocated winding slot. This invalidates the handle. virtual void removeWinding(Slot slot) = 0; - // Sets the winding data + // Updates the winding data. An IRenderEntity change is not supported through updateWinding(), in case the + // winding has to be associated to a different entity, call removeWinding() first. virtual void updateWinding(Slot slot, const std::vector& vertices) = 0; // Mode used to specify how to render a single winding diff --git a/libs/render/RenderableGeometry.h b/libs/render/RenderableGeometry.h index 8a13187ee8..cb7b382348 100644 --- a/libs/render/RenderableGeometry.h +++ b/libs/render/RenderableGeometry.h @@ -173,7 +173,7 @@ class RenderableGeometry : _renderEntity = entity; ensureRenderAdapter(); - _renderEntity->addRenderable(_renderAdapter, _shader); + _renderEntity->addRenderable(_renderAdapter, _shader.get()); } void detachFromEntity() diff --git a/libs/render/RenderableSurface.h b/libs/render/RenderableSurface.h index 0f9444bd17..f1dfcbc9a9 100644 --- a/libs/render/RenderableSurface.h +++ b/libs/render/RenderableSurface.h @@ -106,7 +106,7 @@ class RenderableSurface : } _renderEntity = entity; - _renderEntity->addRenderable(shared_from_this(), shader); + _renderEntity->addRenderable(shared_from_this(), shader.get()); _storageLocation = shader->getSurfaceStorageLocation(_shaders[shader]); } diff --git a/libs/render/WindingRenderer.h b/libs/render/WindingRenderer.h index 41d5657697..c07ffba7d3 100644 --- a/libs/render/WindingRenderer.h +++ b/libs/render/WindingRenderer.h @@ -32,18 +32,24 @@ template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_LINES; } + + constexpr static bool SupportsEntitySurfaces() { return false; } }; template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_TRIANGLES; } + + constexpr static bool SupportsEntitySurfaces() { return true; } }; template<> struct RenderingTraits { constexpr static GLenum Mode() { return GL_POLYGON; } + + constexpr static bool SupportsEntitySurfaces() { return false; } }; template @@ -63,6 +69,9 @@ class WindingRenderer : std::vector pendingDeletions; }; + IGeometryStore& _geometryStore; + Shader* _owningShader; + // Maintain one bucket per winding size, allocated on demand std::vector _buckets; @@ -75,26 +84,168 @@ class WindingRenderer : { BucketIndex bucketIndex = InvalidBucketIndex; typename VertexBuffer::Slot slotNumber = InvalidVertexBufferSlot; + IRenderEntity* renderEntity = nullptr; }; std::vector _slots; static constexpr std::size_t InvalidSlotMapping = std::numeric_limits::max(); std::size_t _freeSlotMappingHint; - std::size_t _windings; + std::size_t _windingCount; + + class WindingGroup : + public IRenderableObject + { + private: + WindingRenderer& _owner; + + std::set _slotIndices; + bool _surfaceNeedsRebuild; + AABB _bounds; + bool _boundsNeedUpdate; + + IGeometryStore::Slot _geometrySlot; + + sigc::signal _sigBoundsChanged; + public: + WindingGroup(WindingRenderer& owner) : + _owner(owner), + _surfaceNeedsRebuild(true), + _boundsNeedUpdate(true), + _geometrySlot(std::numeric_limits::max()) + {} + + void addWinding(std::size_t slotMappingIndex) + { + _slotIndices.insert(slotMappingIndex); + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + void updateWinding(std::size_t slotMappingIndex) + { + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + void removeWinding(std::size_t slotMappingIndex) + { + _slotIndices.erase(slotMappingIndex); + _surfaceNeedsRebuild = true; + _sigBoundsChanged.emit(); + } + + bool empty() const + { + return _slotIndices.empty(); + } + + bool isVisible() override + { + return !empty(); + } + + const Matrix4& getObjectTransform() override + { + static Matrix4 _identity = Matrix4::getIdentity(); + return _identity; + } + + const AABB& getObjectBounds() override + { + if (_surfaceNeedsRebuild || _boundsNeedUpdate) + { + _boundsNeedUpdate = false; + _bounds = _owner._geometryStore.getBounds(_geometrySlot); + } + + return _bounds; + } + + sigc::signal& signal_boundsChanged() override + { + return _sigBoundsChanged; + } + + IGeometryStore::Slot getStorageLocation() override + { + return _geometrySlot; + } + }; + + class EntityWindings + { + private: + WindingRenderer& _owner; + + std::map> _windingsByEntity; + + public: + EntityWindings(WindingRenderer& owner) : + _owner(owner) + {} + + void addWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + + // Find or create a surface for the entity + auto existing = _windingsByEntity.find(slot.renderEntity); + + if (existing != _windingsByEntity.end()) + { + existing = _windingsByEntity.emplace(slot.renderEntity, + std::make_shared(_owner)).first; + + // New surface, register this with the entity + slot.renderEntity->addRenderable(existing->second, _owner._owningShader); + } + + existing->second->addWinding(slotMappingIndex); + } + + void updateWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + _windingsByEntity[slot.renderEntity]->updateWinding(slotMappingIndex); + } + + void removeWinding(std::size_t slotMappingIndex) + { + const auto& slot = _owner._slots[slotMappingIndex]; + + auto& group = _windingsByEntity[slot.renderEntity]; + group->removeWinding(slotMappingIndex); + + if (group->empty()) + { + slot.renderEntity->removeRenderable(group); + _windingsByEntity.erase(slot.renderEntity); + } + } + }; + + std::unique_ptr _entitySurfaces; public: - WindingRenderer() : - _windings(0), + WindingRenderer(IGeometryStore& geometryStore, Shader* owningShader) : + _geometryStore(geometryStore), + _owningShader(owningShader), + _windingCount(0), _freeSlotMappingHint(InvalidSlotMapping) - {} + { + if (RenderingTraits::SupportsEntitySurfaces()) + { + _entitySurfaces.reset(new EntityWindings(*this)); + } + } bool empty() const { - return _windings == 0; + return _windingCount == 0; } - Slot addWinding(const std::vector& vertices) override + Slot addWinding(const std::vector& vertices, IRenderEntity* entity) override { auto windingSize = vertices.size(); @@ -125,7 +276,15 @@ class WindingRenderer : slotMapping.slotNumber = bucket.buffer.pushWinding(vertices); } - ++_windings; + ++_windingCount; + + if (RenderingTraits::SupportsEntitySurfaces()) + { + slotMapping.renderEntity = entity; + + // Add this winding to the surface associated to the render entity + _entitySurfaces->addWinding(slotMappingIndex); + } return slotMappingIndex; } @@ -138,27 +297,18 @@ class WindingRenderer : auto bucketIndex = slotMapping.bucketIndex; assert(bucketIndex != InvalidBucketIndex); -#if 1 - // Mark this winding slot as pending for deletion - _buckets[bucketIndex].pendingDeletions.push_back(slotMapping.slotNumber); -#else - // Remove the winding from the bucket - _buckets[bucketIndex].removeWinding(slotMapping.slotNumber); - - // Update the value in other slot mappings, now that the bucket shrunk - for (auto& mapping : _slots) + if (RenderingTraits::SupportsEntitySurfaces()) { - // Every index in the same bucket beyond the removed winding needs to be shifted to left - if (mapping.bucketIndex == bucketIndex && mapping.slotNumber > slotMapping.slotNumber) - { - --mapping.slotNumber; - } + _entitySurfaces->removeWinding(slot); } -#endif + + // Mark this winding slot as pending for deletion + _buckets[bucketIndex].pendingDeletions.push_back(slotMapping.slotNumber); // Invalidate the slot mapping slotMapping.bucketIndex = InvalidBucketIndex; slotMapping.slotNumber = InvalidVertexBufferSlot; + slotMapping.renderEntity = nullptr; // Update the free slot hint, for the next round we allocate one if (slot < _freeSlotMappingHint) @@ -166,7 +316,7 @@ class WindingRenderer : _freeSlotMappingHint = slot; } - --_windings; + --_windingCount; } void updateWinding(Slot slot, const std::vector& vertices) override @@ -184,6 +334,11 @@ class WindingRenderer : } bucket.buffer.replaceWinding(slotMapping.slotNumber, vertices); + + if (RenderingTraits::SupportsEntitySurfaces()) + { + _entitySurfaces->updateWinding(slot); + } } void renderAllWindings(const RenderInfo& info) override @@ -276,7 +431,6 @@ class WindingRenderer : std::sort(bucket.pendingDeletions.begin(), bucket.pendingDeletions.end()); -#if 1 // Remove the winding from the bucket bucket.buffer.removeWindings(bucket.pendingDeletions); @@ -311,25 +465,7 @@ class WindingRenderer : mapping.slotNumber -= maxOffsetToApply; } } -#else - for (auto s = bucket.pendingDeletions.rbegin(); s != bucket.pendingDeletions.rend(); ++s) - { - auto slotNumber = *s; - - // Remove the winding from the bucket - bucket.buffer.removeWinding(slotNumber); - // Update the value in other slot mappings, now that the bucket shrank - for (auto& mapping : _slots) - { - // Every index in the same bucket beyond the removed winding needs to be shifted to left - if (mapping.bucketIndex == bucketIndex && mapping.slotNumber > slotNumber) - { - --mapping.slotNumber; - } - } - } -#endif bucket.pendingDeletions.clear(); } diff --git a/radiantcore/brush/RenderableWinding.h b/radiantcore/brush/RenderableWinding.h index ffe8672d9c..79b0cd4da3 100644 --- a/radiantcore/brush/RenderableWinding.h +++ b/radiantcore/brush/RenderableWinding.h @@ -13,6 +13,7 @@ class RenderableWinding : private: const Winding& _winding; ShaderPtr _shader; + IRenderEntity* _entity; bool _needsUpdate; IWindingRenderer::Slot _slot; @@ -21,6 +22,7 @@ class RenderableWinding : public: RenderableWinding(const Winding& winding) : _winding(winding), + _entity(nullptr), _needsUpdate(true), _slot(IWindingRenderer::InvalidSlot), _windingSize(0) @@ -31,13 +33,13 @@ class RenderableWinding : _needsUpdate = true; } - void update(const ShaderPtr& shader, const IRenderEntity& entity) + void update(const ShaderPtr& shader, IRenderEntity& entity) { if (!_needsUpdate) return; _needsUpdate = false; - if (_shader != shader) + if (_shader != shader || &entity != _entity) { clear(); } @@ -73,7 +75,7 @@ class RenderableWinding : if (_slot == IWindingRenderer::InvalidSlot) { - _slot = shader->addWinding(vertices); + _slot = shader->addWinding(vertices, &entity); } else { diff --git a/radiantcore/entity/EntityNode.cpp b/radiantcore/entity/EntityNode.cpp index 4ff4678376..fa72356aef 100644 --- a/radiantcore/entity/EntityNode.cpp +++ b/radiantcore/entity/EntityNode.cpp @@ -200,7 +200,7 @@ const Vector3& EntityNode::getDirection() const return _direction; } -void EntityNode::addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) +void EntityNode::addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) { _renderObjects.addRenderable(object, shader); } diff --git a/radiantcore/entity/EntityNode.h b/radiantcore/entity/EntityNode.h index 1c86a6388a..600b9991f5 100644 --- a/radiantcore/entity/EntityNode.h +++ b/radiantcore/entity/EntityNode.h @@ -129,7 +129,7 @@ class EntityNode : virtual float getShaderParm(int parmNum) const override; virtual const Vector3& getDirection() const override; - virtual void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) override; + virtual void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) override; virtual void removeRenderable(const render::IRenderableObject::Ptr& object) override; virtual void foreachRenderableTouchingBounds(const AABB& bounds, const ObjectVisitFunction& functor) override; diff --git a/radiantcore/entity/RenderableObjectCollection.h b/radiantcore/entity/RenderableObjectCollection.h index 353d4d944d..986f98ae90 100644 --- a/radiantcore/entity/RenderableObjectCollection.h +++ b/radiantcore/entity/RenderableObjectCollection.h @@ -20,7 +20,7 @@ class RenderableObjectCollection : struct ObjectData { - ShaderPtr shader; + Shader* shader; sigc::connection boundsChangedConnection; }; @@ -31,7 +31,7 @@ class RenderableObjectCollection : _collectionBoundsNeedUpdate(true) {} - void addRenderable(const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) + void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader) { sigc::connection subscription = object->signal_boundsChanged().connect( sigc::mem_fun(*this, &RenderableObjectCollection::onObjectBoundsChanged)); diff --git a/radiantcore/rendersystem/backend/ColourShader.cpp b/radiantcore/rendersystem/backend/ColourShader.cpp index 31b6cf7f18..807e780faa 100644 --- a/radiantcore/rendersystem/backend/ColourShader.cpp +++ b/radiantcore/rendersystem/backend/ColourShader.cpp @@ -3,6 +3,7 @@ #include #include "string/convert.h" #include "fmt/format.h" +#include "../OpenGLRenderSystem.h" namespace render { @@ -73,7 +74,7 @@ void ColourShader::construct() // Don't touch a renderer that is not empty, this will break any client connections if (getWindingRenderer().empty()) { - setWindingRenderer(std::make_unique>()); + setWindingRenderer(std::make_unique>(getRenderSystem().getGeometryStore(), this)); } state.setRenderFlags(RENDER_DEPTHTEST | RENDER_DEPTHWRITE); diff --git a/radiantcore/rendersystem/backend/LightInteractions.cpp b/radiantcore/rendersystem/backend/LightInteractions.cpp index 0ed408a826..45a623b386 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.cpp +++ b/radiantcore/rendersystem/backend/LightInteractions.cpp @@ -42,13 +42,13 @@ inline void submitObject(IRenderableObject& object, IGeometryStore& store) } -void LightInteractions::addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader& shader) +void LightInteractions::addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader* shader) { auto& objectsByMaterial = _objectsByEntity.emplace( &entity, ObjectsByMaterial{}).first->second; auto& surfaces = objectsByMaterial.emplace( - &shader, ObjectList{}).first->second; + shader, ObjectList{}).first->second; surfaces.emplace_back(std::ref(object)); @@ -66,7 +66,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti for (const auto& entity : entities) { entity->foreachRenderableTouchingBounds(_lightBounds, - [&](const render::IRenderableObject::Ptr& object, const ShaderPtr& shader) + [&](const render::IRenderableObject::Ptr& object, Shader* shader) { // Skip empty objects if (!object->isVisible()) return; @@ -74,7 +74,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti // Don't collect invisible shaders if (!shader->isVisible()) return; - auto glShader = static_cast(shader.get()); + auto glShader = static_cast(shader); // We only consider materials designated for camera rendering if (!glShader->isApplicableTo(RenderViewType::Camera)) @@ -82,7 +82,7 @@ void LightInteractions::collectSurfaces(const std::set& entiti return; } - addObject(*object, *entity, *glShader); + addObject(*object, *entity, glShader); }); } } diff --git a/radiantcore/rendersystem/backend/LightInteractions.h b/radiantcore/rendersystem/backend/LightInteractions.h index 7b2a7af004..cc81488193 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.h +++ b/radiantcore/rendersystem/backend/LightInteractions.h @@ -63,7 +63,7 @@ class LightInteractions return _objectsByEntity.size(); } - void addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader& shader); + void addObject(IRenderableObject& object, IRenderEntity& entity, OpenGLShader* shader); bool isInView(const IRenderView& view); diff --git a/radiantcore/rendersystem/backend/OpenGLShader.cpp b/radiantcore/rendersystem/backend/OpenGLShader.cpp index 4f8bc5b5fb..f5cbb4435e 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.cpp +++ b/radiantcore/rendersystem/backend/OpenGLShader.cpp @@ -66,7 +66,7 @@ OpenGLShader::OpenGLShader(const std::string& name, OpenGLRenderSystem& renderSy _enabledViewTypes(0), _mergeModeActive(false) { - _windingRenderer.reset(new WindingRenderer()); + _windingRenderer.reset(new WindingRenderer(renderSystem.getGeometryStore(), this)); } OpenGLShader::~OpenGLShader() @@ -203,9 +203,9 @@ IGeometryStore::Slot OpenGLShader::getSurfaceStorageLocation(ISurfaceRenderer::S return _surfaceRenderer.getSurfaceStorageLocation(slot); } -IWindingRenderer::Slot OpenGLShader::addWinding(const std::vector& vertices) +IWindingRenderer::Slot OpenGLShader::addWinding(const std::vector& vertices, IRenderEntity* entity) { - return _windingRenderer->addWinding(vertices); + return _windingRenderer->addWinding(vertices, entity); } void OpenGLShader::removeWinding(IWindingRenderer::Slot slot) diff --git a/radiantcore/rendersystem/backend/OpenGLShader.h b/radiantcore/rendersystem/backend/OpenGLShader.h index 121d1742b1..09c4033d0f 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.h +++ b/radiantcore/rendersystem/backend/OpenGLShader.h @@ -124,7 +124,7 @@ class OpenGLShader : void renderSurface(ISurfaceRenderer::Slot slot) override; IGeometryStore::Slot getSurfaceStorageLocation(ISurfaceRenderer::Slot slot) override; - IWindingRenderer::Slot addWinding(const std::vector& vertices) override; + IWindingRenderer::Slot addWinding(const std::vector& vertices, IRenderEntity* entity) override; void removeWinding(IWindingRenderer::Slot slot) override; void updateWinding(IWindingRenderer::Slot slot, const std::vector& vertices) override; bool hasWindings() const;