Graphics: Add RenderElement and ElementRenderer (WIP)
This commit is contained in:
parent
d647fdc59b
commit
08921b36a6
|
|
@ -0,0 +1,32 @@
|
|||
// 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_ELEMENTRENDERER_HPP
|
||||
#define NAZARA_ELEMENTRENDERER_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class CommandBufferBuilder;
|
||||
class RenderElement;
|
||||
|
||||
class ElementRenderer
|
||||
{
|
||||
public:
|
||||
ElementRenderer() = default;
|
||||
virtual ~ElementRenderer();
|
||||
|
||||
virtual void Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/ElementRenderer.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
enum class BasicRenderElement
|
||||
{
|
||||
Submesh = 0,
|
||||
};
|
||||
|
||||
enum class CullTest
|
||||
{
|
||||
Box,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,18 @@
|
|||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/BakedFrameGraph.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/FramePipeline.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/MaterialPass.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
#include <Nazara/Graphics/RenderQueueRegistry.hpp>
|
||||
#include <Nazara/Renderer/ShaderBinding.hpp>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
|
|
@ -66,14 +71,22 @@ namespace Nz
|
|||
ShaderBindingPtr blitShaderBinding;
|
||||
};
|
||||
|
||||
std::size_t m_forwardPass;
|
||||
std::unordered_map<AbstractViewer*, ViewerData> m_viewers;
|
||||
std::unordered_map<MaterialPass*, MaterialData> m_materials;
|
||||
std::unordered_map<WorldInstance*, std::unordered_map<const InstancedRenderable*, RenderableData>> m_renderables;
|
||||
std::unordered_set<AbstractViewer*> m_invalidatedViewerInstances;
|
||||
std::unordered_set<MaterialPass*> m_invalidatedMaterials;
|
||||
std::unordered_set<WorldInstance*> m_invalidatedWorldInstances;
|
||||
std::vector<std::unique_ptr<RenderElement>> m_depthPrepassRenderElements;
|
||||
std::vector<std::unique_ptr<RenderElement>> m_forwardRenderElements;
|
||||
std::vector<std::unique_ptr<ElementRenderer>> m_elementRenderers;
|
||||
std::vector<const RenderElement*> m_temporaryElementList;
|
||||
BakedFrameGraph m_bakedFrameGraph;
|
||||
RenderQueueRegistry m_depthPrepassRegistry;
|
||||
RenderQueueRegistry m_forwardRegistry;
|
||||
RenderQueue<RenderElement*> m_depthPrepassRenderQueue;
|
||||
RenderQueue<RenderElement*> m_forwardRenderQueue;
|
||||
bool m_rebuildDepthPrepass;
|
||||
bool m_rebuildFrameGraph;
|
||||
bool m_rebuildForwardPass;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace Nz
|
|||
{
|
||||
class CommandBufferBuilder;
|
||||
class Material;
|
||||
class RenderElement;
|
||||
class WorldInstance;
|
||||
|
||||
class NAZARA_GRAPHICS_API InstancedRenderable
|
||||
|
|
@ -26,7 +27,7 @@ namespace Nz
|
|||
InstancedRenderable(InstancedRenderable&&) noexcept = default;
|
||||
~InstancedRenderable();
|
||||
|
||||
virtual void Draw(const std::string& pass, CommandBufferBuilder& commandBuffer) const = 0;
|
||||
virtual void BuildElement(const std::string& pass, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const = 0;
|
||||
|
||||
virtual const std::shared_ptr<Material>& GetMaterial(std::size_t i) const = 0;
|
||||
virtual std::size_t GetMaterialCount() const = 0;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Nz
|
|||
class CommandBufferBuilder;
|
||||
class RenderFrame;
|
||||
|
||||
class NAZARA_GRAPHICS_API MaterialPass : public Resource
|
||||
class NAZARA_GRAPHICS_API MaterialPass
|
||||
{
|
||||
public:
|
||||
MaterialPass(std::shared_ptr<const MaterialSettings> settings);
|
||||
|
|
|
|||
|
|
@ -28,15 +28,15 @@ namespace Nz
|
|||
Model(Model&&) noexcept = default;
|
||||
~Model() = default;
|
||||
|
||||
void Draw(const std::string& pass, CommandBufferBuilder& commandBuffer) const override;
|
||||
void BuildElement(const std::string& pass, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const override;
|
||||
|
||||
const std::shared_ptr<AbstractBuffer>& GetIndexBuffer(std::size_t subMeshIndex) const;
|
||||
std::size_t GetIndexCount(std::size_t subMeshIndex) const;
|
||||
const std::shared_ptr<Material>& GetMaterial(std::size_t subMeshIndex) const override;
|
||||
std::size_t GetMaterialCount() const override;
|
||||
inline std::size_t GetSubMeshCount() const;
|
||||
const std::vector<RenderPipelineInfo::VertexBufferData>& GetVertexBufferData(std::size_t subMeshIndex) const;
|
||||
const std::shared_ptr<AbstractBuffer>& GetVertexBuffer(std::size_t subMeshIndex) const;
|
||||
inline std::size_t GetSubMeshCount() const;
|
||||
|
||||
inline void SetMaterial(std::size_t subMeshIndex, std::shared_ptr<Material> material);
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ namespace Nz
|
|||
};
|
||||
|
||||
std::shared_ptr<GraphicalMesh> m_graphicalMesh;
|
||||
std::vector<SubMeshData> m_subMeshes;
|
||||
std::vector<SubMeshData> m_submeshes;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ namespace Nz
|
|||
{
|
||||
inline std::size_t Model::GetSubMeshCount() const
|
||||
{
|
||||
return m_subMeshes.size();
|
||||
return m_submeshes.size();
|
||||
}
|
||||
|
||||
inline void Model::SetMaterial(std::size_t subMeshIndex, std::shared_ptr<Material> material)
|
||||
{
|
||||
assert(subMeshIndex < m_subMeshes.size());
|
||||
assert(subMeshIndex < m_submeshes.size());
|
||||
|
||||
OnMaterialInvalidated(this, subMeshIndex, material);
|
||||
m_subMeshes[subMeshIndex].material = std::move(material);
|
||||
m_submeshes[subMeshIndex].material = std::move(material);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// 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_RENDERELEMENT_HPP
|
||||
#define NAZARA_RENDERELEMENT_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/Enums.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class RenderQueueRegistry;
|
||||
|
||||
class RenderElement
|
||||
{
|
||||
public:
|
||||
inline RenderElement(BasicRenderElement elementType);
|
||||
inline RenderElement(UInt8 elementType);
|
||||
virtual ~RenderElement();
|
||||
|
||||
virtual UInt64 ComputeSortingScore(const RenderQueueRegistry& registry) const = 0;
|
||||
|
||||
inline UInt8 GetElementType() const;
|
||||
|
||||
virtual void Register(RenderQueueRegistry& registry) const = 0;
|
||||
|
||||
private:
|
||||
UInt8 m_elementType;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/RenderElement.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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/RenderElement.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline RenderElement::RenderElement(BasicRenderElement elementType) :
|
||||
RenderElement(static_cast<UInt8>(elementType))
|
||||
{
|
||||
}
|
||||
|
||||
inline RenderElement::RenderElement(UInt8 elementType) :
|
||||
m_elementType(elementType)
|
||||
{
|
||||
}
|
||||
|
||||
inline UInt8 RenderElement::GetElementType() const
|
||||
{
|
||||
return m_elementType;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ namespace Nz
|
|||
class RenderQueueInternal
|
||||
{
|
||||
public:
|
||||
using Index = Nz::UInt64;
|
||||
using Index = UInt64;
|
||||
|
||||
RenderQueueInternal() = default;
|
||||
RenderQueueInternal(const RenderQueueInternal&) = default;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
// 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_RENDERQUEUEREGISTRY_HPP
|
||||
#define NAZARA_RENDERQUEUEREGISTRY_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/Thirdparty/robin_hood/robin_hood.h>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class AbstractBuffer;
|
||||
class RenderPipeline;
|
||||
|
||||
class RenderQueueRegistry
|
||||
{
|
||||
public:
|
||||
RenderQueueRegistry() = default;
|
||||
~RenderQueueRegistry() = default;
|
||||
|
||||
inline void Clear();
|
||||
|
||||
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 void RegisterLayer(int renderLayer);
|
||||
inline void RegisterPipeline(const RenderPipeline* pipeline);
|
||||
inline void RegisterVertexBuffer(const AbstractBuffer* vertexBuffer);
|
||||
|
||||
private:
|
||||
robin_hood::unordered_map<int, std::size_t> m_renderLayerRegistry;
|
||||
robin_hood::unordered_map<const RenderPipeline*, std::size_t> m_pipelineRegistry;
|
||||
robin_hood::unordered_map<const AbstractBuffer*, std::size_t> m_vertexBufferRegistry;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/RenderQueueRegistry.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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/RenderQueueRegistry.hpp>
|
||||
#include <cassert>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline void RenderQueueRegistry::Clear()
|
||||
{
|
||||
m_renderLayerRegistry.clear();
|
||||
m_pipelineRegistry.clear();
|
||||
}
|
||||
|
||||
inline std::size_t RenderQueueRegistry::FetchLayerIndex(int renderLayer) const
|
||||
{
|
||||
auto it = m_renderLayerRegistry.find(renderLayer);
|
||||
assert(it != m_renderLayerRegistry.end());
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
inline std::size_t RenderQueueRegistry::FetchPipelineIndex(const RenderPipeline* pipeline) const
|
||||
{
|
||||
auto it = m_pipelineRegistry.find(pipeline);
|
||||
assert(it != m_pipelineRegistry.end());
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
inline std::size_t RenderQueueRegistry::FetchVertexBuffer(const AbstractBuffer* vertexBuffer) const
|
||||
{
|
||||
auto it = m_vertexBufferRegistry.find(vertexBuffer);
|
||||
assert(it != m_vertexBufferRegistry.end());
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
inline void RenderQueueRegistry::RegisterLayer(int renderLayer)
|
||||
{
|
||||
m_renderLayerRegistry.try_emplace(renderLayer, m_renderLayerRegistry.size());
|
||||
}
|
||||
|
||||
inline void RenderQueueRegistry::RegisterPipeline(const RenderPipeline* pipeline)
|
||||
{
|
||||
m_pipelineRegistry.try_emplace(pipeline, m_pipelineRegistry.size());
|
||||
}
|
||||
|
||||
inline void RenderQueueRegistry::RegisterVertexBuffer(const AbstractBuffer* vertexBuffer)
|
||||
{
|
||||
m_vertexBufferRegistry.try_emplace(vertexBuffer, m_vertexBufferRegistry.size());
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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 <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/RenderQueueRegistry.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class AbstractBuffer;
|
||||
class RenderPipeline;
|
||||
class ShaderBinding;
|
||||
|
||||
class RenderSubmesh : public RenderElement
|
||||
{
|
||||
public:
|
||||
inline RenderSubmesh(int renderLayer, std::shared_ptr<RenderPipeline> renderPipeline, std::size_t indexCount, std::shared_ptr<AbstractBuffer> indexBuffer, std::shared_ptr<AbstractBuffer> vertexBuffer, const ShaderBinding& instanceBinding, const ShaderBinding& materialBinding);
|
||||
~RenderSubmesh() = default;
|
||||
|
||||
inline UInt64 ComputeSortingScore(const RenderQueueRegistry& registry) const override;
|
||||
|
||||
inline const AbstractBuffer* GetIndexBuffer() const;
|
||||
inline std::size_t GetIndexCount() const;
|
||||
inline const RenderPipeline* GetRenderPipeline() const;
|
||||
inline const ShaderBinding& GetInstanceBinding() const;
|
||||
inline const ShaderBinding& GetMaterialBinding() const;
|
||||
inline const AbstractBuffer* GetVertexBuffer() const;
|
||||
|
||||
inline void Register(RenderQueueRegistry& registry) const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<AbstractBuffer> m_indexBuffer;
|
||||
std::shared_ptr<AbstractBuffer> m_vertexBuffer;
|
||||
std::shared_ptr<RenderPipeline> m_renderPipeline;
|
||||
std::size_t m_indexCount;
|
||||
const ShaderBinding& m_instanceBinding;
|
||||
const ShaderBinding& m_materialBinding;
|
||||
int m_renderLayer;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/RenderSubmesh.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// 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/RenderSubmesh.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline RenderSubmesh::RenderSubmesh(int renderLayer, std::shared_ptr<RenderPipeline> renderPipeline, std::size_t indexCount, std::shared_ptr<AbstractBuffer> indexBuffer, std::shared_ptr<AbstractBuffer> vertexBuffer, const ShaderBinding& instanceBinding, const ShaderBinding& materialBinding) :
|
||||
RenderElement(BasicRenderElement::Submesh),
|
||||
m_indexBuffer(std::move(indexBuffer)),
|
||||
m_vertexBuffer(std::move(vertexBuffer)),
|
||||
m_renderPipeline(std::move(renderPipeline)),
|
||||
m_indexCount(indexCount),
|
||||
m_instanceBinding(instanceBinding),
|
||||
m_materialBinding(materialBinding),
|
||||
m_renderLayer(renderLayer)
|
||||
{
|
||||
}
|
||||
|
||||
inline UInt64 RenderSubmesh::ComputeSortingScore(const RenderQueueRegistry& registry) const
|
||||
{
|
||||
UInt64 elementType = GetElementType();
|
||||
UInt64 layerIndex = registry.FetchLayerIndex(m_renderLayer);
|
||||
UInt64 pipelineIndex = registry.FetchPipelineIndex(m_renderPipeline.get());
|
||||
UInt64 vertexBufferIndex = registry.FetchVertexBuffer(m_vertexBuffer.get());
|
||||
|
||||
// RQ index:
|
||||
// - Element type (4bits)
|
||||
// - Layer (8bits)
|
||||
// - Pipeline (16bits)
|
||||
// - VertexBuffer (8bits)
|
||||
|
||||
return (elementType & 0xF) << 60 |
|
||||
(layerIndex & 0xFF) << 52 |
|
||||
(pipelineIndex & 0xFFFF) << 36 |
|
||||
(vertexBufferIndex & 0xFF) << 24;
|
||||
}
|
||||
|
||||
inline const AbstractBuffer* RenderSubmesh::GetIndexBuffer() const
|
||||
{
|
||||
return m_indexBuffer.get();
|
||||
}
|
||||
|
||||
inline std::size_t RenderSubmesh::GetIndexCount() const
|
||||
{
|
||||
return m_indexCount;
|
||||
}
|
||||
|
||||
inline const RenderPipeline* RenderSubmesh::GetRenderPipeline() const
|
||||
{
|
||||
return m_renderPipeline.get();
|
||||
}
|
||||
|
||||
inline const ShaderBinding& RenderSubmesh::GetInstanceBinding() const
|
||||
{
|
||||
return m_instanceBinding;
|
||||
}
|
||||
|
||||
inline const ShaderBinding& RenderSubmesh::GetMaterialBinding() const
|
||||
{
|
||||
return m_materialBinding;
|
||||
}
|
||||
|
||||
inline const AbstractBuffer* RenderSubmesh::GetVertexBuffer() const
|
||||
{
|
||||
return m_vertexBuffer.get();
|
||||
}
|
||||
|
||||
inline void RenderSubmesh::Register(RenderQueueRegistry& registry) const
|
||||
{
|
||||
registry.RegisterLayer(m_renderLayer);
|
||||
registry.RegisterPipeline(m_renderPipeline.get());
|
||||
registry.RegisterVertexBuffer(m_vertexBuffer.get());
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// 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_SUBMESHRENDERER_HPP
|
||||
#define NAZARA_SUBMESHRENDERER_HPP
|
||||
|
||||
#include <Nazara/Prerequisites.hpp>
|
||||
#include <Nazara/Graphics/ElementRenderer.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class SubmeshRenderer : public ElementRenderer
|
||||
{
|
||||
public:
|
||||
SubmeshRenderer() = default;
|
||||
~SubmeshRenderer() = default;
|
||||
|
||||
void Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount) override;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/SubmeshRenderer.inl>
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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/SubmeshRenderer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
||||
#include <Nazara/Graphics/DebugOff.hpp>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,63 @@
|
|||
<Type Name="robin_hood::detail::Table<*,*,*,*,*,*>">
|
||||
<!--
|
||||
$T1 = bool IsFlat
|
||||
$T2 = size_t MaxLoadFactor100
|
||||
$T3 = typename Key
|
||||
$T4 = typename T
|
||||
$T5 = typename Hash
|
||||
$T6 = typename KeyEqual
|
||||
-->
|
||||
<!-- <DisplayString>{map}</DisplayString> -->
|
||||
<Expand>
|
||||
<Synthetic Name="[elements]">
|
||||
<DisplayString>{{size={mNumElements}}}</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="5000" >
|
||||
<Variable Name="itKeyVals" InitialValue="mKeyVals " />
|
||||
<Variable Name="itInfo" InitialValue="mInfo " />
|
||||
<Variable Name="itEndKeyVals" InitialValue="(void *)mInfo " />
|
||||
<Variable Name="n" InitialValue="0ULL " />
|
||||
<Variable Name="inc" InitialValue="(unsigned long)0" />
|
||||
|
||||
<Size>mNumElements</Size>
|
||||
<Loop>
|
||||
<!-- Fast forward -->
|
||||
<Exec>n = *((size_t*)itInfo)</Exec>
|
||||
<Loop>
|
||||
<Break Condition="n != 0" />
|
||||
<Exec>itInfo += sizeof(size_t)</Exec>
|
||||
<Exec>itKeyVals += sizeof(size_t)</Exec>
|
||||
</Loop>
|
||||
<!-- Count Trailing Zeros -->
|
||||
<Exec>
|
||||
inc = n == 0
|
||||
? 64
|
||||
: (
|
||||
63
|
||||
- (((n & (~n + 1)) & 0x00000000FFFFFFFF) ? 32 : 0)
|
||||
- (((n & (~n + 1)) & 0x0000FFFF0000FFFF) ? 16 : 0)
|
||||
- (((n & (~n + 1)) & 0x00FF00FF00FF00FF) ? 8 : 0)
|
||||
- (((n & (~n + 1)) & 0x0F0F0F0F0F0F0F0F) ? 4 : 0)
|
||||
- (((n & (~n + 1)) & 0x3333333333333333) ? 2 : 0)
|
||||
- (((n & (~n + 1)) & 0x5555555555555555) ? 1 : 0)
|
||||
)
|
||||
</Exec>
|
||||
<Exec>itInfo += inc / 8</Exec>
|
||||
<Exec>itKeyVals += inc / 8</Exec>
|
||||
<!-- Fast forward -->
|
||||
<Break Condition="(void*)itKeyVals == itEndKeyVals" />
|
||||
<Item Name="[{itKeyVals->mData.first}]">itKeyVals->mData.second</Item>
|
||||
<!-- <Item>itKeyVals->mData</Item> -->
|
||||
<Exec>itInfo++ </Exec>
|
||||
<Exec>itKeyVals++</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[load_factor]" >float(mNumElements) / float(mMask + 1)</Item>
|
||||
<Item Name="[max_load_factor]">$T2</Item>
|
||||
<Item Name="[IsFlat]" >$T1</Item>
|
||||
<Item Name="[hash_function]" >*(WrapHash<$T5>*)this,nd</Item>
|
||||
<Item Name="[key_eq]" >*(WrapKeyEqual<$T6>*)this,nd</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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/ElementRenderer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
ElementRenderer::~ElementRenderer() = default;
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@
|
|||
#include <Nazara/Graphics/AbstractViewer.hpp>
|
||||
#include <Nazara/Graphics/FrameGraph.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/InstancedRenderable.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/SubmeshRenderer.hpp>
|
||||
#include <Nazara/Graphics/ViewerInstance.hpp>
|
||||
#include <Nazara/Graphics/WorldInstance.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
|
|
@ -22,8 +24,11 @@ namespace Nz
|
|||
{
|
||||
ForwardFramePipeline::ForwardFramePipeline() :
|
||||
m_rebuildFrameGraph(true),
|
||||
m_rebuildDepthPrepass(false),
|
||||
m_rebuildForwardPass(false)
|
||||
{
|
||||
m_elementRenderers.resize(1);
|
||||
m_elementRenderers[UnderlyingCast(BasicRenderElement::Submesh)] = std::make_unique<SubmeshRenderer>();
|
||||
}
|
||||
|
||||
void ForwardFramePipeline::InvalidateViewer(AbstractViewer* viewerInstance)
|
||||
|
|
@ -63,6 +68,7 @@ namespace Nz
|
|||
UnregisterMaterialPass(pass);
|
||||
}
|
||||
|
||||
m_rebuildDepthPrepass = true;
|
||||
m_rebuildForwardPass = true;
|
||||
});
|
||||
|
||||
|
|
@ -79,6 +85,7 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
m_rebuildDepthPrepass = true;
|
||||
m_rebuildForwardPass = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +127,10 @@ namespace Nz
|
|||
for (MaterialPass* material : m_invalidatedMaterials)
|
||||
{
|
||||
if (material->Update(renderFrame, builder))
|
||||
{
|
||||
m_rebuildDepthPrepass = true;
|
||||
m_rebuildForwardPass = true;
|
||||
}
|
||||
}
|
||||
m_invalidatedMaterials.clear();
|
||||
|
||||
|
|
@ -129,6 +139,55 @@ namespace Nz
|
|||
builder.EndDebugRegion();
|
||||
}, QueueType::Transfer);
|
||||
|
||||
if (m_rebuildDepthPrepass)
|
||||
{
|
||||
m_depthPrepassRenderElements.clear();
|
||||
|
||||
for (const auto& [worldInstance, renderables] : m_renderables)
|
||||
{
|
||||
for (const auto& [renderable, renderableData] : renderables)
|
||||
renderable->BuildElement("DepthPass", *worldInstance, m_depthPrepassRenderElements);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_rebuildForwardPass)
|
||||
{
|
||||
m_forwardRenderElements.clear();
|
||||
|
||||
for (const auto& [worldInstance, renderables] : m_renderables)
|
||||
{
|
||||
for (const auto& [renderable, renderableData] : renderables)
|
||||
renderable->BuildElement("ForwardPass", *worldInstance, m_forwardRenderElements);
|
||||
}
|
||||
}
|
||||
|
||||
// RenderQueue handling
|
||||
m_depthPrepassRegistry.Clear();
|
||||
m_depthPrepassRenderQueue.Clear();
|
||||
for (const auto& renderElement : m_depthPrepassRenderElements)
|
||||
{
|
||||
renderElement->Register(m_depthPrepassRegistry);
|
||||
m_depthPrepassRenderQueue.Insert(renderElement.get());
|
||||
}
|
||||
|
||||
m_depthPrepassRenderQueue.Sort([&](const RenderElement* element)
|
||||
{
|
||||
return element->ComputeSortingScore(m_depthPrepassRegistry);
|
||||
});
|
||||
|
||||
m_forwardRegistry.Clear();
|
||||
m_forwardRenderQueue.Clear();
|
||||
for (const auto& renderElement : m_forwardRenderElements)
|
||||
{
|
||||
renderElement->Register(m_forwardRegistry);
|
||||
m_forwardRenderQueue.Insert(renderElement.get());
|
||||
}
|
||||
|
||||
m_forwardRenderQueue.Sort([&](const RenderElement* element)
|
||||
{
|
||||
return element->ComputeSortingScore(m_forwardRegistry);
|
||||
});
|
||||
|
||||
if (m_bakedFrameGraph.Resize(renderFrame))
|
||||
{
|
||||
const std::shared_ptr<TextureSampler>& sampler = graphics->GetSamplerCache().Get({});
|
||||
|
|
@ -152,6 +211,7 @@ namespace Nz
|
|||
|
||||
m_bakedFrameGraph.Execute(renderFrame);
|
||||
m_rebuildForwardPass = false;
|
||||
m_rebuildDepthPrepass = false;
|
||||
m_rebuildFrameGraph = false;
|
||||
|
||||
const Vector2ui& frameSize = renderFrame.GetSize();
|
||||
|
|
@ -179,7 +239,7 @@ namespace Nz
|
|||
builder.SetViewport(renderRegion);
|
||||
|
||||
builder.BindPipeline(*graphics->GetBlitPipeline());
|
||||
builder.BindVertexBuffer(0, graphics->GetFullscreenVertexBuffer().get());
|
||||
builder.BindVertexBuffer(0, *graphics->GetFullscreenVertexBuffer());
|
||||
builder.BindShaderBinding(0, *blitShaderBinding);
|
||||
|
||||
builder.Draw(3);
|
||||
|
|
@ -265,19 +325,35 @@ namespace Nz
|
|||
|
||||
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
|
||||
|
||||
for (const auto& [worldInstance, renderables] : m_renderables)
|
||||
auto it = m_depthPrepassRenderQueue.begin();
|
||||
while (it != m_depthPrepassRenderQueue.end())
|
||||
{
|
||||
builder.BindShaderBinding(Graphics::WorldBindingSet, worldInstance->GetShaderBinding());
|
||||
const RenderElement* element = *it;
|
||||
UInt8 elementType = element->GetElementType();
|
||||
|
||||
for (const auto& [renderable, renderableData] : renderables)
|
||||
renderable->Draw("DepthPass", builder);
|
||||
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");
|
||||
forwardPass.AddOutput(viewerData.colorAttachment);
|
||||
forwardPass.SetDepthStencilInput(viewerData.depthStencilAttachment);
|
||||
forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
|
||||
//forwardPass.SetDepthStencilOutput(viewerData.depthStencilAttachment);
|
||||
|
||||
forwardPass.SetClearColor(0, Color::Black);
|
||||
forwardPass.SetDepthStencilClear(1.f, 0);
|
||||
|
|
@ -299,12 +375,28 @@ namespace Nz
|
|||
|
||||
builder.BindShaderBinding(Graphics::ViewerBindingSet, viewer->GetViewerInstance().GetShaderBinding());
|
||||
|
||||
for (const auto& [worldInstance, renderables] : m_renderables)
|
||||
auto it = m_forwardRenderQueue.begin();
|
||||
while (it != m_forwardRenderQueue.end())
|
||||
{
|
||||
builder.BindShaderBinding(Graphics::WorldBindingSet, worldInstance->GetShaderBinding());
|
||||
const RenderElement* element = *it;
|
||||
UInt8 elementType = element->GetElementType();
|
||||
|
||||
for (const auto& [renderable, renderableData] : renderables)
|
||||
renderable->Draw("ForwardPass", builder);
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -789,6 +789,8 @@ namespace Nz
|
|||
|
||||
if (first)
|
||||
{
|
||||
bool canDiscard = true;
|
||||
|
||||
// Check if a future pass reads from the DS buffer or if we can discard it after this pass
|
||||
if (auto readIt = m_pending.attachmentReadList.find(dsInputAttachment); readIt != m_pending.attachmentReadList.end())
|
||||
{
|
||||
|
|
@ -798,11 +800,13 @@ namespace Nz
|
|||
if (readPhysicalPassIndex > physicalPassIndex) //< Read in a future pass?
|
||||
{
|
||||
// Yes, store it
|
||||
dsAttachment.storeOp = AttachmentStoreOp::Store;
|
||||
canDiscard = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dsAttachment.storeOp = (canDiscard) ? AttachmentStoreOp::Discard : AttachmentStoreOp::Store;
|
||||
}
|
||||
|
||||
depthStencilAttachment = RenderPass::AttachmentReference{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Nazara/Graphics/GraphicalMesh.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/RenderSubmesh.hpp>
|
||||
#include <Nazara/Graphics/WorldInstance.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
|
@ -15,10 +16,10 @@ namespace Nz
|
|||
Model::Model(std::shared_ptr<GraphicalMesh> graphicalMesh) :
|
||||
m_graphicalMesh(std::move(graphicalMesh))
|
||||
{
|
||||
m_subMeshes.reserve(m_graphicalMesh->GetSubMeshCount());
|
||||
m_submeshes.reserve(m_graphicalMesh->GetSubMeshCount());
|
||||
for (std::size_t i = 0; i < m_graphicalMesh->GetSubMeshCount(); ++i)
|
||||
{
|
||||
auto& subMeshData = m_subMeshes.emplace_back();
|
||||
auto& subMeshData = m_submeshes.emplace_back();
|
||||
//subMeshData.material = DefaultMaterial; //< TODO
|
||||
subMeshData.vertexBufferData = {
|
||||
{
|
||||
|
|
@ -29,11 +30,11 @@ namespace Nz
|
|||
}
|
||||
}
|
||||
|
||||
void Model::Draw(const std::string& pass, CommandBufferBuilder& commandBuffer) const
|
||||
void Model::BuildElement(const std::string& pass, WorldInstance& worldInstance, std::vector<std::unique_ptr<RenderElement>>& elements) const
|
||||
{
|
||||
for (std::size_t i = 0; i < m_subMeshes.size(); ++i)
|
||||
for (std::size_t i = 0; i < m_submeshes.size(); ++i)
|
||||
{
|
||||
const auto& submeshData = m_subMeshes[i];
|
||||
const auto& submeshData = m_submeshes[i];
|
||||
|
||||
MaterialPass* materialPass = submeshData.material->GetPass(pass);
|
||||
if (!materialPass)
|
||||
|
|
@ -43,12 +44,7 @@ namespace Nz
|
|||
const auto& vertexBuffer = m_graphicalMesh->GetVertexBuffer(i);
|
||||
const auto& renderPipeline = materialPass->GetPipeline()->GetRenderPipeline(submeshData.vertexBufferData);
|
||||
|
||||
commandBuffer.BindShaderBinding(Graphics::MaterialBindingSet, materialPass->GetShaderBinding());
|
||||
commandBuffer.BindIndexBuffer(indexBuffer.get());
|
||||
commandBuffer.BindVertexBuffer(0, vertexBuffer.get());
|
||||
commandBuffer.BindPipeline(*renderPipeline);
|
||||
|
||||
commandBuffer.DrawIndexed(static_cast<Nz::UInt32>(m_graphicalMesh->GetIndexCount(i)));
|
||||
elements.emplace_back(std::make_unique<RenderSubmesh>(0, renderPipeline, m_graphicalMesh->GetIndexCount(i), indexBuffer, vertexBuffer, worldInstance.GetShaderBinding(), materialPass->GetShaderBinding()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,20 +60,20 @@ namespace Nz
|
|||
|
||||
const std::shared_ptr<Material>& Model::GetMaterial(std::size_t subMeshIndex) const
|
||||
{
|
||||
assert(subMeshIndex < m_subMeshes.size());
|
||||
const auto& subMeshData = m_subMeshes[subMeshIndex];
|
||||
assert(subMeshIndex < m_submeshes.size());
|
||||
const auto& subMeshData = m_submeshes[subMeshIndex];
|
||||
return subMeshData.material;
|
||||
}
|
||||
|
||||
std::size_t Model::GetMaterialCount() const
|
||||
{
|
||||
return m_subMeshes.size();
|
||||
return m_submeshes.size();
|
||||
}
|
||||
|
||||
const std::vector<RenderPipelineInfo::VertexBufferData>& Model::GetVertexBufferData(std::size_t subMeshIndex) const
|
||||
{
|
||||
assert(subMeshIndex < m_subMeshes.size());
|
||||
const auto& subMeshData = m_subMeshes[subMeshIndex];
|
||||
assert(subMeshIndex < m_submeshes.size());
|
||||
const auto& subMeshData = m_submeshes[subMeshIndex];
|
||||
return subMeshData.vertexBufferData;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
// 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/RenderElement.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
RenderElement::~RenderElement() = default;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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/RenderSubmesh.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// 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/SubmeshRenderer.hpp>
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/RenderSubmesh.hpp>
|
||||
#include <Nazara/Renderer/CommandBufferBuilder.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void SubmeshRenderer::Render(CommandBufferBuilder& commandBuffer, const RenderElement** elements, std::size_t elementCount)
|
||||
{
|
||||
const AbstractBuffer* currentIndexBuffer = nullptr;
|
||||
const AbstractBuffer* currentVertexBuffer = nullptr;
|
||||
const RenderPipeline* currentPipeline = nullptr;
|
||||
const ShaderBinding* currentMaterialBinding = nullptr;
|
||||
|
||||
for (std::size_t i = 0; i < elementCount; ++i)
|
||||
{
|
||||
assert(elements[i]->GetElementType() == UnderlyingCast(BasicRenderElement::Submesh));
|
||||
const RenderSubmesh& submesh = static_cast<const RenderSubmesh&>(*elements[i]);
|
||||
|
||||
if (const RenderPipeline* pipeline = submesh.GetRenderPipeline(); currentPipeline != pipeline)
|
||||
{
|
||||
commandBuffer.BindPipeline(*pipeline);
|
||||
currentPipeline = pipeline;
|
||||
}
|
||||
|
||||
if (const ShaderBinding* materialBinding = &submesh.GetMaterialBinding(); currentMaterialBinding != materialBinding)
|
||||
{
|
||||
commandBuffer.BindShaderBinding(Graphics::MaterialBindingSet, *materialBinding);
|
||||
currentMaterialBinding = materialBinding;
|
||||
}
|
||||
|
||||
if (const AbstractBuffer* indexBuffer = submesh.GetIndexBuffer(); currentIndexBuffer != indexBuffer)
|
||||
{
|
||||
commandBuffer.BindIndexBuffer(*indexBuffer);
|
||||
currentIndexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
if (const AbstractBuffer* vertexBuffer = submesh.GetVertexBuffer(); currentVertexBuffer != vertexBuffer)
|
||||
{
|
||||
commandBuffer.BindVertexBuffer(0, *vertexBuffer);
|
||||
currentVertexBuffer = vertexBuffer;
|
||||
}
|
||||
|
||||
commandBuffer.BindShaderBinding(Graphics::WorldBindingSet, submesh.GetInstanceBinding());
|
||||
|
||||
if (currentIndexBuffer)
|
||||
commandBuffer.DrawIndexed(submesh.GetIndexCount());
|
||||
else
|
||||
commandBuffer.Draw(submesh.GetIndexCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue