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);