diff --git a/src/Layers/xrRender_R2/r2_R_lights.cpp b/src/Layers/xrRender_R2/r2_R_lights.cpp index 4e8b413619d..f0d92eedf1e 100644 --- a/src/Layers/xrRender_R2/r2_R_lights.cpp +++ b/src/Layers/xrRender_R2/r2_R_lights.cpp @@ -32,7 +32,8 @@ void CRender::render_lights(light_Package& LP) L->vis_update(); if (!L->vis.visible) { - source.erase(source.begin() + it); + std::swap(source[it], source.back()); + source.pop_back(); it--; } else @@ -42,25 +43,27 @@ void CRender::render_lights(light_Package& LP) } } - // 2. refactor - infact we could go from the backside and sort in ascending order + // 2. refactor { xr_vector& source = LP.v_shadowed; xr_vector refactored; refactored.reserve(source.size()); const size_t total = source.size(); + std::sort(source.begin(), source.end(), [](light* l1, light* l2) + { + const u32 a0 = l1->X.S.size; + const u32 a1 = l2->X.S.size; + return a0 < a1; // ascending order + }); + for (u16 smap_ID = 0; refactored.size() != total; ++smap_ID) { LP_smap_pool.initialize(RImplementation.o.smapsize); - std::sort(source.begin(), source.end(), [](light* l1, light* l2) - { - const u32 a0 = l1->X.S.size; - const u32 a1 = l2->X.S.size; - return a0 > a1; // reverse -> descending - }); - for (size_t test = 0; test < source.size(); ++test) + for (auto& L : source) { - light* L = source[test]; + if (!L) + continue; SMAP_Rect R{}; if (LP_smap_pool.push(R, L->X.S.size)) { @@ -69,14 +72,12 @@ void CRender::render_lights(light_Package& LP) L->X.S.posY = R.min.y; L->vis.smap_ID = smap_ID; refactored.push_back(L); - source.erase(source.begin() + test); - --test; + L = nullptr; } } } // save (lights are popped from back) - std::reverse(refactored.begin(), refactored.end()); LP.v_shadowed = std::move(refactored); } @@ -129,7 +130,7 @@ void CRender::render_lights(light_Package& LP) } }; - const auto& flush_lights = [&]() + const auto& flush_lights = [this] (xr_vector& lights_queue) { for (const auto& [L, task, batch_id] : lights_queue) { @@ -178,7 +179,7 @@ void CRender::render_lights(light_Package& LP) } L->svis[batch_id].end(); // NOTE(DX11): occqs are fetched here, this should be done on the imm context only - RImplementation.release_context(batch_id); + release_context(batch_id); } lights_queue.clear(); @@ -206,7 +207,7 @@ void CRender::render_lights(light_Package& LP) if (batch_id == R_dsgraph_structure::INVALID_CONTEXT_ID) { VERIFY(!lights_queue.empty()); - flush_lights(); + flush_lights(lights_queue); continue; } @@ -228,7 +229,7 @@ void CRender::render_lights(light_Package& LP) } lights_queue.emplace_back(data); } - flush_lights(); // in case if something left + flush_lights(lights_queue); // in case if something left cmd_list.Invalidate(); diff --git a/src/xrAICore/Navigation/level_graph.h b/src/xrAICore/Navigation/level_graph.h index b9793c5aca0..0e062528a98 100644 --- a/src/xrAICore/Navigation/level_graph.h +++ b/src/xrAICore/Navigation/level_graph.h @@ -232,6 +232,7 @@ class XRAICORE_API CLevelGraph IC void assign_y_values(xr_vector& path); template IC void iterate_vertices(const Fvector& min_position, const Fvector& max_position, const P& predicate) const; + IC std::pair get_range(const Fvector& min_position, const Fvector& max_position) const; IC bool check_vertex_in_direction(u32 start_vertex_id, const Fvector2& start_position, u32 finish_vertex_id) const; IC u32 check_position_in_direction( u32 start_vertex_id, const Fvector2& start_position, const Fvector2& finish_position) const; diff --git a/src/xrAICore/Navigation/level_graph_inline.h b/src/xrAICore/Navigation/level_graph_inline.h index 2a79520eb3f..a9f76def73b 100644 --- a/src/xrAICore/Navigation/level_graph_inline.h +++ b/src/xrAICore/Navigation/level_graph_inline.h @@ -572,27 +572,34 @@ template IC void CLevelGraph::iterate_vertices( const Fvector& min_position, const Fvector& max_position, const P& predicate) const { - const auto begin = m_nodes->begin(), end = m_nodes->end(); + auto [begin, end] = get_range(min_position, max_position); - CLevelVertex *I, *E; + for (; begin != end; ++begin) + predicate(*begin); +} + +IC std::pair CLevelGraph::get_range(const Fvector& min_position, const Fvector& max_position) const +{ + const auto _begin = m_nodes->begin(), _end = m_nodes->end(); + + CLevelVertex *begin, *end; if (valid_vertex_position(min_position)) - I = std::lower_bound( - begin, end, vertex_position(min_position).xz(), &vertex::predicate2); + begin = std::lower_bound( + _begin, _end, vertex_position(min_position).xz(), &vertex::predicate2); else - I = begin; + begin = _begin; if (valid_vertex_position(max_position)) { - E = std::upper_bound( - begin, end, vertex_position(max_position).xz(), &vertex::predicate); - if (E != (end)) - ++E; + end = std::upper_bound( + _begin, _end, vertex_position(max_position).xz(), &vertex::predicate); + if (end != (_end)) + ++end; } else - E = end; + end = _end; - for (; I != E; ++I) - predicate(*I); + return { begin, end }; } IC u32 CLevelGraph::max_x() const { return (m_max_x); } diff --git a/src/xrGame/Level.cpp b/src/xrGame/Level.cpp index 2f34fdfa841..8fd66abe36b 100644 --- a/src/xrGame/Level.cpp +++ b/src/xrGame/Level.cpp @@ -443,8 +443,7 @@ void CLevel::OnFrame() if (g_mt_config.test(mtMap)) { R_ASSERT(m_map_manager); - Device.seqParallel.push_back( - fastdelegate::FastDelegate0<>(m_map_manager, &CMapManager::Update)); + Device.seqParallel.emplace_back(m_map_manager, &CMapManager::Update); } else MapManager().Update(); diff --git a/src/xrGame/cover_manager.cpp b/src/xrGame/cover_manager.cpp index 3eafe6685d7..735c763c1fa 100644 --- a/src/xrGame/cover_manager.cpp +++ b/src/xrGame/cover_manager.cpp @@ -74,37 +74,42 @@ void CCoverManager::compute_static_cover() { clear(); xr_delete(m_covers); - m_covers = xr_new( - ai().level_graph().header().box(), ai().level_graph().header().cell_size() * .5f, 8 * 65536, 4 * 65536); - m_temp.resize(ai().level_graph().header().vertex_count()); const CLevelGraph& graph = ai().level_graph(); - const u32 levelVertexCount = ai().level_graph().header().vertex_count(); + const LevelGraph::CHeader& levelGraphHeader = graph.header(); + const u32 levelVertexCount = levelGraphHeader.vertex_count(); + + m_covers = xr_new(levelGraphHeader.box(), levelGraphHeader.cell_size() * .5f, 8 * 65536, 4 * 65536); + + // avoiding extra allocations with a static storage for m_covers + static xr_vector> quadTreeStaticStorage; + quadTreeStaticStorage.resize(levelVertexCount); + m_temp.resize(levelVertexCount); xr_parallel_for(TaskRange(0, levelVertexCount), [&](const TaskRange& range) { for (u32 i = range.begin(); i != range.end(); ++i) { + m_temp[i] = false; + quadTreeStaticStorage[i] = std::nullopt; + const CLevelGraph::CLevelVertex& vertex = *graph.vertex(i); - if (vertex.high_cover(0) + vertex.high_cover(1) + vertex.high_cover(2) + vertex.high_cover(3)) - { - m_temp[i] = edge_vertex(i); - continue; - } + const int highCover = vertex.high_cover(0) + vertex.high_cover(1) + vertex.high_cover(2) + vertex.high_cover(3); + const int lowCover = vertex.low_cover(0) + vertex.low_cover(1) + vertex.low_cover(2) + vertex.low_cover(3); - if (vertex.low_cover(0) + vertex.low_cover(1) + vertex.low_cover(2) + vertex.low_cover(3)) + if (highCover || lowCover) { m_temp[i] = edge_vertex(i); - continue; + if (m_temp[i] && critical_cover(i)) + { + quadTreeStaticStorage[i] = CCoverPoint(graph.vertex_position(graph.vertex(i)), i); + } } - - m_temp[i] = false; } }); - - for (u32 i = 0; i < levelVertexCount; ++i) - if (m_temp[i] && critical_cover(i)) - m_covers->insert(xr_new(ai().level_graph().vertex_position(ai().level_graph().vertex(i)), i)); + for (auto& p : quadTreeStaticStorage) + if (p.has_value()) + m_covers->insert(&p.value()); VERIFY(!m_smart_covers_storage); m_smart_covers_storage = xr_new(); diff --git a/src/xrGame/cover_manager.h b/src/xrGame/cover_manager.h index 35abc233025..3d4cb36417a 100644 --- a/src/xrGame/cover_manager.h +++ b/src/xrGame/cover_manager.h @@ -44,7 +44,9 @@ class CCoverManager protected: CPointQuadTree* m_covers; - xr_vector m_temp; + // vector is not applicable for `m_temp` + // since it is filled in parallel_for (https://timsong-cpp.github.io/cppwp/container.requirements.dataraces). + xr_vector m_temp; mutable PointVector m_nearest; private: diff --git a/src/xrGame/space_restriction_shape.cpp b/src/xrGame/space_restriction_shape.cpp index 59b3dcb3cb0..dfddf02133f 100644 --- a/src/xrGame/space_restriction_shape.cpp +++ b/src/xrGame/space_restriction_shape.cpp @@ -12,6 +12,7 @@ #include "xrAICore/Navigation/level_graph.h" #include "space_restrictor.h" #include "xrAICore/Navigation/graph_engine.h" +#include "xrCore/Threading/ParallelFor.hpp" struct CBorderMergePredicate { @@ -79,10 +80,45 @@ void CSpaceRestrictionShape::fill_shape(const CCF_Shape::shape_def& shape) } default: NODEFAULT; } - ai().level_graph().iterate_vertices(start, dest, CBorderMergePredicate(this)); + + CLevelGraph& graph = ai().level_graph(); + + std::mutex mergeMutex; + + auto [begin, end] = graph.get_range(start, dest); + xr_parallel_for(TaskRange(begin, end), [this, &mergeMutex, &graph] (const auto& range) + { + xr_vector m_border_chunk; + m_border_chunk.reserve(range.size()); + for (const auto& vertex : range) + { + if (inside(graph.vertex_id(&vertex), true) && + !inside(graph.vertex_id(&vertex), false)) + m_border_chunk.push_back(graph.vertex_id(&vertex)); + } + std::lock_guard lock(mergeMutex); + if (m_border.capacity() < m_border.size() + m_border_chunk.size()) + m_border.reserve(m_border.size() + m_border_chunk.size()); + for (auto x : m_border_chunk) + m_border.push_back(x); + }); #ifdef DEBUG - ai().level_graph().iterate_vertices(start, dest, CShapeTestPredicate(this)); + xr_parallel_for(TaskRange(begin, end), [this, &mergeMutex, &graph] (const auto& range) + { + xr_vector m_test_storage_chunk; + m_test_storage_chunk.reserve(range.size()); + for (const auto& vertex : range) + { + if (inside(graph.vertex_id(&vertex), false)) + m_test_storage_chunk.push_back(graph.vertex_id(&vertex)); + } + std::lock_guard lock(mergeMutex); + if (m_test_storage.capacity() < m_test_storage.size() + m_test_storage_chunk.size()) + m_test_storage.reserve(m_test_storage.size() + m_test_storage_chunk.size()); + for (auto x : m_test_storage_chunk) + m_test_storage.push_back(x); + }); #endif } diff --git a/src/xrGame/space_restrictor.cpp b/src/xrGame/space_restrictor.cpp index c2287aeb4ce..4f04e87c26b 100644 --- a/src/xrGame/space_restrictor.cpp +++ b/src/xrGame/space_restrictor.cpp @@ -100,7 +100,14 @@ void CSpaceRestrictor::net_Destroy() bool CSpaceRestrictor::inside(const Fsphere& sphere) const { if (!actual()) - prepare(); + { + static std::mutex prepareMutex; + std::lock_guard lock(prepareMutex); + + // Double-checked locking + if (!actual()) + prepare(); + } if (!m_selfbounds.intersect(sphere)) return (false);