Remade instancing

Former-commit-id: b297ed84e86a714c58d42219cc1dd8337e3a732c
This commit is contained in:
Lynix 2013-08-07 01:17:20 +02:00
parent c75887f600
commit 69d150272f
6 changed files with 146 additions and 93 deletions

View File

@ -17,13 +17,20 @@ class NzScene;
class NAZARA_API NzAbstractRenderTechnique : NzNonCopyable class NAZARA_API NzAbstractRenderTechnique : NzNonCopyable
{ {
public: public:
NzAbstractRenderTechnique() = default; NzAbstractRenderTechnique();
virtual ~NzAbstractRenderTechnique(); virtual ~NzAbstractRenderTechnique();
virtual void Clear(const NzScene* scene) = 0; virtual void Clear(const NzScene* scene) = 0;
virtual void Draw(const NzScene* scene) = 0; virtual void Draw(const NzScene* scene) = 0;
virtual void EnableInstancing(bool instancing);
virtual NzAbstractRenderQueue* GetRenderQueue() = 0; virtual NzAbstractRenderQueue* GetRenderQueue() = 0;
virtual bool IsInstancingEnabled() const;
protected:
bool m_instancingEnabled;
}; };
#endif // NAZARA_ABSTRACTRENDERTECHNIQUE_HPP #endif // NAZARA_ABSTRACTRENDERTECHNIQUE_HPP

View File

@ -30,7 +30,7 @@
/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci /// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci
// Le nombre maximum d'instances pouvant être géré par le Renderer // Le nombre maximum d'instances pouvant être géré par le Renderer
#define NAZARA_RENDERER_MAX_INSTANCES 8192 #define NAZARA_RENDERER_INSTANCE_BUFFER_SIZE 8192*64
// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) // Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution)
#define NAZARA_RENDERER_MEMORYLEAKTRACKER 0 #define NAZARA_RENDERER_MEMORYLEAKTRACKER 0

View File

@ -41,8 +41,10 @@ class NAZARA_API NzRenderer
static void Enable(nzRendererParameter parameter, bool enable); static void Enable(nzRendererParameter parameter, bool enable);
static void Flush(); static void Flush();
static NzVertexBuffer* GetInstanceBuffer();
static float GetLineWidth(); static float GetLineWidth();
static NzMatrix4f GetMatrix(nzMatrixType type); static NzMatrix4f GetMatrix(nzMatrixType type);
static nzUInt8 GetMaxAnisotropyLevel(); static nzUInt8 GetMaxAnisotropyLevel();
@ -72,8 +74,6 @@ class NAZARA_API NzRenderer
static void SetFaceCulling(nzFaceCulling cullingMode); static void SetFaceCulling(nzFaceCulling cullingMode);
static void SetFaceFilling(nzFaceFilling fillingMode); static void SetFaceFilling(nzFaceFilling fillingMode);
static void SetIndexBuffer(const NzIndexBuffer* indexBuffer); static void SetIndexBuffer(const NzIndexBuffer* indexBuffer);
static void SetInstancingData(const void* instancingData, unsigned int instanceCount);
static void SetInstancingDeclaration(const NzVertexDeclaration* declaration, unsigned int* newMaxInstanceCount);
static void SetLineWidth(float size); static void SetLineWidth(float size);
static void SetMatrix(nzMatrixType type, const NzMatrix4f& matrix); static void SetMatrix(nzMatrixType type, const NzMatrix4f& matrix);
static void SetPointSize(float size); static void SetPointSize(float size);

View File

@ -3,6 +3,34 @@
// 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/AbstractRenderTechnique.hpp> #include <Nazara/Graphics/AbstractRenderTechnique.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
NzAbstractRenderTechnique::NzAbstractRenderTechnique()
{
#ifdef NAZARA_DEBUG
if (!NzRenderer::IsInitialized())
{
NazaraError("NazaraRenderer is not initialized");
return;
}
#endif
m_instancingEnabled = NzRenderer::HasCapability(nzRendererCap_Instancing);
}
NzAbstractRenderTechnique::~NzAbstractRenderTechnique() = default; NzAbstractRenderTechnique::~NzAbstractRenderTechnique() = default;
void NzAbstractRenderTechnique::EnableInstancing(bool instancing)
{
if (NzRenderer::HasCapability(nzRendererCap_Instancing))
m_instancingEnabled = instancing;
else if (instancing)
NazaraError("NazaraRenderer does not support instancing");
}
bool NzAbstractRenderTechnique::IsInstancingEnabled() const
{
return m_instancingEnabled;
}

