From 510a9585b810846eb0f874d788cbfdf02215f7ea Mon Sep 17 00:00:00 2001
From: Silverlan <silverlann@gmail.com>
Date: Wed, 25 Dec 2024 15:25:20 +0100
Subject: [PATCH] feat: improve performance of world render queue building

---
 .../entities/components/c_model_component.hpp |  2 +-
 core/client/src/entities/c_world.cpp          | 89 +++++++++++--------
 .../entities/components/c_model_component.cpp |  6 +-
 .../classes/component/c_lmodel_component.cpp  | 13 ++-
 4 files changed, 59 insertions(+), 51 deletions(-)

diff --git a/core/client/include/pragma/entities/components/c_model_component.hpp b/core/client/include/pragma/entities/components/c_model_component.hpp
index f490467a9..e2dfc25a8 100644
--- a/core/client/include/pragma/entities/components/c_model_component.hpp
+++ b/core/client/include/pragma/entities/components/c_model_component.hpp
@@ -109,6 +109,7 @@ namespace pragma {
 		virtual void OnTick(double tDelta) override;
 		void FlushRenderData();
 
+		void UpdateRenderBufferList();
 		void UpdateRenderMeshes(bool requireBoundingVolumeUpdate = true);
 		void ReloadRenderBufferList(bool immediate = false);
 		// Only use if LOD is handled externally!
@@ -116,7 +117,6 @@ namespace pragma {
 	  protected:
 		void UpdateBaseShaderSpecializationFlags();
 		virtual void OnModelChanged(const std::shared_ptr<Model> &model) override;
-		void UpdateRenderBufferList();
 
 		std::unordered_map<const CModelSubMesh *, std::shared_ptr<prosper::IBuffer>> m_lightmapUvBuffers {};
 		std::vector<msys::MaterialHandle> m_materialOverrides = {};
diff --git a/core/client/src/entities/c_world.cpp b/core/client/src/entities/c_world.cpp
index ce1e6233f..232e1bd3c 100644
--- a/core/client/src/entities/c_world.cpp
+++ b/core/client/src/entities/c_world.cpp
@@ -26,6 +26,7 @@
 #include "pragma/rendering/render_processor.hpp"
 #include "pragma/lua/c_lentity_handles.hpp"
 #include <buffers/prosper_buffer.hpp>
+#include <sharedutils/BS_thread_pool.hpp>
 #include <prosper_util.hpp>
 #include <pragma/entities/entity_iterator.hpp>
 #include <pragma/entities/components/base_transform_component.hpp>
@@ -273,49 +274,59 @@ void CWorldComponent::BuildOfflineRenderQueues(bool rebuild)
 		}
 	}
 
-	clusterRenderQueues.reserve(numClusters);
-	clusterRenderTranslucentQueues.reserve(numClusters);
+	clusterRenderQueues.resize(numClusters);
+	clusterRenderTranslucentQueues.resize(numClusters);
+	BS::thread_pool tp {std::thread::hardware_concurrency()};
+	std::atomic<bool> failure {false};
 	auto &context = c_engine->GetRenderContext();
