Graphics/CullingList: Improve culling list
- Now supports box culling - Removed branch - Removed complex hash combination and replaced it with a much faster algorithm - It now supports partial visibility
This commit is contained in:
parent
56873b92b0
commit
7bb6c84752
|
|
@ -217,7 +217,10 @@ namespace Ndk
|
|||
if (camComponent.UpdateVisibility(visibilityHash) || m_forceRenderQueueInvalidation || forceInvalidation)
|
||||
{
|
||||
renderQueue->Clear();
|
||||
for (const GraphicsComponent* gfxComponent : m_drawableCulling)
|
||||
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetFullyVisibleResults())
|
||||
gfxComponent->AddToRenderQueue(renderQueue);
|
||||
|
||||
for (const GraphicsComponent* gfxComponent : m_drawableCulling.GetPartiallyVisibleResults())
|
||||
gfxComponent->AddToRenderQueue(renderQueue);
|
||||
|
||||
for (const Ndk::EntityHandle& light : m_lights)
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ namespace Nz
|
|||
{
|
||||
public:
|
||||
template<CullTest> class Entry;
|
||||
class BoxEntry;
|
||||
class NoTestEntry;
|
||||
class SphereEntry;
|
||||
class VolumeEntry;
|
||||
|
||||
template<CullTest> friend class Entry;
|
||||
friend BoxEntry;
|
||||
friend NoTestEntry;
|
||||
friend SphereEntry;
|
||||
friend VolumeEntry;
|
||||
|
|
@ -43,6 +45,10 @@ namespace Nz
|
|||
|
||||
std::size_t FillWithAllEntries(bool* forceInvalidation = nullptr);
|
||||
|
||||
const ResultContainer& GetFullyVisibleResults() const;
|
||||
const ResultContainer& GetPartiallyVisibleResults() const;
|
||||
|
||||
BoxEntry RegisterBoxTest(const T* renderable);
|
||||
NoTestEntry RegisterNoTest(const T* renderable);
|
||||
SphereEntry RegisterSphereTest(const T* renderable);
|
||||
VolumeEntry RegisterVolumeTest(const T* renderable);
|
||||
|
|
@ -50,37 +56,24 @@ namespace Nz
|
|||
CullingList& operator=(const CullingList& renderable) = delete;
|
||||
CullingList& operator=(CullingList&& renderable) = delete;
|
||||
|
||||
// STL API
|
||||
typename ResultContainer::iterator begin();
|
||||
typename ResultContainer::const_iterator begin() const;
|
||||
|
||||
typename ResultContainer::const_iterator cbegin() const;
|
||||
typename ResultContainer::const_iterator cend() const;
|
||||
typename ResultContainer::const_reverse_iterator crbegin() const;
|
||||
typename ResultContainer::const_reverse_iterator crend() const;
|
||||
|
||||
bool empty() const;
|
||||
|
||||
typename ResultContainer::iterator end();
|
||||
typename ResultContainer::const_iterator end() const;
|
||||
|
||||
typename ResultContainer::reverse_iterator rbegin();
|
||||
typename ResultContainer::const_reverse_iterator rbegin() const;
|
||||
|
||||
typename ResultContainer::reverse_iterator rend();
|
||||
typename ResultContainer::const_reverse_iterator rend() const;
|
||||
|
||||
typename ResultContainer::size_type size() const;
|
||||
|
||||
NazaraSignal(OnCullingListRelease, CullingList* /*cullingList*/);
|
||||
|
||||
private:
|
||||
inline void NotifyBoxUpdate(std::size_t index, const Boxf& boundingVolume);
|
||||
inline void NotifyForceInvalidation(CullTest type, std::size_t index);
|
||||
inline void NotifyMovement(CullTest type, std::size_t index, void* oldPtr, void* newPtr);
|
||||
inline void NotifyRelease(CullTest type, std::size_t index);
|
||||
inline void NotifySphereUpdate(std::size_t index, const Spheref& sphere);
|
||||
inline void NotifyVolumeUpdate(std::size_t index, const BoundingVolumef& boundingVolume);
|
||||
|
||||
struct BoxVisibilityEntry
|
||||
{
|
||||
Boxf box;
|
||||
BoxEntry* entry;
|
||||
const T* renderable;
|
||||
bool forceInvalidation;
|
||||
};
|
||||
|
||||
struct NoTestVisibilityEntry
|
||||
{
|
||||
NoTestEntry* entry;
|
||||
|
|
@ -104,10 +97,12 @@ namespace Nz
|
|||
bool forceInvalidation;
|
||||
};
|
||||
|
||||
std::vector<BoxVisibilityEntry> m_boxTestList;
|
||||
std::vector<NoTestVisibilityEntry> m_noTestList;
|
||||
std::vector<SphereVisibilityEntry> m_sphereTestList;
|
||||
std::vector<VolumeVisibilityEntry> m_volumeTestList;
|
||||
ResultContainer m_results;
|
||||
ResultContainer m_fullyVisibleResults;
|
||||
ResultContainer m_partiallyVisibleResults;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -135,6 +130,24 @@ namespace Nz
|
|||
std::size_t m_index;
|
||||
CullingList* m_parent;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CullingList<T>::BoxEntry : public CullingList<T>::template Entry<CullTest::Box>
|
||||
{
|
||||
friend CullingList;
|
||||
|
||||
public:
|
||||
BoxEntry();
|
||||
BoxEntry(BoxEntry&&) = default;
|
||||
~BoxEntry() = default;
|
||||
|
||||
void UpdateBox(const Boxf& box);
|
||||
|
||||
BoxEntry& operator=(BoxEntry&&) = default;
|
||||
|
||||
private:
|
||||
BoxEntry(CullingList* parent, std::size_t index);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CullingList<T>::NoTestEntry : public CullingList<T>::template Entry<CullTest::NoTest>
|
||||
|
|
|
|||
|
|
@ -16,16 +16,48 @@ namespace Nz
|
|||
template<typename T>
|
||||
std::size_t CullingList<T>::Cull(const Frustumf& frustum, bool* forceInvalidation)
|
||||
{
|
||||
m_results.clear();
|
||||
m_fullyVisibleResults.clear();
|
||||
m_partiallyVisibleResults.clear();
|
||||
|
||||
bool forcedInvalidation = false;
|
||||
|
||||
std::size_t visibleHash = 0U;
|
||||
std::size_t fullyVisibleHash = 5U;
|
||||
std::size_t partiallyVisibleHash = 5U;
|
||||
|
||||
auto CombineHash = [](std::size_t currentHash, std::size_t newHash)
|
||||
{
|
||||
return currentHash * 23 + newHash;
|
||||
};
|
||||
|
||||
for (BoxVisibilityEntry& entry : m_boxTestList)
|
||||
{
|
||||
switch (frustum.Intersect(entry.box))
|
||||
{
|
||||
case IntersectionSide_Inside:
|
||||
m_fullyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
break;
|
||||
|
||||
case IntersectionSide_Intersecting:
|
||||
m_partiallyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
break;
|
||||
|
||||
case IntersectionSide_Outside:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (NoTestVisibilityEntry& entry : m_noTestList)
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
m_fullyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
|
|
@ -36,34 +68,87 @@ namespace Nz
|
|||
|
||||
for (SphereVisibilityEntry& entry : m_sphereTestList)
|
||||
{
|
||||
if (frustum.Contains(entry.sphere))
|
||||
switch (frustum.Intersect(entry.sphere))
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
case IntersectionSide_Inside:
|
||||
m_fullyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
forcedInvalidation = true;
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntersectionSide_Intersecting:
|
||||
m_partiallyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
break;
|
||||
|
||||
case IntersectionSide_Outside:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (VolumeVisibilityEntry& entry : m_volumeTestList)
|
||||
{
|
||||
if (frustum.Contains(entry.volume))
|
||||
switch (frustum.Intersect(entry.volume))
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
case IntersectionSide_Inside:
|
||||
m_fullyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(fullyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
forcedInvalidation = true;
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntersectionSide_Intersecting:
|
||||
m_partiallyVisibleResults.push_back(entry.renderable);
|
||||
CombineHash(partiallyVisibleHash, std::hash<const T*>()(entry.renderable));
|
||||
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
break;
|
||||
|
||||
case IntersectionSide_Outside:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (forceInvalidation)
|
||||
*forceInvalidation = forcedInvalidation;
|
||||
|
||||
return 5 + partiallyVisibleHash * 17 + fullyVisibleHash;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::size_t CullingList<T>::FillWithAllEntries(bool* forceInvalidation)
|
||||
{
|
||||
m_fullyVisibleResults.clear();
|
||||
m_partiallyVisibleResults.clear();
|
||||
|
||||
bool forcedInvalidation = false;
|
||||
|
||||
std::size_t visibleHash = 5U;
|
||||
|
||||
auto FillWithList = [&](auto& testList)
|
||||
{
|
||||
for (auto& entry : testList)
|
||||
{
|
||||
m_fullyVisibleResults.push_back(entry.renderable);
|
||||
visibleHash = visibleHash * 23 + std::hash<const T*>()(entry.renderable);
|
||||
|
||||
forcedInvalidation = forcedInvalidation | entry.forceInvalidation;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
};
|
||||
|
||||
FillWithList(m_boxTestList);
|
||||
FillWithList(m_noTestList);
|
||||
FillWithList(m_sphereTestList);
|
||||
FillWithList(m_volumeTestList);
|
||||
|
||||
if (forceInvalidation)
|
||||
*forceInvalidation = forcedInvalidation;
|
||||
|
||||
|
|
@ -71,53 +156,24 @@ namespace Nz
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
std::size_t CullingList<T>::FillWithAllEntries(bool* forceInvalidation)
|
||||
auto CullingList<T>::GetFullyVisibleResults() const -> const ResultContainer&
|
||||
{
|
||||
m_results.clear();
|
||||
return m_fullyVisibleResults;
|
||||
}
|
||||
|
||||
bool forcedInvalidation = false;
|
||||
template<typename T>
|
||||
auto CullingList<T>::GetPartiallyVisibleResults() const -> const ResultContainer&
|
||||
{
|
||||
return m_partiallyVisibleResults;
|
||||
}
|
||||
|
||||
std::size_t visibleHash = 0U;
|
||||
for (NoTestVisibilityEntry& entry : m_noTestList)
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
template<typename T>
|
||||
auto CullingList<T>::RegisterBoxTest(const T* renderable) -> BoxEntry
|
||||
{
|
||||
BoxEntry newEntry(this, m_boxTestList.size());
|
||||
m_boxTestList.emplace_back(BoxVisibilityEntry{ Nz::Boxf(), &newEntry, renderable, false }); //< Address of entry will be updated when moving
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
forcedInvalidation = true;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (SphereVisibilityEntry& entry : m_sphereTestList)
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
forcedInvalidation = true;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (VolumeVisibilityEntry& entry : m_volumeTestList)
|
||||
{
|
||||
m_results.push_back(entry.renderable);
|
||||
Nz::HashCombine(visibleHash, entry.renderable);
|
||||
|
||||
if (entry.forceInvalidation)
|
||||
{
|
||||
forcedInvalidation = true;
|
||||
entry.forceInvalidation = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (forceInvalidation)
|
||||
*forceInvalidation = forcedInvalidation;
|
||||
|
||||
return visibleHash;
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -132,7 +188,7 @@ namespace Nz
|
|||
template<typename T>
|
||||
auto CullingList<T>::RegisterSphereTest(const T* renderable) -> SphereEntry
|
||||
{
|
||||
SphereEntry newEntry(this, m_sphereTestList.size() - 1);
|
||||
SphereEntry newEntry(this, m_sphereTestList.size());
|
||||
m_sphereTestList.emplace_back(SphereVisibilityEntry{Nz::Spheref(), &newEntry, renderable, false}); //< Address of entry will be updated when moving
|
||||
|
||||
return newEntry;
|
||||
|
|
@ -147,89 +203,10 @@ namespace Nz
|
|||
return newEntry;
|
||||
}
|
||||
|
||||
// Interface STD
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::iterator CullingList<T>::begin()
|
||||
inline void CullingList<T>::NotifyBoxUpdate(std::size_t index, const Boxf& box)
|
||||
{
|
||||
return m_results.begin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::begin() const
|
||||
{
|
||||
return m_results.begin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cbegin() const
|
||||
{
|
||||
return m_results.cbegin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::cend() const
|
||||
{
|
||||
return m_results.cend();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crbegin() const
|
||||
{
|
||||
return m_results.crbegin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::crend() const
|
||||
{
|
||||
return m_results.crend();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool CullingList<T>::empty() const
|
||||
{
|
||||
return m_results.empty();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::iterator CullingList<T>::end()
|
||||
{
|
||||
return m_results.end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_iterator CullingList<T>::end() const
|
||||
{
|
||||
return m_results.end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rbegin()
|
||||
{
|
||||
return m_results.rbegin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rbegin() const
|
||||
{
|
||||
return m_results.rbegin();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::reverse_iterator CullingList<T>::rend()
|
||||
{
|
||||
return m_results.rend();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::const_reverse_iterator CullingList<T>::rend() const
|
||||
{
|
||||
return m_results.rend();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename CullingList<T>::ResultContainer::size_type CullingList<T>::size() const
|
||||
{
|
||||
return m_results.size();
|
||||
m_boxTestList[index].box = box;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -237,6 +214,12 @@ namespace Nz
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
case CullTest::Box:
|
||||
{
|
||||
m_boxTestList[index].forceInvalidation = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case CullTest::NoTest:
|
||||
{
|
||||
m_noTestList[index].forceInvalidation = true;
|
||||
|
|
@ -268,6 +251,15 @@ namespace Nz
|
|||
|
||||
switch (type)
|
||||
{
|
||||
case CullTest::Box:
|
||||
{
|
||||
BoxVisibilityEntry& entry = m_boxTestList[index];
|
||||
NazaraAssert(entry.entry == oldPtr, "Invalid box entry");
|
||||
|
||||
entry.entry = static_cast<BoxEntry*>(newPtr);
|
||||
break;
|
||||
}
|
||||
|
||||
case CullTest::NoTest:
|
||||
{
|
||||
NoTestVisibilityEntry& entry = m_noTestList[index];
|
||||
|
|
@ -306,6 +298,14 @@ namespace Nz
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
case CullTest::Box:
|
||||
{
|
||||
m_boxTestList[index] = std::move(m_boxTestList.back());
|
||||
m_boxTestList[index].entry->UpdateIndex(index);
|
||||
m_boxTestList.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
case CullTest::NoTest:
|
||||
{
|
||||
m_noTestList[index] = std::move(m_noTestList.back());
|
||||
|
|
@ -424,6 +424,26 @@ namespace Nz
|
|||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
CullingList<T>::BoxEntry::BoxEntry() :
|
||||
Entry<CullTest::Box>()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
CullingList<T>::BoxEntry::BoxEntry(CullingList* parent, std::size_t index) :
|
||||
Entry<CullTest::Box>(parent, index)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void CullingList<T>::BoxEntry::UpdateBox(const Boxf& box)
|
||||
{
|
||||
this->m_parent->NotifyBoxUpdate(this->m_index, box);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace Nz
|
|||
|
||||
enum class CullTest
|
||||
{
|
||||
Box,
|
||||
NoTest,
|
||||
Sphere,
|
||||
Volume
|
||||
|
|
|
|||
Loading…
Reference in New Issue