View File

@ -150,8 +150,16 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
{ {
const NzMaterial* material = matIt.first; const NzMaterial* material = matIt.first;
// Nous utilisons de l'instancing lorsqu'aucune lumière (autre que directionnelle) n'est active
// Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches
// (Le deferred shading n'a pas ce problème)
///FIXME: l'instancing fait-il réellement gagner en performances ?
///TODO: Activer l'instancing uniquement si plusieurs instances sont à rendre ?
bool instancing = m_instancingEnabled && m_renderQueue.lights.empty();
// On commence par récupérer le programme du matériau // On commence par récupérer le programme du matériau
const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0); const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0);
unsigned int lightCount = 0; unsigned int lightCount = 0;
@ -198,39 +206,79 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
// Gestion du draw call avant la boucle de rendu // Gestion du draw call avant la boucle de rendu
std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> drawFunc; std::function<void(nzPrimitiveMode, unsigned int, unsigned int)> drawFunc;
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> instancedDrawFunc;
unsigned int indexCount; unsigned int indexCount;
if (indexBuffer) if (indexBuffer)
{ {
drawFunc = NzRenderer::DrawIndexedPrimitives; drawFunc = NzRenderer::DrawIndexedPrimitives;
indexCount = indexBuffer->GetIndexCount(); indexCount = indexBuffer->GetIndexCount();
instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced;
} }
else else
{ {
drawFunc = NzRenderer::DrawPrimitives; drawFunc = NzRenderer::DrawPrimitives;
indexCount = vertexBuffer->GetVertexCount(); indexCount = vertexBuffer->GetVertexCount();
instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced;
} }
NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetIndexBuffer(indexBuffer);
NzRenderer::SetVertexBuffer(vertexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer);
for (const NzForwardRenderQueue::StaticData& data : staticData) nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode();
if (instancing)
{ {
// Calcul des lumières les plus proches NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer();
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4));
unsigned int stride = instanceBuffer->GetStride();
const NzForwardRenderQueue::StaticData* data = &staticData[0];
unsigned int instanceCount = staticData.size();
unsigned int maxInstanceCount = instanceBuffer->GetVertexCount();
while (instanceCount > 0)
{ {
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.aabb.GetCenter(), data.aabb.GetSquaredRadius()); unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount);
count -= lightCount; instanceCount -= renderedInstanceCount;
for (unsigned int i = 0; i < count; ++i) NzBufferMapper<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite);
lightManager.GetLight(i)->Enable(program, lightCount++); nzUInt8* ptr = reinterpret_cast<nzUInt8*>(mapper.GetPointer());
for (unsigned int i = 0; i < renderedInstanceCount; ++i)
{
std::memcpy(ptr, data->transformMatrix, sizeof(float)*16);
data++;
ptr += stride;
}
mapper.Unmap();
instancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount);
} }
}
else
{
for (const NzForwardRenderQueue::StaticData& data : staticData)
{
// Calcul des lumières les plus proches
if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty())
{
unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.aabb.GetCenter(), data.aabb.GetSquaredRadius());
count -= lightCount;
for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières for (unsigned int i = 0; i < count; ++i)
NzLight::Disable(program, i); lightManager.GetLight(i)->Enable(program, lightCount++);
}
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières
drawFunc(mesh->GetPrimitiveMode(), 0, indexCount); NzLight::Disable(program, i);
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
drawFunc(primitiveMode, 0, indexCount);
}
} }
staticData.clear(); staticData.clear();
} }

View File

