Graphics/RenderQueue: Remake it with a naive implementation

The idea is to improve it in the future, after profiling
This commit is contained in:
Jérôme Leclercq 2021-07-28 13:11:03 +02:00
parent 335b48056f
commit 3de0edec6f
8 changed files with 48 additions and 228 deletions

View File

@ -71,6 +71,9 @@ namespace Nz
using type = void; //< FIXME: I can't make SFINAE work using type = void; //< FIXME: I can't make SFINAE work
}; };
template<typename T>
using Pointer = T*;
template<typename T> template<typename T>
bool Serialize(SerializationContext& context, T&& value); bool Serialize(SerializationContext& context, T&& value);

View File

@ -8,6 +8,7 @@
#define NAZARA_ELEMENTRENDERER_HPP #define NAZARA_ELEMENTRENDERER_HPP
#include <Nazara/Prerequisites.hpp> #include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Graphics/Enums.hpp> #include <Nazara/Graphics/Enums.hpp>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -23,7 +24,7 @@ namespace Nz
ElementRenderer() = default; ElementRenderer() = default;
virtual ~ElementRenderer(); virtual ~ElementRenderer();
virtual void Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount) = 0; virtual void Render(CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount) = 0;
}; };
} }

View File

@ -8,38 +8,15 @@
#define NAZARA_RENDERQUEUE_HPP #define NAZARA_RENDERQUEUE_HPP
#include <Nazara/Prerequisites.hpp> #include <Nazara/Prerequisites.hpp>
#include <memory>
#include <vector> #include <vector>
namespace Nz namespace Nz
{ {
class RenderQueueInternal
{
public:
using Index = UInt64;
RenderQueueInternal() = default;
RenderQueueInternal(const RenderQueueInternal&) = default;
RenderQueueInternal(RenderQueueInternal&&) = default;
~RenderQueueInternal() = default;
RenderQueueInternal& operator=(const RenderQueueInternal&) = default;
RenderQueueInternal& operator=(RenderQueueInternal&&) = default;
protected:
using RenderDataPair = std::pair<Index, std::size_t>;
void Sort();
std::vector<RenderDataPair> m_orderedRenderQueue;
};
template<typename RenderData> template<typename RenderData>
class RenderQueue : public RenderQueueInternal class RenderQueue
{ {
public: public:
class const_iterator; using const_iterator = const RenderData*;
friend const_iterator;
using size_type = std::size_t; using size_type = std::size_t;
RenderQueue() = default; RenderQueue() = default;
@ -63,37 +40,8 @@ namespace Nz
RenderQueue& operator=(RenderQueue&&) noexcept = default; RenderQueue& operator=(RenderQueue&&) noexcept = default;
private: private:
const RenderData& GetData(std::size_t i) const;
std::vector<RenderData> m_data; std::vector<RenderData> m_data;
}; };
template<typename RenderData>
class RenderQueue<RenderData>::const_iterator : public std::iterator<std::forward_iterator_tag, const RenderData>
{
friend RenderQueue;
public:
const_iterator(const const_iterator& it);
const RenderData& operator*() const;
const_iterator& operator=(const const_iterator& it);
const_iterator& operator++();
const_iterator operator++(int);
bool operator==(const const_iterator& rhs) const;
bool operator!=(const const_iterator& rhs) const;
void swap(const_iterator& rhs);
private:
const_iterator(const RenderQueue* queue, std::size_t nextId);
std::size_t m_nextDataId;
const RenderQueue* m_queue;
};
} }
#include <Nazara/Graphics/RenderQueue.inl> #include <Nazara/Graphics/RenderQueue.inl>

View File

