| @@ -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; | |||
| } | |||
| } | |||