diff --git a/include/Nazara/Graphics/AbstractRenderTechnique.hpp b/include/Nazara/Graphics/AbstractRenderTechnique.hpp index b390597e3..f59548145 100644 --- a/include/Nazara/Graphics/AbstractRenderTechnique.hpp +++ b/include/Nazara/Graphics/AbstractRenderTechnique.hpp @@ -17,13 +17,20 @@ class NzScene; class NAZARA_API NzAbstractRenderTechnique : NzNonCopyable { public: - NzAbstractRenderTechnique() = default; + NzAbstractRenderTechnique(); virtual ~NzAbstractRenderTechnique(); virtual void Clear(const NzScene* scene) = 0; virtual void Draw(const NzScene* scene) = 0; + virtual void EnableInstancing(bool instancing); + virtual NzAbstractRenderQueue* GetRenderQueue() = 0; + + virtual bool IsInstancingEnabled() const; + + protected: + bool m_instancingEnabled; }; #endif // NAZARA_ABSTRACTRENDERTECHNIQUE_HPP diff --git a/include/Nazara/Renderer/Config.hpp b/include/Nazara/Renderer/Config.hpp index a300a3691..09331cb4a 100644 --- a/include/Nazara/Renderer/Config.hpp +++ b/include/Nazara/Renderer/Config.hpp @@ -30,7 +30,7 @@ /// 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 -#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) #define NAZARA_RENDERER_MEMORYLEAKTRACKER 0 diff --git a/include/Nazara/Renderer/Renderer.hpp b/include/Nazara/Renderer/Renderer.hpp index 856175598..c20e87b8d 100644 --- a/include/Nazara/Renderer/Renderer.hpp +++ b/include/Nazara/Renderer/Renderer.hpp @@ -41,8 +41,10 @@ class NAZARA_API NzRenderer static void Enable(nzRendererParameter parameter, bool enable); + static void Flush(); + static NzVertexBuffer* GetInstanceBuffer(); static float GetLineWidth(); static NzMatrix4f GetMatrix(nzMatrixType type); static nzUInt8 GetMaxAnisotropyLevel(); @@ -72,8 +74,6 @@ class NAZARA_API NzRenderer static void SetFaceCulling(nzFaceCulling cullingMode); static void SetFaceFilling(nzFaceFilling fillingMode); 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 SetMatrix(nzMatrixType type, const NzMatrix4f& matrix); static void SetPointSize(float size); diff --git a/src/Nazara/Graphics/AbstractRenderTechnique.cpp b/src/Nazara/Graphics/AbstractRenderTechnique.cpp index c41001b7b..2d1bfdcd3 100644 --- a/src/Nazara/Graphics/AbstractRenderTechnique.cpp +++ b/src/Nazara/Graphics/AbstractRenderTechnique.cpp @@ -3,6 +3,34 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include #include +NzAbstractRenderTechnique::NzAbstractRenderTechnique() +{ + #ifdef NAZARA_DEBUG + if (!NzRenderer::IsInitialized()) + { + NazaraError("NazaraRenderer is not initialized"); + return; + } + #endif + + m_instancingEnabled = NzRenderer::HasCapability(nzRendererCap_Instancing); +} + 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; +} diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index d875ac3a5..aa6e93c5e 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -150,8 +150,16 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) { 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 - const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0); + const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0); unsigned int lightCount = 0; @@ -198,39 +206,79 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) // Gestion du draw call avant la boucle de rendu std::function drawFunc; + std::function instancedDrawFunc; unsigned int indexCount; if (indexBuffer) { drawFunc = NzRenderer::DrawIndexedPrimitives; indexCount = indexBuffer->GetIndexCount(); + instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; } else { drawFunc = NzRenderer::DrawPrimitives; indexCount = vertexBuffer->GetVertexCount(); + instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; } NzRenderer::SetIndexBuffer(indexBuffer); NzRenderer::SetVertexBuffer(vertexBuffer); - for (const NzForwardRenderQueue::StaticData& data : staticData) + nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode(); + if (instancing) { - // Calcul des lumières les plus proches - if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty()) + NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); + + 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()); - count -= lightCount; + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; - for (unsigned int i = 0; i < count; ++i) - lightManager.GetLight(i)->Enable(program, lightCount++); + NzBufferMapper mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite); + nzUInt8* ptr = reinterpret_cast(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 - NzLight::Disable(program, i); + for (unsigned int i = 0; i < count; ++i) + lightManager.GetLight(i)->Enable(program, lightCount++); + } - NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); - drawFunc(mesh->GetPrimitiveMode(), 0, indexCount); + for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières + NzLight::Disable(program, i); + + NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); + drawFunc(primitiveMode, 0, indexCount); + } } staticData.clear(); } diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 1de197a6f..f7a94ba4e 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -69,8 +69,8 @@ namespace std::set s_dirtyTextureUnits; std::vector s_textureUnits; GLuint s_currentVAO = 0; - NzVertexBuffer* s_instancingBuffer = nullptr; - NzVertexBuffer* s_fullscreenQuadBuffer = nullptr; + NzVertexBuffer s_instanceBuffer; + NzVertexBuffer s_fullscreenQuadBuffer; MatrixUnit s_matrices[nzMatrixType_Max+1]; NzRenderStates s_states; NzVector2ui s_targetSize; @@ -133,7 +133,7 @@ void NzRenderer::DrawFullscreenQuad() EnableInstancing(false); SetIndexBuffer(nullptr); - SetVertexBuffer(s_fullscreenQuadBuffer); + SetVertexBuffer(&s_fullscreenQuadBuffer); if (!EnsureStateUpdate()) { @@ -142,6 +142,9 @@ void NzRenderer::DrawFullscreenQuad() } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + if (s_useVertexArrayObjects) + glBindVertexArray(0); } 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); - glBindVertexArray(0); + + if (s_useVertexArrayObjects) + glBindVertexArray(0); } 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; } - 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) ")" ); return; @@ -259,7 +265,9 @@ void NzRenderer::DrawIndexedPrimitivesInstanced(unsigned int instanceCount, nzPr } 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) @@ -287,7 +295,9 @@ void NzRenderer::DrawPrimitives(nzPrimitiveMode mode, unsigned int firstVertex, } 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) @@ -319,7 +329,8 @@ void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitive 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) ")" ); return; @@ -335,7 +346,9 @@ void NzRenderer::DrawPrimitivesInstanced(unsigned int instanceCount, nzPrimitive } glDrawArraysInstanced(NzOpenGL::PrimitiveMode[mode], firstVertex, vertexCount, instanceCount); - glBindVertexArray(0); + + if (s_useVertexArrayObjects) + glBindVertexArray(0); } void NzRenderer::Enable(nzRendererParameter parameter, bool enable) @@ -370,6 +383,20 @@ void NzRenderer::Flush() 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() { #ifdef NAZARA_DEBUG @@ -579,7 +606,8 @@ bool NzRenderer::Initialize() s_vertexBuffer = nullptr; 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] = { -1.f, -1.f, @@ -588,7 +616,7 @@ bool NzRenderer::Initialize() 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"); Uninitialize(); @@ -600,17 +628,14 @@ bool NzRenderer::Initialize() { 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) { s_capabilities[nzRendererCap_Instancing] = false; - s_instancingBuffer = nullptr; NazaraError("Failed to create instancing buffer: " + NzString(e.what())); ///TODO: Noexcept } } - else - s_instancingBuffer = nullptr; 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) { #if NAZARA_RENDERER_SAFE @@ -1185,11 +1155,8 @@ void NzRenderer::Uninitialize() NzDebugDrawer::Uninitialize(); // Libération des buffers - delete s_fullscreenQuadBuffer; - delete s_instancingBuffer; - - s_fullscreenQuadBuffer = nullptr; - s_instancingBuffer = nullptr; + s_fullscreenQuadBuffer.Reset(); + s_instanceBuffer.Reset(); // Libération des VAOs for (auto& pair : s_vaos) @@ -1419,8 +1386,11 @@ bool NzRenderer::EnsureStateUpdate() if (s_instancing) { - bufferOffset = s_instancingBuffer->GetStartOffset(); - vertexDeclaration = s_instancingBuffer->GetVertexDeclaration(); + NzHardwareBuffer* instanceBufferImpl = static_cast(s_instanceBuffer.GetBuffer()->GetImpl()); + instanceBufferImpl->Bind(); + + bufferOffset = s_instanceBuffer.GetStartOffset(); + vertexDeclaration = s_instanceBuffer.GetVertexDeclaration(); stride = vertexDeclaration->GetStride(); for (unsigned int i = nzAttributeUsage_FirstInstanceData; i <= nzAttributeUsage_LastInstanceData; ++i) {