-	for(auto clusterIdx = decltype(meshesPerClusters.size()) {0u}; clusterIdx < meshesPerClusters.size(); ++clusterIdx) {
-		clusterRenderQueues.push_back(pragma::rendering::RenderQueue::Create("world_cluster_" + std::to_string(clusterIdx)));
-		std::shared_ptr<pragma::rendering::RenderQueue> clusterRenderTranslucentQueue = nullptr;
-		auto &clusterRenderQueue = clusterRenderQueues.back();
-		auto &meshes = meshesPerClusters.at(clusterIdx);
-		for(auto subMeshIdx : meshes) {
-			if(subMeshIdx >= renderMeshes.size()) {
-				// Something went wrong (Maybe world model is missing?)
-				clusterRenderQueues.clear();
-				clusterRenderTranslucentQueues.clear();
+	tp.submit_blocks<size_t>(0u, meshesPerClusters.size(), [&](size_t start, size_t end) {
+		for(auto clusterIdx = start; clusterIdx < end; ++clusterIdx) {
+			if(failure == true)
 				return;
+			auto clusterRenderQueue = pragma::rendering::RenderQueue::Create("world_cluster_" + std::to_string(clusterIdx));
+			clusterRenderQueues[clusterIdx] = clusterRenderQueue;
+			std::shared_ptr<pragma::rendering::RenderQueue> clusterRenderTranslucentQueue = nullptr;
+			auto &meshes = meshesPerClusters.at(clusterIdx);
+			for(auto subMeshIdx : meshes) {
+				if(subMeshIdx >= renderMeshes.size()) {
+					failure = true;
+					return;
+				}
+				auto subMesh = renderMeshes.at(subMeshIdx);
+				auto *mat = mdlC->GetRenderMaterial(subMesh->GetSkinTextureIndex());
+				if(mat == nullptr)
+					continue;
+				auto hShader = mat->GetPrimaryShader();
+				if(!hShader)
+					continue;
+				auto *shader = dynamic_cast<pragma::ShaderGameWorldLightingPass *>(hShader);
+				if(shader == nullptr)
+					continue;
+				uint32_t pipelineIdx = 0;
+				auto t = shader->FindPipelineIndex(pragma::rendering::PassType::Generic, renderC->GetShaderPipelineSpecialization(), shader->GetMaterialPipelineSpecializationRequirements(*mat)); // | pragma::GameShaderSpecializationConstantFlag::Enable3dOriginBit);
+				if(t.has_value())
+					pipelineIdx = *t;
+				prosper::PipelineID pipelineId;
+				if(shader->GetPipelineId(pipelineId, pipelineIdx) == false || pipelineId == std::numeric_limits<decltype(pipelineId)>::max())
+					continue;
+				if(mat->GetAlphaMode() == AlphaMode::Blend || shader->IsTranslucentPipeline(pipelineIdx)) {
+					clusterRenderTranslucentQueue = clusterRenderTranslucentQueue ? clusterRenderTranslucentQueue : pragma::rendering::RenderQueue::Create("world_translucent_cluster_" + std::to_string(clusterIdx));
+					clusterRenderTranslucentQueue->Add(static_cast<CBaseEntity &>(GetEntity()), subMeshIdx, *mat, pipelineId);
+					continue;
+				}
+				clusterRenderQueue->Add(static_cast<CBaseEntity &>(GetEntity()), subMeshIdx, *mat, pipelineId);
 			}
-			auto subMesh = renderMeshes.at(subMeshIdx);
-			auto *mat = mdlC->GetRenderMaterial(subMesh->GetSkinTextureIndex());
-			if(mat == nullptr)
-				continue;
-			auto hShader = mat->GetPrimaryShader();
-			if(!hShader)
-				continue;
-			auto *shader = dynamic_cast<pragma::ShaderGameWorldLightingPass *>(hShader);
-			if(shader == nullptr)
-				continue;
-			uint32_t pipelineIdx = 0;
-			auto t = shader->FindPipelineIndex(pragma::rendering::PassType::Generic, renderC->GetShaderPipelineSpecialization(), shader->GetMaterialPipelineSpecializationRequirements(*mat)); // | pragma::GameShaderSpecializationConstantFlag::Enable3dOriginBit);
-			if(t.has_value())
-				pipelineIdx = *t;
-			prosper::PipelineID pipelineId;
-			if(shader->GetPipelineId(pipelineId, pipelineIdx) == false || pipelineId == std::numeric_limits<decltype(pipelineId)>::max())
-				continue;
-			if(mat->GetAlphaMode() == AlphaMode::Blend) {
-				clusterRenderTranslucentQueue = clusterRenderTranslucentQueue ? clusterRenderTranslucentQueue : pragma::rendering::RenderQueue::Create("world_translucent_cluster_" + std::to_string(clusterIdx));
-				clusterRenderTranslucentQueue->Add(static_cast<CBaseEntity &>(GetEntity()), subMeshIdx, *mat, pipelineId);
-				continue;
-			}
-			clusterRenderQueue->Add(static_cast<CBaseEntity &>(GetEntity()), subMeshIdx, *mat, pipelineId);
+			clusterRenderTranslucentQueues[clusterIdx] = clusterRenderTranslucentQueue;
+			clusterRenderQueue->Sort();
+			if(clusterRenderTranslucentQueue)
+				clusterRenderTranslucentQueue->Sort();
 		}
-		clusterRenderTranslucentQueues.push_back(clusterRenderTranslucentQueue);
-		clusterRenderQueue->Sort();
-		if(clusterRenderTranslucentQueue)
-			clusterRenderTranslucentQueue->Sort();
+	});
+	tp.wait();
+	if(failure == true) {
+		// Something went wrong (Maybe world model is missing?)
+		clusterRenderQueues.clear();
+		clusterRenderTranslucentQueues.clear();
 	}
 }
 
diff --git a/core/client/src/entities/components/c_model_component.cpp b/core/client/src/entities/components/c_model_component.cpp
index 99434d492..b47b56a11 100644
--- a/core/client/src/entities/components/c_model_component.cpp
+++ b/core/client/src/entities/components/c_model_component.cpp
@@ -101,7 +101,7 @@ void CModelComponent::SetMaterialOverride(uint32_t idx, CMaterial &mat)
 	if(idx >= m_materialOverrides.size())
 		m_materialOverrides.resize(idx + 1);
 	m_materialOverrides.at(idx) = mat.GetHandle();
-	umath::set_flag(m_stateFlags, StateFlags::RenderMeshUpdateRequired);
+	umath::set_flag(m_stateFlags, StateFlags::RenderBufferListUpdateRequired);
 	mat.UpdateTextures(); // Ensure all textures have been fully loaded
 }
 void CModelComponent::ClearMaterialOverride(uint32_t idx)
@@ -109,14 +109,14 @@ void CModelComponent::ClearMaterialOverride(uint32_t idx)
 	if(idx >= m_materialOverrides.size())
 		return;
 	m_materialOverrides.at(idx) = {};
-	umath::set_flag(m_stateFlags, StateFlags::RenderMeshUpdateRequired);
+	umath::set_flag(m_stateFlags, StateFlags::RenderBufferListUpdateRequired);
 }
 void CModelComponent::ClearMaterialOverrides()
 {
 	if(m_materialOverrides.empty())
 		return;
 	m_materialOverrides.clear();
-	umath::set_flag(m_stateFlags, StateFlags::RenderMeshUpdateRequired);
+	umath::set_flag(m_stateFlags, StateFlags::RenderBufferListUpdateRequired);
 	BroadcastEvent(EVENT_ON_MATERIAL_OVERRIDES_CLEARED);
 }
 CMaterial *CModelComponent::GetMaterialOverride(uint32_t idx) const { return (idx < m_materialOverrides.size()) ? static_cast<CMaterial *>(m_materialOverrides.at(idx).get()) : nullptr; }
