@@ -61,7 +61,7 @@ static int const TEXTURE_WIDTH = 256; | |||
#define HST_CLAMP 1.f | |||
#define WITH_TEXTURE 0 | |||
#define WITH_THREAD_MANAGER 0 // FIXME: this was removed from Lol Engine | |||
#define HAS_KBOARD (m_input_usage & (1<<IPT_MV_KBOARD)) | |||
#define HAS_MOUSE (m_input_usage & (1<<IPT_MV_MOUSE)) | |||
@@ -163,7 +163,9 @@ void MeshViewer::Start() | |||
//Threads setup | |||
m_entities << (m_file_check = new FileUpdateTester()); | |||
#if WITH_THREAD_MANAGER | |||
m_entities << (m_file_loader = new DefaultThreadManager(4, 0)); | |||
#endif | |||
//Scene setup | |||
m_ssetup_file_name = "../data/meshviewer.init.lua"; | |||
@@ -171,7 +173,9 @@ void MeshViewer::Start() | |||
//Mesh file | |||
m_file_status = m_file_check->RegisterFile(m_file_name); | |||
#if WITH_THREAD_MANAGER | |||
m_file_loader->AddJob(GetLoadJob(m_file_name)); | |||
#endif | |||
//Camera setup | |||
m_camera = new Camera(); | |||
@@ -233,7 +237,9 @@ void MeshViewer::Stop() | |||
m_camera = nullptr; | |||
m_controller = nullptr; | |||
m_file_check = nullptr; | |||
#if WITH_THREAD_MANAGER | |||
m_file_loader = nullptr; | |||
#endif | |||
/** ----- Init is needed ----- **/ | |||
m_init = false; | |||
@@ -345,15 +351,19 @@ void MeshViewer::tick_game(float seconds) | |||
//Check file update | |||
ASSERT(m_file_status); | |||
//if (false) //DEBUG | |||
#if WITH_THREAD_MANAGER | |||
//m_file_status->GetTime() | |||
if (m_file_status->HasUpdated()) | |||
m_file_loader->AddJob(GetLoadJob(m_file_name)); | |||
#endif | |||
//Check work done | |||
//if (false) //DEBUG | |||
{ | |||
array<ThreadJob*> result; | |||
#if WITH_THREAD_MANAGER | |||
m_file_loader->GetWorkResult(result); | |||
#endif | |||
if (result.count()) | |||
{ | |||
for (ThreadJob* job : result) | |||
@@ -114,8 +114,7 @@ liblol_core_sources = \ | |||
mesh/mesh.cpp mesh/mesh.h \ | |||
mesh/primitivemesh.cpp mesh/primitivemesh.h \ | |||
\ | |||
sys/init.cpp sys/file.cpp sys/hacks.cpp \ | |||
sys/thread.cpp sys/threadtypes.cpp sys/getopt.cpp \ | |||
sys/init.cpp sys/file.cpp sys/hacks.cpp sys/getopt.cpp \ | |||
\ | |||
image/resource.cpp image/resource-private.h \ | |||
image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \ | |||
@@ -177,8 +177,6 @@ | |||
<ClCompile Include="sys\getopt.cpp" /> | |||
<ClCompile Include="sys\hacks.cpp" /> | |||
<ClCompile Include="sys\init.cpp" /> | |||
<ClCompile Include="sys\thread.cpp" /> | |||
<ClCompile Include="sys\threadtypes.cpp" /> | |||
<ClCompile Include="text.cpp" /> | |||
<ClCompile Include="textureimage.cpp" /> | |||
<ClCompile Include="tileset.cpp" /> | |||
@@ -277,7 +275,6 @@ | |||
<ClInclude Include="lol\sys\getopt.h" /> | |||
<ClInclude Include="lol\sys\init.h" /> | |||
<ClInclude Include="lol\sys\thread.h" /> | |||
<ClInclude Include="lol\sys\threadtypes.h" /> | |||
<ClInclude Include="lol\sys\timer.h" /> | |||
<ClInclude Include="mesh\mesh.h" /> | |||
<ClInclude Include="mesh\primitivemesh.h" /> | |||
@@ -303,9 +303,6 @@ | |||
<ClCompile Include="image\codec\zed-palette-image.cpp"> | |||
<Filter>image\codec</Filter> | |||
</ClCompile> | |||
<ClCompile Include="sys\thread.cpp"> | |||
<Filter>sys</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\filter\colors.cpp"> | |||
<Filter>image\filter</Filter> | |||
</ClCompile> | |||
@@ -366,9 +363,6 @@ | |||
<ClCompile Include="lolua\baselua.cpp"> | |||
<Filter>lolua</Filter> | |||
</ClCompile> | |||
<ClCompile Include="sys\threadtypes.cpp"> | |||
<Filter>sys</Filter> | |||
</ClCompile> | |||
<ClCompile Include="tileset.cpp"> | |||
<Filter>tileset</Filter> | |||
</ClCompile> | |||
@@ -712,9 +706,6 @@ | |||
<ClInclude Include="lolua\baselua.h"> | |||
<Filter>lolua</Filter> | |||
</ClInclude> | |||
<ClInclude Include="lol\sys\threadtypes.h"> | |||
<Filter>lol\sys</Filter> | |||
</ClInclude> | |||
<ClInclude Include="tileset.h"> | |||
<Filter>tileset</Filter> | |||
</ClInclude> | |||
@@ -1,7 +1,7 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2016 Sam Hocevar <sam@hocevar.net> | |||
// Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net> | |||
// | |||
// Lol Engine is free software. It comes without any warranty, to | |||
// the extent permitted by applicable law. You can redistribute it | |||
@@ -14,7 +14,6 @@ | |||
#include <lol/sys/thread.h> | |||
#include <lol/sys/timer.h> /* requires thread.h */ | |||
#include <lol/sys/threadtypes.h> | |||
#include <lol/sys/getopt.h> | |||
#include <lol/sys/init.h> | |||
#include <lol/sys/file.h> | |||
@@ -1,278 +0,0 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net> | |||
// © 2013—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com> | |||
// | |||
// Lol Engine is free software. It comes without any warranty, to | |||
// the extent permitted by applicable law. You can redistribute it | |||
// and/or modify it under the terms of the Do What the Fuck You Want | |||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||
// See http://www.wtfpl.net/ for more details. | |||
// | |||
#pragma once | |||
// | |||
// The Threading classes | |||
// --------------------- | |||
// | |||
#include "engine/entity.h" | |||
#include <map> | |||
namespace lol | |||
{ | |||
//ThreadStatus ---------------------------------------------------------------- | |||
struct ThreadStatusBase : public StructSafeEnum | |||
{ | |||
enum Type | |||
{ | |||
NOTHING, | |||
THREAD_STARTED, | |||
THREAD_WORKING, | |||
THREAD_IDLE, | |||
THREAD_STOPPED, | |||
}; | |||
protected: | |||
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map) | |||
{ | |||
enum_map[NOTHING] = "NOTHING"; | |||
enum_map[THREAD_STARTED] = "THREAD_STARTED"; | |||
enum_map[THREAD_STOPPED] = "THREAD_STOPPED"; | |||
return true; | |||
} | |||
}; | |||
typedef SafeEnum<ThreadStatusBase> ThreadStatus; | |||
struct ThreadJobTypeBase : public StructSafeEnum | |||
{ | |||
enum Type | |||
{ | |||
NONE, | |||
WORK_TODO, | |||
WORK_SUCCEEDED, | |||
WORK_FAILED, | |||
WORK_DONE, | |||
THREAD_STOP | |||
}; | |||
protected: | |||
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map) | |||
{ | |||
enum_map[NONE] = "NONE"; | |||
enum_map[WORK_TODO] = "WORK_TODO"; | |||
enum_map[WORK_SUCCEEDED] = "WORK_SUCCEEDED"; | |||
enum_map[WORK_FAILED] = "WORK_FAILED"; | |||
enum_map[WORK_DONE] = "WORK_DONE"; | |||
enum_map[THREAD_STOP] = "THREAD_STOP"; | |||
return true; | |||
} | |||
}; | |||
typedef SafeEnum<ThreadJobTypeBase> ThreadJobType; | |||
//ThreadJob ------------------------------------------------------------------- | |||
class ThreadJob | |||
{ | |||
friend class BaseThreadManager; | |||
protected: | |||
inline ThreadJob(ThreadJobType type) : m_type(type) {} | |||
public: | |||
std::string GetName() const { return "<ThreadJob>"; } | |||
inline ThreadJob() : m_type(ThreadJobType::NONE) {} | |||
virtual ~ThreadJob() {} | |||
ThreadJobType GetJobType() { return m_type; } | |||
void SetJobType(ThreadJobType type) { m_type = type; } | |||
bool operator==(const ThreadJobType& o) { return GetJobType() == o; } | |||
protected: | |||
virtual bool DoWork() { return false; } | |||
ThreadJobType m_type; | |||
}; | |||
//Base class for thread manager ----------------------------------------------- | |||
class BaseThreadManager : public Entity | |||
{ | |||
public: | |||
std::string GetName() const { return "<BaseThreadManager>"; } | |||
BaseThreadManager(int thread_max); | |||
BaseThreadManager(int thread_max, int thread_min); | |||
virtual ~BaseThreadManager(); | |||
//Base setup | |||
void Setup(int thread_max); | |||
void Setup(int thread_max, int thread_min); | |||
//Initialize, Ticker::Ref and start the thread | |||
bool Start(); | |||
//Stop the threads | |||
bool Stop(); | |||
private: | |||
//Work stuff | |||
bool AddThreadWork(ThreadJob* job); | |||
protected: | |||
//Thread count management | |||
void AdjustThreadCount(int count); | |||
void CleanAddedThread(bool wait = false); | |||
void CleanRemovedThread(bool wait = false); | |||
int GetDispatchCount(); | |||
int GetDispatchedCount(); | |||
//Dispatch job to threads | |||
void DispatchJob(ThreadJob* job); | |||
void DispatchJob(array<ThreadJob*> const& jobs); | |||
//Fetch Results | |||
bool FetchResult(array<ThreadJob*>& results); | |||
//Base thread work function | |||
void BaseThreadWork(thread* inst); | |||
virtual void tick_game(float seconds); | |||
//Default behaviour : delete the job result | |||
virtual void TreatResult(ThreadJob* result) { delete(result); } | |||
private: | |||
/* Jobs */ | |||
array<ThreadJob*> m_job_dispatch; | |||
int m_job_dispatched = 0; | |||
#if LOL_FEATURE_THREADS | |||
/* Worker threads */ | |||
int m_thread_max = 0; | |||
int m_thread_min = 0; | |||
int m_thread_active = 0; | |||
array<thread*> m_threads; | |||
timer m_thread_added_timer; | |||
int m_thread_added = 0; | |||
timer m_thread_removed_timer; | |||
int m_thread_removed = 0; | |||
queue<ThreadStatus> m_statusqueue; | |||
queue<thread*> m_spawnqueue, m_donequeue; | |||
#endif //LOL_FEATURE_THREADS | |||
queue<ThreadJob*> m_jobqueue; | |||
queue<ThreadJob*> m_resultqueue; | |||
}; | |||
//Generic class for thread manager, executes work and store results, with no specific treatment | |||
class DefaultThreadManager : public BaseThreadManager | |||
{ | |||
public: | |||
std::string GetName() const { return "<DefaultThreadManager>"; } | |||
DefaultThreadManager(int thread_max) | |||
: BaseThreadManager(thread_max, thread_max) | |||
{ } | |||
DefaultThreadManager(int thread_max, int thread_min) | |||
: BaseThreadManager(thread_max, thread_min) | |||
{ } | |||
//Work stuff | |||
void AddJob(ThreadJob* job); | |||
bool GetWorkResult(array<ThreadJob*>& results); | |||
protected: | |||
virtual void TreatResult(ThreadJob* result) { m_job_result << result; } | |||
array<ThreadJob*> m_job_result; | |||
}; | |||
//FileUpdateTester ------------------------------------------------------------ | |||
//Test the files registered and warns when they update | |||
class FileUpdateTester : public BaseThreadManager | |||
{ | |||
typedef BaseThreadManager super; | |||
public: | |||
class Status | |||
{ | |||
friend class FileUpdateTester; | |||
protected: | |||
mutex m_mutex; | |||
long int m_time = 0; | |||
bool m_updated = false; | |||
public: | |||
void SetTime(long int time) | |||
{ | |||
m_mutex.lock(); | |||
m_time = time; | |||
m_mutex.unlock(); | |||
} | |||
long int GetTime() | |||
{ | |||
m_mutex.lock(); | |||
long int time = m_time; | |||
m_mutex.unlock(); | |||
return time; | |||
} | |||
void SetUpdated(bool updated) | |||
{ | |||
m_mutex.lock(); | |||
m_updated = updated; | |||
m_mutex.unlock(); | |||
} | |||
bool HasUpdated() | |||
{ | |||
m_mutex.lock(); | |||
bool updated = m_updated; | |||
m_mutex.unlock(); | |||
return updated; | |||
} | |||
}; | |||
public: | |||
std::string GetName() const { return "<FileUpdateTester>"; } | |||
FileUpdateTester(uint32_t frame_skip = 4) | |||
: BaseThreadManager(1) | |||
{ m_frame_skip = frame_skip; } | |||
virtual ~FileUpdateTester(); | |||
//------------------------------------------------------------------------- | |||
FileUpdateTester::Status* RegisterFile(std::string const& path); | |||
protected: | |||
void UnregisterFile(std::string const& path); | |||
public: | |||
void UnregisterFile(FileUpdateTester::Status*& status); | |||
//TODO: Add directories | |||
//FileUpdateTester::Status* RegisterDirectory(std::string const& path, bool check_files=false); | |||
virtual void tick_game(float seconds); | |||
virtual void TreatResult(ThreadJob* result); | |||
private: | |||
uint32_t m_frame_skip = 4; | |||
uint32_t m_frame_count = 0; | |||
array<ThreadJob*> m_job_done; | |||
std::map<std::string, Status*> m_files; | |||
}; | |||
typedef FileUpdateTester::Status FileUpdateStatus; | |||
//AsyncImageLoader ------------------------------------------------------------ | |||
//Load images asynchronously, automatically updating the dummy image | |||
class AsyncImageLoader : public BaseThreadManager | |||
{ | |||
public: | |||
std::string GetName() const { return "<AsyncImageLoader>"; } | |||
AsyncImageLoader(int thread_max) | |||
: BaseThreadManager(thread_max, 0) | |||
{ | |||
m_dummy_image.DummyFill(); | |||
} | |||
virtual ~AsyncImageLoader() | |||
{ } | |||
//Returns a dummy image, and start a job to load the image on a thread | |||
image* Load(const std::string& path); | |||
bool CheckStatus(image* img); | |||
protected: | |||
virtual void TreatResult(ThreadJob* result); | |||
private: | |||
image m_dummy_image; | |||
std::map<std::string, image*> m_images; | |||
array<image*> m_loaded_images; | |||
}; | |||
} /* namespace lol */ | |||
@@ -1,286 +0,0 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net> | |||
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com> | |||
// | |||
// Lol Engine is free software. It comes without any warranty, to | |||
// the extent permitted by applicable law. You can redistribute it | |||
// and/or modify it under the terms of the Do What the Fuck You Want | |||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||
// See http://www.wtfpl.net/ for more details. | |||
// | |||
#include <lol/engine-internal.h> | |||
namespace lol | |||
{ | |||
//BaseThreadManager ----------------------------------------------------------- | |||
BaseThreadManager::BaseThreadManager(int thread_max) : BaseThreadManager(thread_max, thread_max) | |||
{ } | |||
BaseThreadManager::BaseThreadManager(int thread_max, int thread_min) | |||
{ | |||
Setup(thread_max, thread_min); | |||
} | |||
BaseThreadManager::~BaseThreadManager() | |||
{ | |||
Stop(); | |||
} | |||
//Base Setup ------------------------------------------------------------------ | |||
void BaseThreadManager::Setup(int thread_max) | |||
{ | |||
Setup(thread_max, thread_max); | |||
} | |||
void BaseThreadManager::Setup(int thread_max, int thread_min) | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
m_thread_max = thread_max; | |||
m_thread_min = thread_min; | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//Initialize, Ticker::Ref and start the thread -------------------------------- | |||
bool BaseThreadManager::Start() | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
ASSERT(!!m_thread_max, "Thread count shouldn't be zero"); | |||
if (m_threads.count() > 0) | |||
return false; | |||
//Add minimum threads | |||
m_threads.reserve(m_thread_max); | |||
//AddThreads(m_thread_min); | |||
AdjustThreadCount(m_thread_min); | |||
#endif //LOL_FEATURE_THREADS | |||
return true; | |||
} | |||
//Stop the threads ------------------------------------------------------------ | |||
bool BaseThreadManager::Stop() | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
if (m_threads.count() <= 0) | |||
return false; | |||
//End all threads | |||
//RemoveThreads((int)m_threads.count()); | |||
AdjustThreadCount(0); | |||
CleanAddedThread(true); | |||
CleanRemovedThread(true); | |||
#endif //LOL_FEATURE_THREADS | |||
return true; | |||
} | |||
//Work stuff ------------------------------------------------------------------ | |||
bool BaseThreadManager::AddThreadWork(ThreadJob* job) | |||
{ | |||
return m_jobqueue.try_push(job); | |||
} | |||
//----------------------------------------------------------------------------- | |||
void BaseThreadManager::AdjustThreadCount(int count) | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
int actual_count = (int)m_threads.count() - m_thread_removed; | |||
int diff = count - actual_count; | |||
for (int i = 0; i < lol::abs(diff); i++) | |||
{ | |||
if (diff > 0) | |||
{ | |||
//Spawn worker threads and ... | |||
m_threads << new thread(std::bind(&BaseThreadManager::BaseThreadWork, this, std::placeholders::_1)); | |||
m_thread_added++; | |||
m_thread_added_timer.reset(); | |||
} | |||
else | |||
{ | |||
//Signal worker threads for completion and ... | |||
m_jobqueue.push(new ThreadJob(ThreadJobType::THREAD_STOP)); | |||
m_thread_removed++; | |||
m_thread_removed_timer.reset(); | |||
} | |||
} | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//----------------------------------------------------------------------------- | |||
void BaseThreadManager::CleanAddedThread(bool wait) | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
//... Wait for their readiness. | |||
while (m_thread_added > 0) | |||
{ | |||
thread* inst = nullptr; | |||
bool found = false; | |||
if (wait) | |||
found = !!(inst = m_spawnqueue.pop()); | |||
else | |||
found = m_spawnqueue.try_pop(inst); | |||
if (found) | |||
m_thread_added--; | |||
else | |||
break; | |||
} | |||
//Assert if spawning took too much time | |||
//ASSERT(!(m_thread_added > 0 && m_thread_added_timer.poll() > 5.f)); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//----------------------------------------------------------------------------- | |||
void BaseThreadManager::CleanRemovedThread(bool wait) | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
//... Wait for them to quit. | |||
while (m_thread_removed > 0) | |||
{ | |||
thread* inst = nullptr; | |||
bool found = false; | |||
if (wait) | |||
found = !!(inst = m_donequeue.pop()); | |||
else | |||
found = m_donequeue.try_pop(inst); | |||
if (found) | |||
{ | |||
m_thread_removed--; | |||
m_threads.remove_swap_item(inst); | |||
delete inst; | |||
} | |||
else | |||
break; | |||
} | |||
//Assert if stopping took too much time | |||
//ASSERT(!(m_thread_removed > 0 && m_thread_removed_timer.poll() > 5.f)); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//----------------------------------------------------------------------------- | |||
int BaseThreadManager::GetDispatchCount() | |||
{ | |||
return (int)m_job_dispatch.count(); | |||
} | |||
int BaseThreadManager::GetDispatchedCount() | |||
{ | |||
return m_job_dispatched; | |||
} | |||
//----------------------------------------------------------------------------- | |||
void BaseThreadManager::DispatchJob(ThreadJob* job) | |||
{ | |||
if (job) m_job_dispatch << job; | |||
} | |||
void BaseThreadManager::DispatchJob(array<ThreadJob*> const& jobs) | |||
{ | |||
m_job_dispatch += jobs; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool BaseThreadManager::FetchResult(array<ThreadJob*>& results) | |||
{ | |||
ThreadJob* result; | |||
while (m_resultqueue.try_pop(result)) | |||
results << result; | |||
return results.count() > 0; | |||
} | |||
//Base thread work function --------------------------------------------------- | |||
void BaseThreadManager::BaseThreadWork(thread* inst) | |||
{ | |||
ThreadJob* job = nullptr; | |||
#if LOL_FEATURE_THREADS | |||
//Register that the thread has started | |||
m_statusqueue.push(ThreadStatus::THREAD_STARTED); | |||
m_spawnqueue.push(inst); | |||
for (;;) | |||
#else //LOL_FEATURE_THREADS | |||
while (m_jobqueue.try_pop(job)) | |||
#endif | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
//Retrieve a job | |||
job = m_jobqueue.pop(); | |||
//Stop thread | |||
if (job->GetJobType() == ThreadJobType::THREAD_STOP) | |||
{ | |||
delete job; | |||
break; | |||
} | |||
//Or work | |||
else | |||
#endif //LOL_FEATURE_THREADS | |||
if (*job == ThreadJobType::WORK_TODO) | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
m_statusqueue.push(ThreadStatus::THREAD_WORKING); | |||
#endif //LOL_FEATURE_THREADS | |||
if (job->DoWork()) | |||
job->SetJobType(ThreadJobType::WORK_SUCCEEDED); | |||
else | |||
job->SetJobType(ThreadJobType::WORK_FAILED); | |||
m_resultqueue.push(job); | |||
#if LOL_FEATURE_THREADS | |||
m_statusqueue.push(ThreadStatus::THREAD_IDLE); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
} | |||
#if LOL_FEATURE_THREADS | |||
//Register that the thread has stopped | |||
m_statusqueue.push(ThreadStatus::THREAD_STOPPED); | |||
m_donequeue.push(inst); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//----------------------------------------------------------------------------- | |||
void BaseThreadManager::tick_game(float seconds) | |||
{ | |||
Entity::tick_game(seconds); | |||
//Start if needed | |||
Start(); | |||
//Dispatch work task | |||
while (m_job_dispatch.count() > 0 && AddThreadWork(m_job_dispatch[0])) | |||
{ | |||
m_job_dispatch.remove(0); | |||
//Keep track of added jobs | |||
m_job_dispatched++; | |||
} | |||
//Execute one task per frame if thread are not available | |||
#if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS | |||
BaseThreadWork(nullptr); | |||
#endif // !LOL_FEATURE_THREADS | |||
array<ThreadJob*> result; | |||
//Fetch and treat results | |||
if (FetchResult(result)) | |||
{ | |||
for (int i = 0; i < result.count(); i++) | |||
{ | |||
ThreadJob* job = result[i]; | |||
TreatResult(job); | |||
//Remove job from count as it has been treated | |||
m_job_dispatched--; | |||
} | |||
} | |||
//Resize thread count if needed | |||
#if LOL_FEATURE_THREADS | |||
ThreadStatus status; | |||
while (m_statusqueue.try_pop(status)) | |||
m_thread_active += status == ThreadStatus::THREAD_IDLE ? -1 : +1; | |||
AdjustThreadCount(lol::clamp(m_job_dispatched, m_thread_min, m_thread_max)); | |||
CleanAddedThread(); | |||
CleanRemovedThread(); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
} /* namespace lol */ |
@@ -1,235 +0,0 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net> | |||
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com> | |||
// | |||
// Lol Engine is free software. It comes without any warranty, to | |||
// the extent permitted by applicable law. You can redistribute it | |||
// and/or modify it under the terms of the Do What the Fuck You Want | |||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||
// See http://www.wtfpl.net/ for more details. | |||
// | |||
#include <lol/engine-internal.h> | |||
using namespace lol; | |||
//DefaultThreadManager -------------------------------------------------------- | |||
void DefaultThreadManager::AddJob(ThreadJob* job) | |||
{ | |||
DispatchJob(job); | |||
} | |||
bool DefaultThreadManager::GetWorkResult(array<ThreadJob*>& results) | |||
{ | |||
results += m_job_result; | |||
m_job_result.clear(); | |||
return results.count() > 0; | |||
} | |||
//FileUpdateTesterJob --------------------------------------------------------- | |||
class FileUpdateTesterJob : public ThreadJob | |||
{ | |||
friend class FileUpdateTester; | |||
public: | |||
std::string GetName() const { return "<FileUpdateTesterJob>"; } | |||
FileUpdateTesterJob() | |||
: ThreadJob(ThreadJobType::NONE) | |||
{ | |||
} | |||
FileUpdateTesterJob(std::string const &path) | |||
: ThreadJob(ThreadJobType::WORK_TODO), | |||
m_path(path) | |||
{ | |||
} | |||
std::string const &GetPath() { return m_path; } | |||
long int GetTime() { return m_time; } | |||
bool HasUpdated() { return m_updated; } | |||
void Restart() | |||
{ | |||
SetJobType(ThreadJobType::WORK_TODO); | |||
m_updated = false; | |||
} | |||
protected: | |||
virtual bool DoWork() | |||
{ | |||
array<std::string> pathlist = sys::get_path_list(m_path); | |||
File f; | |||
for (auto const &path : pathlist) | |||
{ | |||
f.Open(path, FileAccess::Read); | |||
if (f.IsValid()) | |||
{ | |||
long int new_time = f.GetModificationTime(); | |||
if (!m_ready) | |||
{ | |||
m_time = new_time; | |||
m_ready = true; | |||
} | |||
else if (new_time > m_time) | |||
{ | |||
m_time = new_time; | |||
m_updated = true; | |||
} | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
//----------------- | |||
bool m_ready = false; | |||
std::string m_path; | |||
long int m_time = 0; | |||
bool m_updated = false; | |||
}; | |||
//FileUpdateTester ------------------------------------------------------------ | |||
FileUpdateTester::~FileUpdateTester() | |||
{ | |||
ASSERT(!m_files.size(), "Files need to be unregistered before destroying FileUpdateTester"); | |||
} | |||
//File interface -------------------------------------------------------------- | |||
FileUpdateTester::Status* FileUpdateTester::RegisterFile(std::string const& path) | |||
{ | |||
DispatchJob(new FileUpdateTesterJob(path)); | |||
m_files[path] = new FileUpdateTester::Status(); | |||
return m_files[path]; | |||
} | |||
void FileUpdateTester::UnregisterFile(std::string const& path) | |||
{ | |||
ASSERT(has_key(m_files, path)); | |||
delete m_files[path]; | |||
m_files.erase(path); | |||
} | |||
void FileUpdateTester::UnregisterFile(FileUpdateTester::Status*& status) | |||
{ | |||
ASSERT(status); | |||
for (auto const &key : keys(m_files)) | |||
{ | |||
if (m_files[key] == status) | |||
{ | |||
UnregisterFile(key); | |||
status = nullptr; | |||
return; | |||
} | |||
} | |||
ASSERT(false, "Status wasn't found"); | |||
} | |||
//----------------------------------------------------------------------------- | |||
void FileUpdateTester::tick_game(float seconds) | |||
{ | |||
// Reset update for this frame | |||
for (auto &kv : m_files) | |||
kv.second->SetUpdated(false); | |||
super::tick_game(seconds); | |||
if (!GetDispatchCount() && m_job_done.count()) | |||
{ | |||
if (m_frame_count++ < m_frame_skip) | |||
return; | |||
m_frame_count = 0; | |||
DispatchJob(m_job_done); | |||
m_job_done.clear(); | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
void FileUpdateTester::TreatResult(ThreadJob* result) | |||
{ | |||
FileUpdateTesterJob* job = static_cast<FileUpdateTesterJob*>(result); | |||
if (job->HasUpdated()) | |||
{ | |||
m_files[job->GetPath()]->SetTime(job->GetTime()); | |||
m_files[job->GetPath()]->SetUpdated(true); | |||
} | |||
job->Restart(); | |||
m_job_done << job; | |||
} | |||
//AsyncImageJob --------------------------------------------------------------- | |||
class AsyncImageJob : public ThreadJob | |||
{ | |||
public: | |||
std::string GetName() const { return "<AsyncImageJob>"; } | |||
AsyncImageJob() | |||
: ThreadJob(ThreadJobType::NONE) | |||
{ | |||
} | |||
AsyncImageJob(std::string const &path) | |||
: ThreadJob(ThreadJobType::WORK_TODO), | |||
m_path(path) | |||
{ | |||
} | |||
std::string const& GetPath() { return m_path; } | |||
Image const& GetImage() { return m_image; } | |||
protected: | |||
virtual bool DoWork() | |||
{ | |||
return m_image.load(m_path); | |||
} | |||
std::string m_path; | |||
Image m_image; | |||
}; | |||
//----------------------------------------------------------------------------- | |||
Image* AsyncImageLoader::Load(std::string const &path) | |||
{ | |||
//Create a job | |||
AsyncImageJob* job = new AsyncImageJob(path); | |||
//Create a dummy image | |||
Image* image = new Image(m_dummy_image); | |||
//Link the two | |||
m_images[path] = image; | |||
//Add job | |||
DispatchJob(job); | |||
//return Dummy image | |||
return image; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool AsyncImageLoader::CheckStatus(Image* image) | |||
{ | |||
if (m_loaded_images.find(image) != INDEX_NONE) | |||
{ | |||
m_loaded_images.remove_item(image); | |||
return true; | |||
} | |||
return false; | |||
} | |||
//----------------------------------------------------------------------------- | |||
void AsyncImageLoader::TreatResult(ThreadJob* result) | |||
{ | |||
AsyncImageJob* job = dynamic_cast<AsyncImageJob*>(result); | |||
ASSERT(job); | |||
//Copy image if work is OK | |||
if (job->GetJobType() == ThreadJobType::WORK_SUCCEEDED) | |||
{ | |||
Image* src = m_images[job->GetPath()]; | |||
m_images.erase(job->GetPath()); | |||
src->Copy(job->GetImage()); | |||
m_loaded_images.push_unique(src); | |||
} | |||
else | |||
{ | |||
msg::error("AsyncImageJob FAILED. See load image error above.\n"); | |||
} | |||
//Delete all that | |||
delete(result); | |||
} |
@@ -1,7 +1,7 @@ | |||
// | |||
// Lol Engine — Unit tests | |||
// | |||
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net> | |||
// Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net> | |||
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com> | |||
// | |||
// Lol Engine is free software. It comes without any warranty, to | |||
@@ -23,130 +23,6 @@ namespace lol | |||
lolunit_declare_fixture(thread_test) | |||
{ | |||
//FileUpdateTesterJob --------------------------------------------------------- | |||
class UnitTestJob : public ThreadJob | |||
{ | |||
friend class UnitTestThreadManager; | |||
public: | |||
std::string GetName() const { return "<UnitTestJob>"; } | |||
UnitTestJob() | |||
: ThreadJob(ThreadJobType::WORK_TODO) | |||
{ } | |||
bool IsDone() | |||
{ | |||
return m_done; | |||
} | |||
protected: | |||
virtual bool DoWork() | |||
{ | |||
timer t; | |||
m_done = false; | |||
msg::info("%s: STARTED WORK\n", GetName().c_str()); | |||
t.wait(2.f); | |||
msg::info("%s: ENDED WORK\n", GetName().c_str()); | |||
m_done = true; | |||
return true; | |||
} | |||
bool m_done = false; | |||
}; | |||
//Unit test thread manager | |||
class UnitTestThreadManager : public BaseThreadManager | |||
{ | |||
typedef BaseThreadManager super; | |||
struct UnitTestStatusBase : public StructSafeEnum | |||
{ | |||
enum Type | |||
{ | |||
NOT_QUEUED, | |||
QUEUED, | |||
RETRIEVED, | |||
DONE, | |||
}; | |||
protected: | |||
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map) | |||
{ | |||
enum_map[NOT_QUEUED] = "NOT_QUEUED"; | |||
enum_map[QUEUED] = "QUEUED"; | |||
enum_map[RETRIEVED] = "RETRIEVED"; | |||
enum_map[DONE] = "DONE"; | |||
return true; | |||
} | |||
}; | |||
typedef SafeEnum<UnitTestStatusBase> UnitTestStatus; | |||
public: | |||
std::string GetName() const { return "<UnitTestThreadManager>"; } | |||
UnitTestThreadManager() : BaseThreadManager(4, 1) | |||
{ } | |||
virtual ~UnitTestThreadManager() | |||
{ } | |||
void AddJob(ThreadJob* job) | |||
{ | |||
msg::info("%s DISPATCHING JOB %s\n", GetName().c_str(), job->GetName().c_str()); | |||
DispatchJob(job); | |||
} | |||
bool GetWorkResult(array<ThreadJob*>& results) | |||
{ | |||
results += m_job_result; | |||
m_job_result.clear(); | |||
msg::info("%s GETWORKRESULT (%i)\n", GetName().c_str(), results.count()); | |||
return results.count() > 0; | |||
} | |||
virtual void tick_game(float seconds) | |||
{ | |||
switch (m_status.ToScalar()) | |||
{ | |||
case UnitTestStatus::NOT_QUEUED: | |||
if (!!GetDispatchCount()) | |||
{ | |||
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str()); | |||
m_status = UnitTestStatus::QUEUED; | |||
} | |||
break; | |||
case UnitTestStatus::QUEUED: | |||
#if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS | |||
if (!GetDispatchedCount()) | |||
#else //LOL_FEATURE_THREADS | |||
if (GetDispatchedCount()) | |||
#endif | |||
{ | |||
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str()); | |||
m_status = UnitTestStatus::RETRIEVED; | |||
} | |||
break; | |||
case UnitTestStatus::RETRIEVED: | |||
if (m_job_result.count() == 4) | |||
{ | |||
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str()); | |||
m_status = UnitTestStatus::DONE; | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
//Debug error fix-up | |||
#if !LOL_BUILD_RELEASE | |||
m_tickstate = STATE_PRETICK_GAME; | |||
#endif | |||
super::tick_game(seconds); | |||
} | |||
bool IsDone() { return m_status == UnitTestStatus::DONE; } | |||
int Test_GetDispatchCount() { return GetDispatchCount(); } | |||
int Test_GetDispatchedCount() { return GetDispatchedCount(); } | |||
protected: | |||
virtual void TreatResult(ThreadJob* result) { m_job_result << result; } | |||
array<ThreadJob*> m_job_result; | |||
UnitTestStatus m_status; | |||
}; | |||
UnitTestThreadManager m_manager; | |||
void setup() | |||
{ | |||
} | |||