@ -69,8 +69,8 @@ namespace
std::set<unsigned int> s_dirtyTextureUnits; std::set<unsigned int> s_dirtyTextureUnits;
std::vector<TextureUnit> s_textureUnits; std::vector<TextureUnit> s_textureUnits;
GLuint s_currentVAO = 0; GLuint s_currentVAO = 0;
NzVertexBuffer* s_instancingBuffer = nullptr; NzVertexBuffer s_instanceBuffer;
NzVertexBuffer* s_fullscreenQuadBuffer = nullptr; NzVertexBuffer s_fullscreenQuadBuffer;
MatrixUnit s_matrices[nzMatrixType_Max+1]; MatrixUnit s_matrices[nzMatrixType_Max+1];
NzRenderStates s_states; NzRenderStates s_states;
NzVector2ui s_targetSize; NzVector2ui s_targetSize;
@ -133,7 +133,7 @@ void NzRenderer::DrawFullscreenQuad()
EnableInstancing(false); EnableInstancing(false);
SetIndexBuffer(nullptr); SetIndexBuffer(nullptr);
SetVertexBuffer(s_fullscreenQuadBuffer); SetVertexBuffer(&s_fullscreenQuadBuffer);
if (!EnsureStateUpdate()) if (!EnsureStateUpdate())
{ {
@ -142,6 +142,9 @@ void NzRenderer::DrawFullscreenQuad()
} }
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if (s_useVertexArrayObjects)
glBindVertexArray(0);
} }
void NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount) void NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount)
@ -191,7 +194,9 @@ void NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode mode, unsigned int firstI
} }
glDrawElements(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset); glDrawElements(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset);
glBindVertexArray(0);
if (s_useVertexArrayObjects)
glBindVertexArray(0);
} }
void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount) void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstIndex, unsigned int indexCount)
@ -229,7 +234,8 @@ void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPr
return; return;
} }
if (instanceCount > NAZARA_RENDERER_MAX_INSTANCES) unsigned int maxInstanceCount = s_instanceBuffer.GetVertexCount();
if (instanceCount > maxInstanceCount)
{ {
NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" ); NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" );
return; return;
@ -259,7 +265,9 @@ void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPr
} }
glDrawElementsInstanced(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset, instanceCount); glDrawElementsInstanced(NzOpenGL::PrimitiveMode[mode], indexCount, type, offset, instanceCount);
glBindVertexArray(0);
if (s_useVertexArrayObjects)
glBindVertexArray(0);
} }
void NzRenderer::DrawPrimitives(nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount) void NzRenderer::DrawPrimitives(nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount)
@ -287,7 +295,9 @@ void NzRenderer::DrawPrimitives(nzPrimitiveMode mode, unsigned int firstVertex,
} }
glDrawArrays(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount); glDrawArrays(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount);
glBindVertexArray(0);
if (s_useVertexArrayObjects)
glBindVertexArray(0);
} }
void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount) void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitiveMode mode, unsigned int firstVertex, unsigned int vertexCount)
@ -319,7 +329,8 @@ void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitive
return; return;
} }
if (instanceCount > NAZARA_RENDERER_MAX_INSTANCES) unsigned int maxInstanceCount = s_instanceBuffer.GetVertexCount();
if (instanceCount > maxInstanceCount)
{ {
NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" ); NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " NazaraStringifyMacro(NAZARA_RENDERER_MAX_INSTANCES) ")" );
return; return;
@ -335,7 +346,9 @@ void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitive
} }
glDrawArraysInstanced(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount, instanceCount); glDrawArraysInstanced(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount, instanceCount);
glBindVertexArray(0);
if (s_useVertexArrayObjects)
glBindVertexArray(0);
} }
void NzRenderer::Enable(nzRendererParameter parameter, bool enable) void NzRenderer::Enable(nzRendererParameter parameter, bool enable)
@ -370,6 +383,20 @@ void NzRenderer::Flush()
glFlush(); glFlush();
} }
NzVertexBuffer* NzRenderer::GetInstanceBuffer()
{
#if NAZARA_RENDERER_SAFE
if (!s_capabilities[nzRendererCap_Instancing])
{
NazaraError("Instancing not supported");
return nullptr;
}
#endif
s_updateFlags |= Update_VAO;
return &s_instanceBuffer;
}
float NzRenderer::GetLineWidth() float NzRenderer::GetLineWidth()
{ {
#ifdef NAZARA_DEBUG #ifdef NAZARA_DEBUG
@ -579,7 +606,8 @@ bool NzRenderer::Initialize()
s_vertexBuffer = nullptr; s_vertexBuffer = nullptr;
s_updateFlags = (Update_Matrices | Update_Program | Update_VAO); s_updateFlags = (Update_Matrices | Update_Program | Update_VAO);
s_fullscreenQuadBuffer = new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzBufferStorage_Hardware, nzBufferUsage_Static); s_fullscreenQuadBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_XY), 4, nzBufferStorage_Hardware, nzBufferUsage_Static);
float vertices[4*2] = float vertices[4*2] =
{ {
-1.f, -1.f, -1.f, -1.f,
@ -588,7 +616,7 @@ bool NzRenderer::Initialize()
1.f, 1.f, 1.f, 1.f,
}; };
if (!s_fullscreenQuadBuffer->Fill(vertices, 0, 4)) if (!s_fullscreenQuadBuffer.Fill(vertices, 0, 4))
{ {
NazaraError("Failed to fill fullscreen quad buffer"); NazaraError("Failed to fill fullscreen quad buffer");
Uninitialize(); Uninitialize();
@ -600,17 +628,14 @@ bool NzRenderer::Initialize()
{ {
try try
{ {
s_instancingBuffer = new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_Matrix4), NAZARA_RENDERER_MAX_INSTANCES, nzBufferStorage_Hardware, nzBufferUsage_Dynamic); s_instanceBuffer.Reset(NzVertexDeclaration::Get(nzVertexLayout_Matrix4), NAZARA_RENDERER_INSTANCE_BUFFER_SIZE/sizeof(NzMatrix4f), nzBufferStorage_Hardware, nzBufferUsage_Dynamic);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
s_capabilities[nzRendererCap_Instancing] = false; s_capabilities[nzRendererCap_Instancing] = false;
s_instancingBuffer = nullptr;
NazaraError("Failed to create instancing buffer: " + NzString(e.what())); ///TODO: Noexcept NazaraError("Failed to create instancing buffer: " + NzString(e.what())); ///TODO: Noexcept
} }
} }
else
s_instancingBuffer = nullptr;
if (!NzMaterial::Initialize()) if (!NzMaterial::Initialize())
{ {
@ -788,61 +813,6 @@ void NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer)
} }
} }
void NzRenderer::SetInstancingData(const void* instancingData, unsigned int instanceCount)
{
#if NAZARA_RENDERER_SAFE
if (!s_capabilities[nzRendererCap_Instancing])
{
NazaraError("Instancing not supported");
return;
}
if (!instancingData)
{
NazaraError("Instancing data must be valid");
return;
}
if (instanceCount == 0)
{
NazaraError("Instance count must be over 0");
return;
}
unsigned int maxInstanceCount = s_instancingBuffer->GetVertexCount();
if (instanceCount > maxInstanceCount)
{
NazaraError("Instance count is over maximum instance count (" + NzString::Number(instanceCount) + " >= " + NzString::Number(maxInstanceCount) + ")");
return;
}
#endif
if (!s_instancingBuffer->FillVertices(instancingData, 0, instanceCount, true))
NazaraError("Failed to fill instancing buffer");
}
void NzRenderer::SetInstancingDeclaration(const NzVertexDeclaration* declaration, unsigned int* newMaxInstanceCount)
{
#if NAZARA_RENDERER_SAFE
if (!s_capabilities[nzRendererCap_Instancing])
{
NazaraError("Instancing not supported");
return;
}
if (!declaration)
{
NazaraError("Declaration must be valid");
return;
}
#endif
s_instancingBuffer->SetVertexDeclaration(declaration);
if (newMaxInstanceCount)
*newMaxInstanceCount = s_instancingBuffer->GetVertexCount();
}
void NzRenderer::SetLineWidth(float width) void NzRenderer::SetLineWidth(float width)
{ {
#if NAZARA_RENDERER_SAFE #if NAZARA_RENDERER_SAFE
@ -1185,11 +1155,8 @@ void NzRenderer::Uninitialize()
NzDebugDrawer::Uninitialize(); NzDebugDrawer::Uninitialize();
// Libération des buffers // Libération des buffers
delete s_fullscreenQuadBuffer; s_fullscreenQuadBuffer.Reset();
delete s_instancingBuffer; s_instanceBuffer.Reset();
s_fullscreenQuadBuffer = nullptr;
s_instancingBuffer = nullptr;
// Libération des VAOs // Libération des VAOs
for (auto& pair : s_vaos) for (auto& pair : s_vaos)
@ -1419,8 +1386,11 @@ bool NzRenderer::EnsureStateUpdate()
if (s_instancing) if (s_instancing)
{ {
bufferOffset = s_instancingBuffer->GetStartOffset(); NzHardwareBuffer* instanceBufferImpl = static_cast<NzHardwareBuffer*>(s_instanceBuffer.GetBuffer()->GetImpl());
vertexDeclaration = s_instancingBuffer->GetVertexDeclaration(); instanceBufferImpl->Bind();
bufferOffset = s_instanceBuffer.GetStartOffset();
vertexDeclaration = s_instanceBuffer.GetVertexDeclaration();
stride = vertexDeclaration->GetStride(); stride = vertexDeclaration->GetStride();
for (unsigned int i = nzAttributeUsage_FirstInstanceData; i <= nzAttributeUsage_LastInstanceData; ++i) for (unsigned int i = nzAttributeUsage_FirstInstanceData; i <= nzAttributeUsage_LastInstanceData; ++i)
{ {