@ -3,14 +3,13 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/RenderQueue.hpp> #include <Nazara/Graphics/RenderQueue.hpp>
#include <Nazara/Core/Error.hpp> #include <algorithm>
namespace Nz namespace Nz
{ {
template<typename RenderData> template<typename RenderData>
void RenderQueue<RenderData>::Clear() void RenderQueue<RenderData>::Clear()
{ {
m_orderedRenderQueue.clear();
m_data.clear(); m_data.clear();
} }
@ -24,113 +23,33 @@ namespace Nz
template<typename IndexFunc> template<typename IndexFunc>
void RenderQueue<RenderData>::Sort(IndexFunc&& func) void RenderQueue<RenderData>::Sort(IndexFunc&& func)
{ {
m_orderedRenderQueue.clear(); std::sort(m_data.begin(), m_data.end(), [&](const RenderData& lhs, const RenderData& rhs)
m_orderedRenderQueue.reserve(m_data.size()); {
return func(lhs) < func(rhs);
std::size_t dataIndex = 0; });
for (const RenderData& renderData : m_data)
m_orderedRenderQueue.emplace_back(func(renderData), dataIndex++);
RenderQueueInternal::Sort();
} }
template<typename RenderData> template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::begin() const auto RenderQueue<RenderData>::begin() const -> const_iterator
{ {
return const_iterator(this, 0); return &m_data[0];
} }
template<typename RenderData> template<typename RenderData>
bool RenderQueue<RenderData>::empty() const bool RenderQueue<RenderData>::empty() const
{ {
return m_orderedRenderQueue.empty(); return m_data.empty();
} }
template<typename RenderData> template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::end() const auto RenderQueue<RenderData>::end() const -> const_iterator
{ {
return const_iterator(this, m_orderedRenderQueue.size()); return begin() + m_data.size();
} }
template<typename RenderData> template<typename RenderData>
typename RenderQueue<RenderData>::size_type RenderQueue<RenderData>::size() const auto RenderQueue<RenderData>::size() const -> size_type
{ {
return m_orderedRenderQueue.size(); return m_data.size();
}
template<typename RenderData>
const RenderData& RenderQueue<RenderData>::GetData(std::size_t i) const
{
NazaraAssert(i < m_orderedRenderQueue.size(), "Cannot dereference post-end iterator");
return m_data[m_orderedRenderQueue[i].second];
}
template<typename RenderData>
RenderQueue<RenderData>::const_iterator::const_iterator(const RenderQueue* queue, std::size_t nextId) :
m_nextDataId(nextId),
m_queue(queue)
{
}
template<typename RenderData>
RenderQueue<RenderData>::const_iterator::const_iterator(const const_iterator& it) :
m_nextDataId(it.m_nextDataId),
m_queue(it.m_queue)
{
}
template<typename RenderData>
const RenderData& RenderQueue<RenderData>::const_iterator::operator*() const
{
return m_queue->GetData(m_nextDataId);
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator& RenderQueue<RenderData>::const_iterator::operator=(const const_iterator& it)
{
m_nextDataId = it.m_nextDataId;
m_queue = it.m_queue;
return *this;
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator& RenderQueue<RenderData>::const_iterator::operator++()
{
++m_nextDataId;
return *this;
}
template<typename RenderData>
typename RenderQueue<RenderData>::const_iterator RenderQueue<RenderData>::const_iterator::operator++(int)
{
return iterator(m_queue, m_nextDataId++);
}
template<typename RenderData>
bool RenderQueue<RenderData>::const_iterator::operator==(const typename RenderQueue<RenderData>::const_iterator& rhs) const
{
NazaraAssert(m_queue == rhs.m_queue, "Cannot compare iterator coming from different queues");
return m_nextDataId == rhs.m_nextDataId;
}
template<typename RenderData>
bool RenderQueue<RenderData>::const_iterator::operator!=(const typename RenderQueue<RenderData>::const_iterator& rhs) const
{
return !operator==(rhs);
}
template<typename RenderData>
void RenderQueue<RenderData>::const_iterator::swap(typename RenderQueue<RenderData>::const_iterator& rhs)
{
NazaraAssert(m_queue == rhs.m_queue, "Cannot swap iterator coming from different queues");
using std::swap;
swap(m_nextDataId, rhs.m_nextDataId);
} }
} }

View File

@ -18,7 +18,7 @@ namespace Nz
SubmeshRenderer() = default; SubmeshRenderer() = default;
~SubmeshRenderer() = default; ~SubmeshRenderer() = default;
void Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount) override; void Render(CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount) override;
}; };
} }