diff --git a/core/client/src/lua/classes/component/c_lmodel_component.cpp b/core/client/src/lua/classes/component/c_lmodel_component.cpp
index 3e55d02ef..f619aee46 100644
--- a/core/client/src/lua/classes/component/c_lmodel_component.cpp
+++ b/core/client/src/lua/classes/component/c_lmodel_component.cpp
@@ -80,8 +80,7 @@ void Lua::ModelDef::register_class(lua_State *l, luabind::module_ &entsMod)
 	defCModel.def("ClearMaterialOverride", &pragma::CModelComponent::ClearMaterialOverride);
 	defCModel.def("ClearMaterialOverrides", &pragma::CModelComponent::ClearMaterialOverrides);
 	defCModel.def("GetMaterialOverride", &pragma::CModelComponent::GetMaterialOverride);
-	defCModel.def(
-	  "GetMaterialOverrideCount", +[](pragma::CModelComponent &c) -> size_t { return c.GetMaterialOverrides().size(); });
+	defCModel.def("GetMaterialOverrideCount", +[](pragma::CModelComponent &c) -> size_t { return c.GetMaterialOverrides().size(); });
 	defCModel.def("GetRenderMaterial", static_cast<CMaterial *(pragma::CModelComponent::*)(uint32_t, uint32_t) const>(&pragma::CModelComponent::GetRenderMaterial));
 	defCModel.def("GetRenderMaterial", static_cast<CMaterial *(pragma::CModelComponent::*)(uint32_t) const>(&pragma::CModelComponent::GetRenderMaterial));
 	defCModel.def("GetLOD", &pragma::CModelComponent::GetLOD);
