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:
Jérôme Leclercq
2018-08-31 17:26:50 +02:00
parent 56873b92b0
commit 7bb6c84752
4 changed files with 205 additions and 168 deletions

View File

@@ -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);
}
//////////////////////////////////////////////////////////////////////////