@@ -41,12 +41,9 @@ nacl_phystest_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/bullet \ | |||
nacl_phystest_DEPENDENCIES = @LOL_DEPS@ | |||
nacl_phystest_LDFLAGS = $(AM_LDFLAGS) | |||
meshviewer_SOURCES = meshviewer.cpp \ | |||
meshviewer_SOURCES = meshviewer.cpp meshviewer.h \ | |||
shinymvtexture.lolfx shinyfur.lolfx \ | |||
scenesetup.cpp scenesetup.h \ | |||
scenesetup-compiler.cpp scenesetup-compiler.h \ | |||
generated/scenesetup-scanner.cpp generated/scenesetup-scanner.h \ | |||
generated/scenesetup-parser.cpp | |||
scenesetup.cpp scenesetup.h | |||
meshviewer_CPPFLAGS = $(AM_CPPFLAGS) | |||
meshviewer_DEPENDENCIES = @LOL_DEPS@ | |||
meshviewer_LDFLAGS = $(AM_LDFLAGS) | |||
@@ -18,6 +18,7 @@ | |||
#include <lol/engine.h> | |||
#include "scenesetup.h" | |||
#include "meshviewer.h" | |||
using namespace lol; | |||
@@ -184,7 +185,7 @@ public: | |||
m_controller = nullptr; | |||
//Scene setup | |||
m_setup_loader.ExecLua("meshviewer_init.lua"); | |||
m_setup_loader.ExecLuaFile("meshviewer_init.lua"); | |||
//Compile ref meshes | |||
m_gizmos << new EasyMesh(); | |||
@@ -91,6 +91,7 @@ | |||
<LolFxCompile Include="shinymvtexture.lolfx" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="meshviewer.h" /> | |||
<ClInclude Include="scenesetup.h" /> | |||
</ItemGroup> | |||
<PropertyGroup Label="Globals"> | |||
@@ -28,7 +28,7 @@ public: | |||
EasyMeshTutorial() | |||
{ | |||
EasyMeshLuaLoader EzMhLoader; | |||
EzMhLoader.ExecLua("05_easymesh.lua"); | |||
EzMhLoader.ExecLuaFile("05_easymesh.lua"); | |||
EasyMeshLuaObject* gears0 = EzMhLoader.GetPtr<EasyMeshLuaObject>("g0"); | |||
EasyMeshLuaObject* gears1 = EzMhLoader.GetPtr<EasyMeshLuaObject>("g1"); | |||
@@ -152,7 +152,7 @@ public: | |||
LoluaDemoLoader* demo_loader = new LoluaDemoLoader(); | |||
//Execute script | |||
demo_loader->ExecLua("14_lol_lua.lua"); | |||
demo_loader->ExecLuaFile("14_lol_lua.lua"); | |||
demo_loader->TestStuff(); | |||
//Grab global test values | |||
@@ -109,6 +109,7 @@ liblolcore_sources = \ | |||
\ | |||
sys/init.cpp sys/timer.cpp sys/file.cpp sys/hacks.cpp \ | |||
sys/thread.cpp sys/threadbase.h \ | |||
sys/threadtypes.cpp \ | |||
\ | |||
image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \ | |||
image/crop.cpp image/resample.cpp image/noise.cpp image/combine.cpp \ | |||
@@ -72,7 +72,7 @@ static void AppCallback() | |||
Application::Application(char const *name, ivec2 resolution, float framerate) | |||
{ | |||
data = new ApplicationData(name, resolution, framerate); | |||
g_world.ExecLua("lua/init.lua"); | |||
g_world.ExecLuaFile("lua/init.lua"); | |||
} | |||
bool Application::MustTick() | |||
@@ -225,6 +225,7 @@ float AxisBinding::RetrieveCurrentValue() | |||
array<Controller*> Controller::controllers; | |||
//----------------------------------------------------------------------------- | |||
Controller::Controller(String const &name, int nb_keys, int nb_axis) | |||
{ | |||
m_gamegroup = GAMEGROUP_BEFORE; | |||
@@ -241,8 +242,15 @@ Controller::Controller(String const &name, int nb_keys, int nb_axis) | |||
controllers.Push(this); | |||
} | |||
Controller::Controller(String const &name, InputProfile const& profile) | |||
: Controller(name, 0, 0) | |||
{ | |||
Init(profile); | |||
} | |||
Controller::~Controller() | |||
{ | |||
ClearProfile(); | |||
for (int i = 0; i < controllers.Count(); ++i) | |||
{ | |||
if (controllers[i] == this) | |||
@@ -253,6 +261,7 @@ Controller::~Controller() | |||
} | |||
} | |||
//----------------------------------------------------------------------------- | |||
Controller* Controller::Get(String const &name) | |||
{ | |||
for (int i = 0; i < controllers.Count(); ++i) | |||
@@ -263,6 +272,102 @@ Controller* Controller::Get(String const &name) | |||
return nullptr; | |||
} | |||
//Input profile system -------------------------------------------------------- | |||
void Controller::UnbindProfile() | |||
{ | |||
if (m_profile.IsEmpty()) | |||
return; | |||
m_mutex.lock(); | |||
//Keyboard | |||
if (m_keyboard) | |||
{ | |||
for (InputProfile::Keyboard& key : m_profile.m_keys) | |||
GetKey(key.m_idx).UnbindKeyboard(key.m_name); | |||
m_keyboard = nullptr; | |||
} | |||
//Mouse | |||
if (m_mouse) | |||
{ | |||
for (InputProfile::MouseKey& key : m_profile.m_mouse_keys) | |||
GetKey(key.m_idx).UnbindMouse(key.m_name); | |||
for (InputProfile::MouseAxis& axis : m_profile.m_mouse_axis) | |||
GetAxis(axis.m_idx).UnbindMouse(axis.m_name); | |||
m_mouse = nullptr; | |||
} | |||
//Joystick | |||
for (InputProfile::JoystickKey& key : m_profile.m_joystick_keys) | |||
{ | |||
if (m_joystick_idx.Find(key.m_joy) != INDEX_NONE) | |||
GetKey(key.m_idx).UnbindJoystick(key.m_joy, key.m_name); | |||
} | |||
for (InputProfile::JoystickAxis& axis : m_profile.m_joystick_axis) | |||
{ | |||
if (m_joystick_idx.Find(axis.m_joy) != INDEX_NONE) | |||
GetAxis(axis.m_idx).UnbindJoystick(axis.m_joy, axis.m_name); | |||
} | |||
m_joystick.Empty(); | |||
m_joystick_idx.Empty(); | |||
m_mutex.unlock(); | |||
} | |||
//Input profile system -------------------------------------------------------- | |||
void Controller::BindProfile(InputProfile const& setup) | |||
{ | |||
ASSERT(!setup.IsEmpty()); | |||
m_mutex.lock(); | |||
m_profile = setup; | |||
m_keys.Resize(m_profile.GetKeyCount()); | |||
m_axis.Resize(m_profile.GetAxisCount()); | |||
//Keyboard | |||
m_keyboard = InputDevice::GetKeyboard(); | |||
if (m_keyboard) | |||
{ | |||
for (InputProfile::Keyboard& key : m_profile.m_keys) | |||
GetKey(key.m_idx).BindKeyboard(key.m_name); | |||
} | |||
//Mouse | |||
m_mouse = InputDevice::GetMouse(); | |||
if (m_mouse) | |||
{ | |||
for (InputProfile::MouseKey& key : m_profile.m_mouse_keys) | |||
GetKey(key.m_idx).BindMouse(key.m_name); | |||
for (InputProfile::MouseAxis& axis : m_profile.m_mouse_axis) | |||
GetAxis(axis.m_idx).BindMouse(axis.m_name); | |||
} | |||
//Joystick | |||
for (uint64_t joy_idx : m_profile.m_joystick) | |||
{ | |||
class InputDevice* joystick = InputDevice::GetJoystick(joy_idx); | |||
if (joystick) | |||
{ | |||
m_joystick << joystick; | |||
m_joystick_idx << joy_idx; | |||
} | |||
} | |||
for (InputProfile::JoystickKey& key : m_profile.m_joystick_keys) | |||
{ | |||
if (m_joystick_idx.Find(key.m_joy) != INDEX_NONE) | |||
GetKey(key.m_idx).BindJoystick(key.m_joy, key.m_name); | |||
} | |||
for (InputProfile::JoystickAxis& axis : m_profile.m_joystick_axis) | |||
{ | |||
if (m_joystick_idx.Find(axis.m_joy) != INDEX_NONE) | |||
GetAxis(axis.m_idx).BindJoystick(axis.m_joy, axis.m_name); | |||
} | |||
m_mutex.unlock(); | |||
} | |||
//----------------------------------------------------------------------------- | |||
void Controller::TickGame(float seconds) | |||
{ | |||
Entity::TickGame(seconds); | |||
@@ -286,6 +391,7 @@ void Controller::TickGame(float seconds) | |||
m_deactivate_nextframe = false; | |||
} | |||
//----------------------------------------------------------------------------- | |||
void Controller::Activate() | |||
{ | |||
m_activate_nextframe = true; | |||
@@ -298,6 +404,7 @@ void Controller::Deactivate() | |||
m_activate_nextframe = false; | |||
} | |||
//----------------------------------------------------------------------------- | |||
array<Controller*> Controller::DeactivateAll() | |||
{ | |||
array<Controller*> result; | |||
@@ -133,11 +133,190 @@ protected: | |||
friend class Controller; | |||
}; | |||
//------------------------------------------------------------------------- | |||
class InputProfile | |||
{ | |||
friend class Controller; | |||
private: | |||
//--------------------------------------------------------------------- | |||
class Key | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
Key() { } | |||
Key(int idx, String const& name) : m_idx(idx), m_name(name) { } | |||
Key(const Key& other) : m_idx(other.m_idx), m_name(other.m_name) { } | |||
~Key() { } | |||
bool operator==(const Key& other) { return m_name == other.m_name; } | |||
private: | |||
int m_idx = 0; | |||
String m_name; | |||
}; | |||
//--------------------------------------------------------------------- | |||
class Joystick | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
Joystick() { } | |||
Joystick(uint64_t joy, int idx, String const& name) : m_joy(joy), m_idx(idx), m_name(name) { } | |||
Joystick(const Joystick& other) : m_joy(other.m_joy), m_idx(other.m_idx), m_name(other.m_name) { } | |||
~Joystick() { } | |||
bool operator==(const Joystick& other) { return m_name == other.m_name; } | |||
private: | |||
uint64_t m_joy = 0; | |||
int m_idx = 0; | |||
String m_name; | |||
}; | |||
public: | |||
//--------------------------------------------------------------------- | |||
class Keyboard : public Key | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
Keyboard() : Key() { } | |||
Keyboard(int idx, String const& name) : Key(idx, name) { } | |||
Keyboard(const Keyboard& other) : Key(other.m_idx, other.m_name) { } | |||
}; | |||
//--------------------------------------------------------------------- | |||
class MouseKey : public Key | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
MouseKey() : Key() { } | |||
MouseKey(int idx, String const& name) : Key(idx, name) { } | |||
MouseKey(const Keyboard& other) : Key(other.m_idx, other.m_name) { } | |||
}; | |||
//--------------------------------------------------------------------- | |||
class MouseAxis : public Key | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
MouseAxis() : Key() { } | |||
MouseAxis(int idx, String const& name) : Key(idx, name) { } | |||
MouseAxis(const Keyboard& other) : Key(other.m_idx, other.m_name) { } | |||
}; | |||
//--------------------------------------------------------------------- | |||
class JoystickKey : public Joystick | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
JoystickKey() : Joystick() { } | |||
JoystickKey(uint64_t joy, int idx, String const& name) : Joystick(joy, idx, name) { } | |||
JoystickKey(const JoystickKey& other) : Joystick(other.m_joy, other.m_idx, other.m_name) { } | |||
}; | |||
//--------------------------------------------------------------------- | |||
class JoystickAxis : public Joystick | |||
{ | |||
friend class Controller; | |||
friend class InputProfile; | |||
public: | |||
JoystickAxis() : Joystick() { } | |||
JoystickAxis(uint64_t joy, int idx, String const& name) : Joystick(joy, idx, name) { } | |||
JoystickAxis(const JoystickAxis& other) : Joystick(other.m_joy, other.m_idx, other.m_name) { } | |||
}; | |||
public: | |||
InputProfile() { } | |||
InputProfile(const InputProfile& other) | |||
{ | |||
m_keys = other.m_keys; | |||
m_mouse_keys = other.m_mouse_keys; | |||
m_mouse_axis = other.m_mouse_axis; | |||
m_joystick = other.m_joystick; | |||
m_joystick_keys = other.m_joystick_keys; | |||
m_joystick_axis = other.m_joystick_axis; | |||
} | |||
virtual ~InputProfile() { } | |||
bool IsEmpty() const | |||
{ | |||
return !(GetKeyCount() && GetAxisCount()); | |||
} | |||
int GetKeyCount() const | |||
{ | |||
return (int)(m_keys.Count() + m_mouse_keys.Count() + m_joystick_keys.Count()); | |||
} | |||
int GetAxisCount() const | |||
{ | |||
return (int)(m_mouse_axis.Count() + m_joystick_axis.Count()); | |||
} | |||
InputProfile& operator<<(InputProfile::Keyboard const& binding) | |||
{ | |||
m_keys.PushUnique(binding); | |||
return *this; | |||
} | |||
InputProfile& operator<<(array<InputProfile::Keyboard> const& bindings) | |||
{ | |||
m_keys += bindings; | |||
return *this; | |||
} | |||
InputProfile& operator<<(InputProfile::MouseKey const& binding) | |||
{ | |||
m_mouse_keys.PushUnique(binding); | |||
return *this; | |||
} | |||
InputProfile& operator<<(array<InputProfile::MouseKey> const& bindings) | |||
{ | |||
m_mouse_keys += bindings; | |||
return *this; | |||
} | |||
InputProfile& operator<<(InputProfile::MouseAxis const& binding) | |||
{ | |||
m_mouse_axis.PushUnique(binding); | |||
return *this; | |||
} | |||
InputProfile& operator<<(array<InputProfile::MouseAxis> const& bindings) | |||
{ | |||
m_mouse_axis += bindings; | |||
return *this; | |||
} | |||
InputProfile& operator<<(InputProfile::JoystickKey const& binding) | |||
{ | |||
m_joystick.PushUnique(binding.m_joy); | |||
m_joystick_keys.PushUnique(binding); | |||
return *this; | |||
} | |||
InputProfile& operator<<(array<InputProfile::JoystickKey> const& bindings) | |||
{ | |||
for (InputProfile::JoystickKey const& binding : bindings) | |||
m_joystick.PushUnique(binding.m_joy); | |||
m_joystick_keys += bindings; | |||
return *this; | |||
} | |||
InputProfile& operator<<(InputProfile::JoystickAxis const& binding) | |||
{ | |||
m_joystick.PushUnique(binding.m_joy); | |||
m_joystick_axis.PushUnique(binding); | |||
return *this; | |||
} | |||
InputProfile& operator<<(array<InputProfile::JoystickAxis> const& bindings) | |||
{ | |||
for (InputProfile::JoystickAxis const& binding : bindings) | |||
m_joystick.PushUnique(binding.m_joy); | |||
m_joystick_axis += bindings; | |||
return *this; | |||
} | |||
private: | |||
array<Keyboard> m_keys; | |||
array<MouseKey> m_mouse_keys; | |||
array<MouseAxis> m_mouse_axis; | |||
array<uint64_t> m_joystick; | |||
array<JoystickKey> m_joystick_keys; | |||
array<JoystickAxis> m_joystick_axis; | |||
}; | |||
//----------------------------------------------------------------------------- | |||
class Controller : public Entity | |||
{ | |||
public: | |||
Controller(String const &name, int nb_keys, int nb_axis); | |||
Controller(String const &name, int nb_keys = 0, int nb_axis = 0); | |||
Controller(String const &name, InputProfile const& setup); | |||
virtual ~Controller(); | |||
virtual void TickGame(float seconds); | |||
@@ -149,21 +328,45 @@ public: | |||
/** Deactivate every active controller on next frame and return an array of deactivated (previously active) controllers */ | |||
static array<Controller*> DeactivateAll(); | |||
/** Input profile system */ | |||
void Init(InputProfile const& profile) | |||
{ | |||
UnbindProfile(); | |||
BindProfile(profile); | |||
} | |||
void ClearProfile() | |||
{ | |||
UnbindProfile(); | |||
} | |||
/** GetKeys/Axis stuff */ | |||
KeyBinding& GetKey(int index) { return m_keys[index]; } | |||
AxisBinding& GetAxis(int index) { return m_axis[index]; } | |||
static Controller* Get(String const &name); | |||
protected: | |||
/** Input profile system */ | |||
void UnbindProfile(); | |||
void BindProfile(InputProfile const& setup); | |||
private: | |||
array<KeyBinding> m_keys; | |||
array<AxisBinding> m_axis; | |||
private: | |||
static array<Controller*> controllers; | |||
String m_name; | |||
bool m_activate_nextframe; | |||
bool m_deactivate_nextframe; | |||
bool m_active; | |||
//Input profile stuff | |||
mutex m_mutex; | |||
class InputProfile m_profile; | |||
class InputDevice* m_keyboard = nullptr; | |||
class InputDevice* m_mouse = nullptr; | |||
array<class InputDevice*> m_joystick; | |||
array<uint64_t> m_joystick_idx; | |||
}; | |||
} /* namespace lol */ | |||
@@ -11,6 +11,7 @@ | |||
#pragma once | |||
#include <lol/sys/thread.h> | |||
#include <lol/sys/threadtypes.h> | |||
#include <lol/sys/init.h> | |||
#include <lol/sys/file.h> | |||
#include <lol/sys/timer.h> | |||
@@ -82,6 +82,7 @@ public: | |||
long int GetPosFromStart(); | |||
void SetPosFromStart(long int pos); | |||
long int GetSize(); | |||
long int GetModificationTime(); | |||
private: | |||
class FileData *m_data; | |||
@@ -38,7 +38,6 @@ public: | |||
queue() : queue_base<T, N>() {} | |||
}; | |||
#if LOL_FEATURE_THREADS | |||
class thread : thread_base | |||
{ | |||
public: | |||
@@ -91,6 +90,7 @@ protected: | |||
}; | |||
typedef SafeEnum<ThreadJobTypeBase> ThreadJobType; | |||
//ThreadJob ------------------------------------------------------------------- | |||
class ThreadJob | |||
{ | |||
friend class BaseThreadManager; | |||
@@ -109,11 +109,12 @@ protected: | |||
ThreadJobType m_type; | |||
}; | |||
//Base class for thread manager | |||
//Base class for thread manager ----------------------------------------------- | |||
class BaseThreadManager : public Entity | |||
{ | |||
public: | |||
BaseThreadManager(int thread_count); | |||
BaseThreadManager(int thread_count, int thread_min); | |||
~BaseThreadManager(); | |||
char const *GetName() { return "<BaseThreadManager>"; } | |||
@@ -122,7 +123,16 @@ public: | |||
bool Start(); | |||
//Stop the threads | |||
bool Stop(); | |||
//Children class intergace | |||
virtual bool AddJob(ThreadJob* job) { ASSERT(false); return false; } | |||
virtual bool GetWorkResult(array<ThreadJob*>& results) { ASSERT(false); return false; } | |||
protected: | |||
//Thread addition | |||
void AddThreads(int nb); | |||
void StopThreads(int nb); | |||
//Work stuff | |||
bool AddWork(ThreadJob* job); | |||
@@ -137,6 +147,7 @@ protected: | |||
/* Worker threads */ | |||
int m_thread_count; | |||
int m_thread_min; | |||
array<thread*> m_threads; | |||
queue<ThreadStatus> m_spawnqueue, m_donequeue; | |||
queue<ThreadJob*> m_jobqueue; | |||
@@ -144,30 +155,5 @@ protected: | |||
array<ThreadJob*> m_job_dispatch; | |||
}; | |||
//Generic class for thread manager, executes work and store results, for you to use | |||
class GenericThreadManager : public BaseThreadManager | |||
{ | |||
public: | |||
GenericThreadManager(int thread_count) | |||
: BaseThreadManager(thread_count) { } | |||
char const *GetName() { return "<GenericThreadManager>"; } | |||
//Work stuff | |||
bool AddJob(ThreadJob* job) { return AddWork(job); } | |||
bool GetWorkResult(array<ThreadJob*>& results) | |||
{ | |||
results += m_job_result; | |||
m_job_result.Empty(); | |||
return results.Count() > 0; | |||
} | |||
protected: | |||
virtual void TreatResult(ThreadJob* result) { m_job_result << result; } | |||
array<ThreadJob*> m_job_result; | |||
}; | |||
#endif | |||
} /* namespace lol */ | |||
@@ -0,0 +1,98 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net> | |||
// © 2013—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com> | |||
// | |||
// This library 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 | |||
// --------------------- | |||
// | |||
namespace lol | |||
{ | |||
//Generic class for thread manager, executes work and store results, with no specific treatment | |||
class DefaultThreadManager : public BaseThreadManager | |||
{ | |||
public: | |||
DefaultThreadManager(int thread_count) | |||
: DefaultThreadManager(thread_count, thread_count) | |||
{ } | |||
DefaultThreadManager(int thread_count, int thread_min) | |||
: DefaultThreadManager(thread_count, thread_min) | |||
{ } | |||
char const *GetName() { return "<DefaultThreadManager>"; } | |||
//Work stuff | |||
bool AddJob(ThreadJob* job); | |||
bool GetWorkResult(array<ThreadJob*>& results); | |||
protected: | |||
virtual void TreatResult(ThreadJob* result) { m_job_result << result; } | |||
array<ThreadJob*> m_job_result; | |||
}; | |||
//FileUpdateTesterJob --------------------------------------------------------- | |||
class FileUpdateTesterJob : public ThreadJob | |||
{ | |||
public: | |||
FileUpdateTesterJob() | |||
: ThreadJob(ThreadJobType::NONE) { } | |||
FileUpdateTesterJob(String path) | |||
: ThreadJob(ThreadJobType::WORK_TODO) | |||
{ m_path = path; } | |||
String& GetPath() { return m_path; } | |||
bool HasUpdated() { return m_updated; } | |||
void Restart() | |||
{ | |||
SetJobType(ThreadJobType::WORK_TODO); | |||
m_updated = false; | |||
} | |||
protected: | |||
virtual bool DoWork(); | |||
//----------------- | |||
bool m_ready = false; | |||
String m_path = String(); | |||
long int m_time = 0; | |||
bool m_updated = false; | |||
}; | |||
//Test the files registered and warns when they update ------------------------ | |||
class FileUpdateTester : public BaseThreadManager | |||
{ | |||
typedef BaseThreadManager super; | |||
public: | |||
struct Status | |||
{ | |||
bool m_updated = false; | |||
}; | |||
public: | |||
FileUpdateTester() : BaseThreadManager(1) { } | |||
~FileUpdateTester() { } | |||
char const *GetName() { return "<FileUpdateTester>"; } | |||
//------------------------------------------------------------------------- | |||
FileUpdateTester::Status* RegisterFile(String const& path); | |||
virtual void TickGame(float seconds); | |||
virtual void TreatResult(ThreadJob* result); | |||
private: | |||
array<ThreadJob*> m_job_done; | |||
map<String, Status*> m_files; | |||
}; | |||
} /* namespace lol */ | |||
@@ -222,6 +222,7 @@ | |||
<ClCompile Include="sys\hacks.cpp" /> | |||
<ClCompile Include="sys\init.cpp" /> | |||
<ClCompile Include="sys\thread.cpp" /> | |||
<ClCompile Include="sys\threadtypes.cpp" /> | |||
<ClCompile Include="sys\timer.cpp" /> | |||
<ClCompile Include="text.cpp" /> | |||
<ClCompile Include="ticker.cpp" /> | |||
@@ -319,6 +320,7 @@ | |||
<ClInclude Include="lol\sys\file.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="lol\unit.h" /> | |||
<ClInclude Include="mesh\mesh.h" /> | |||
@@ -412,6 +412,9 @@ | |||
<ClCompile Include="lolua\baselua.cpp"> | |||
<Filter>lolua</Filter> | |||
</ClCompile> | |||
<ClCompile Include="sys\threadtypes.cpp"> | |||
<Filter>sys</Filter> | |||
</ClCompile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="debug\fps.h"> | |||
@@ -769,6 +772,9 @@ | |||
<ClInclude Include="lolua\baselua.h"> | |||
<Filter>lolua</Filter> | |||
</ClInclude> | |||
<ClInclude Include="lol\sys\threadtypes.h"> | |||
<Filter>lol\sys</Filter> | |||
</ClInclude> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<LolFxCompile Include="gpu\emptymaterial.lolfx"> | |||
@@ -33,6 +33,11 @@ class LuaBaseData | |||
return 0; | |||
} | |||
static int LuaDoCode(LuaState *l, String const& s) | |||
{ | |||
return luaL_dostring(l, s.C()); | |||
} | |||
static int LuaDoFile(LuaState *l) | |||
{ | |||
if (lua_isnoneornil(l, 1)) | |||
@@ -53,7 +58,7 @@ class LuaBaseData | |||
f.Close(); | |||
Log::Debug("loading Lua file %s\n", pathlist[i].C()); | |||
status = luaL_dostring(l, s.C()); | |||
status = LuaDoCode(l, s); | |||
break; | |||
} | |||
} | |||
@@ -94,13 +99,19 @@ Loader::~Loader() | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool Loader::ExecLua(String const &lua) | |||
bool Loader::ExecLuaFile(String const &lua) | |||
{ | |||
const char* c = lua_pushstring(m_lua_state, lua.C()); | |||
int status = LuaBaseData::LuaDoFile(m_lua_state); | |||
return status == 0; | |||
} | |||
//----------------------------------------------------------------------------- | |||
bool Loader::ExecLuaCode(String const &lua) | |||
{ | |||
return 0 == LuaBaseData::LuaDoCode(m_lua_state, lua); | |||
} | |||
//----------------------------------------------------------------------------- | |||
LuaState* Loader::GetLuaState() | |||
{ | |||
@@ -54,9 +54,9 @@ struct ObjectLib | |||
} ClassVarStr; | |||
ObjectLib(String class_name, | |||
array<ClassMethod> statics, | |||
array<ClassMethod> methods, | |||
array<ClassVar> variables) | |||
array<ClassMethod> const& statics, | |||
array<ClassMethod> const& methods, | |||
array<ClassVar> const& variables) | |||
{ | |||
m_class_name = class_name; | |||
m_static_name = class_name + "_lib"; | |||
@@ -74,7 +74,7 @@ struct ObjectLib | |||
|| m_methods.Last().func != nullptr) | |||
m_methods.Push({ nullptr, nullptr }); | |||
for (ClassVar& cv : variables) | |||
for (ClassVar const& cv : variables) | |||
{ | |||
if (cv.name && cv.get && cv.set) | |||
{ | |||
@@ -934,7 +934,8 @@ public: | |||
Loader(); | |||
virtual ~Loader(); | |||
bool ExecLua(String const &lua); | |||
bool ExecLuaFile(String const &lua); | |||
bool ExecLuaCode(String const &lua); | |||
template<typename T> | |||
T GetVar(String const &name) | |||
@@ -192,7 +192,18 @@ class FileData | |||
#endif | |||
} | |||
long int GetModificationTime() | |||
{ | |||
#if __ANDROID__ | |||
return 0; | |||
#elif HAVE_STDIO_H | |||
return m_stat.st_mtime; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
//----------------------- | |||
#if __ANDROID__ | |||
AAsset *m_asset; | |||
#elif HAVE_STDIO_H | |||
@@ -314,6 +325,12 @@ long int File::GetSize() | |||
return m_data->GetSize(); | |||
} | |||
//-- | |||
long int File::GetModificationTime() | |||
{ | |||
return m_data->GetModificationTime(); | |||
} | |||
//--------------- | |||
class DirectoryData | |||
{ | |||
@@ -16,9 +16,16 @@ | |||
namespace lol | |||
{ | |||
#if LOL_FEATURE_THREADS | |||
//BaseThreadManager ----------------------------------------------------------- | |||
BaseThreadManager::BaseThreadManager(int thread_count) | |||
{ | |||
m_thread_min = thread_count; | |||
m_thread_count = thread_count; | |||
} | |||
BaseThreadManager::BaseThreadManager(int thread_min, int thread_count) | |||
{ | |||
m_thread_min = thread_min; | |||
m_thread_count = thread_count; | |||
} | |||
@@ -30,16 +37,12 @@ BaseThreadManager::~BaseThreadManager() | |||
//Initialize, Ticker::Ref and start the thread | |||
bool BaseThreadManager::Start() | |||
{ | |||
if (m_threads.Count() > 0) | |||
if (m_threads.count() > 0) | |||
return false; | |||
/* Spawn worker threads and wait for their readiness. */ | |||
//Add minimum threads | |||
m_threads.Resize(m_thread_count); | |||
for (int i = 0; i < m_thread_count; i++) | |||
m_threads[i] = new thread(std::bind(&BaseThreadManager::BaseThreadWork, | |||
this)); | |||
for (int i = 0; i < m_thread_count; i++) | |||
m_spawnqueue.pop(); | |||
AddThreads(m_thread_min); | |||
return true; | |||
} | |||
@@ -47,18 +50,44 @@ bool BaseThreadManager::Start() | |||
//Stop the threads | |||
bool BaseThreadManager::Stop() | |||
{ | |||
if (m_threads.Count() <= 0) | |||
if (m_threads.count() <= 0) | |||
return false; | |||
/* Signal worker threads for completion and wait for | |||
* them to quit. */ | |||
ThreadJob stop_job(ThreadJobType::THREAD_STOP); | |||
//Stop all threads | |||
StopThreads(m_threads.count()); | |||
return true; | |||
} | |||
//---- | |||
void BaseThreadManager::AddThreads(int nb) | |||
{ | |||
//Don't add threads if not availables | |||
#if LOL_FEATURE_THREADS | |||
//Spawn worker threads and ... | |||
for (int i = 0; i < nb; i++) | |||
m_threads << new thread(std::bind(&BaseThreadManager::BaseThreadWork, this)); | |||
//... Wait for their readiness. | |||
for (int i = 0; i < m_thread_count; i++) | |||
m_spawnqueue.pop(); | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//---- | |||
void BaseThreadManager::StopThreads(int nb) | |||
{ | |||
//Don't stop threads if not availables | |||
#if LOL_FEATURE_THREADS | |||
//Signal worker threads for completion and ... | |||
ThreadJob stop_job(ThreadJobType::THREAD_STOP); | |||
for (int i = 0; i < nb; i++) | |||
m_jobqueue.push(&stop_job); | |||
for (int i = 0; i < m_thread_count; i++) | |||
//... Wait for them to quit. | |||
for (int i = 0; i < nb; i++) | |||
m_donequeue.pop(); | |||
return true; | |||
#endif //LOL_FEATURE_THREADS | |||
} | |||
//Work stuff | |||
@@ -69,23 +98,34 @@ bool BaseThreadManager::AddWork(ThreadJob* job) | |||
return false; | |||
} | |||
//---- | |||
bool BaseThreadManager::FetchResult(array<ThreadJob*>& results) | |||
{ | |||
ThreadJob* result; | |||
while (m_resultqueue.try_pop(result)) | |||
results << result; | |||
return results.Count() > 0; | |||
return results.count() > 0; | |||
} | |||
//Base thread work function | |||
void BaseThreadManager::BaseThreadWork() | |||
{ | |||
#if !LOL_FEATURE_THREADS | |||
//Register that the thread has started | |||
m_spawnqueue.push(ThreadStatus::THREAD_STARTED); | |||
for ( ; ; ) | |||
#endif //!LOL_FEATURE_THREADS | |||
{ | |||
//Try to retrieve a job | |||
ThreadJob* job = m_jobqueue.pop(); | |||
//Stop thread | |||
if (job->GetJobType() == ThreadJobType::THREAD_STOP) | |||
{ | |||
#if !LOL_FEATURE_THREADS | |||
break; | |||
#endif //!LOL_FEATURE_THREADS | |||
} | |||
//Or work | |||
else if (*job == ThreadJobType::WORK_TODO) | |||
{ | |||
if (job->DoWork()) | |||
@@ -95,9 +135,13 @@ void BaseThreadManager::BaseThreadWork() | |||
m_resultqueue.push(job); | |||
} | |||
} | |||
#if !LOL_FEATURE_THREADS | |||
//Register that the thread has stopped | |||
m_donequeue.push(ThreadStatus::THREAD_STOPPED); | |||
#endif //!LOL_FEATURE_THREADS | |||
} | |||
//---- | |||
void BaseThreadManager::TickGame(float seconds) | |||
{ | |||
Entity::TickGame(seconds); | |||
@@ -109,11 +153,16 @@ void BaseThreadManager::TickGame(float seconds) | |||
while (m_job_dispatch.Count() > 0 && AddWork(m_job_dispatch.Last())) | |||
m_job_dispatch.pop(); | |||
//Execute one task per frame if thread are not available | |||
#if !LOL_FEATURE_THREADS | |||
BaseThreadWork(); | |||
#endif // !LOL_FEATURE_THREADS | |||
array<ThreadJob*> result; | |||
//Fetch and treat results | |||
if (FetchResult(result)) | |||
{ | |||
for (int i = 0; i < result.Count(); i++) | |||
for (int i = 0; i < result.count(); i++) | |||
{ | |||
ThreadJob* job = result[i]; | |||
if (job->GetJobType() == ThreadJobType::WORK_DONE) | |||
@@ -123,7 +172,12 @@ void BaseThreadManager::TickGame(float seconds) | |||
} | |||
} | |||
} | |||
//Resize thread count if needed | |||
if (m_threads.count() > m_jobqueue.count() && m_threads.count() > m_thread_min) | |||
StopThreads(m_threads.Count() - m_thread_min); | |||
else if (m_threads.count() < m_jobqueue.count()) | |||
AddThreads(lol::min(m_jobqueue.count(), (ptrdiff_t)m_thread_count) - m_threads.count()); | |||
} | |||
#endif //LOL_FEATURE_THREADS | |||
} /* namespace lol */ |
@@ -114,6 +114,68 @@ public: | |||
#endif | |||
} | |||
ptrdiff_t count() | |||
{ | |||
ptrdiff_t current_count = 0; | |||
#if defined HAVE_PTHREAD_H | |||
pthread_mutex_lock(&m_mutex); | |||
/* If queue is full, wait on the "full" cond var. */ | |||
m_pushers++; | |||
while (m_count == CAPACITY) | |||
pthread_cond_wait(&m_full_cond, &m_mutex); | |||
m_pushers--; | |||
#elif defined _WIN32 | |||
WaitForSingleObject(m_empty_sem, INFINITE); | |||
EnterCriticalSection(&m_mutex); | |||
#endif | |||
current_count = (ptrdiff_t)m_count; | |||
#if defined HAVE_PTHREAD_H | |||
/* If there were poppers waiting, signal the "empty" cond var. */ | |||
if (m_poppers) | |||
pthread_cond_signal(&m_empty_cond); | |||
pthread_mutex_unlock(&m_mutex); | |||
#elif defined _WIN32 | |||
LeaveCriticalSection(&m_mutex); | |||
ReleaseSemaphore(m_full_sem, 1, nullptr); | |||
#endif | |||
return current_count; | |||
} | |||
ptrdiff_t try_count() | |||
{ | |||
ptrdiff_t current_count = 0; | |||
#if defined HAVE_PTHREAD_H | |||
pthread_mutex_lock(&m_mutex); | |||
/* If queue is full, wait on the "full" cond var. */ | |||
if (m_count == CAPACITY) | |||
{ | |||
pthread_mutex_unlock(&m_mutex); | |||
return false; | |||
} | |||
#elif defined _WIN32 | |||
DWORD status = WaitForSingleObject(m_empty_sem, 0); | |||
if (status == WAIT_TIMEOUT) | |||
return 0; | |||
EnterCriticalSection(&m_mutex); | |||
#endif | |||
current_count = (ptrdiff_t)m_count; | |||
#if defined HAVE_PTHREAD_H | |||
/* If there were poppers waiting, signal the "empty" cond var. */ | |||
if (m_poppers) | |||
pthread_cond_signal(&m_empty_cond); | |||
pthread_mutex_unlock(&m_mutex); | |||
#elif defined _WIN32 | |||
LeaveCriticalSection(&m_mutex); | |||
ReleaseSemaphore(m_full_sem, 1, nullptr); | |||
#endif | |||
return current_count; | |||
} | |||
void push(T value) | |||
{ | |||
#if defined HAVE_PTHREAD_H | |||
@@ -0,0 +1,78 @@ | |||
// | |||
// Lol Engine | |||
// | |||
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net> | |||
// © 2014—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com> | |||
// | |||
// This library 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 -------------------------------------------------------- | |||
bool DefaultThreadManager::AddJob(ThreadJob* job) | |||
{ | |||
return AddWork(job); | |||
} | |||
bool DefaultThreadManager::GetWorkResult(array<ThreadJob*>& results) | |||
{ | |||
results += m_job_result; | |||
m_job_result.Empty(); | |||
return results.Count() > 0; | |||
} | |||
//FileUpdateTesterJob --------------------------------------------------------- | |||
bool FileUpdateTesterJob::DoWork() | |||
{ | |||
File f; | |||
f.Open(m_path, FileAccess::Read); | |||
if (!f.IsValid()) | |||
return false; | |||
if (!m_ready) | |||
m_time = f.GetModificationTime(); | |||
else | |||
{ | |||
long int new_time = f.GetModificationTime(); | |||
if (new_time > m_time) | |||
m_updated = true; | |||
} | |||
return true; | |||
} | |||
//FileUpdateTester ------------------------------------------------------------ | |||
FileUpdateTester::Status* FileUpdateTester::RegisterFile(String const& path) | |||
{ | |||
m_job_dispatch << new FileUpdateTesterJob(path); | |||
m_files[path] = new FileUpdateTester::Status(); | |||
return m_files[path]; | |||
} | |||
//------------------------------------------------------------------------- | |||
void FileUpdateTester::TickGame(float seconds) | |||
{ | |||
super::TickGame(seconds); | |||
if (!m_job_dispatch.count() && m_job_done.count()) | |||
{ | |||
m_job_dispatch = m_job_done; | |||
m_job_done.empty(); | |||
} | |||
} | |||
//------------------------------------------------------------------------- | |||
void FileUpdateTester::TreatResult(ThreadJob* result) | |||
{ | |||
FileUpdateTesterJob* job = static_cast<FileUpdateTesterJob*>(result); | |||
if (job->HasUpdated()) | |||
{ | |||
m_files[job->GetPath()]->m_updated = true; | |||
job->Restart(); | |||
m_job_done << job; | |||
} | |||
} |