View File

@ -325,29 +325,7 @@ namespace Nz
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
auto it = m_depthPrepassRenderQueue.begin(); ProcessRenderQueue(builder, m_depthPrepassRenderQueue);
while (it != m_depthPrepassRenderQueue.end())
{
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
m_temporaryElementList.push_back(element);
++it;
while (it != m_depthPrepassRenderQueue.end() && (*it)->GetElementType() == elementType)
{
m_temporaryElementList.push_back(*it);
++it;
}
if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType])
continue;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, m_temporaryElementList.data(), m_temporaryElementList.size());
m_temporaryElementList.clear();
}
}); });
FramePass& forwardPass = frameGraph.AddPass("Forward pass"); FramePass& forwardPass = frameGraph.AddPass("Forward pass");
@ -375,29 +353,7 @@ namespace Nz
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
auto it = m_forwardRenderQueue.begin(); ProcessRenderQueue(builder, m_forwardRenderQueue);
while (it != m_forwardRenderQueue.end())
{
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
m_temporaryElementList.push_back(element);
++it;
while (it != m_forwardRenderQueue.end() && (*it)->GetElementType() == elementType)
{
m_temporaryElementList.push_back(*it);
++it;
}
if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType])
continue;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, m_temporaryElementList.data(), m_temporaryElementList.size());
m_temporaryElementList.clear();
}
}); });
} }
@ -425,6 +381,31 @@ namespace Nz
it->second.usedCount++; it->second.usedCount++;
} }
void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue<RenderElement*>& renderQueue)
{
auto it = renderQueue.begin();
auto itEnd = renderQueue.end();
while (it != itEnd)
{
const RenderElement* element = *it;
UInt8 elementType = element->GetElementType();
const Pointer<const RenderElement>* first = it;
++it;
while (it != itEnd && (*it)->GetElementType() == elementType)
++it;
std::size_t count = it - first;
if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType])
continue;
ElementRenderer& elementRenderer = *m_elementRenderers[elementType];
elementRenderer.Render(builder, first, count);
}
}
void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* material) void ForwardFramePipeline::UnregisterMaterialPass(MaterialPass* material)
{ {
auto it = m_materials.find(material); auto it = m_materials.find(material);

View File

@ -1,32 +0,0 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/RenderQueue.hpp>
#include <algorithm>
#include <Nazara/Graphics/Debug.hpp>
namespace Nz
{
void RenderQueueInternal::Sort()
{
std::sort(m_orderedRenderQueue.begin(), m_orderedRenderQueue.end(), [](const RenderDataPair& lhs, const RenderDataPair& rhs)
{
/*
Original code:
if (lhs.first == rhs.first)
return lhs.second < rhs.second;
else
return lhs.first < rhs.first;
Clang seems to the the only one to prevent branching with the original code (using cmov)
Rewriting the code with bit ops seems to prevent branching with Clang/GCC/MSVC
*/
bool equal = lhs.first == rhs.first;
bool compareFirst = lhs.first < rhs.first;
bool compareSecond = lhs.second < rhs.second;
return (equal & compareSecond) | (!equal & compareFirst);
});
}
}

View File

@ -10,7 +10,7 @@
namespace Nz namespace Nz
{ {
void SubmeshRenderer::Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount) void SubmeshRenderer::Render(CommandBufferBuilder& commandBuffer, const Pointer<const RenderElement>* elements, std::size_t elementCount)
{ {
const AbstractBuffer* currentIndexBuffer = nullptr; const AbstractBuffer* currentIndexBuffer = nullptr;
const AbstractBuffer* currentVertexBuffer = nullptr; const AbstractBuffer* currentVertexBuffer = nullptr;