@@ -91,23 +90,21 @@ void Lua::ModelDef::register_class(lua_State *l, luabind::module_ &entsMod)
 	defCModel.def("GetMaxDrawDistance", &pragma::CModelComponent::GetMaxDrawDistance);
 	defCModel.def("UpdateRenderMeshes", &pragma::CModelComponent::UpdateRenderMeshes, luabind::default_parameter_policy<2, true> {});
 	defCModel.def("UpdateRenderMeshes", &pragma::CModelComponent::UpdateRenderMeshes);
+	defCModel.def("UpdateRenderBufferList", &pragma::CModelComponent::UpdateRenderBufferList);
 	defCModel.def("SetRenderMeshesDirty", &pragma::CModelComponent::SetRenderMeshesDirty);
 	defCModel.def("ReloadRenderBufferList", &pragma::CModelComponent::ReloadRenderBufferList, luabind::default_parameter_policy<2, false> {});
 	defCModel.def("ReloadRenderBufferList", &pragma::CModelComponent::ReloadRenderBufferList);
 	defCModel.def("IsDepthPrepassEnabled", &pragma::CModelComponent::IsDepthPrepassEnabled);
 	defCModel.def("SetDepthPrepassEnabled", &pragma::CModelComponent::SetDepthPrepassEnabled);
 	defCModel.def("SetRenderBufferData", &pragma::CModelComponent::SetRenderBufferData);
-	defCModel.def(
-	  "GetRenderBufferData", +[](pragma::CModelComponent &c) -> std::vector<pragma::rendering::RenderBufferData> { return c.GetRenderBufferData(); });
+	defCModel.def("GetRenderBufferData", +[](pragma::CModelComponent &c) -> std::vector<pragma::rendering::RenderBufferData> { return c.GetRenderBufferData(); });
 	defCModel.def("AddRenderMesh", &pragma::CModelComponent::AddRenderMesh);
 	defCModel.def("AddRenderMesh", &pragma::CModelComponent::AddRenderMesh, luabind::default_parameter_policy<4, pragma::rendering::RenderBufferData::StateFlags::EnableDepthPrepass> {});
-	defCModel.def(
-	  "GetRenderMeshes", +[](pragma::CModelComponent &c) -> std::vector<std::shared_ptr<ModelSubMesh>> { return c.GetRenderMeshes(); });
+	defCModel.def("GetRenderMeshes", +[](pragma::CModelComponent &c) -> std::vector<std::shared_ptr<ModelSubMesh>> { return c.GetRenderMeshes(); });
 	defCModel.def("GetBaseShaderSpecializationFlags", &pragma::CModelComponent::GetBaseShaderSpecializationFlags);
 	defCModel.def("SetBaseShaderSpecializationFlags", &pragma::CModelComponent::SetBaseShaderSpecializationFlags);
 	defCModel.def("SetBaseShaderSpecializationFlag", &pragma::CModelComponent::SetBaseShaderSpecializationFlag);
-	defCModel.def(
-	  "SetBaseShaderSpecializationFlag", +[](pragma::CModelComponent &c, pragma::GameShaderSpecializationConstantFlag flag) { c.SetBaseShaderSpecializationFlag(flag); });
+	defCModel.def("SetBaseShaderSpecializationFlag", +[](pragma::CModelComponent &c, pragma::GameShaderSpecializationConstantFlag flag) { c.SetBaseShaderSpecializationFlag(flag); });
 	defCModel.def("GetStaticShaderSpecializationFlags", &pragma::CModelComponent::GetStaticShaderSpecializationFlags);
 	defCModel.def("SetStaticShaderSpecializationFlags", &pragma::CModelComponent::SetStaticShaderSpecializationFlags);