diff --git a/include/Nazara/Graphics/ElementRenderer.hpp b/include/Nazara/Graphics/ElementRenderer.hpp index a2a07a28e..656ef354b 100644 --- a/include/Nazara/Graphics/ElementRenderer.hpp +++ b/include/Nazara/Graphics/ElementRenderer.hpp @@ -18,6 +18,8 @@ namespace Nz { class CommandBufferBuilder; class RenderElement; + class RenderFrame; + struct ElementRendererData; class NAZARA_GRAPHICS_API ElementRenderer { @@ -25,7 +27,16 @@ namespace Nz ElementRenderer() = default; virtual ~ElementRenderer(); - virtual void Render(CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) = 0; + virtual std::unique_ptr InstanciateData() = 0; + virtual void Prepare(ElementRendererData& rendererData, RenderFrame& currentFrame, const Pointer* elements, std::size_t elementCount); + virtual void Render(ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) = 0; + virtual void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame); + }; + + struct NAZARA_GRAPHICS_API ElementRendererData + { + ElementRendererData() = default; + virtual ~ElementRendererData(); }; } diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 6d5ad854b..76adb8436 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -7,13 +7,20 @@ #ifndef NAZARA_ENUMS_GRAPHICS_HPP #define NAZARA_ENUMS_GRAPHICS_HPP +#include + namespace Nz { enum class BasicRenderElement { - Submesh = 0, + SpriteChain = 0, + Submesh = 1, + + Max = Submesh }; + constexpr std::size_t BasicRenderElementCount = UnderlyingCast(BasicRenderElement::Max) + 1; + enum class CullTest { Box, diff --git a/include/Nazara/Graphics/ForwardFramePipeline.hpp b/include/Nazara/Graphics/ForwardFramePipeline.hpp index 9e2f9e333..f5baa96a3 100644 --- a/include/Nazara/Graphics/ForwardFramePipeline.hpp +++ b/include/Nazara/Graphics/ForwardFramePipeline.hpp @@ -25,6 +25,8 @@ namespace Nz { + class RenderFrame; + class NAZARA_GRAPHICS_API ForwardFramePipeline : public FramePipeline { public: @@ -51,7 +53,7 @@ namespace Nz BakedFrameGraph BuildFrameGraph(); void RegisterMaterialPass(MaterialPass* material); - void ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue& renderQueue); + template void ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback); void UnregisterMaterialPass(MaterialPass* material); struct MaterialData @@ -79,6 +81,7 @@ namespace Nz std::size_t visibilityHash = 0; std::vector> depthPrepassRenderElements; std::vector> forwardRenderElements; + std::vector> elementRendererData; RenderQueueRegistry depthPrepassRegistry; RenderQueueRegistry forwardRegistry; RenderQueue depthPrepassRenderQueue; @@ -100,6 +103,7 @@ namespace Nz std::vector> m_elementRenderers; std::vector m_visibleRenderables; BakedFrameGraph m_bakedFrameGraph; + RenderFrame* m_currentRenderFrame; bool m_rebuildFrameGraph; }; } diff --git a/include/Nazara/Graphics/RenderQueueRegistry.hpp b/include/Nazara/Graphics/RenderQueueRegistry.hpp index f3ada7c06..b91ced164 100644 --- a/include/Nazara/Graphics/RenderQueueRegistry.hpp +++ b/include/Nazara/Graphics/RenderQueueRegistry.hpp @@ -14,6 +14,7 @@ namespace Nz { class AbstractBuffer; class RenderPipeline; + class VertexDeclaration; class RenderQueueRegistry { @@ -26,15 +27,18 @@ namespace Nz inline std::size_t FetchLayerIndex(int renderLayer) const; inline std::size_t FetchPipelineIndex(const RenderPipeline* pipeline) const; inline std::size_t FetchVertexBuffer(const AbstractBuffer* vertexBuffer) const; + inline std::size_t FetchVertexDeclaration(const VertexDeclaration* vertexDeclaration) const; inline void RegisterLayer(int renderLayer); inline void RegisterPipeline(const RenderPipeline* pipeline); inline void RegisterVertexBuffer(const AbstractBuffer* vertexBuffer); + inline void RegisterVertexDeclaration(const VertexDeclaration* vertexDeclaration); private: robin_hood::unordered_map m_renderLayerRegistry; robin_hood::unordered_map m_pipelineRegistry; robin_hood::unordered_map m_vertexBufferRegistry; + robin_hood::unordered_map m_vertexDeclarationRegistry; }; } diff --git a/include/Nazara/Graphics/RenderQueueRegistry.inl b/include/Nazara/Graphics/RenderQueueRegistry.inl index 7889e7530..8a3b24643 100644 --- a/include/Nazara/Graphics/RenderQueueRegistry.inl +++ b/include/Nazara/Graphics/RenderQueueRegistry.inl @@ -38,6 +38,14 @@ namespace Nz return it->second; } + inline std::size_t RenderQueueRegistry::FetchVertexDeclaration(const VertexDeclaration* vertexDeclaration) const + { + auto it = m_vertexDeclarationRegistry.find(vertexDeclaration); + assert(it != m_vertexDeclarationRegistry.end()); + + return it->second; + } + inline void RenderQueueRegistry::RegisterLayer(int renderLayer) { m_renderLayerRegistry.try_emplace(renderLayer, m_renderLayerRegistry.size()); @@ -52,6 +60,11 @@ namespace Nz { m_vertexBufferRegistry.try_emplace(vertexBuffer, m_vertexBufferRegistry.size()); } + + inline void RenderQueueRegistry::RegisterVertexDeclaration(const VertexDeclaration* vertexDeclaration) + { + m_vertexDeclarationRegistry.try_emplace(vertexDeclaration, m_vertexDeclarationRegistry.size()); + } } #include diff --git a/include/Nazara/Graphics/RenderSpriteChain.hpp b/include/Nazara/Graphics/RenderSpriteChain.hpp new file mode 100644 index 000000000..a60749377 --- /dev/null +++ b/include/Nazara/Graphics/RenderSpriteChain.hpp @@ -0,0 +1,53 @@ +// 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 + +#pragma once + +#ifndef NAZARA_RENDERSUBMESH_HPP +#define NAZARA_RENDERSUBMESH_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class AbstractBuffer; + class RenderPipeline; + class ShaderBinding; + class VertexDeclaration; + + class RenderSpriteChain : public RenderElement + { + public: + inline RenderSpriteChain(int renderLayer, std::shared_ptr renderPipeline, std::shared_ptr vertexDeclaration, std::size_t spriteCount, const void* spriteData, const ShaderBinding& materialBinding, const ShaderBinding& instanceBinding); + ~RenderSpriteChain() = default; + + inline UInt64 ComputeSortingScore(const RenderQueueRegistry& registry) const override; + + inline const ShaderBinding& GetInstanceBinding() const; + inline const ShaderBinding& GetMaterialBinding() const; + inline const RenderPipeline* GetRenderPipeline() const; + inline std::size_t GetSpriteCount() const; + inline const void* GetSpriteData() const; + inline const VertexDeclaration* GetVertexDeclaration() const; + + inline void Register(RenderQueueRegistry& registry) const override; + + private: + std::shared_ptr m_renderPipeline; + std::shared_ptr m_vertexDeclaration; + std::size_t m_spriteCount; + const void* m_spriteData; + const ShaderBinding& m_instanceBinding; + const ShaderBinding& m_materialBinding; + int m_renderLayer; + }; +} + +#include + +#endif diff --git a/include/Nazara/Graphics/RenderSpriteChain.inl b/include/Nazara/Graphics/RenderSpriteChain.inl new file mode 100644 index 000000000..f94a758cd --- /dev/null +++ b/include/Nazara/Graphics/RenderSpriteChain.inl @@ -0,0 +1,80 @@ +// 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 +#include + +namespace Nz +{ + inline RenderSpriteChain::RenderSpriteChain(int renderLayer, std::shared_ptr renderPipeline, std::shared_ptr vertexDeclaration, std::size_t spriteCount, const void* spriteData, const ShaderBinding& materialBinding, const ShaderBinding& instanceBinding) : + RenderElement(BasicRenderElement::SpriteChain), + m_renderPipeline(std::move(renderPipeline)), + m_vertexDeclaration(std::move(vertexDeclaration)), + m_spriteCount(spriteCount), + m_spriteData(spriteData), + m_instanceBinding(instanceBinding), + m_materialBinding(materialBinding), + m_renderLayer(renderLayer) + { + } + + inline UInt64 RenderSpriteChain::ComputeSortingScore(const RenderQueueRegistry& registry) const + { + UInt64 layerIndex = registry.FetchLayerIndex(m_renderLayer); + UInt64 elementType = GetElementType(); + UInt64 pipelineIndex = registry.FetchPipelineIndex(m_renderPipeline.get()); + UInt64 vertexDeclarationIndex = registry.FetchVertexDeclaration(m_vertexDeclaration.get()); + + // RQ index: + // - Layer (8bits) + // - Element type (4bits) + // - Pipeline (16bits) + // - VertexDeclaration (8bits) + // - ?? (24bits) - Depth? + + return (layerIndex & 0xFF) << 60 | + (elementType & 0xF) << 52 | + (pipelineIndex & 0xFFFF) << 36 | + (vertexDeclarationIndex & 0xFF) << 24; + } + + inline const ShaderBinding& RenderSpriteChain::GetInstanceBinding() const + { + return m_instanceBinding; + } + + inline const ShaderBinding& RenderSpriteChain::GetMaterialBinding() const + { + return m_materialBinding; + } + + inline const RenderPipeline* RenderSpriteChain::GetRenderPipeline() const + { + return m_renderPipeline.get(); + } + + inline std::size_t RenderSpriteChain::GetSpriteCount() const + { + return m_spriteCount; + } + + inline const void* RenderSpriteChain::GetSpriteData() const + { + return m_spriteData; + } + + inline const VertexDeclaration* RenderSpriteChain::GetVertexDeclaration() const + { + return m_vertexDeclaration.get(); + } + + inline void RenderSpriteChain::Register(RenderQueueRegistry& registry) const + { + registry.RegisterLayer(m_renderLayer); + registry.RegisterPipeline(m_renderPipeline.get()); + registry.RegisterVertexDeclaration(m_vertexDeclaration.get()); + } +} + +#include diff --git a/include/Nazara/Graphics/RenderSubmesh.inl b/include/Nazara/Graphics/RenderSubmesh.inl index a47b0166f..e263eb65b 100644 --- a/include/Nazara/Graphics/RenderSubmesh.inl +++ b/include/Nazara/Graphics/RenderSubmesh.inl @@ -27,8 +27,8 @@ namespace Nz UInt64 vertexBufferIndex = registry.FetchVertexBuffer(m_vertexBuffer.get()); // RQ index: - // - Element type (4bits) // - Layer (8bits) + // - Element type (4bits) // - Pipeline (16bits) // - VertexBuffer (8bits) // - ?? (24bits) - Depth? diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp new file mode 100644 index 000000000..3326c2071 --- /dev/null +++ b/include/Nazara/Graphics/Sprite.hpp @@ -0,0 +1,46 @@ +// 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 + +#pragma once + +#ifndef NAZARA_SPRITE_HPP +#define NAZARA_SPRITE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_GRAPHICS_API Sprite : public InstancedRenderable + { + public: + Sprite(std::shared_ptr material); + Sprite(const Sprite&) = delete; + Sprite(Sprite&&) noexcept = default; + ~Sprite() = default; + + void BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const override; + + const std::shared_ptr& GetMaterial(std::size_t i) const; + std::size_t GetMaterialCount() const; + + inline void SetMaterial(std::shared_ptr material); + + Sprite& operator=(const Sprite&) = delete; + Sprite& operator=(Sprite&&) noexcept = default; + + private: + std::array m_vertices; + std::shared_ptr m_material; + }; +} + +#include + +#endif diff --git a/include/Nazara/Graphics/Sprite.inl b/include/Nazara/Graphics/Sprite.inl new file mode 100644 index 000000000..858c9cfb7 --- /dev/null +++ b/include/Nazara/Graphics/Sprite.inl @@ -0,0 +1,17 @@ +// 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 +#include +#include + +namespace Nz +{ + inline void Sprite::SetMaterial(std::shared_ptr material) + { + m_material = std::move(material); + } +} + +#include diff --git a/include/Nazara/Graphics/SpriteChainRenderer.hpp b/include/Nazara/Graphics/SpriteChainRenderer.hpp new file mode 100644 index 000000000..8868ef94a --- /dev/null +++ b/include/Nazara/Graphics/SpriteChainRenderer.hpp @@ -0,0 +1,68 @@ +// 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 + +#pragma once + +#ifndef NAZARA_SPRITECHAINRENDERER_HPP +#define NAZARA_SPRITECHAINRENDERER_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class AbstractBuffer; + class RenderDevice; + class RenderPipeline; + class RenderSpriteChain; + class ShaderBinding; + + class NAZARA_GRAPHICS_API SpriteChainRenderer : public ElementRenderer + { + public: + SpriteChainRenderer(RenderDevice& device, std::size_t maxVertexBufferSize = 32 * 1024); + ~SpriteChainRenderer() = default; + + std::unique_ptr InstanciateData(); + void Prepare(ElementRendererData& rendererData, RenderFrame& currentFrame, const Pointer* elements, std::size_t elementCount); + void Render(ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) override; + void Reset(ElementRendererData& rendererData, RenderFrame& currentFrame); + + private: + std::shared_ptr m_indexBuffer; + std::size_t m_maxVertexBufferSize; + std::size_t m_maxVertexCount; + RenderDevice& m_device; + }; + + struct SpriteChainRendererData : public ElementRendererData + { + struct DrawCall + { + const AbstractBuffer* vertexBuffer; + const RenderPipeline* renderPipeline; + const ShaderBinding* instanceBinding; + const ShaderBinding* materialBinding; + std::size_t firstIndex; + std::size_t quadCount; + }; + + struct DrawCallIndices + { + std::size_t start; + std::size_t count; + }; + + std::unordered_map drawCallPerElement; + std::vector drawCalls; + std::vector> vertexBuffers; + }; +} + +#include + +#endif diff --git a/src/Nazara/Graphics/RenderSubmesh.cpp b/include/Nazara/Graphics/SpriteChainRenderer.inl similarity index 71% rename from src/Nazara/Graphics/RenderSubmesh.cpp rename to include/Nazara/Graphics/SpriteChainRenderer.inl index f8f29b387..81df48065 100644 --- a/src/Nazara/Graphics/RenderSubmesh.cpp +++ b/include/Nazara/Graphics/SpriteChainRenderer.inl @@ -2,9 +2,11 @@ // This file is part of the "Nazara Engine - Graphics module" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include #include namespace Nz { } + +#include diff --git a/include/Nazara/Graphics/SubmeshRenderer.hpp b/include/Nazara/Graphics/SubmeshRenderer.hpp index 0360ab4d9..16464f94e 100644 --- a/include/Nazara/Graphics/SubmeshRenderer.hpp +++ b/include/Nazara/Graphics/SubmeshRenderer.hpp @@ -18,7 +18,8 @@ namespace Nz SubmeshRenderer() = default; ~SubmeshRenderer() = default; - void Render(CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) override; + std::unique_ptr InstanciateData(); + void Render(ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) override; }; } diff --git a/src/Nazara/Graphics/ElementRenderer.cpp b/src/Nazara/Graphics/ElementRenderer.cpp index 814e1026a..1e8f913ea 100644 --- a/src/Nazara/Graphics/ElementRenderer.cpp +++ b/src/Nazara/Graphics/ElementRenderer.cpp @@ -8,4 +8,14 @@ namespace Nz { ElementRenderer::~ElementRenderer() = default; + + void ElementRenderer::Prepare(ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/, const Pointer* /*elements*/, std::size_t /*elementCount*/) + { + } + + void ElementRenderer::Reset(ElementRendererData& /*rendererData*/, RenderFrame& /*currentFrame*/) + { + } + + ElementRendererData::~ElementRendererData() = default; } diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 14023c3cb..a3a4d1c71 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,8 @@ namespace Nz m_depthPassIndex = passRegistry.GetPassIndex("DepthPass"); m_forwardPassIndex = passRegistry.GetPassIndex("ForwardPass"); - m_elementRenderers.resize(1); + m_elementRenderers.resize(BasicRenderElementCount); + m_elementRenderers[UnderlyingCast(BasicRenderElement::SpriteChain)] = std::make_unique(*Graphics::Instance()->GetRenderDevice()); m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique(); } @@ -113,6 +115,8 @@ namespace Nz void ForwardFramePipeline::Render(RenderFrame& renderFrame) { + m_currentRenderFrame = &renderFrame; + Graphics* graphics = Graphics::Instance(); renderFrame.PushForRelease(std::move(m_removedWorldInstances)); @@ -167,6 +171,7 @@ namespace Nz }; // Render queues handling + bool prepare = false; for (auto&& [viewer, data] : m_viewers) { auto& viewerData = data; @@ -208,6 +213,7 @@ namespace Nz { viewerData.rebuildDepthPrepass = true; viewerData.rebuildForwardPass = true; + prepare = true; viewerData.visibilityHash = visibilityHash; } @@ -256,6 +262,48 @@ namespace Nz }); } + if (prepare) + { + for (std::size_t i = 0; i < m_elementRenderers.size(); ++i) + { + auto& elementRendererPtr = m_elementRenderers[i]; + + for (auto&& [_, viewerData] : m_viewers) + { + if (i >= viewerData.elementRendererData.size() || !viewerData.elementRendererData[i]) + { + if (i >= viewerData.elementRendererData.size()) + viewerData.elementRendererData.resize(i + 1); + + viewerData.elementRendererData[i] = elementRendererPtr->InstanciateData(); + } + + if (elementRendererPtr) + elementRendererPtr->Reset(*viewerData.elementRendererData[i], renderFrame); + } + } + + for (auto&& [_, viewerData] : m_viewers) + { + auto& rendererData = viewerData.elementRendererData; + + ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; + elementRenderer.Prepare(*rendererData[elementType], renderFrame, elements, elementCount); + }); + + ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; + elementRenderer.Prepare(*rendererData[elementType], renderFrame, elements, elementCount); + }); + + viewerData.rebuildForwardPass = true; + viewerData.rebuildDepthPrepass = true; + } + } + if (m_bakedFrameGraph.Resize(renderFrame)) { const std::shared_ptr& sampler = graphics->GetSamplerCache().Get({}); @@ -406,7 +454,11 @@ namespace Nz builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); - ProcessRenderQueue(builder, viewerData.depthPrepassRenderQueue); + ProcessRenderQueue(viewerData.depthPrepassRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; + elementRenderer.Render(*viewerData.elementRendererData[elementType], builder, elements, elementCount); + }); }); FramePass& forwardPass = frameGraph.AddPass("Forward pass"); @@ -433,8 +485,12 @@ namespace Nz builder.SetViewport(viewport); builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding()); - - ProcessRenderQueue(builder, viewerData.forwardRenderQueue); + + ProcessRenderQueue(viewerData.forwardRenderQueue, [&](std::size_t elementType, const Pointer* elements, std::size_t elementCount) + { + ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; + elementRenderer.Render(*viewerData.elementRendererData[elementType], builder, elements, elementCount); + }); }); } @@ -462,7 +518,8 @@ namespace Nz it->second.usedCount++; } - void ForwardFramePipeline::ProcessRenderQueue(CommandBufferBuilder& builder, const RenderQueue& renderQueue) + template + void ForwardFramePipeline::ProcessRenderQueue(const RenderQueue& renderQueue, F&& callback) { if (renderQueue.empty()) return; @@ -485,8 +542,7 @@ namespace Nz if (elementType >= m_elementRenderers.size() || !m_elementRenderers[elementType]) continue; - ElementRenderer& elementRenderer = *m_elementRenderers[elementType]; - elementRenderer.Render(builder, first, count); + callback(elementType, first, count); } } diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp new file mode 100644 index 000000000..1cdcbf32c --- /dev/null +++ b/src/Nazara/Graphics/Sprite.cpp @@ -0,0 +1,56 @@ +// 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 +#include +#include +#include +#include + +namespace Nz +{ + Sprite::Sprite(std::shared_ptr material) : + InstancedRenderable(Nz::Boxf(-1000.f, -1000.f, -1000.f, 2000.f, 2000.f, 2000.f)), + m_material(std::move(material)) + { + m_vertices[0] = VertexStruct_XYZ_Color_UV{ Vector3f(0.f, 0.f, 0.f), Nz::Color::White, Vector2f(0.f, 0.f) }; + m_vertices[1] = VertexStruct_XYZ_Color_UV{ Vector3f(1.f, 0.f, 0.f), Nz::Color::White, Vector2f(1.f, 0.f) }; + m_vertices[2] = VertexStruct_XYZ_Color_UV{ Vector3f(0.f, 1.f, 0.f), Nz::Color::White, Vector2f(0.f, 1.f) }; + m_vertices[3] = VertexStruct_XYZ_Color_UV{ Vector3f(1.f, 1.f, 0.f), Nz::Color::White, Vector2f(1.f, 1.f) }; + } + + void Sprite::BuildElement(std::size_t passIndex, const WorldInstance& worldInstance, std::vector>& elements) const + { + MaterialPass* materialPass = m_material->GetPass(passIndex); + if (!materialPass) + return; + + const std::shared_ptr& vertexDeclaration = VertexDeclaration::Get(VertexLayout::XYZ_Color_UV); + + std::vector vertexBufferData = { + { + { + 0, + vertexDeclaration + } + } + }; + const auto& renderPipeline = materialPass->GetPipeline()->GetRenderPipeline(vertexBufferData); + + elements.emplace_back(std::make_unique(0, renderPipeline, vertexDeclaration, 1, m_vertices.data(), materialPass->GetShaderBinding(), worldInstance.GetShaderBinding())); + } + + const std::shared_ptr& Sprite::GetMaterial(std::size_t i) const + { + assert(i == 0); + NazaraUnused(i); + + return m_material; + } + + std::size_t Sprite::GetMaterialCount() const + { + return 1; + } +} diff --git a/src/Nazara/Graphics/SpriteChainRenderer.cpp b/src/Nazara/Graphics/SpriteChainRenderer.cpp new file mode 100644 index 000000000..033e36ab9 --- /dev/null +++ b/src/Nazara/Graphics/SpriteChainRenderer.cpp @@ -0,0 +1,258 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + SpriteChainRenderer::SpriteChainRenderer(RenderDevice& device, std::size_t maxVertexBufferSize) : + m_device(device), + m_maxVertexBufferSize(maxVertexBufferSize), + m_maxVertexCount(m_maxVertexBufferSize / (2 * sizeof(float))) // Treat vec2 as the minimum declaration possible + { + std::size_t maxQuadCount = m_maxVertexCount / 4; + std::size_t indexCount = 6 * maxQuadCount; + + m_indexBuffer = m_device.InstantiateBuffer(BufferType::Index); + if (!m_indexBuffer->Initialize(indexCount * sizeof(UInt16), BufferUsage::DeviceLocal)) + throw std::runtime_error("failed to initialize index buffer"); + + // Generate indices for quad (0, 1, 2, 2, 1, 3, ...) + std::vector indices(indexCount); + UInt16* indexPtr = indices.data(); + + for (std::size_t i = 0; i < maxQuadCount; ++i) + { + *indexPtr++ = i * 4 + 0; + *indexPtr++ = i * 4 + 1; + *indexPtr++ = i * 4 + 2; + + *indexPtr++ = i * 4 + 2; + *indexPtr++ = i * 4 + 1; + *indexPtr++ = i * 4 + 3; + } + + m_indexBuffer->Fill(indices.data(), 0, indexCount * sizeof(UInt16)); + } + + std::unique_ptr SpriteChainRenderer::InstanciateData() + { + return std::make_unique(); + } + + void SpriteChainRenderer::Prepare(ElementRendererData& rendererData, RenderFrame& currentFrame, const Pointer* elements, std::size_t elementCount) + { + auto& data = static_cast(rendererData); + + std::vector> pendingCopies; + + std::size_t firstQuadIndex = 0; + SpriteChainRendererData::DrawCall* currentDrawCall = nullptr; + UploadPool::Allocation* currentAllocation = nullptr; + UInt8* currentAllocationMemPtr = nullptr; + const VertexDeclaration* currentVertexDeclaration = nullptr; + AbstractBuffer* currentVertexBuffer = nullptr; + const RenderPipeline* currentPipeline = nullptr; + const ShaderBinding* currentInstanceBinding = nullptr; + const ShaderBinding* currentMaterialBinding = nullptr; + + auto FlushDrawCall = [&]() + { + currentDrawCall = nullptr; + }; + + auto Flush = [&]() + { + // changing vertex buffer always mean we have to switch draw calls + FlushDrawCall(); + + if (currentAllocation) + { + pendingCopies.emplace_back(currentAllocation, currentVertexBuffer); + + firstQuadIndex = 0; + currentAllocation = nullptr; + currentVertexBuffer = nullptr; + } + }; + + std::size_t oldDrawCallCount = data.drawCalls.size(); + + for (std::size_t i = 0; i < elementCount; ++i) + { + assert(elements[i]->GetElementType() == UnderlyingCast(BasicRenderElement::SpriteChain)); + const RenderSpriteChain& spriteChain = static_cast(*elements[i]); + + const VertexDeclaration* vertexDeclaration = spriteChain.GetVertexDeclaration(); + std::size_t stride = vertexDeclaration->GetStride(); + + if (currentVertexDeclaration != vertexDeclaration) + { + // TODO: It's be possible to use another vertex declaration with the same vertex buffer but currently very complicated + // Wait until buffer rewrite + Flush(); + currentVertexDeclaration = vertexDeclaration; + } + + if (currentPipeline != spriteChain.GetRenderPipeline()) + { + FlushDrawCall(); + currentPipeline = spriteChain.GetRenderPipeline(); + } + + if (currentMaterialBinding != &spriteChain.GetMaterialBinding()) + { + FlushDrawCall(); + currentMaterialBinding = &spriteChain.GetMaterialBinding(); + } + + if (currentInstanceBinding != &spriteChain.GetInstanceBinding()) + { + // TODO: Flushing draw calls on instance binding means we can have e.g. 1000 sprites rendered using a draw call for each one + // which is far from being efficient, using some bindless could help (or at least instancing?) + FlushDrawCall(); + currentInstanceBinding = &spriteChain.GetInstanceBinding(); + } + + std::size_t remainingQuads = spriteChain.GetSpriteCount(); + while (remainingQuads > 0) + { + if (!currentAllocation) + { + currentAllocation = ¤tFrame.GetUploadPool().Allocate(m_maxVertexBufferSize); + currentAllocationMemPtr = static_cast(currentAllocation->mappedPtr); + + std::shared_ptr vertexBuffer = m_device.InstantiateBuffer(BufferType::Vertex); + vertexBuffer->Initialize(m_maxVertexBufferSize, BufferUsage::DeviceLocal); + + currentVertexBuffer = vertexBuffer.get(); + + data.vertexBuffers.emplace_back(std::move(vertexBuffer)); + } + + if (!currentDrawCall) + { + data.drawCalls.push_back(SpriteChainRendererData::DrawCall{ + currentVertexBuffer, + currentPipeline, + currentInstanceBinding, + currentMaterialBinding, + 6 * firstQuadIndex, + 0, + }); + + currentDrawCall = &data.drawCalls.back(); + } + + std::size_t remainingSpace = m_maxVertexBufferSize - (currentAllocationMemPtr - currentAllocation->mappedPtr); + std::size_t maxQuads = remainingSpace / (4 * stride); + if (maxQuads == 0) + { + Flush(); + continue; + } + + std::size_t copiedQuadCount = std::min(maxQuads, remainingQuads); + std::size_t copiedSize = 4 * copiedQuadCount * stride; + + std::memcpy(currentAllocationMemPtr, spriteChain.GetSpriteData(), copiedSize); + currentAllocationMemPtr += copiedSize; + + firstQuadIndex += copiedQuadCount; + currentDrawCall->quadCount += copiedQuadCount; + remainingQuads -= copiedQuadCount; + + // If there's still data to copy, it means buffer is full, flush it + if (remainingQuads > 0) + Flush(); + } + } + + //TODO: Add Finish()/PrepareEnd() call to allow to reuse buffers/draw calls for multiple Prepare calls + Flush(); + + const RenderSpriteChain* firstSpriteChain = static_cast(elements[0]); + std::size_t drawCallCount = data.drawCalls.size() - oldDrawCallCount; + data.drawCallPerElement[firstSpriteChain] = SpriteChainRendererData::DrawCallIndices{ oldDrawCallCount, drawCallCount }; + + if (!pendingCopies.empty()) + { + currentFrame.Execute([&](CommandBufferBuilder& builder) + { + for (auto&& [allocation, buffer] : pendingCopies) + builder.CopyBuffer(*allocation, buffer); + + builder.PostTransferBarrier(); + }, Nz::QueueType::Transfer); + } + } + + void SpriteChainRenderer::Render(ElementRendererData& rendererData, CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) + { + auto& data = static_cast(rendererData); + + commandBuffer.BindIndexBuffer(*m_indexBuffer); + + const AbstractBuffer* currentVertexBuffer = nullptr; + const RenderPipeline* currentPipeline = nullptr; + const ShaderBinding* currentInstanceBinding = nullptr; + const ShaderBinding* currentMaterialBinding = nullptr; + + const RenderSpriteChain* firstSpriteChain = static_cast(elements[0]); + auto it = data.drawCallPerElement.find(firstSpriteChain); + assert(it != data.drawCallPerElement.end()); + + const auto& indices = it->second; + + for (std::size_t i = 0; i < indices.count; ++i) + { + const auto& drawCall = data.drawCalls[indices.start + i]; + + if (currentVertexBuffer != drawCall.vertexBuffer) + { + commandBuffer.BindVertexBuffer(0, *drawCall.vertexBuffer); + currentVertexBuffer = drawCall.vertexBuffer; + } + + if (currentPipeline != drawCall.renderPipeline) + { + commandBuffer.BindPipeline(*drawCall.renderPipeline); + currentPipeline = drawCall.renderPipeline; + } + + if (currentMaterialBinding != drawCall.materialBinding) + { + commandBuffer.BindShaderBinding(Graphics::MaterialBindingSet, *drawCall.materialBinding); + currentMaterialBinding = drawCall.materialBinding; + } + + if (currentInstanceBinding != drawCall.instanceBinding) + { + commandBuffer.BindShaderBinding(Graphics::WorldBindingSet, *drawCall.instanceBinding); + currentInstanceBinding = drawCall.instanceBinding; + } + + commandBuffer.DrawIndexed(drawCall.quadCount * 6, 1U, drawCall.firstIndex); + } + } + + void SpriteChainRenderer::Reset(ElementRendererData& rendererData, RenderFrame& currentFrame) + { + auto& data = static_cast(rendererData); + + // TODO: Reuse vertex buffers + for (auto& vertexBufferPtr : data.vertexBuffers) + currentFrame.PushForRelease(std::move(vertexBufferPtr)); + + data.drawCalls.clear(); + data.vertexBuffers.clear(); + } +} diff --git a/src/Nazara/Graphics/SubmeshRenderer.cpp b/src/Nazara/Graphics/SubmeshRenderer.cpp index 4b86f1509..dd7a7dbe8 100644 --- a/src/Nazara/Graphics/SubmeshRenderer.cpp +++ b/src/Nazara/Graphics/SubmeshRenderer.cpp @@ -10,7 +10,12 @@ namespace Nz { - void SubmeshRenderer::Render(CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) + std::unique_ptr SubmeshRenderer::InstanciateData() + { + return {}; + } + + void SubmeshRenderer::Render(ElementRendererData& /*rendererData*/, CommandBufferBuilder& commandBuffer, const Pointer* elements, std::size_t elementCount) { const AbstractBuffer* currentIndexBuffer = nullptr; const AbstractBuffer* currentVertexBuffer = nullptr;