diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 6e58881b8..0dfb05a06 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -450,22 +450,13 @@ void HWDrawInfo::RenderPVS(bool drawpsprites, FRenderState& state) uselevelmesh = true; Bsp.Clock(); - - // To do: do this over many threads - static HWVisibleSet VisibleSet; - VisibleSet.FindPVS(this); - // Merge results - SeenSectors.Clear(); - SeenSides.Clear(); - for (int sectorIndex : VisibleSet.SeenSectors.Get()) - SeenSectors.Add(sectorIndex); - for (int sideIndex : VisibleSet.SeenSides.Get()) - SeenSides.Add(sideIndex); + static HWVisibleSetThreads threads; + threads.FindPVS(this); if (!gl_levelmesh) // Update sector heights for the non-levelmesh rendering of sectors { - for (int sectorIndex : VisibleSet.SeenSectors.Get()) + for (int sectorIndex : SeenSectors.Get()) CheckUpdate(state, &Level->sectors[sectorIndex]); } diff --git a/src/rendering/hwrenderer/scene/hw_visibleset.cpp b/src/rendering/hwrenderer/scene/hw_visibleset.cpp index c417293f0..a21740746 100644 --- a/src/rendering/hwrenderer/scene/hw_visibleset.cpp +++ b/src/rendering/hwrenderer/scene/hw_visibleset.cpp @@ -45,7 +45,7 @@ angle_t HWVisibleSet::FrustumAngle() return a1; } -void HWVisibleSet::FindPVS(HWDrawInfo* di) +void HWVisibleSet::FindPVS(HWDrawInfo* di, int sliceIndex, int sliceCount) { Level = di->Level; Viewpoint = di->Viewpoint; @@ -62,7 +62,14 @@ void HWVisibleSet::FindPVS(HWDrawInfo* di) const auto& vp = Viewpoint; angle_t a1 = FrustumAngle(); - mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1); + + // Find the range for our slice + //uint64_t start = static_cast(vp.Angles.Yaw.BAMs() + a1); + uint64_t end = static_cast(vp.Angles.Yaw.BAMs() - a1); + uint64_t length = static_cast(a1) * 2; + uint32_t sliceend = static_cast(end + length * sliceIndex / sliceCount); + uint32_t slicestart = static_cast(end + length * (sliceIndex + 1) / sliceCount); + mClipper->SafeAddClipRangeRealAngles(slicestart, sliceend); drawctx.portalState.StartFrame(); @@ -601,3 +608,103 @@ void HWVisibleSet::RenderParticles(subsector_t* sub, sector_t* front) SetupSprite.Unclock(); #endif } + +///////////////////////////////////////////////////////////////////////////// + +HWVisibleSetThreads::HWVisibleSetThreads() +{ + SliceCount = std::max((int)(std::thread::hardware_concurrency() * 3 / 4), 1); + Slices.Resize(SliceCount); + WorkFlags.resize(SliceCount); + + size_t threadCount = SliceCount - 1; + while (Threads.size() < threadCount) + { + int sliceIndex = Threads.size() + 1; + Threads.push_back(std::thread([=]() { WorkerMain(sliceIndex); })); + } +} + +HWVisibleSetThreads::~HWVisibleSetThreads() +{ + std::unique_lock lock(Mutex); + StopFlag = true; + lock.unlock(); + Condvar.notify_all(); + for (auto& thread : Threads) + thread.join(); +} + +void HWVisibleSetThreads::FindPVS(HWDrawInfo* di) +{ + // Tell workers there's work to do! + std::unique_lock lock(Mutex); + DrawInfo = di; + for (int i = 0; i < SliceCount; i++) + WorkFlags[i] = true; + lock.unlock(); + Condvar.notify_all(); + + // Process slice zero ourselves + Slices[0].FindPVS(DrawInfo, 0, SliceCount); + + // Wait for all workers + lock.lock(); + WorkFlags[0] = false; + while (true) + { + bool allDone = true; + for (int i = 0; i < SliceCount; i++) + { + if (WorkFlags[i]) + { + allDone = false; + break; + } + } + if (allDone) + break; + Condvar.wait(lock); + } + + // Merge results + di->SeenSectors.Clear(); + di->SeenSides.Clear(); + for (int i = 0; i < SliceCount; i++) + { + for (int sectorIndex : Slices[i].SeenSectors.Get()) + di->SeenSectors.Add(sectorIndex); + for (int sideIndex : Slices[i].SeenSides.Get()) + di->SeenSides.Add(sideIndex); + } +} + +void HWVisibleSetThreads::WorkerMain(int sliceIndex) +{ + std::unique_lock lock(Mutex); + while (true) + { + if (StopFlag) + break; + if (WorkFlags[sliceIndex]) + { + lock.unlock(); + Slices[sliceIndex].FindPVS(DrawInfo, sliceIndex, SliceCount); + lock.lock(); + WorkFlags[sliceIndex] = false; + + bool allDone = true; + for (int i = 0; i < SliceCount; i++) + { + if (WorkFlags[i]) + { + allDone = false; + break; + } + } + if (allDone) + Condvar.notify_all(); + } + Condvar.wait(lock); + } +} diff --git a/src/rendering/hwrenderer/scene/hw_visibleset.h b/src/rendering/hwrenderer/scene/hw_visibleset.h index 06d5db50a..502dbacd7 100644 --- a/src/rendering/hwrenderer/scene/hw_visibleset.h +++ b/src/rendering/hwrenderer/scene/hw_visibleset.h @@ -4,6 +4,8 @@ #include "r_utility.h" #include "hw_fakeflat.h" #include "hw_drawcontext.h" +#include +#include class Clipper; class HWPortal; @@ -12,7 +14,7 @@ struct HWDrawInfo; class HWVisibleSet { public: - void FindPVS(HWDrawInfo* di); + void FindPVS(HWDrawInfo* di, int sliceIndex, int sliceCount); struct VisList { @@ -91,3 +93,24 @@ private: int rendered_lines = 0; }; + +class HWVisibleSetThreads +{ +public: + HWVisibleSetThreads(); + ~HWVisibleSetThreads(); + + void FindPVS(HWDrawInfo* di); + +private: + void WorkerMain(int sliceIndex); + + int SliceCount = 1; + TArray Slices; + std::vector Threads; + std::mutex Mutex; + std::condition_variable Condvar; + std::vector WorkFlags; + bool StopFlag = false; + HWDrawInfo* DrawInfo = nullptr; +};