From 4feccaa64af02a7061b60fb17dcedca1e3e3269e Mon Sep 17 00:00:00 2001 From: tycho Date: Sat, 30 May 2015 00:18:52 +0100 Subject: [PATCH] Clean up Spawn Prepare Made cSpawnPrepare execute on the same thread since it is a syncronous operation, and most of the code happens on the lighting thread. Also moved cSpawnPrepare into its own file --- src/CMakeLists.txt | 2 + src/SpawnPrepare.cpp | 105 ++++++++++++++++++++++++++++++++ src/SpawnPrepare.h | 47 +++++++++++++++ src/World.cpp | 138 +------------------------------------------ 4 files changed, 156 insertions(+), 136 deletions(-) create mode 100644 src/SpawnPrepare.cpp create mode 100644 src/SpawnPrepare.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a23e02789..5556ddc4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,7 @@ SET (SRCS Scoreboard.cpp Server.cpp SetChunkData.cpp + SpawnPrepare.cpp Statistics.cpp StringCompression.cpp StringUtils.cpp @@ -133,6 +134,7 @@ SET (HDRS Server.h SetChunkData.h SettingsRepositoryInterface.h + SpawnPrepare.h Statistics.h StringCompression.h StringUtils.h diff --git a/src/SpawnPrepare.cpp b/src/SpawnPrepare.cpp new file mode 100644 index 000000000..80d2c52a1 --- /dev/null +++ b/src/SpawnPrepare.cpp @@ -0,0 +1,105 @@ + +#include "Globals.h" + +#include "SpawnPrepare.h" +#include "World.h" + + + + + + + +cSpawnPrepare::cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance, int a_FirstIdx): + m_World(a_World), + m_SpawnChunkX(a_SpawnChunkX), + m_SpawnChunkZ(a_SpawnChunkZ), + m_PrepareDistance(a_PrepareDistance), + m_NextIdx(a_FirstIdx), + m_MaxIdx(a_PrepareDistance * a_PrepareDistance), + m_NumPrepared(0), + m_LastReportTime(std::chrono::steady_clock::now()), + m_LastReportChunkCount(0) +{ +} + + + + + + + +void cSpawnPrepare::PrepareChunks(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance) +{ + + // Queue the initial chunks: + int MaxIdx = a_PrepareDistance * a_PrepareDistance; + int maxQueue = std::min(MaxIdx - 1, 100); // Number of chunks to queue at once + cSpawnPrepare prep(a_World, a_SpawnChunkX, a_SpawnChunkZ, a_PrepareDistance, maxQueue); + for (int i = 0; i < maxQueue; i++) + { + int chunkX, chunkZ; + prep.DecodeChunkCoords(i, chunkX, chunkZ); + a_World.PrepareChunk(chunkX, chunkZ, &prep); + } // for i + + // Wait for the lighting thread to prepare everything. Event is set in the Call() callback: + prep.m_EvtFinished.Wait(); +} + + + + + +void cSpawnPrepare::DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ) +{ + // A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x: + int z = a_Idx / m_PrepareDistance; + int x = a_Idx % m_PrepareDistance; + if ((z & 1) == 0) + { + // Reverse every second row: + x = m_PrepareDistance - 1 - x; + } + a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2; + a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2; +} + + + + + +void cSpawnPrepare::Call(int a_ChunkX, int a_ChunkZ) +{ + // Check if this was the last chunk: + m_NumPrepared += 1; + if (m_NumPrepared >= m_MaxIdx) + { + m_EvtFinished.Set(); + // Must return here, because "this" may have gotten deleted by the previous line + return; + } + + // Queue another chunk, if appropriate: + if (m_NextIdx < m_MaxIdx) + { + int chunkX, chunkZ; + DecodeChunkCoords(m_NextIdx, chunkX, chunkZ); + m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this); + m_NextIdx += 1; + } + + // Report progress every 1 second: + auto Now = std::chrono::steady_clock::now(); + if (Now - m_LastReportTime > std::chrono::seconds(1)) + { + float PercentDone = static_cast(m_NumPrepared * 100) / m_MaxIdx; + float ChunkSpeed = static_cast((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast(Now - m_LastReportTime).count(); + LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks / sec)", + m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed + ); + m_LastReportTime = Now; + m_LastReportChunkCount = m_NumPrepared; + } +} + diff --git a/src/SpawnPrepare.h b/src/SpawnPrepare.h new file mode 100644 index 000000000..bd5c0e0c6 --- /dev/null +++ b/src/SpawnPrepare.h @@ -0,0 +1,47 @@ + +#pragma once + +class cWorld; + + + +/** Generates and lights the spawn area of the world. Runs as a separate thread. */ +class cSpawnPrepare: + public cChunkCoordCallback +{ + +public: + static void PrepareChunks(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance); + +protected: + cWorld & m_World; + int m_SpawnChunkX; + int m_SpawnChunkZ; + int m_PrepareDistance; + + /** The index of the next chunk to be queued in the lighting thread. */ + int m_NextIdx; + + /** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */ + int m_MaxIdx; + + /** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */ + int m_NumPrepared; + + /** Event used to signal that the preparation is finished. */ + cEvent m_EvtFinished; + + /** The timestamp of the last progress report emitted. */ + std::chrono::steady_clock::time_point m_LastReportTime; + + /** Number of chunks prepared when the last progress report was emitted. */ + int m_LastReportChunkCount; + + cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance, int a_FirstIdx); + + virtual void Call(int a_ChunkX, int a_ChunkZ) override; + + /** Decodes the index into chunk coords. Provides the specific chunk ordering. */ + void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ); +}; + diff --git a/src/World.cpp b/src/World.cpp index 27bb3bdbd..aabbd7276 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -58,6 +58,7 @@ #endif #include "Broadcaster.h" +#include "SpawnPrepare.h" @@ -71,140 +72,6 @@ const int TIME_SPAWN_DIVISOR = 148; -//////////////////////////////////////////////////////////////////////////////// -// cSpawnPrepare: - -/** Generates and lights the spawn area of the world. Runs as a separate thread. */ -class cSpawnPrepare: - public cIsThread, - public cChunkCoordCallback -{ - typedef cIsThread super; - -public: - cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance): - super("SpawnPrepare"), - m_World(a_World), - m_SpawnChunkX(a_SpawnChunkX), - m_SpawnChunkZ(a_SpawnChunkZ), - m_PrepareDistance(a_PrepareDistance), - m_MaxIdx(a_PrepareDistance * a_PrepareDistance), - m_NumPrepared(0), - m_LastReportChunkCount(0) - { - // Start the thread: - Start(); - - // Wait for start confirmation, so that the thread can be waited-upon after the constructor returns: - m_EvtStarted.Wait(); - } - - - // cIsThread override: - virtual void Execute(void) override - { - // Confirm thread start: - m_EvtStarted.Set(); - - // Queue the initial chunks: - m_MaxIdx = m_PrepareDistance * m_PrepareDistance; - int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once - m_NextIdx = maxQueue; - m_LastReportTime = std::chrono::steady_clock::now(); - for (int i = 0; i < maxQueue; i++) - { - int chunkX, chunkZ; - DecodeChunkCoords(i, chunkX, chunkZ); - m_World.PrepareChunk(chunkX, chunkZ, this); - } // for i - - // Wait for the lighting thread to prepare everything. Event is set in the Call() callback: - m_EvtFinished.Wait(); - } - -protected: - cWorld & m_World; - int m_SpawnChunkX; - int m_SpawnChunkZ; - int m_PrepareDistance; - - /** The index of the next chunk to be queued in the lighting thread. */ - int m_NextIdx; - - /** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */ - int m_MaxIdx; - - /** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */ - int m_NumPrepared; - - /** Event used to signal that the thread has started. */ - cEvent m_EvtStarted; - - /** Event used to signal that the preparation is finished. */ - cEvent m_EvtFinished; - - /** The timestamp of the last progress report emitted. */ - std::chrono::steady_clock::time_point m_LastReportTime; - - /** Number of chunks prepared when the last progress report was emitted. */ - int m_LastReportChunkCount; - - // cChunkCoordCallback override: - virtual void Call(int a_ChunkX, int a_ChunkZ) override - { - // Check if this was the last chunk: - m_NumPrepared += 1; - if (m_NumPrepared >= m_MaxIdx) - { - m_EvtFinished.Set(); - // Must return here, because "this" may have gotten deleted by the previous line - return; - } - - // Queue another chunk, if appropriate: - if (m_NextIdx < m_MaxIdx) - { - int chunkX, chunkZ; - DecodeChunkCoords(m_NextIdx, chunkX, chunkZ); - m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this); - m_NextIdx += 1; - } - - // Report progress every 1 second: - auto Now = std::chrono::steady_clock::now(); - if (Now - m_LastReportTime > std::chrono::seconds(1)) - { - float PercentDone = static_cast(m_NumPrepared * 100) / m_MaxIdx; - float ChunkSpeed = static_cast((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast(Now - m_LastReportTime).count(); - LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks / sec)", - m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed - ); - m_LastReportTime = Now; - m_LastReportChunkCount = m_NumPrepared; - } - } - - - /** Decodes the index into chunk coords. Provides the specific chunk ordering. */ - void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ) - { - // A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x: - int z = a_Idx / m_PrepareDistance; - int x = a_Idx % m_PrepareDistance; - if ((z & 1) == 0) - { - // Reverse every second row: - x = m_PrepareDistance - 1 - x; - } - a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2; - a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2; - } -}; - - - - - //////////////////////////////////////////////////////////////////////////////// // cWorld::cLock: @@ -470,8 +337,7 @@ void cWorld::InitializeSpawn(void) int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist); IniFile.WriteFile(m_IniFileName); - cSpawnPrepare prep(*this, ChunkX, ChunkZ, ViewDist); - prep.Wait(); + cSpawnPrepare::PrepareChunks(*this, ChunkX, ChunkZ, ViewDist); #ifdef TEST_LINEBLOCKTRACER // DEBUG: Test out the cLineBlockTracer class by tracing a few lines: